aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2018-02-15 05:42:11 +0100
committerChristian Grothoff <christian@grothoff.org>2018-02-15 05:45:28 +0100
commit78dc7915f89198b472b9e10ee532f9971bcc0ada (patch)
tree4e42b1b536d194c2c63c531292bb8adffdfa600e
parent9914072c16b773a828709ecc61062c37c864e858 (diff)
downloadlibmicrohttpd-78dc7915f89198b472b9e10ee532f9971bcc0ada.tar.gz
libmicrohttpd-78dc7915f89198b472b9e10ee532f9971bcc0ada.zip
migrating main parts of event loops
-rw-r--r--src/include/microhttpd2.h35
-rw-r--r--src/lib/Makefile.am5
-rw-r--r--src/lib/daemon_epoll.c504
-rw-r--r--src/lib/daemon_epoll.h46
-rw-r--r--src/lib/daemon_get_fdset.c269
-rw-r--r--src/lib/daemon_get_timeout.c74
-rw-r--r--src/lib/daemon_poll.c453
-rw-r--r--src/lib/daemon_poll.h71
-rw-r--r--src/lib/daemon_run.c30
-rw-r--r--src/lib/daemon_run_from_select.c59
-rw-r--r--src/lib/daemon_select.c711
-rw-r--r--src/lib/daemon_select.h43
-rw-r--r--src/lib/daemon_start.c22
-rw-r--r--src/lib/internal.h26
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 */
50static bool
51is_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 */
96static enum MHD_StatusCode
97run_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 */
208enum MHD_StatusCode
209MHD_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 */
39enum MHD_StatusCode
40MHD_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 */
58enum MHD_StatusCode
59MHD_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 */
87static enum MHD_StatusCode
88internal_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 */
231enum MHD_StatusCode
232MHD_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
48MHD_daemon_get_timeout (struct MHD_Daemon *daemon, 48MHD_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 */
40static void
41urh_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 */
81static void
82urh_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 */
97static void
98urh_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 */
133enum MHD_StatusCode
134MHD_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 &ltimeout)) )
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 */
351enum MHD_StatusCode
352MHD_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 */
434enum MHD_StatusCode
435MHD_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 */
38enum MHD_StatusCode
39MHD_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 */
51enum MHD_StatusCode
52MHD_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 */
64enum MHD_StatusCode
65MHD_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 @@
46enum MHD_StatusCode 49enum MHD_StatusCode
47MHD_daemon_run (struct MHD_Daemon *daemon) 50MHD_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 */
50enum MHD_StatusCode
51MHD_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 */
59enum MHD_StatusCode
60MHD_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 */
88static bool
89urh_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 */
171static enum MHD_StatusCode
172internal_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 */
306enum MHD_StatusCode
307MHD_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 */
354static void
355urh_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 */
398static enum MHD_StatusCode
399internal_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 */
502enum MHD_StatusCode
503MHD_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 */
545enum MHD_StatusCode
546MHD_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 &ltimeout)) )
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 */
37enum MHD_StatusCode
38MHD_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;