diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-02-15 05:42:11 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-02-15 05:45:28 +0100 |
commit | 78dc7915f89198b472b9e10ee532f9971bcc0ada (patch) | |
tree | 4e42b1b536d194c2c63c531292bb8adffdfa600e | |
parent | 9914072c16b773a828709ecc61062c37c864e858 (diff) | |
download | libmicrohttpd-78dc7915f89198b472b9e10ee532f9971bcc0ada.tar.gz libmicrohttpd-78dc7915f89198b472b9e10ee532f9971bcc0ada.zip |
migrating main parts of event loops
-rw-r--r-- | src/include/microhttpd2.h | 35 | ||||
-rw-r--r-- | src/lib/Makefile.am | 5 | ||||
-rw-r--r-- | src/lib/daemon_epoll.c | 504 | ||||
-rw-r--r-- | src/lib/daemon_epoll.h | 46 | ||||
-rw-r--r-- | src/lib/daemon_get_fdset.c | 269 | ||||
-rw-r--r-- | src/lib/daemon_get_timeout.c | 74 | ||||
-rw-r--r-- | src/lib/daemon_poll.c | 453 | ||||
-rw-r--r-- | src/lib/daemon_poll.h | 71 | ||||
-rw-r--r-- | src/lib/daemon_run.c | 30 | ||||
-rw-r--r-- | src/lib/daemon_run_from_select.c | 59 | ||||
-rw-r--r-- | src/lib/daemon_select.c | 711 | ||||
-rw-r--r-- | src/lib/daemon_select.h | 43 | ||||
-rw-r--r-- | src/lib/daemon_start.c | 22 | ||||
-rw-r--r-- | src/lib/internal.h | 26 |
14 files changed, 2000 insertions, 348 deletions
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index 2ec39acb..1a0abcd5 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h | |||
@@ -343,6 +343,11 @@ enum MHD_StatusCode | |||
343 | */ | 343 | */ |
344 | MHD_SC_DAEMON_STARTED = 10000, | 344 | MHD_SC_DAEMON_STARTED = 10000, |
345 | 345 | ||
346 | /** | ||
347 | * Informational event, there is no timeout. | ||
348 | */ | ||
349 | MHD_SC_NO_TIMEOUT = 10001, | ||
350 | |||
346 | 351 | ||
347 | /** | 352 | /** |
348 | * MHD does not support the requested combination of | 353 | * MHD does not support the requested combination of |
@@ -563,6 +568,36 @@ enum MHD_StatusCode | |||
563 | */ | 568 | */ |
564 | MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_FDSET = 50037, | 569 | MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_FDSET = 50037, |
565 | 570 | ||
571 | /** | ||
572 | * This daemon was not configured with options that | ||
573 | * would allow us to obtain a meaningful timeout. | ||
574 | */ | ||
575 | MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_TIMEOUT = 50038, | ||
576 | |||
577 | /** | ||
578 | * This daemon was not configured with options that | ||
579 | * would allow us to run with select() data. | ||
580 | */ | ||
581 | MHD_SC_CONFIGURATION_MISSMATCH_FOR_RUN_SELECT = 50039, | ||
582 | |||
583 | /** | ||
584 | * This daemon was not configured to run with an | ||
585 | * external event loop. | ||
586 | */ | ||
587 | MHD_SC_CONFIGURATION_MISSMATCH_FOR_RUN_EXTERNAL = 50040, | ||
588 | |||
589 | /** | ||
590 | * Encountered an unexpected event loop style | ||
591 | * (should never happen). | ||
592 | */ | ||
593 | MHD_SC_CONFIGURATION_UNEXPECTED_ELS = 50041, | ||
594 | |||
595 | /** | ||
596 | * Encountered an unexpected error from select() | ||
597 | * (should never happen). | ||
598 | */ | ||
599 | MHD_SC_CONFIGURATION_UNEXPECTED_SELECT_ERROR = 50042, | ||
600 | |||
566 | }; | 601 | }; |
567 | 602 | ||
568 | 603 | ||
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index b527d7e5..e5ea0d13 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am | |||
@@ -63,12 +63,13 @@ libmicrohttpd_la_SOURCES = \ | |||
63 | daemon_add_connection.c \ | 63 | daemon_add_connection.c \ |
64 | daemon_create.c \ | 64 | daemon_create.c \ |
65 | daemon_destroy.c \ | 65 | daemon_destroy.c \ |
66 | daemon_get_fdset.c \ | 66 | daemon_epoll.c daemon_epoll.h \ |
67 | daemon_get_timeout.c \ | 67 | daemon_get_timeout.c \ |
68 | daemon_info.c \ | 68 | daemon_info.c \ |
69 | daemon_options.c \ | 69 | daemon_options.c \ |
70 | daemon_poll.c daemon_poll.h \ | ||
70 | daemon_run.c \ | 71 | daemon_run.c \ |
71 | daemon_run_from_select.c \ | 72 | daemon_select.c daemon_select.h \ |
72 | daemon_start.c \ | 73 | daemon_start.c \ |
73 | daemon_quiesce.c \ | 74 | daemon_quiesce.c \ |
74 | init.c init.h \ | 75 | init.c init.h \ |
diff --git a/src/lib/daemon_epoll.c b/src/lib/daemon_epoll.c new file mode 100644 index 00000000..2acb3cc6 --- /dev/null +++ b/src/lib/daemon_epoll.c | |||
@@ -0,0 +1,504 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_epoll.c | ||
22 | * @brief functions to run epoll()-based event loop | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "internal.h" | ||
26 | #include "daemon_epoll.h" | ||
27 | |||
28 | #ifdef EPOLL_SUPPORT | ||
29 | |||
30 | /** | ||
31 | * How many events to we process at most per epoll() call? Trade-off | ||
32 | * between required stack-size and number of system calls we have to | ||
33 | * make; 128 should be way enough to avoid more than one system call | ||
34 | * for most scenarios, and still be moderate in stack size | ||
35 | * consumption. Embedded systems might want to choose a smaller value | ||
36 | * --- but why use epoll() on such a system in the first place? | ||
37 | */ | ||
38 | #define MAX_EVENTS 128 | ||
39 | |||
40 | |||
41 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
42 | |||
43 | /** | ||
44 | * Checks whether @a urh has some data to process. | ||
45 | * | ||
46 | * @param urh upgrade handler to analyse | ||
47 | * @return 'true' if @a urh has some data to process, | ||
48 | * 'false' otherwise | ||
49 | */ | ||
50 | static bool | ||
51 | is_urh_ready (struct MHD_UpgradeResponseHandle * const urh) | ||
52 | { | ||
53 | const struct MHD_Connection * const connection = urh->connection; | ||
54 | |||
55 | if ( (0 == urh->in_buffer_size) && | ||
56 | (0 == urh->out_buffer_size) && | ||
57 | (0 == urh->in_buffer_used) && | ||
58 | (0 == urh->out_buffer_used) ) | ||
59 | return false; | ||
60 | |||
61 | if (connection->daemon->shutdown) | ||
62 | return true; | ||
63 | |||
64 | if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) || | ||
65 | (connection->tls_read_ready) ) && | ||
66 | (urh->in_buffer_used < urh->in_buffer_size) ) | ||
67 | return true; | ||
68 | |||
69 | if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && | ||
70 | (urh->out_buffer_used < urh->out_buffer_size) ) | ||
71 | return true; | ||
72 | |||
73 | if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && | ||
74 | (urh->out_buffer_used > 0) ) | ||
75 | return true; | ||
76 | |||
77 | if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && | ||
78 | (urh->in_buffer_used > 0) ) | ||
79 | return true; | ||
80 | |||
81 | return false; | ||
82 | } | ||
83 | |||
84 | |||
85 | /** | ||
86 | * Do epoll()-based processing for TLS connections that have been | ||
87 | * upgraded. This requires a separate epoll() invocation as we | ||
88 | * cannot use the `struct MHD_Connection` data structures for | ||
89 | * the `union epoll_data` in this case. | ||
90 | * @remark To be called only from thread that process | ||
91 | * daemon's select()/poll()/etc. | ||
92 | * | ||
93 | * @param daemon the daemmon for which we process connections | ||
94 | * @return #MHD_SC_OK on success | ||
95 | */ | ||
96 | static enum MHD_StatusCode | ||
97 | run_epoll_for_upgrade (struct MHD_Daemon *daemon) | ||
98 | { | ||
99 | struct epoll_event events[MAX_EVENTS]; | ||
100 | int num_events; | ||
101 | struct MHD_UpgradeResponseHandle * pos; | ||
102 | struct MHD_UpgradeResponseHandle * prev; | ||
103 | |||
104 | num_events = MAX_EVENTS; | ||
105 | while (MAX_EVENTS == num_events) | ||
106 | { | ||
107 | unsigned int i; | ||
108 | |||
109 | /* update event masks */ | ||
110 | num_events = epoll_wait (daemon->epoll_upgrade_fd, | ||
111 | events, | ||
112 | MAX_EVENTS, | ||
113 | 0); | ||
114 | if (-1 == num_events) | ||
115 | { | ||
116 | const int err = MHD_socket_get_error_ (); | ||
117 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
118 | return MHD_SC_OK; | ||
119 | #ifdef HAVE_MESSAGES | ||
120 | MHD_DLOG (daemon, | ||
121 | MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR, | ||
122 | _("Call to epoll_wait failed: %s\n"), | ||
123 | MHD_socket_strerr_ (err)); | ||
124 | #endif | ||
125 | return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR; | ||
126 | } | ||
127 | for (i = 0; i < (unsigned int) num_events; i++) | ||
128 | { | ||
129 | struct UpgradeEpollHandle * const ueh = events[i].data.ptr; | ||
130 | struct MHD_UpgradeResponseHandle * const urh = ueh->urh; | ||
131 | bool new_err_state = false; | ||
132 | |||
133 | if (urh->clean_ready) | ||
134 | continue; | ||
135 | |||
136 | /* Update ueh state based on what is ready according to epoll() */ | ||
137 | if (0 != (events[i].events & EPOLLIN)) | ||
138 | ueh->celi |= MHD_EPOLL_STATE_READ_READY; | ||
139 | if (0 != (events[i].events & EPOLLOUT)) | ||
140 | ueh->celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
141 | if (0 != (events[i].events & EPOLLHUP)) | ||
142 | ueh->celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; | ||
143 | |||
144 | if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) && | ||
145 | (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) ) | ||
146 | { | ||
147 | /* Process new error state only one time | ||
148 | * and avoid continuously marking this connection | ||
149 | * as 'ready'. */ | ||
150 | ueh->celi |= MHD_EPOLL_STATE_ERROR; | ||
151 | new_err_state = true; | ||
152 | } | ||
153 | |||
154 | if (! urh->in_eready_list) | ||
155 | { | ||
156 | if (new_err_state || | ||
157 | is_urh_ready(urh)) | ||
158 | { | ||
159 | EDLL_insert (daemon->eready_urh_head, | ||
160 | daemon->eready_urh_tail, | ||
161 | urh); | ||
162 | urh->in_eready_list = true; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | prev = daemon->eready_urh_tail; | ||
168 | while (NULL != (pos = prev)) | ||
169 | { | ||
170 | prev = pos->prevE; | ||
171 | process_urh (pos); | ||
172 | if (! is_urh_ready(pos)) | ||
173 | { | ||
174 | EDLL_remove (daemon->eready_urh_head, | ||
175 | daemon->eready_urh_tail, | ||
176 | pos); | ||
177 | pos->in_eready_list = false; | ||
178 | } | ||
179 | /* Finished forwarding? */ | ||
180 | if ( (0 == pos->in_buffer_size) && | ||
181 | (0 == pos->out_buffer_size) && | ||
182 | (0 == pos->in_buffer_used) && | ||
183 | (0 == pos->out_buffer_used) ) | ||
184 | { | ||
185 | MHD_connection_finish_forward_ (pos->connection); | ||
186 | pos->clean_ready = true; | ||
187 | /* If 'pos->was_closed' already was set to true, connection | ||
188 | * will be moved immediately to cleanup list. Otherwise | ||
189 | * connection will stay in suspended list until 'pos' will | ||
190 | * be marked with 'was_closed' by application. */ | ||
191 | MHD_resume_connection (pos->connection); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return MHD_SC_OK; | ||
196 | } | ||
197 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
198 | |||
199 | |||
200 | /** | ||
201 | * Do epoll()-based processing (this function is allowed to | ||
202 | * block if @a may_block is set to #MHD_YES). | ||
203 | * | ||
204 | * @param daemon daemon to run poll loop for | ||
205 | * @param may_block true if blocking, false if non-blocking | ||
206 | * @return #MHD_SC_OK on success | ||
207 | */ | ||
208 | enum MHD_StatusCode | ||
209 | MHD_daemon_epoll_ (struct MHD_Daemon *daemon, | ||
210 | bool may_block) | ||
211 | { | ||
212 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
213 | static const char * const upgrade_marker = "upgrade_ptr"; | ||
214 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
215 | struct MHD_Connection *pos; | ||
216 | struct MHD_Connection *prev; | ||
217 | struct epoll_event events[MAX_EVENTS]; | ||
218 | struct epoll_event event; | ||
219 | int timeout_ms; | ||
220 | MHD_UNSIGNED_LONG_LONG timeout_ll; | ||
221 | int num_events; | ||
222 | unsigned int i; | ||
223 | MHD_socket ls; | ||
224 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
225 | bool run_upgraded = false; | ||
226 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
227 | |||
228 | if (-1 == daemon->epoll_fd) | ||
229 | return MHD_SC_EPOLL_FD_INVALID; /* we're down! */ | ||
230 | if (daemon->shutdown) | ||
231 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
232 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
233 | (! daemon->was_quiesced) && | ||
234 | (daemon->connections < daemon->global_connection_limit) && | ||
235 | (! daemon->listen_socket_in_epoll) && | ||
236 | (! daemon->at_limit) ) | ||
237 | { | ||
238 | event.events = EPOLLIN; | ||
239 | event.data.ptr = daemon; | ||
240 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
241 | EPOLL_CTL_ADD, | ||
242 | ls, | ||
243 | &event)) | ||
244 | { | ||
245 | #ifdef HAVE_MESSAGES | ||
246 | MHD_DLOG (daemon, | ||
247 | MHD_SC_EPOLL_CTL_ADD_FAILED, | ||
248 | _("Call to epoll_ctl failed: %s\n"), | ||
249 | MHD_socket_last_strerr_ ()); | ||
250 | #endif | ||
251 | return MHD_SC_EPOLL_CTL_ADD_FAILED; | ||
252 | } | ||
253 | daemon->listen_socket_in_epoll = true; | ||
254 | } | ||
255 | if ( (daemon->was_quiesced) && | ||
256 | (daemon->listen_socket_in_epoll) ) | ||
257 | { | ||
258 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
259 | EPOLL_CTL_DEL, | ||
260 | ls, | ||
261 | NULL)) | ||
262 | MHD_PANIC ("Failed to remove listen FD from epoll set\n"); | ||
263 | daemon->listen_socket_in_epoll = false; | ||
264 | } | ||
265 | |||
266 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
267 | if ( (! daemon->upgrade_fd_in_epoll) && | ||
268 | (-1 != daemon->epoll_upgrade_fd) ) | ||
269 | { | ||
270 | event.events = EPOLLIN | EPOLLOUT; | ||
271 | event.data.ptr = (void *) upgrade_marker; | ||
272 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
273 | EPOLL_CTL_ADD, | ||
274 | daemon->epoll_upgrade_fd, | ||
275 | &event)) | ||
276 | { | ||
277 | #ifdef HAVE_MESSAGES | ||
278 | MHD_DLOG (daemon, | ||
279 | MHD_SC_EPOLL_CTL_ADD_FAILED, | ||
280 | _("Call to epoll_ctl failed: %s\n"), | ||
281 | MHD_socket_last_strerr_ ()); | ||
282 | #endif | ||
283 | return MHD_SC_EPOLL_CTL_ADD_FAILED; | ||
284 | } | ||
285 | daemon->upgrade_fd_in_epoll = true; | ||
286 | } | ||
287 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
288 | if ( (daemon->listen_socket_in_epoll) && | ||
289 | ( (daemon->connections == daemon->global_connection_limit) || | ||
290 | (daemon->at_limit) || | ||
291 | (daemon->was_quiesced) ) ) | ||
292 | { | ||
293 | /* we're at the connection limit, disable listen socket | ||
294 | for event loop for now */ | ||
295 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
296 | EPOLL_CTL_DEL, | ||
297 | ls, | ||
298 | NULL)) | ||
299 | MHD_PANIC (_("Failed to remove listen FD from epoll set\n")); | ||
300 | daemon->listen_socket_in_epoll = false; | ||
301 | } | ||
302 | |||
303 | if ( (! daemon->disallow_suspend_resume) && | ||
304 | (MHD_YES == resume_suspended_connections (daemon)) ) | ||
305 | may_block = false; | ||
306 | |||
307 | if (may_block) | ||
308 | { | ||
309 | if (MHD_SC_OK == /* FIXME: distinguish between NO_TIMEOUT and errors */ | ||
310 | MHD_daemon_get_timeout (daemon, | ||
311 | &timeout_ll)) | ||
312 | { | ||
313 | if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX) | ||
314 | timeout_ms = INT_MAX; | ||
315 | else | ||
316 | timeout_ms = (int) timeout_ll; | ||
317 | } | ||
318 | else | ||
319 | timeout_ms = -1; | ||
320 | } | ||
321 | else | ||
322 | timeout_ms = 0; | ||
323 | |||
324 | /* Reset. New value will be set when connections are processed. */ | ||
325 | /* Note: Used mostly for uniformity here as same situation is | ||
326 | * signaled in epoll mode by non-empty eready DLL. */ | ||
327 | daemon->data_already_pending = false; | ||
328 | |||
329 | /* drain 'epoll' event queue; need to iterate as we get at most | ||
330 | MAX_EVENTS in one system call here; in practice this should | ||
331 | pretty much mean only one round, but better an extra loop here | ||
332 | than unfair behavior... */ | ||
333 | num_events = MAX_EVENTS; | ||
334 | while (MAX_EVENTS == num_events) | ||
335 | { | ||
336 | /* update event masks */ | ||
337 | num_events = epoll_wait (daemon->epoll_fd, | ||
338 | events, | ||
339 | MAX_EVENTS, | ||
340 | timeout_ms); | ||
341 | if (-1 == num_events) | ||
342 | { | ||
343 | const int err = MHD_socket_get_error_ (); | ||
344 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
345 | return MHD_SC_OK; | ||
346 | #ifdef HAVE_MESSAGES | ||
347 | MHD_DLOG (daemon, | ||
348 | MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR, | ||
349 | _("Call to epoll_wait failed: %s\n"), | ||
350 | MHD_socket_strerr_ (err)); | ||
351 | #endif | ||
352 | return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR; | ||
353 | } | ||
354 | for (i=0;i<(unsigned int) num_events;i++) | ||
355 | { | ||
356 | /* First, check for the values of `ptr` that would indicate | ||
357 | that this event is not about a normal connection. */ | ||
358 | if (NULL == events[i].data.ptr) | ||
359 | continue; /* shutdown signal! */ | ||
360 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
361 | if (upgrade_marker == events[i].data.ptr) | ||
362 | { | ||
363 | /* activity on an upgraded connection, we process | ||
364 | those in a separate epoll() */ | ||
365 | run_upgraded = true; | ||
366 | continue; | ||
367 | } | ||
368 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
369 | if (daemon->epoll_itc_marker == events[i].data.ptr) | ||
370 | { | ||
371 | /* It's OK to clear ITC here as all external | ||
372 | conditions will be processed later. */ | ||
373 | MHD_itc_clear_ (daemon->itc); | ||
374 | continue; | ||
375 | } | ||
376 | if (daemon == events[i].data.ptr) | ||
377 | { | ||
378 | /* Check for error conditions on listen socket. */ | ||
379 | /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */ | ||
380 | if (0 == (events[i].events & (EPOLLERR | EPOLLHUP))) | ||
381 | { | ||
382 | unsigned int series_length = 0; | ||
383 | /* Run 'accept' until it fails or daemon at limit of connections. | ||
384 | * Do not accept more then 10 connections at once. The rest will | ||
385 | * be accepted on next turn (level trigger is used for listen | ||
386 | * socket). */ | ||
387 | while ( (MHD_YES == | ||
388 | MHD_accept_connection (daemon)) && | ||
389 | (series_length < 10) && | ||
390 | (daemon->connections < daemon->global_connection_limit) && | ||
391 | (! daemon->at_limit) ) | ||
392 | series_length++; | ||
393 | } | ||
394 | continue; | ||
395 | } | ||
396 | /* this is an event relating to a 'normal' connection, | ||
397 | remember the event and if appropriate mark the | ||
398 | connection as 'eready'. */ | ||
399 | pos = events[i].data.ptr; | ||
400 | /* normal processing: update read/write data */ | ||
401 | if (0 != (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP))) | ||
402 | { | ||
403 | pos->epoll_state |= MHD_EPOLL_STATE_ERROR; | ||
404 | if (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) | ||
405 | { | ||
406 | EDLL_insert (daemon->eready_head, | ||
407 | daemon->eready_tail, | ||
408 | pos); | ||
409 | pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; | ||
410 | } | ||
411 | } | ||
412 | else | ||
413 | { | ||
414 | if (0 != (events[i].events & EPOLLIN)) | ||
415 | { | ||
416 | pos->epoll_state |= MHD_EPOLL_STATE_READ_READY; | ||
417 | if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->request.event_loop_info) || | ||
418 | (pos->request.read_buffer_size > pos->request.read_buffer_offset) ) && | ||
419 | (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) ) | ||
420 | { | ||
421 | EDLL_insert (daemon->eready_head, | ||
422 | daemon->eready_tail, | ||
423 | pos); | ||
424 | pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; | ||
425 | } | ||
426 | } | ||
427 | if (0 != (events[i].events & EPOLLOUT)) | ||
428 | { | ||
429 | pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY; | ||
430 | if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->request.event_loop_info) && | ||
431 | (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) ) | ||
432 | { | ||
433 | EDLL_insert (daemon->eready_head, | ||
434 | daemon->eready_tail, | ||
435 | pos); | ||
436 | pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | |||
443 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
444 | if (run_upgraded) | ||
445 | run_epoll_for_upgrade (daemon); /* FIXME: return value? */ | ||
446 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
447 | |||
448 | /* process events for connections */ | ||
449 | prev = daemon->eready_tail; | ||
450 | while (NULL != (pos = prev)) | ||
451 | { | ||
452 | prev = pos->prevE; | ||
453 | call_handlers (pos, | ||
454 | 0 != (pos->epoll_state & MHD_EPOLL_STATE_READ_READY), | ||
455 | 0 != (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY), | ||
456 | 0 != (pos->epoll_state & MHD_EPOLL_STATE_ERROR)); | ||
457 | if (MHD_EPOLL_STATE_IN_EREADY_EDLL == | ||
458 | (pos->epoll_state & (MHD_EPOLL_STATE_SUSPENDED | MHD_EPOLL_STATE_IN_EREADY_EDLL))) | ||
459 | { | ||
460 | if ( (MHD_EVENT_LOOP_INFO_READ == pos->request.event_loop_info && | ||
461 | 0 == (pos->epoll_state & MHD_EPOLL_STATE_READ_READY) ) || | ||
462 | (MHD_EVENT_LOOP_INFO_WRITE == pos->request.event_loop_info && | ||
463 | 0 == (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY) ) || | ||
464 | MHD_EVENT_LOOP_INFO_CLEANUP == pos->request.event_loop_info) | ||
465 | { | ||
466 | EDLL_remove (daemon->eready_head, | ||
467 | daemon->eready_tail, | ||
468 | pos); | ||
469 | pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL; | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | |||
474 | /* Finally, handle timed-out connections; we need to do this here | ||
475 | as the epoll mechanism won't call the 'MHD_connection_handle_idle()' on everything, | ||
476 | as the other event loops do. As timeouts do not get an explicit | ||
477 | event, we need to find those connections that might have timed out | ||
478 | here. | ||
479 | |||
480 | Connections with custom timeouts must all be looked at, as we | ||
481 | do not bother to sort that (presumably very short) list. */ | ||
482 | prev = daemon->manual_timeout_tail; | ||
483 | while (NULL != (pos = prev)) | ||
484 | { | ||
485 | prev = pos->prevX; | ||
486 | MHD_connection_handle_idle (pos); | ||
487 | } | ||
488 | /* Connections with the default timeout are sorted by prepending | ||
489 | them to the head of the list whenever we touch the connection; | ||
490 | thus it suffices to iterate from the tail until the first | ||
491 | connection is NOT timed out */ | ||
492 | prev = daemon->normal_timeout_tail; | ||
493 | while (NULL != (pos = prev)) | ||
494 | { | ||
495 | prev = pos->prevX; | ||
496 | MHD_connection_handle_idle (pos); | ||
497 | if (MHD_REQUEST_CLOSED != pos->request.state) | ||
498 | break; /* sorted by timeout, no need to visit the rest! */ | ||
499 | } | ||
500 | return MHD_SC_OK; | ||
501 | } | ||
502 | #endif | ||
503 | |||
504 | /* end of daemon_epoll.c */ | ||
diff --git a/src/lib/daemon_epoll.h b/src/lib/daemon_epoll.h new file mode 100644 index 00000000..50452d8d --- /dev/null +++ b/src/lib/daemon_epoll.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_epoll.h | ||
22 | * @brief non-public functions provided by daemon_epoll.c | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | |||
26 | #ifndef DAEMON_EPOLL_H | ||
27 | #define DAEMON_EPOLL_H | ||
28 | |||
29 | #ifdef EPOLL_SUPPORT | ||
30 | |||
31 | /** | ||
32 | * Do epoll()-based processing (this function is allowed to | ||
33 | * block if @a may_block is set to #MHD_YES). | ||
34 | * | ||
35 | * @param daemon daemon to run poll loop for | ||
36 | * @param may_block true if blocking, false if non-blocking | ||
37 | * @return #MHD_SC_OK on success | ||
38 | */ | ||
39 | enum MHD_StatusCode | ||
40 | MHD_daemon_epoll_ (struct MHD_Daemon *daemon, | ||
41 | bool may_block) | ||
42 | MHD_NONNULL (1); | ||
43 | |||
44 | #endif | ||
45 | |||
46 | #endif | ||
diff --git a/src/lib/daemon_get_fdset.c b/src/lib/daemon_get_fdset.c deleted file mode 100644 index c3b0dbd9..00000000 --- a/src/lib/daemon_get_fdset.c +++ /dev/null | |||
@@ -1,269 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_get_fdset.c | ||
22 | * @brief function to get select() fdset of a daemon | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "internal.h" | ||
26 | |||
27 | /** | ||
28 | * We defined a macro with the same name as a function we | ||
29 | * are implementing here. Need to undef the macro to avoid | ||
30 | * causing a conflict. | ||
31 | */ | ||
32 | #undef MHD_daemon_get_fdset | ||
33 | |||
34 | /** | ||
35 | * Obtain the `select()` sets for this daemon. Daemon's FDs will be | ||
36 | * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO | ||
37 | * for each fd_set before calling this function. FD_SETSIZE is assumed | ||
38 | * to be platform's default. | ||
39 | * | ||
40 | * This function should only be called in when MHD is configured to | ||
41 | * use external select with 'select()' or with 'epoll'. In the latter | ||
42 | * case, it will only add the single 'epoll()' file descriptor used by | ||
43 | * MHD to the sets. It's necessary to use #MHD_get_timeout() in | ||
44 | * combination with this function. | ||
45 | * | ||
46 | * This function must be called only for daemon started without | ||
47 | * #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
48 | * | ||
49 | * @param daemon daemon to get sets from | ||
50 | * @param read_fd_set read set | ||
51 | * @param write_fd_set write set | ||
52 | * @param except_fd_set except set | ||
53 | * @param max_fd increased to largest FD added (if larger | ||
54 | * than existing value); can be NULL | ||
55 | * @return #MHD_SC_OK on success, otherwise error code | ||
56 | * @ingroup event | ||
57 | */ | ||
58 | enum MHD_StatusCode | ||
59 | MHD_daemon_get_fdset (struct MHD_Daemon *daemon, | ||
60 | fd_set *read_fd_set, | ||
61 | fd_set *write_fd_set, | ||
62 | fd_set *except_fd_set, | ||
63 | MHD_socket *max_fd) | ||
64 | { | ||
65 | return MHD_daemon_get_fdset2 (daemon, | ||
66 | read_fd_set, | ||
67 | write_fd_set, | ||
68 | except_fd_set, | ||
69 | max_fd, | ||
70 | _MHD_SYS_DEFAULT_FD_SETSIZE); | ||
71 | } | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Internal version of #MHD_daemon_get_fdset2(). | ||
76 | * | ||
77 | * @param daemon daemon to get sets from | ||
78 | * @param read_fd_set read set | ||
79 | * @param write_fd_set write set | ||
80 | * @param except_fd_set except set | ||
81 | * @param max_fd increased to largest FD added (if larger | ||
82 | * than existing value); can be NULL | ||
83 | * @param fd_setsize value of FD_SETSIZE | ||
84 | * @return #MHD_SC_OK on success | ||
85 | * @ingroup event | ||
86 | */ | ||
87 | static enum MHD_StatusCode | ||
88 | internal_get_fdset2 (struct MHD_Daemon *daemon, | ||
89 | fd_set *read_fd_set, | ||
90 | fd_set *write_fd_set, | ||
91 | fd_set *except_fd_set, | ||
92 | MHD_socket *max_fd, | ||
93 | unsigned int fd_setsize) | ||
94 | |||
95 | { | ||
96 | struct MHD_Connection *pos; | ||
97 | struct MHD_Connection *posn; | ||
98 | int result = MHD_YES; | ||
99 | MHD_socket ls; | ||
100 | |||
101 | if (daemon->shutdown) | ||
102 | return MHD_NO; | ||
103 | |||
104 | ls = daemon->listen_socket; | ||
105 | if ( (MHD_INVALID_SOCKET != ls) && | ||
106 | (! daemon->was_quiesced) && | ||
107 | (! MHD_add_to_fd_set_ (ls, | ||
108 | read_fd_set, | ||
109 | max_fd, | ||
110 | fd_setsize)) ) | ||
111 | result = MHD_NO; | ||
112 | |||
113 | /* Add all sockets to 'except_fd_set' as well to watch for | ||
114 | * out-of-band data. However, ignore errors if INFO_READ | ||
115 | * or INFO_WRITE sockets will not fit 'except_fd_set'. */ | ||
116 | /* Start from oldest connections. Make sense for W32 FDSETs. */ | ||
117 | for (pos = daemon->connections_tail; NULL != pos; pos = posn) | ||
118 | { | ||
119 | posn = pos->prev; | ||
120 | |||
121 | switch (pos->request.event_loop_info) | ||
122 | { | ||
123 | case MHD_EVENT_LOOP_INFO_READ: | ||
124 | if (! MHD_add_to_fd_set_ (pos->socket_fd, | ||
125 | read_fd_set, | ||
126 | max_fd, | ||
127 | fd_setsize)) | ||
128 | result = MHD_NO; | ||
129 | #ifdef MHD_POSIX_SOCKETS | ||
130 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
131 | except_fd_set, | ||
132 | max_fd, | ||
133 | fd_setsize); | ||
134 | #endif /* MHD_POSIX_SOCKETS */ | ||
135 | break; | ||
136 | case MHD_EVENT_LOOP_INFO_WRITE: | ||
137 | if (! MHD_add_to_fd_set_ (pos->socket_fd, | ||
138 | write_fd_set, | ||
139 | max_fd, | ||
140 | fd_setsize)) | ||
141 | result = MHD_NO; | ||
142 | #ifdef MHD_POSIX_SOCKETS | ||
143 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
144 | except_fd_set, | ||
145 | max_fd, | ||
146 | fd_setsize); | ||
147 | #endif /* MHD_POSIX_SOCKETS */ | ||
148 | break; | ||
149 | case MHD_EVENT_LOOP_INFO_BLOCK: | ||
150 | if ( (NULL == except_fd_set) || | ||
151 | ! MHD_add_to_fd_set_ (pos->socket_fd, | ||
152 | except_fd_set, | ||
153 | max_fd, | ||
154 | fd_setsize)) | ||
155 | result = MHD_NO; | ||
156 | break; | ||
157 | case MHD_EVENT_LOOP_INFO_CLEANUP: | ||
158 | /* this should never happen */ | ||
159 | break; | ||
160 | } | ||
161 | } | ||
162 | #ifdef MHD_WINSOCK_SOCKETS | ||
163 | /* W32 use limited array for fd_set so add INFO_READ/INFO_WRITE sockets | ||
164 | * only after INFO_BLOCK sockets to ensure that INFO_BLOCK sockets will | ||
165 | * not be pushed out. */ | ||
166 | for (pos = daemon->connections_tail; NULL != pos; pos = posn) | ||
167 | { | ||
168 | posn = pos->prev; | ||
169 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
170 | except_fd_set, | ||
171 | max_fd, | ||
172 | fd_setsize); | ||
173 | } | ||
174 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
175 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
176 | { | ||
177 | struct MHD_UpgradeResponseHandle *urh; | ||
178 | |||
179 | for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) | ||
180 | { | ||
181 | if (MHD_NO == | ||
182 | urh_to_fdset (urh, | ||
183 | read_fd_set, | ||
184 | write_fd_set, | ||
185 | except_fd_set, | ||
186 | max_fd, | ||
187 | fd_setsize)) | ||
188 | result = MHD_NO; | ||
189 | } | ||
190 | } | ||
191 | #endif | ||
192 | #if DEBUG_CONNECT | ||
193 | #ifdef HAVE_MESSAGES | ||
194 | if (NULL != max_fd) | ||
195 | MHD_DLOG (daemon, | ||
196 | _("Maximum socket in select set: %d\n"), | ||
197 | *max_fd); | ||
198 | #endif | ||
199 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
200 | return result; | ||
201 | } | ||
202 | |||
203 | |||
204 | /** | ||
205 | * Obtain the `select()` sets for this daemon. Daemon's FDs will be | ||
206 | * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO | ||
207 | * for each fd_set before calling this function. | ||
208 | * | ||
209 | * Passing custom FD_SETSIZE as @a fd_setsize allow usage of | ||
210 | * larger/smaller than platform's default fd_sets. | ||
211 | * | ||
212 | * This function should only be called in when MHD is configured to | ||
213 | * use external select with 'select()' or with 'epoll'. In the latter | ||
214 | * case, it will only add the single 'epoll' file descriptor used by | ||
215 | * MHD to the sets. It's necessary to use #MHD_get_timeout() in | ||
216 | * combination with this function. | ||
217 | * | ||
218 | * This function must be called only for daemon started | ||
219 | * without #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
220 | * | ||
221 | * @param daemon daemon to get sets from | ||
222 | * @param read_fd_set read set | ||
223 | * @param write_fd_set write set | ||
224 | * @param except_fd_set except set | ||
225 | * @param max_fd increased to largest FD added (if larger | ||
226 | * than existing value); can be NULL | ||
227 | * @param fd_setsize value of FD_SETSIZE | ||
228 | * @return #MHD_SC_OK on success, otherwise error code | ||
229 | * @ingroup event | ||
230 | */ | ||
231 | enum MHD_StatusCode | ||
232 | MHD_daemon_get_fdset2 (struct MHD_Daemon *daemon, | ||
233 | fd_set *read_fd_set, | ||
234 | fd_set *write_fd_set, | ||
235 | fd_set *except_fd_set, | ||
236 | MHD_socket *max_fd, | ||
237 | unsigned int fd_setsize) | ||
238 | { | ||
239 | if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) || | ||
240 | (MHD_ELS_POLL == daemon->event_loop_syscall) ) | ||
241 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_FDSET; | ||
242 | |||
243 | #ifdef EPOLL_SUPPORT | ||
244 | if (MHD_ELS_EPOLL == daemon->event_loop_syscall) | ||
245 | { | ||
246 | if (daemon->shutdown) | ||
247 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
248 | |||
249 | /* we're in epoll mode, use the epoll FD as a stand-in for | ||
250 | the entire event set */ | ||
251 | |||
252 | return MHD_add_to_fd_set_ (daemon->epoll_fd, | ||
253 | read_fd_set, | ||
254 | max_fd, | ||
255 | fd_setsize) | ||
256 | ? MHD_SC_OK | ||
257 | : MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
258 | } | ||
259 | #endif | ||
260 | |||
261 | return internal_get_fdset2 (daemon, | ||
262 | read_fd_set, | ||
263 | write_fd_set, | ||
264 | except_fd_set, | ||
265 | max_fd, | ||
266 | fd_setsize); | ||
267 | } | ||
268 | |||
269 | /* end of daemon_get_fdset.c */ | ||
diff --git a/src/lib/daemon_get_timeout.c b/src/lib/daemon_get_timeout.c index fa3e39f9..0e39ab4d 100644 --- a/src/lib/daemon_get_timeout.c +++ b/src/lib/daemon_get_timeout.c | |||
@@ -48,7 +48,79 @@ enum MHD_StatusCode | |||
48 | MHD_daemon_get_timeout (struct MHD_Daemon *daemon, | 48 | MHD_daemon_get_timeout (struct MHD_Daemon *daemon, |
49 | MHD_UNSIGNED_LONG_LONG *timeout) | 49 | MHD_UNSIGNED_LONG_LONG *timeout) |
50 | { | 50 | { |
51 | return -1; | 51 | time_t earliest_deadline; |
52 | time_t now; | ||
53 | struct MHD_Connection *pos; | ||
54 | bool have_timeout; | ||
55 | |||
56 | if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) | ||
57 | { | ||
58 | #ifdef HAVE_MESSAGES | ||
59 | MHD_DLOG (daemon, | ||
60 | MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_TIMEOUT, | ||
61 | _("Illegal call to MHD_get_timeout\n")); | ||
62 | #endif | ||
63 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_TIMEOUT; | ||
64 | } | ||
65 | |||
66 | if (daemon->data_already_pending) | ||
67 | { | ||
68 | /* Some data already waiting to be processed. */ | ||
69 | *timeout = 0; | ||
70 | return MHD_SC_OK; | ||
71 | } | ||
72 | |||
73 | #ifdef EPOLL_SUPPORT | ||
74 | if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && | ||
75 | ((NULL != daemon->eready_head) | ||
76 | #if defined(UPGRADE_SUPPORT) && defined(HTTPS_SUPPORT) | ||
77 | || (NULL != daemon->eready_urh_head) | ||
78 | #endif /* UPGRADE_SUPPORT && HTTPS_SUPPORT */ | ||
79 | ) ) | ||
80 | { | ||
81 | /* Some connection(s) already have some data pending. */ | ||
82 | *timeout = 0; | ||
83 | return MHD_SC_OK; | ||
84 | } | ||
85 | #endif /* EPOLL_SUPPORT */ | ||
86 | |||
87 | have_timeout = false; | ||
88 | earliest_deadline = 0; /* avoid compiler warnings */ | ||
89 | for (pos = daemon->manual_timeout_tail; NULL != pos; pos = pos->prevX) | ||
90 | { | ||
91 | if (0 != pos->connection_timeout) | ||
92 | { | ||
93 | if ( (! have_timeout) || | ||
94 | (earliest_deadline - pos->last_activity > pos->connection_timeout) ) | ||
95 | earliest_deadline = pos->last_activity + pos->connection_timeout; | ||
96 | have_timeout = true; | ||
97 | } | ||
98 | } | ||
99 | /* normal timeouts are sorted, so we only need to look at the 'tail' (oldest) */ | ||
100 | pos = daemon->normal_timeout_tail; | ||
101 | if ( (NULL != pos) && | ||
102 | (0 != pos->connection_timeout) ) | ||
103 | { | ||
104 | if ( (! have_timeout) || | ||
105 | (earliest_deadline - pos->connection_timeout > pos->last_activity) ) | ||
106 | earliest_deadline = pos->last_activity + pos->connection_timeout; | ||
107 | have_timeout = true; | ||
108 | } | ||
109 | |||
110 | if (! have_timeout) | ||
111 | return MHD_SC_NO_TIMEOUT; | ||
112 | now = MHD_monotonic_sec_counter(); | ||
113 | if (earliest_deadline < now) | ||
114 | *timeout = 0; | ||
115 | else | ||
116 | { | ||
117 | const time_t second_left = earliest_deadline - now; | ||
118 | if (second_left > ULLONG_MAX / 1000) /* Ignore compiler warning: 'second_left' is always positive. */ | ||
119 | *timeout = ULLONG_MAX; | ||
120 | else | ||
121 | *timeout = 1000LL * second_left; | ||
122 | } | ||
123 | return MHD_SC_OK; | ||
52 | } | 124 | } |
53 | 125 | ||
54 | /* end of daemon_get_timeout.c */ | 126 | /* end of daemon_get_timeout.c */ |
diff --git a/src/lib/daemon_poll.c b/src/lib/daemon_poll.c new file mode 100644 index 00000000..118c0579 --- /dev/null +++ b/src/lib/daemon_poll.c | |||
@@ -0,0 +1,453 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | /** | ||
20 | * @file lib/daemon_poll.c | ||
21 | * @brief functions to run poll-based event loop | ||
22 | * @author Christian Grothoff | ||
23 | */ | ||
24 | #include "internal.h" | ||
25 | #include "daemon_poll.h" | ||
26 | |||
27 | #ifdef HAVE_POLL | ||
28 | |||
29 | |||
30 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
31 | |||
32 | /** | ||
33 | * Set required 'event' members in 'pollfd' elements, | ||
34 | * assuming that @a p[0].fd is MHD side of socketpair | ||
35 | * and @a p[1].fd is TLS connected socket. | ||
36 | * | ||
37 | * @param urh upgrade handle to watch for | ||
38 | * @param p pollfd array to update | ||
39 | */ | ||
40 | static void | ||
41 | urh_update_pollfd (struct MHD_UpgradeResponseHandle *urh, | ||
42 | struct pollfd p[2]) | ||
43 | { | ||
44 | p[0].events = 0; | ||
45 | p[1].events = 0; | ||
46 | |||
47 | if (urh->in_buffer_used < urh->in_buffer_size) | ||
48 | p[0].events |= POLLIN; | ||
49 | if (0 != urh->out_buffer_used) | ||
50 | p[0].events |= POLLOUT; | ||
51 | |||
52 | /* Do not monitor again for errors if error was detected before as | ||
53 | * error state is remembered. */ | ||
54 | if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) && | ||
55 | ((0 != urh->in_buffer_size) || | ||
56 | (0 != urh->out_buffer_size) || | ||
57 | (0 != urh->out_buffer_used))) | ||
58 | p[0].events |= MHD_POLL_EVENTS_ERR_DISC; | ||
59 | |||
60 | if (urh->out_buffer_used < urh->out_buffer_size) | ||
61 | p[1].events |= POLLIN; | ||
62 | if (0 != urh->in_buffer_used) | ||
63 | p[1].events |= POLLOUT; | ||
64 | |||
65 | /* Do not monitor again for errors if error was detected before as | ||
66 | * error state is remembered. */ | ||
67 | if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) && | ||
68 | ((0 != urh->out_buffer_size) || | ||
69 | (0 != urh->in_buffer_size) || | ||
70 | (0 != urh->in_buffer_used))) | ||
71 | p[1].events |= MHD_POLL_EVENTS_ERR_DISC; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Set @a p to watch for @a urh. | ||
77 | * | ||
78 | * @param urh upgrade handle to watch for | ||
79 | * @param p pollfd array to set | ||
80 | */ | ||
81 | static void | ||
82 | urh_to_pollfd (struct MHD_UpgradeResponseHandle *urh, | ||
83 | struct pollfd p[2]) | ||
84 | { | ||
85 | p[0].fd = urh->connection->socket_fd; | ||
86 | p[1].fd = urh->mhd.socket; | ||
87 | urh_update_pollfd (urh, | ||
88 | p); | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Update ready state in @a urh based on pollfd. | ||
94 | * @param urh upgrade handle to update | ||
95 | * @param p 'poll()' processed pollfd. | ||
96 | */ | ||
97 | static void | ||
98 | urh_from_pollfd (struct MHD_UpgradeResponseHandle *urh, | ||
99 | struct pollfd p[2]) | ||
100 | { | ||
101 | /* Reset read/write ready, preserve error state. */ | ||
102 | urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
103 | urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
104 | |||
105 | if (0 != (p[0].revents & POLLIN)) | ||
106 | urh->app.celi |= MHD_EPOLL_STATE_READ_READY; | ||
107 | if (0 != (p[0].revents & POLLOUT)) | ||
108 | urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
109 | if (0 != (p[0].revents & POLLHUP)) | ||
110 | urh->app.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; | ||
111 | if (0 != (p[0].revents & MHD_POLL_REVENTS_ERRROR)) | ||
112 | urh->app.celi |= MHD_EPOLL_STATE_ERROR; | ||
113 | if (0 != (p[1].revents & POLLIN)) | ||
114 | urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; | ||
115 | if (0 != (p[1].revents & POLLOUT)) | ||
116 | urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
117 | if (0 != (p[1].revents & POLLHUP)) | ||
118 | urh->mhd.celi |= MHD_EPOLL_STATE_ERROR; | ||
119 | if (0 != (p[1].revents & MHD_POLL_REVENTS_ERRROR)) | ||
120 | urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; | ||
121 | } | ||
122 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Process all of our connections and possibly the server | ||
127 | * socket using poll(). | ||
128 | * | ||
129 | * @param daemon daemon to run poll loop for | ||
130 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
131 | * @return #MHD_SC_OK on success | ||
132 | */ | ||
133 | enum MHD_StatusCode | ||
134 | MHD_daemon_poll_all_ (struct MHD_Daemon *daemon, | ||
135 | bool may_block) | ||
136 | { | ||
137 | unsigned int num_connections; | ||
138 | struct MHD_Connection *pos; | ||
139 | struct MHD_Connection *prev; | ||
140 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
141 | struct MHD_UpgradeResponseHandle *urh; | ||
142 | struct MHD_UpgradeResponseHandle *urhn; | ||
143 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
144 | |||
145 | if ( (! daemon->disallow_suspend_resume) && | ||
146 | (MHD_YES == resume_suspended_connections (daemon)) ) | ||
147 | may_block = false; | ||
148 | |||
149 | /* count number of connections and thus determine poll set size */ | ||
150 | num_connections = 0; | ||
151 | for (pos = daemon->connections_head; NULL != pos; pos = pos->next) | ||
152 | num_connections++; | ||
153 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
154 | for (urh = daemon->urh_head; NULL != urh; urh = urh->next) | ||
155 | num_connections += 2; | ||
156 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
157 | { | ||
158 | MHD_UNSIGNED_LONG_LONG ltimeout; | ||
159 | unsigned int i; | ||
160 | int timeout; | ||
161 | unsigned int poll_server; | ||
162 | int poll_listen; | ||
163 | int poll_itc_idx; | ||
164 | struct pollfd *p; | ||
165 | MHD_socket ls; | ||
166 | |||
167 | p = MHD_calloc_ ((2 + num_connections), | ||
168 | sizeof (struct pollfd)); | ||
169 | if (NULL == p) | ||
170 | { | ||
171 | #ifdef HAVE_MESSAGES | ||
172 | MHD_DLOG (daemon, | ||
173 | MHD_SC_POLL_MALLOC_FAILURE, | ||
174 | _("Error allocating memory: %s\n"), | ||
175 | MHD_strerror_(errno)); | ||
176 | #endif | ||
177 | return MHD_SC_POLL_MALLOC_FAILURE; | ||
178 | } | ||
179 | poll_server = 0; | ||
180 | poll_listen = -1; | ||
181 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
182 | (! daemon->was_quiesced) && | ||
183 | (daemon->connections < daemon->global_connection_limit) && | ||
184 | (! daemon->at_limit) ) | ||
185 | { | ||
186 | /* only listen if we are not at the connection limit */ | ||
187 | p[poll_server].fd = ls; | ||
188 | p[poll_server].events = POLLIN; | ||
189 | p[poll_server].revents = 0; | ||
190 | poll_listen = (int) poll_server; | ||
191 | poll_server++; | ||
192 | } | ||
193 | poll_itc_idx = -1; | ||
194 | if (MHD_ITC_IS_VALID_(daemon->itc)) | ||
195 | { | ||
196 | p[poll_server].fd = MHD_itc_r_fd_ (daemon->itc); | ||
197 | p[poll_server].events = POLLIN; | ||
198 | p[poll_server].revents = 0; | ||
199 | poll_itc_idx = (int) poll_server; | ||
200 | poll_server++; | ||
201 | } | ||
202 | if (! may_block) | ||
203 | timeout = 0; | ||
204 | else if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) || | ||
205 | (MHD_SC_OK != /* FIXME: distinguish between NO_TIMEOUT and errors! */ | ||
206 | MHD_daemon_get_timeout (daemon, | ||
207 | <imeout)) ) | ||
208 | timeout = -1; | ||
209 | else | ||
210 | timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout; | ||
211 | |||
212 | i = 0; | ||
213 | for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev) | ||
214 | { | ||
215 | p[poll_server+i].fd = pos->socket_fd; | ||
216 | switch (pos->request.event_loop_info) | ||
217 | { | ||
218 | case MHD_EVENT_LOOP_INFO_READ: | ||
219 | p[poll_server+i].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC; | ||
220 | break; | ||
221 | case MHD_EVENT_LOOP_INFO_WRITE: | ||
222 | p[poll_server+i].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC; | ||
223 | break; | ||
224 | case MHD_EVENT_LOOP_INFO_BLOCK: | ||
225 | p[poll_server+i].events |= MHD_POLL_EVENTS_ERR_DISC; | ||
226 | break; | ||
227 | case MHD_EVENT_LOOP_INFO_CLEANUP: | ||
228 | timeout = 0; /* clean up "pos" immediately */ | ||
229 | break; | ||
230 | } | ||
231 | i++; | ||
232 | } | ||
233 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
234 | for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) | ||
235 | { | ||
236 | urh_to_pollfd (urh, | ||
237 | &(p[poll_server+i])); | ||
238 | i += 2; | ||
239 | } | ||
240 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
241 | if (0 == poll_server + num_connections) | ||
242 | { | ||
243 | free (p); | ||
244 | return MHD_SC_OK; | ||
245 | } | ||
246 | if (MHD_sys_poll_(p, | ||
247 | poll_server + num_connections, | ||
248 | timeout) < 0) | ||
249 | { | ||
250 | const int err = MHD_socket_get_error_ (); | ||
251 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
252 | { | ||
253 | free(p); | ||
254 | return MHD_SC_OK; | ||
255 | } | ||
256 | #ifdef HAVE_MESSAGES | ||
257 | MHD_DLOG (daemon, | ||
258 | MHD_SC_UNEXPECTED_POLL_ERROR, | ||
259 | _("poll failed: %s\n"), | ||
260 | MHD_socket_strerr_ (err)); | ||
261 | #endif | ||
262 | free(p); | ||
263 | return MHD_SC_UNEXPECTED_POLL_ERROR; | ||
264 | } | ||
265 | |||
266 | /* Reset. New value will be set when connections are processed. */ | ||
267 | daemon->data_already_pending = false; | ||
268 | |||
269 | /* handle ITC FD */ | ||
270 | /* do it before any other processing so | ||
271 | new signals will be processed in next loop */ | ||
272 | if ( (-1 != poll_itc_idx) && | ||
273 | (0 != (p[poll_itc_idx].revents & POLLIN)) ) | ||
274 | MHD_itc_clear_ (daemon->itc); | ||
275 | |||
276 | /* handle shutdown */ | ||
277 | if (daemon->shutdown) | ||
278 | { | ||
279 | free(p); | ||
280 | return MHD_NO; | ||
281 | } | ||
282 | i = 0; | ||
283 | prev = daemon->connections_tail; | ||
284 | while (NULL != (pos = prev)) | ||
285 | { | ||
286 | prev = pos->prev; | ||
287 | /* first, sanity checks */ | ||
288 | if (i >= num_connections) | ||
289 | break; /* connection list changed somehow, retry later ... */ | ||
290 | if (p[poll_server+i].fd != pos->socket_fd) | ||
291 | continue; /* fd mismatch, something else happened, retry later ... */ | ||
292 | call_handlers (pos, | ||
293 | 0 != (p[poll_server+i].revents & POLLIN), | ||
294 | 0 != (p[poll_server+i].revents & POLLOUT), | ||
295 | 0 != (p[poll_server+i].revents & MHD_POLL_REVENTS_ERR_DISC)); | ||
296 | i++; | ||
297 | } | ||
298 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
299 | for (urh = daemon->urh_tail; NULL != urh; urh = urhn) | ||
300 | { | ||
301 | if (i >= num_connections) | ||
302 | break; /* connection list changed somehow, retry later ... */ | ||
303 | |||
304 | /* Get next connection here as connection can be removed | ||
305 | * from 'daemon->urh_head' list. */ | ||
306 | urhn = urh->prev; | ||
307 | /* Check for fd mismatch. FIXME: required for safety? */ | ||
308 | if ((p[poll_server+i].fd != urh->connection->socket_fd) || | ||
309 | (p[poll_server+i+1].fd != urh->mhd.socket)) | ||
310 | break; | ||
311 | urh_from_pollfd (urh, | ||
312 | &(p[poll_server+i])); | ||
313 | i += 2; | ||
314 | process_urh (urh); | ||
315 | /* Finished forwarding? */ | ||
316 | if ( (0 == urh->in_buffer_size) && | ||
317 | (0 == urh->out_buffer_size) && | ||
318 | (0 == urh->in_buffer_used) && | ||
319 | (0 == urh->out_buffer_used) ) | ||
320 | { | ||
321 | /* MHD_connection_finish_forward_() will remove connection from | ||
322 | * 'daemon->urh_head' list. */ | ||
323 | MHD_connection_finish_forward_ (urh->connection); | ||
324 | urh->clean_ready = true; | ||
325 | /* If 'urh->was_closed' already was set to true, connection will be | ||
326 | * moved immediately to cleanup list. Otherwise connection | ||
327 | * will stay in suspended list until 'urh' will be marked | ||
328 | * with 'was_closed' by application. */ | ||
329 | MHD_resume_connection(urh->connection); | ||
330 | } | ||
331 | } | ||
332 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
333 | /* handle 'listen' FD */ | ||
334 | if ( (-1 != poll_listen) && | ||
335 | (0 != (p[poll_listen].revents & POLLIN)) ) | ||
336 | (void) MHD_accept_connection (daemon); | ||
337 | |||
338 | free(p); | ||
339 | } | ||
340 | return MHD_YES; | ||
341 | } | ||
342 | |||
343 | |||
344 | /** | ||
345 | * Process only the listen socket using poll(). | ||
346 | * | ||
347 | * @param daemon daemon to run poll loop for | ||
348 | * @param may_block true if blocking, false if non-blocking | ||
349 | * @return #MHD_SC_OK on success | ||
350 | */ | ||
351 | enum MHD_StatusCode | ||
352 | MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon, | ||
353 | bool may_block) | ||
354 | { | ||
355 | struct pollfd p[2]; | ||
356 | int timeout; | ||
357 | unsigned int poll_count; | ||
358 | int poll_listen; | ||
359 | int poll_itc_idx; | ||
360 | MHD_socket ls; | ||
361 | |||
362 | memset (&p, | ||
363 | 0, | ||
364 | sizeof (p)); | ||
365 | poll_count = 0; | ||
366 | poll_listen = -1; | ||
367 | poll_itc_idx = -1; | ||
368 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
369 | (! daemon->was_quiesced) ) | ||
370 | |||
371 | { | ||
372 | p[poll_count].fd = ls; | ||
373 | p[poll_count].events = POLLIN; | ||
374 | p[poll_count].revents = 0; | ||
375 | poll_listen = poll_count; | ||
376 | poll_count++; | ||
377 | } | ||
378 | if (MHD_ITC_IS_VALID_(daemon->itc)) | ||
379 | { | ||
380 | p[poll_count].fd = MHD_itc_r_fd_ (daemon->itc); | ||
381 | p[poll_count].events = POLLIN; | ||
382 | p[poll_count].revents = 0; | ||
383 | poll_itc_idx = poll_count; | ||
384 | poll_count++; | ||
385 | } | ||
386 | |||
387 | if (! daemon->disallow_suspend_resume) | ||
388 | (void) resume_suspended_connections (daemon); | ||
389 | |||
390 | if (! may_block) | ||
391 | timeout = 0; | ||
392 | else | ||
393 | timeout = -1; | ||
394 | if (0 == poll_count) | ||
395 | return MHD_SC_OK; | ||
396 | if (MHD_sys_poll_(p, | ||
397 | poll_count, | ||
398 | timeout) < 0) | ||
399 | { | ||
400 | const int err = MHD_socket_get_error_ (); | ||
401 | |||
402 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
403 | return MHD_SC_OK; | ||
404 | #ifdef HAVE_MESSAGES | ||
405 | MHD_DLOG (daemon, | ||
406 | MHD_SC_UNEXPECTED_POLL_ERROR, | ||
407 | _("poll failed: %s\n"), | ||
408 | MHD_socket_strerr_ (err)); | ||
409 | #endif | ||
410 | return MHD_SC_UNEXPECTED_POLL_ERROR; | ||
411 | } | ||
412 | if ( (-1 != poll_itc_idx) && | ||
413 | (0 != (p[poll_itc_idx].revents & POLLIN)) ) | ||
414 | MHD_itc_clear_ (daemon->itc); | ||
415 | |||
416 | /* handle shutdown */ | ||
417 | if (daemon->shutdown) | ||
418 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
419 | if ( (-1 != poll_listen) && | ||
420 | (0 != (p[poll_listen].revents & POLLIN)) ) | ||
421 | (void) MHD_accept_connection (daemon); | ||
422 | return MHD_SC_OK; | ||
423 | } | ||
424 | #endif | ||
425 | |||
426 | |||
427 | /** | ||
428 | * Do poll()-based processing. | ||
429 | * | ||
430 | * @param daemon daemon to run poll()-loop for | ||
431 | * @param may_block true if blocking, false if non-blocking | ||
432 | * @return #MHD_SC_OK on success | ||
433 | */ | ||
434 | enum MHD_StatusCode | ||
435 | MHD_daemon_poll_ (struct MHD_Daemon *daemon, | ||
436 | bool may_block) | ||
437 | { | ||
438 | #ifdef HAVE_POLL | ||
439 | if (daemon->shutdown) | ||
440 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
441 | if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) | ||
442 | return MHD_daemon_poll_all_ (daemon, | ||
443 | may_block); | ||
444 | return MHD_daemon_poll_listen_socket_ (daemon, | ||
445 | may_block); | ||
446 | #else | ||
447 | /* This code should be dead, as we should have checked | ||
448 | this earlier... */ | ||
449 | return MHD_SC_POLL_NOT_SUPPORTED; | ||
450 | #endif | ||
451 | } | ||
452 | |||
453 | /* end of daemon_poll.c */ | ||
diff --git a/src/lib/daemon_poll.h b/src/lib/daemon_poll.h new file mode 100644 index 00000000..c5dbb88b --- /dev/null +++ b/src/lib/daemon_poll.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_poll.h | ||
22 | * @brief non-public functions provided by daemon_poll.c | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #ifndef DAEMON_POLL_H | ||
26 | #define DAEMON_POLL_H | ||
27 | |||
28 | |||
29 | #ifdef HAVE_POLL | ||
30 | /** | ||
31 | * Process all of our connections and possibly the server | ||
32 | * socket using poll(). | ||
33 | * | ||
34 | * @param daemon daemon to run poll loop for | ||
35 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
36 | * @return #MHD_SC_OK on success | ||
37 | */ | ||
38 | enum MHD_StatusCode | ||
39 | MHD_daemon_poll_all_ (struct MHD_Daemon *daemon, | ||
40 | bool may_block) | ||
41 | MHD_NONNULL(1); | ||
42 | |||
43 | |||
44 | /** | ||
45 | * Process only the listen socket using poll(). | ||
46 | * | ||
47 | * @param daemon daemon to run poll loop for | ||
48 | * @param may_block true if blocking, false if non-blocking | ||
49 | * @return #MHD_SC_OK on success | ||
50 | */ | ||
51 | enum MHD_StatusCode | ||
52 | MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon, | ||
53 | bool may_block) | ||
54 | MHD_NONNULL (1); | ||
55 | |||
56 | |||
57 | /** | ||
58 | * Do poll()-based processing. | ||
59 | * | ||
60 | * @param daemon daemon to run poll()-loop for | ||
61 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
62 | * @return #MHD_SC_OK on success | ||
63 | */ | ||
64 | enum MHD_StatusCode | ||
65 | MHD_daemon_poll_ (struct MHD_Daemon *daemon, | ||
66 | bool may_block) | ||
67 | MHD_NONNULL (1); | ||
68 | #endif | ||
69 | |||
70 | |||
71 | #endif | ||
diff --git a/src/lib/daemon_run.c b/src/lib/daemon_run.c index cf9ab5de..c93a5f83 100644 --- a/src/lib/daemon_run.c +++ b/src/lib/daemon_run.c | |||
@@ -23,6 +23,9 @@ | |||
23 | * @author Christian Grothoff | 23 | * @author Christian Grothoff |
24 | */ | 24 | */ |
25 | #include "internal.h" | 25 | #include "internal.h" |
26 | #include "daemon_epoll.h" | ||
27 | #include "daemon_poll.h" | ||
28 | #include "daemon_select.h" | ||
26 | 29 | ||
27 | 30 | ||
28 | /** | 31 | /** |
@@ -46,7 +49,32 @@ | |||
46 | enum MHD_StatusCode | 49 | enum MHD_StatusCode |
47 | MHD_daemon_run (struct MHD_Daemon *daemon) | 50 | MHD_daemon_run (struct MHD_Daemon *daemon) |
48 | { | 51 | { |
49 | return -1; | 52 | if (daemon->shutdown) |
53 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
54 | if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) | ||
55 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_RUN_EXTERNAL; | ||
56 | switch (daemon->event_loop_syscall) | ||
57 | { | ||
58 | case MHD_ELS_POLL: | ||
59 | MHD_daemon_poll_ (daemon, | ||
60 | MHD_NO); | ||
61 | MHD_cleanup_connections (daemon); | ||
62 | break; | ||
63 | #ifdef EPOLL_SUPPORT | ||
64 | case MHD_ELS_EPOLL: | ||
65 | MHD_daemon_epoll_ (daemon, | ||
66 | MHD_NO); | ||
67 | MHD_cleanup_connections (daemon); | ||
68 | break; | ||
69 | #endif | ||
70 | case MHD_ELS_SELECT: | ||
71 | MHD_daemon_select_ (daemon, | ||
72 | MHD_NO); | ||
73 | /* MHD_select does MHD_cleanup_connections already */ | ||
74 | break; | ||
75 | default: | ||
76 | return MHD_SC_CONFIGURATION_UNEXPECTED_ELS; | ||
77 | } | ||
50 | } | 78 | } |
51 | 79 | ||
52 | /* end of daemon_run.c */ | 80 | /* end of daemon_run.c */ |
diff --git a/src/lib/daemon_run_from_select.c b/src/lib/daemon_run_from_select.c index b8063f79..e69de29b 100644 --- a/src/lib/daemon_run_from_select.c +++ b/src/lib/daemon_run_from_select.c | |||
@@ -1,59 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_run_from_select.c | ||
22 | * @brief functions to run select-based event loop | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "internal.h" | ||
26 | |||
27 | |||
28 | /** | ||
29 | * Run webserver operations. This method should be called by clients | ||
30 | * in combination with #MHD_get_fdset and #MHD_get_timeout() if the | ||
31 | * client-controlled select method is used. | ||
32 | * | ||
33 | * You can use this function instead of #MHD_run if you called | ||
34 | * `select()` on the result from #MHD_get_fdset. File descriptors in | ||
35 | * the sets that are not controlled by MHD will be ignored. Calling | ||
36 | * this function instead of #MHD_run is more efficient as MHD will not | ||
37 | * have to call `select()` again to determine which operations are | ||
38 | * ready. | ||
39 | * | ||
40 | * This function cannot be used with daemon started with | ||
41 | * #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
42 | * | ||
43 | * @param daemon daemon to run select loop for | ||
44 | * @param read_fd_set read set | ||
45 | * @param write_fd_set write set | ||
46 | * @param except_fd_set except set | ||
47 | * @return #MHD_SC_OK on success | ||
48 | * @ingroup event | ||
49 | */ | ||
50 | enum MHD_StatusCode | ||
51 | MHD_daemon_run_from_select (struct MHD_Daemon *daemon, | ||
52 | const fd_set *read_fd_set, | ||
53 | const fd_set *write_fd_set, | ||
54 | const fd_set *except_fd_set) | ||
55 | { | ||
56 | return -1; | ||
57 | } | ||
58 | |||
59 | /* end of daemon_run_from_select.c */ | ||
diff --git a/src/lib/daemon_select.c b/src/lib/daemon_select.c new file mode 100644 index 00000000..f61ba36a --- /dev/null +++ b/src/lib/daemon_select.c | |||
@@ -0,0 +1,711 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_select.c | ||
22 | * @brief function to run select()-based event loop of a daemon | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "internal.h" | ||
26 | #include "daemon_select.h" | ||
27 | |||
28 | /** | ||
29 | * We defined a macro with the same name as a function we | ||
30 | * are implementing here. Need to undef the macro to avoid | ||
31 | * causing a conflict. | ||
32 | */ | ||
33 | #undef MHD_daemon_get_fdset | ||
34 | |||
35 | /** | ||
36 | * Obtain the `select()` sets for this daemon. Daemon's FDs will be | ||
37 | * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO | ||
38 | * for each fd_set before calling this function. FD_SETSIZE is assumed | ||
39 | * to be platform's default. | ||
40 | * | ||
41 | * This function should only be called in when MHD is configured to | ||
42 | * use external select with 'select()' or with 'epoll'. In the latter | ||
43 | * case, it will only add the single 'epoll()' file descriptor used by | ||
44 | * MHD to the sets. It's necessary to use #MHD_daemon_get_timeout() in | ||
45 | * combination with this function. | ||
46 | * | ||
47 | * This function must be called only for daemon started without | ||
48 | * #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
49 | * | ||
50 | * @param daemon daemon to get sets from | ||
51 | * @param read_fd_set read set | ||
52 | * @param write_fd_set write set | ||
53 | * @param except_fd_set except set | ||
54 | * @param max_fd increased to largest FD added (if larger | ||
55 | * than existing value); can be NULL | ||
56 | * @return #MHD_SC_OK on success, otherwise error code | ||
57 | * @ingroup event | ||
58 | */ | ||
59 | enum MHD_StatusCode | ||
60 | MHD_daemon_get_fdset (struct MHD_Daemon *daemon, | ||
61 | fd_set *read_fd_set, | ||
62 | fd_set *write_fd_set, | ||
63 | fd_set *except_fd_set, | ||
64 | MHD_socket *max_fd) | ||
65 | { | ||
66 | return MHD_daemon_get_fdset2 (daemon, | ||
67 | read_fd_set, | ||
68 | write_fd_set, | ||
69 | except_fd_set, | ||
70 | max_fd, | ||
71 | _MHD_SYS_DEFAULT_FD_SETSIZE); | ||
72 | } | ||
73 | |||
74 | |||
75 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
76 | /** | ||
77 | * Obtain the select() file descriptor sets for the | ||
78 | * given @a urh. | ||
79 | * | ||
80 | * @param urh upgrade handle to wait for | ||
81 | * @param[out] rs read set to initialize | ||
82 | * @param[out] ws write set to initialize | ||
83 | * @param[out] es except set to initialize | ||
84 | * @param[out] max_fd maximum FD to update | ||
85 | * @param fd_setsize value of FD_SETSIZE | ||
86 | * @return true on success, false on error | ||
87 | */ | ||
88 | static bool | ||
89 | urh_to_fdset (struct MHD_UpgradeResponseHandle *urh, | ||
90 | fd_set *rs, | ||
91 | fd_set *ws, | ||
92 | fd_set *es, | ||
93 | MHD_socket *max_fd, | ||
94 | unsigned int fd_setsize) | ||
95 | { | ||
96 | const MHD_socket conn_sckt = urh->connection->socket_fd; | ||
97 | const MHD_socket mhd_sckt = urh->mhd.socket; | ||
98 | bool res = true; | ||
99 | |||
100 | /* Do not add to 'es' only if socket is closed | ||
101 | * or not used anymore. */ | ||
102 | if (MHD_INVALID_SOCKET != conn_sckt) | ||
103 | { | ||
104 | if ( (urh->in_buffer_used < urh->in_buffer_size) && | ||
105 | (! MHD_add_to_fd_set_ (conn_sckt, | ||
106 | rs, | ||
107 | max_fd, | ||
108 | fd_setsize)) ) | ||
109 | res = false; | ||
110 | if ( (0 != urh->out_buffer_used) && | ||
111 | (! MHD_add_to_fd_set_ (conn_sckt, | ||
112 | ws, | ||
113 | max_fd, | ||
114 | fd_setsize)) ) | ||
115 | res = false; | ||
116 | /* Do not monitor again for errors if error was detected before as | ||
117 | * error state is remembered. */ | ||
118 | if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) && | ||
119 | ((0 != urh->in_buffer_size) || | ||
120 | (0 != urh->out_buffer_size) || | ||
121 | (0 != urh->out_buffer_used))) | ||
122 | MHD_add_to_fd_set_ (conn_sckt, | ||
123 | es, | ||
124 | max_fd, | ||
125 | fd_setsize); | ||
126 | } | ||
127 | if (MHD_INVALID_SOCKET != mhd_sckt) | ||
128 | { | ||
129 | if ( (urh->out_buffer_used < urh->out_buffer_size) && | ||
130 | (! MHD_add_to_fd_set_ (mhd_sckt, | ||
131 | rs, | ||
132 | max_fd, | ||
133 | fd_setsize)) ) | ||
134 | res = false; | ||
135 | if ( (0 != urh->in_buffer_used) && | ||
136 | (! MHD_add_to_fd_set_ (mhd_sckt, | ||
137 | ws, | ||
138 | max_fd, | ||
139 | fd_setsize)) ) | ||
140 | res = false; | ||
141 | /* Do not monitor again for errors if error was detected before as | ||
142 | * error state is remembered. */ | ||
143 | if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) && | ||
144 | ((0 != urh->out_buffer_size) || | ||
145 | (0 != urh->in_buffer_size) || | ||
146 | (0 != urh->in_buffer_used))) | ||
147 | MHD_add_to_fd_set_ (mhd_sckt, | ||
148 | es, | ||
149 | max_fd, | ||
150 | fd_setsize); | ||
151 | } | ||
152 | |||
153 | return res; | ||
154 | } | ||
155 | #endif | ||
156 | |||
157 | |||
158 | /** | ||
159 | * Internal version of #MHD_daemon_get_fdset2(). | ||
160 | * | ||
161 | * @param daemon daemon to get sets from | ||
162 | * @param read_fd_set read set | ||
163 | * @param write_fd_set write set | ||
164 | * @param except_fd_set except set | ||
165 | * @param max_fd increased to largest FD added (if larger | ||
166 | * than existing value); can be NULL | ||
167 | * @param fd_setsize value of FD_SETSIZE | ||
168 | * @return #MHD_SC_OK on success | ||
169 | * @ingroup event | ||
170 | */ | ||
171 | static enum MHD_StatusCode | ||
172 | internal_get_fdset2 (struct MHD_Daemon *daemon, | ||
173 | fd_set *read_fd_set, | ||
174 | fd_set *write_fd_set, | ||
175 | fd_set *except_fd_set, | ||
176 | MHD_socket *max_fd, | ||
177 | unsigned int fd_setsize) | ||
178 | |||
179 | { | ||
180 | struct MHD_Connection *pos; | ||
181 | struct MHD_Connection *posn; | ||
182 | enum MHD_StatusCode result = MHD_SC_OK; | ||
183 | MHD_socket ls; | ||
184 | |||
185 | if (daemon->shutdown) | ||
186 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
187 | |||
188 | ls = daemon->listen_socket; | ||
189 | if ( (MHD_INVALID_SOCKET != ls) && | ||
190 | (! daemon->was_quiesced) && | ||
191 | (! MHD_add_to_fd_set_ (ls, | ||
192 | read_fd_set, | ||
193 | max_fd, | ||
194 | fd_setsize)) ) | ||
195 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
196 | |||
197 | /* Add all sockets to 'except_fd_set' as well to watch for | ||
198 | * out-of-band data. However, ignore errors if INFO_READ | ||
199 | * or INFO_WRITE sockets will not fit 'except_fd_set'. */ | ||
200 | /* Start from oldest connections. Make sense for W32 FDSETs. */ | ||
201 | for (pos = daemon->connections_tail; NULL != pos; pos = posn) | ||
202 | { | ||
203 | posn = pos->prev; | ||
204 | |||
205 | switch (pos->request.event_loop_info) | ||
206 | { | ||
207 | case MHD_EVENT_LOOP_INFO_READ: | ||
208 | if (! MHD_add_to_fd_set_ (pos->socket_fd, | ||
209 | read_fd_set, | ||
210 | max_fd, | ||
211 | fd_setsize)) | ||
212 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
213 | #ifdef MHD_POSIX_SOCKETS | ||
214 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
215 | except_fd_set, | ||
216 | max_fd, | ||
217 | fd_setsize); | ||
218 | #endif /* MHD_POSIX_SOCKETS */ | ||
219 | break; | ||
220 | case MHD_EVENT_LOOP_INFO_WRITE: | ||
221 | if (! MHD_add_to_fd_set_ (pos->socket_fd, | ||
222 | write_fd_set, | ||
223 | max_fd, | ||
224 | fd_setsize)) | ||
225 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
226 | #ifdef MHD_POSIX_SOCKETS | ||
227 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
228 | except_fd_set, | ||
229 | max_fd, | ||
230 | fd_setsize); | ||
231 | #endif /* MHD_POSIX_SOCKETS */ | ||
232 | break; | ||
233 | case MHD_EVENT_LOOP_INFO_BLOCK: | ||
234 | if ( (NULL == except_fd_set) || | ||
235 | ! MHD_add_to_fd_set_ (pos->socket_fd, | ||
236 | except_fd_set, | ||
237 | max_fd, | ||
238 | fd_setsize)) | ||
239 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
240 | break; | ||
241 | case MHD_EVENT_LOOP_INFO_CLEANUP: | ||
242 | /* this should never happen */ | ||
243 | break; | ||
244 | } | ||
245 | } | ||
246 | #ifdef MHD_WINSOCK_SOCKETS | ||
247 | /* W32 use limited array for fd_set so add INFO_READ/INFO_WRITE sockets | ||
248 | * only after INFO_BLOCK sockets to ensure that INFO_BLOCK sockets will | ||
249 | * not be pushed out. */ | ||
250 | for (pos = daemon->connections_tail; NULL != pos; pos = posn) | ||
251 | { | ||
252 | posn = pos->prev; | ||
253 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
254 | except_fd_set, | ||
255 | max_fd, | ||
256 | fd_setsize); | ||
257 | } | ||
258 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
259 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
260 | { | ||
261 | struct MHD_UpgradeResponseHandle *urh; | ||
262 | |||
263 | for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) | ||
264 | { | ||
265 | if (! urh_to_fdset (urh, | ||
266 | read_fd_set, | ||
267 | write_fd_set, | ||
268 | except_fd_set, | ||
269 | max_fd, | ||
270 | fd_setsize)) | ||
271 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
272 | } | ||
273 | } | ||
274 | #endif | ||
275 | return result; | ||
276 | } | ||
277 | |||
278 | |||
279 | /** | ||
280 | * Obtain the `select()` sets for this daemon. Daemon's FDs will be | ||
281 | * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO | ||
282 | * for each fd_set before calling this function. | ||
283 | * | ||
284 | * Passing custom FD_SETSIZE as @a fd_setsize allow usage of | ||
285 | * larger/smaller than platform's default fd_sets. | ||
286 | * | ||
287 | * This function should only be called in when MHD is configured to | ||
288 | * use external select with 'select()' or with 'epoll'. In the latter | ||
289 | * case, it will only add the single 'epoll' file descriptor used by | ||
290 | * MHD to the sets. It's necessary to use #MHD_get_timeout() in | ||
291 | * combination with this function. | ||
292 | * | ||
293 | * This function must be called only for daemon started | ||
294 | * without #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
295 | * | ||
296 | * @param daemon daemon to get sets from | ||
297 | * @param read_fd_set read set | ||
298 | * @param write_fd_set write set | ||
299 | * @param except_fd_set except set | ||
300 | * @param max_fd increased to largest FD added (if larger | ||
301 | * than existing value); can be NULL | ||
302 | * @param fd_setsize value of FD_SETSIZE | ||
303 | * @return #MHD_SC_OK on success, otherwise error code | ||
304 | * @ingroup event | ||
305 | */ | ||
306 | enum MHD_StatusCode | ||
307 | MHD_daemon_get_fdset2 (struct MHD_Daemon *daemon, | ||
308 | fd_set *read_fd_set, | ||
309 | fd_set *write_fd_set, | ||
310 | fd_set *except_fd_set, | ||
311 | MHD_socket *max_fd, | ||
312 | unsigned int fd_setsize) | ||
313 | { | ||
314 | if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) || | ||
315 | (MHD_ELS_POLL == daemon->event_loop_syscall) ) | ||
316 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_FDSET; | ||
317 | |||
318 | #ifdef EPOLL_SUPPORT | ||
319 | if (MHD_ELS_EPOLL == daemon->event_loop_syscall) | ||
320 | { | ||
321 | if (daemon->shutdown) | ||
322 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
323 | |||
324 | /* we're in epoll mode, use the epoll FD as a stand-in for | ||
325 | the entire event set */ | ||
326 | |||
327 | return MHD_add_to_fd_set_ (daemon->epoll_fd, | ||
328 | read_fd_set, | ||
329 | max_fd, | ||
330 | fd_setsize) | ||
331 | ? MHD_SC_OK | ||
332 | : MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
333 | } | ||
334 | #endif | ||
335 | |||
336 | return internal_get_fdset2 (daemon, | ||
337 | read_fd_set, | ||
338 | write_fd_set, | ||
339 | except_fd_set, | ||
340 | max_fd, | ||
341 | fd_setsize); | ||
342 | } | ||
343 | |||
344 | |||
345 | /** | ||
346 | * Update the @a urh based on the ready FDs in | ||
347 | * the @a rs, @a ws, and @a es. | ||
348 | * | ||
349 | * @param urh upgrade handle to update | ||
350 | * @param rs read result from select() | ||
351 | * @param ws write result from select() | ||
352 | * @param es except result from select() | ||
353 | */ | ||
354 | static void | ||
355 | urh_from_fdset (struct MHD_UpgradeResponseHandle *urh, | ||
356 | const fd_set *rs, | ||
357 | const fd_set *ws, | ||
358 | const fd_set *es) | ||
359 | { | ||
360 | const MHD_socket conn_sckt = urh->connection->socket_fd; | ||
361 | const MHD_socket mhd_sckt = urh->mhd.socket; | ||
362 | |||
363 | /* Reset read/write ready, preserve error state. */ | ||
364 | urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
365 | urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
366 | |||
367 | if (MHD_INVALID_SOCKET != conn_sckt) | ||
368 | { | ||
369 | if (FD_ISSET (conn_sckt, rs)) | ||
370 | urh->app.celi |= MHD_EPOLL_STATE_READ_READY; | ||
371 | if (FD_ISSET (conn_sckt, ws)) | ||
372 | urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
373 | if (FD_ISSET (conn_sckt, es)) | ||
374 | urh->app.celi |= MHD_EPOLL_STATE_ERROR; | ||
375 | } | ||
376 | if ((MHD_INVALID_SOCKET != mhd_sckt)) | ||
377 | { | ||
378 | if (FD_ISSET (mhd_sckt, rs)) | ||
379 | urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; | ||
380 | if (FD_ISSET (mhd_sckt, ws)) | ||
381 | urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
382 | if (FD_ISSET (mhd_sckt, es)) | ||
383 | urh->mhd.celi |= MHD_EPOLL_STATE_ERROR; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | |||
388 | /** | ||
389 | * Internal version of #MHD_run_from_select(). | ||
390 | * | ||
391 | * @param daemon daemon to run select loop for | ||
392 | * @param read_fd_set read set | ||
393 | * @param write_fd_set write set | ||
394 | * @param except_fd_set except set | ||
395 | * @return #MHD_SC_OK on success | ||
396 | * @ingroup event | ||
397 | */ | ||
398 | static enum MHD_StatusCode | ||
399 | internal_run_from_select (struct MHD_Daemon *daemon, | ||
400 | const fd_set *read_fd_set, | ||
401 | const fd_set *write_fd_set, | ||
402 | const fd_set *except_fd_set) | ||
403 | { | ||
404 | MHD_socket ds; | ||
405 | struct MHD_Connection *pos; | ||
406 | struct MHD_Connection *prev; | ||
407 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
408 | struct MHD_UpgradeResponseHandle *urh; | ||
409 | struct MHD_UpgradeResponseHandle *urhn; | ||
410 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
411 | /* Reset. New value will be set when connections are processed. */ | ||
412 | /* Note: no-op for thread-per-connection as it is always false in that mode. */ | ||
413 | daemon->data_already_pending = false; | ||
414 | |||
415 | /* Clear ITC to avoid spinning select */ | ||
416 | /* Do it before any other processing so new signals | ||
417 | will trigger select again and will be processed */ | ||
418 | if ( (MHD_ITC_IS_VALID_(daemon->itc)) && | ||
419 | (FD_ISSET (MHD_itc_r_fd_ (daemon->itc), | ||
420 | read_fd_set)) ) | ||
421 | MHD_itc_clear_ (daemon->itc); | ||
422 | |||
423 | /* select connection thread handling type */ | ||
424 | if ( (MHD_INVALID_SOCKET != (ds = daemon->listen_socket)) && | ||
425 | (! daemon->was_quiesced) && | ||
426 | (FD_ISSET (ds, | ||
427 | read_fd_set)) ) | ||
428 | (void) MHD_accept_connection (daemon); | ||
429 | |||
430 | if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) | ||
431 | { | ||
432 | /* do not have a thread per connection, process all connections now */ | ||
433 | prev = daemon->connections_tail; | ||
434 | while (NULL != (pos = prev)) | ||
435 | { | ||
436 | prev = pos->prev; | ||
437 | ds = pos->socket_fd; | ||
438 | if (MHD_INVALID_SOCKET == ds) | ||
439 | continue; | ||
440 | call_handlers (pos, | ||
441 | FD_ISSET (ds, | ||
442 | read_fd_set), | ||
443 | FD_ISSET (ds, | ||
444 | write_fd_set), | ||
445 | FD_ISSET (ds, | ||
446 | except_fd_set)); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
451 | /* handle upgraded HTTPS connections */ | ||
452 | for (urh = daemon->urh_tail; NULL != urh; urh = urhn) | ||
453 | { | ||
454 | urhn = urh->prev; | ||
455 | /* update urh state based on select() output */ | ||
456 | urh_from_fdset (urh, | ||
457 | read_fd_set, | ||
458 | write_fd_set, | ||
459 | except_fd_set); | ||
460 | /* call generic forwarding function for passing data */ | ||
461 | process_urh (urh); | ||
462 | /* Finished forwarding? */ | ||
463 | if ( (0 == urh->in_buffer_size) && | ||
464 | (0 == urh->out_buffer_size) && | ||
465 | (0 == urh->in_buffer_used) && | ||
466 | (0 == urh->out_buffer_used) ) | ||
467 | { | ||
468 | MHD_connection_finish_forward_ (urh->connection); | ||
469 | urh->clean_ready = true; | ||
470 | /* Resuming will move connection to cleanup list. */ | ||
471 | MHD_resume_connection(urh->connection); | ||
472 | } | ||
473 | } | ||
474 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
475 | MHD_cleanup_connections (daemon); | ||
476 | return MHD_SC_OK; | ||
477 | } | ||
478 | |||
479 | |||
480 | /** | ||
481 | * Run webserver operations. This method should be called by clients | ||
482 | * in combination with #MHD_get_fdset and #MHD_get_timeout() if the | ||
483 | * client-controlled select method is used. | ||
484 | * | ||
485 | * You can use this function instead of #MHD_run if you called | ||
486 | * `select()` on the result from #MHD_get_fdset. File descriptors in | ||
487 | * the sets that are not controlled by MHD will be ignored. Calling | ||
488 | * this function instead of #MHD_run is more efficient as MHD will not | ||
489 | * have to call `select()` again to determine which operations are | ||
490 | * ready. | ||
491 | * | ||
492 | * This function cannot be used with daemon started with | ||
493 | * #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
494 | * | ||
495 | * @param daemon daemon to run select loop for | ||
496 | * @param read_fd_set read set | ||
497 | * @param write_fd_set write set | ||
498 | * @param except_fd_set except set | ||
499 | * @return #MHD_SC_OK on success | ||
500 | * @ingroup event | ||
501 | */ | ||
502 | enum MHD_StatusCode | ||
503 | MHD_daemon_run_from_select (struct MHD_Daemon *daemon, | ||
504 | const fd_set *read_fd_set, | ||
505 | |||
506 | const fd_set *write_fd_set, | ||
507 | const fd_set *except_fd_set) | ||
508 | { | ||
509 | if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) || | ||
510 | (MHD_ELS_POLL == daemon->event_loop_syscall) ) | ||
511 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_RUN_SELECT; | ||
512 | if (MHD_ELS_EPOLL == daemon->event_loop_syscall) | ||
513 | { | ||
514 | #ifdef EPOLL_SUPPORT | ||
515 | enum MHD_StatusCode sc; | ||
516 | |||
517 | sc = MHD_daemon_epoll_ (daemon, | ||
518 | MHD_NO); | ||
519 | MHD_cleanup_connections (daemon); | ||
520 | return sc; | ||
521 | #else /* ! EPOLL_SUPPORT */ | ||
522 | return MHD_NO; | ||
523 | #endif /* ! EPOLL_SUPPORT */ | ||
524 | } | ||
525 | |||
526 | /* Resuming external connections when using an extern mainloop */ | ||
527 | if (! daemon->disallow_suspend_resume) | ||
528 | resume_suspended_connections (daemon); | ||
529 | |||
530 | return internal_run_from_select (daemon, | ||
531 | read_fd_set, | ||
532 | write_fd_set, | ||
533 | except_fd_set); | ||
534 | } | ||
535 | |||
536 | |||
537 | /** | ||
538 | * Main internal select() call. Will compute select sets, call | ||
539 | * select() and then #internal_run_from_select() with the result. | ||
540 | * | ||
541 | * @param daemon daemon to run select() loop for | ||
542 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
543 | * @return #MHD_SC_OK on success | ||
544 | */ | ||
545 | enum MHD_StatusCode | ||
546 | MHD_daemon_select_ (struct MHD_Daemon *daemon, | ||
547 | int may_block) | ||
548 | { | ||
549 | int num_ready; | ||
550 | fd_set rs; | ||
551 | fd_set ws; | ||
552 | fd_set es; | ||
553 | MHD_socket maxsock; | ||
554 | struct timeval timeout; | ||
555 | struct timeval *tv; | ||
556 | MHD_UNSIGNED_LONG_LONG ltimeout; | ||
557 | int err_state; | ||
558 | MHD_socket ls; | ||
559 | enum MHD_StatusCode sc; | ||
560 | enum MHD_StatusCode sc2; | ||
561 | |||
562 | timeout.tv_sec = 0; | ||
563 | timeout.tv_usec = 0; | ||
564 | if (daemon->shutdown) | ||
565 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
566 | FD_ZERO (&rs); | ||
567 | FD_ZERO (&ws); | ||
568 | FD_ZERO (&es); | ||
569 | maxsock = MHD_INVALID_SOCKET; | ||
570 | sc = MHD_SC_OK; | ||
571 | if ( (! daemon->disallow_suspend_resume) && | ||
572 | (MHD_YES == resume_suspended_connections (daemon)) && | ||
573 | (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) ) | ||
574 | may_block = MHD_NO; | ||
575 | |||
576 | if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) | ||
577 | { | ||
578 | |||
579 | /* single-threaded, go over everything */ | ||
580 | if (MHD_SC_OK != | ||
581 | (sc = internal_get_fdset2 (daemon, | ||
582 | &rs, | ||
583 | &ws, | ||
584 | &es, | ||
585 | &maxsock, | ||
586 | FD_SETSIZE))) | ||
587 | { | ||
588 | #ifdef HAVE_MESSAGES | ||
589 | MHD_DLOG (daemon, | ||
590 | sc, | ||
591 | _("Could not obtain daemon fdsets")); | ||
592 | #endif | ||
593 | } | ||
594 | } | ||
595 | else | ||
596 | { | ||
597 | /* accept only, have one thread per connection */ | ||
598 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
599 | (! daemon->was_quiesced) && | ||
600 | (! MHD_add_to_fd_set_ (ls, | ||
601 | &rs, | ||
602 | &maxsock, | ||
603 | FD_SETSIZE)) ) | ||
604 | { | ||
605 | #ifdef HAVE_MESSAGES | ||
606 | MHD_DLOG (daemon, | ||
607 | MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, | ||
608 | _("Could not add listen socket to fdset")); | ||
609 | #endif | ||
610 | return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
611 | } | ||
612 | } | ||
613 | if ( (MHD_ITC_IS_VALID_(daemon->itc)) && | ||
614 | (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), | ||
615 | &rs, | ||
616 | &maxsock, | ||
617 | FD_SETSIZE)) ) | ||
618 | { | ||
619 | #if defined(MHD_WINSOCK_SOCKETS) | ||
620 | /* fdset limit reached, new connections | ||
621 | cannot be handled. Remove listen socket FD | ||
622 | from fdset and retry to add ITC FD. */ | ||
623 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
624 | (! daemon->was_quiesced) ) | ||
625 | { | ||
626 | FD_CLR (ls, | ||
627 | &rs); | ||
628 | if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_(daemon->itc), | ||
629 | &rs, | ||
630 | &maxsock, | ||
631 | FD_SETSIZE)) | ||
632 | { | ||
633 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
634 | sc = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
635 | #ifdef HAVE_MESSAGES | ||
636 | MHD_DLOG (daemon, | ||
637 | sc, | ||
638 | _("Could not add control inter-thread communication channel FD to fdset")); | ||
639 | #endif | ||
640 | #if defined(MHD_WINSOCK_SOCKETS) | ||
641 | } | ||
642 | } | ||
643 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
644 | } | ||
645 | /* Stop listening if we are at the configured connection limit */ | ||
646 | /* If we're at the connection limit, no point in really | ||
647 | accepting new connections; however, make sure we do not miss | ||
648 | the shutdown OR the termination of an existing connection; so | ||
649 | only do this optimization if we have a signaling ITC in | ||
650 | place. */ | ||
651 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
652 | (MHD_ITC_IS_VALID_(daemon->itc)) && | ||
653 | ( (daemon->connections == daemon->global_connection_limit) || | ||
654 | (daemon->at_limit) ) ) | ||
655 | { | ||
656 | FD_CLR (ls, | ||
657 | &rs); | ||
658 | } | ||
659 | tv = NULL; | ||
660 | if (MHD_SC_OK != sc) | ||
661 | may_block = MHD_NO; | ||
662 | if (MHD_NO == may_block) | ||
663 | { | ||
664 | timeout.tv_usec = 0; | ||
665 | timeout.tv_sec = 0; | ||
666 | tv = &timeout; | ||
667 | } | ||
668 | else if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) && | ||
669 | (MHD_SC_OK == | ||
670 | MHD_daemon_get_timeout (daemon, | ||
671 | <imeout)) ) | ||
672 | { | ||
673 | /* ltimeout is in ms */ | ||
674 | timeout.tv_usec = (ltimeout % 1000) * 1000; | ||
675 | if (ltimeout / 1000 > TIMEVAL_TV_SEC_MAX) | ||
676 | timeout.tv_sec = TIMEVAL_TV_SEC_MAX; | ||
677 | else | ||
678 | timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE)(ltimeout / 1000); | ||
679 | tv = &timeout; | ||
680 | } | ||
681 | num_ready = MHD_SYS_select_ (maxsock + 1, | ||
682 | &rs, | ||
683 | &ws, | ||
684 | &es, | ||
685 | tv); | ||
686 | if (daemon->shutdown) | ||
687 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
688 | if (num_ready < 0) | ||
689 | { | ||
690 | const int err = MHD_socket_get_error_ (); | ||
691 | |||
692 | if (MHD_SCKT_ERR_IS_EINTR_(err)) | ||
693 | return sc; | ||
694 | #ifdef HAVE_MESSAGES | ||
695 | MHD_DLOG (daemon, | ||
696 | MHD_SC_UNEXPECTED_SELECT_ERROR, | ||
697 | _("select failed: %s\n"), | ||
698 | MHD_socket_strerr_ (err)); | ||
699 | #endif | ||
700 | return MHD_SC_UNEXPECTED_SELECT_ERROR; | ||
701 | } | ||
702 | if (MHD_SC_OK != | ||
703 | (sc2 = internal_run_from_select (daemon, | ||
704 | &rs, | ||
705 | &ws, | ||
706 | &es))) | ||
707 | return sc2; | ||
708 | return sc; | ||
709 | } | ||
710 | |||
711 | /* end of daemon_select.c */ | ||
diff --git a/src/lib/daemon_select.h b/src/lib/daemon_select.h new file mode 100644 index 00000000..3625ea87 --- /dev/null +++ b/src/lib/daemon_select.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file lib/daemon_select.h | ||
22 | * @brief non-public functions provided by daemon_select.c | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | |||
26 | #ifndef DAEMON_SELECT_H | ||
27 | #define DAEMON_SELECT_H | ||
28 | |||
29 | /** | ||
30 | * Main internal select() call. Will compute select sets, call | ||
31 | * select() and then #internal_run_from_select() with the result. | ||
32 | * | ||
33 | * @param daemon daemon to run select() loop for | ||
34 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
35 | * @return #MHD_SC_OK on success | ||
36 | */ | ||
37 | enum MHD_StatusCode | ||
38 | MHD_daemon_select_ (struct MHD_Daemon *daemon, | ||
39 | int may_block) | ||
40 | MHD_NONNULL(1); | ||
41 | |||
42 | |||
43 | #endif | ||
diff --git a/src/lib/daemon_start.c b/src/lib/daemon_start.c index b0b1f3c3..d2db4d79 100644 --- a/src/lib/daemon_start.c +++ b/src/lib/daemon_start.c | |||
@@ -23,15 +23,7 @@ | |||
23 | * @author Christian Grothoff | 23 | * @author Christian Grothoff |
24 | */ | 24 | */ |
25 | #include "internal.h" | 25 | #include "internal.h" |
26 | 26 | #include "daemon_select.h" | |
27 | /* ************************* event loops ********************** */ | ||
28 | |||
29 | |||
30 | |||
31 | /* TODO: migrate! */ | ||
32 | |||
33 | |||
34 | /* ************* Functions for MHD_daemon_start() ************ */ | ||
35 | 27 | ||
36 | 28 | ||
37 | /** | 29 | /** |
@@ -626,17 +618,17 @@ MHD_polling_thread (void *cls) | |||
626 | MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style"); | 618 | MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style"); |
627 | break; | 619 | break; |
628 | case MHD_ELS_SELECT: | 620 | case MHD_ELS_SELECT: |
629 | MHD_select (daemon, | 621 | MHD_daemon_select_ (daemon, |
630 | MHD_YES); | 622 | MHD_YES); |
631 | break; | 623 | break; |
632 | case MHD_ELS_POLL: | 624 | case MHD_ELS_POLL: |
633 | MHD_poll (daemon, | 625 | MHD_daemon_poll_ (daemon, |
634 | MHD_YES); | 626 | MHD_YES); |
635 | break; | 627 | break; |
636 | case MHD_ELS_EPOLL: | 628 | case MHD_ELS_EPOLL: |
637 | #ifdef EPOLL_SUPPORT | 629 | #ifdef EPOLL_SUPPORT |
638 | MHD_epoll (daemon, | 630 | MHD_daemon_epoll_ (daemon, |
639 | MHD_YES); | 631 | MHD_YES); |
640 | #else | 632 | #else |
641 | MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier"); | 633 | MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier"); |
642 | #endif | 634 | #endif |
diff --git a/src/lib/internal.h b/src/lib/internal.h index 17e5c634..92b5b511 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h | |||
@@ -18,7 +18,7 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | /** | 20 | /** |
21 | * @file microhttpd/internal.h | 21 | * @file lib/internal.h |
22 | * @brief internal shared structures | 22 | * @brief internal shared structures |
23 | * @author Daniel Pittman | 23 | * @author Daniel Pittman |
24 | * @author Christian Grothoff | 24 | * @author Christian Grothoff |
@@ -801,6 +801,11 @@ struct MHD_Connection | |||
801 | bool suspended; | 801 | bool suspended; |
802 | 802 | ||
803 | /** | 803 | /** |
804 | * Are we ready to read from TLS for this connection? | ||
805 | */ | ||
806 | bool tls_read_ready; | ||
807 | |||
808 | /** | ||
804 | * Is the connection wanting to resume? | 809 | * Is the connection wanting to resume? |
805 | */ | 810 | */ |
806 | bool resuming; | 811 | bool resuming; |
@@ -1501,6 +1506,15 @@ struct MHD_Daemon | |||
1501 | bool disallow_upgrade; | 1506 | bool disallow_upgrade; |
1502 | 1507 | ||
1503 | /** | 1508 | /** |
1509 | * Did we hit some system or process-wide resource limit while | ||
1510 | * trying to accept() the last time? If so, we don't accept new | ||
1511 | * connections until we close an existing one. This effectively | ||
1512 | * temporarily lowers the "connection_limit" to the current | ||
1513 | * number of connections. | ||
1514 | */ | ||
1515 | bool at_limit; | ||
1516 | |||
1517 | /** | ||
1504 | * Disables optional calls to `shutdown()` and enables aggressive | 1518 | * Disables optional calls to `shutdown()` and enables aggressive |
1505 | * non-blocking optimistic reads and other potentially unsafe | 1519 | * non-blocking optimistic reads and other potentially unsafe |
1506 | * optimizations. See #MHD_daemon_enable_turbo(). | 1520 | * optimizations. See #MHD_daemon_enable_turbo(). |
@@ -1508,6 +1522,16 @@ struct MHD_Daemon | |||
1508 | bool enable_turbo; | 1522 | bool enable_turbo; |
1509 | 1523 | ||
1510 | /** | 1524 | /** |
1525 | * 'True' if some data is already waiting to be processed. If set | ||
1526 | * to 'true' - zero timeout for select()/poll*() is used. Should be | ||
1527 | * reset each time before processing connections and raised by any | ||
1528 | * connection which require additional immediately processing | ||
1529 | * (application does not provide data for response, data waiting in | ||
1530 | * TLS buffers etc.) | ||
1531 | */ | ||
1532 | bool data_already_pending; | ||
1533 | |||
1534 | /** | ||
1511 | * MHD_daemon_quiesce() was run against this daemon. | 1535 | * MHD_daemon_quiesce() was run against this daemon. |
1512 | */ | 1536 | */ |
1513 | bool was_quiesced; | 1537 | bool was_quiesced; |