GNU libmicrohttpd 0.9.77
Loading...
Searching...
No Matches
daemon_epoll.c
Go to the documentation of this file.
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
25#include "internal.h"
26#include "connection_add.h"
29#include "daemon_epoll.h"
30#include "upgrade_process.h"
31#include "request_resume.h"
32
33#ifdef EPOLL_SUPPORT
34
43#define MAX_EVENTS 128
44
45
46#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
47
55static bool
56is_urh_ready (struct MHD_UpgradeResponseHandle *const urh)
57{
58 const struct MHD_Connection *const connection = urh->connection;
59
60 if ( (0 == urh->in_buffer_size) &&
61 (0 == urh->out_buffer_size) &&
62 (0 == urh->in_buffer_used) &&
63 (0 == urh->out_buffer_used) )
64 return false;
65
66 if (connection->daemon->shutdown)
67 return true;
68
69 if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) ||
70 (connection->tls_read_ready) ) &&
71 (urh->in_buffer_used < urh->in_buffer_size) )
72 return true;
73
74 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
75 (urh->out_buffer_used < urh->out_buffer_size) )
76 return true;
77
78 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
79 (urh->out_buffer_used > 0) )
80 return true;
81
82 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
83 (urh->in_buffer_used > 0) )
84 return true;
85
86 return false;
87}
88
89
101static enum MHD_StatusCode
102run_epoll_for_upgrade (struct MHD_Daemon *daemon)
103{
104 struct epoll_event events[MAX_EVENTS];
105 int num_events;
106 struct MHD_UpgradeResponseHandle *pos;
107 struct MHD_UpgradeResponseHandle *prev;
108
109 num_events = MAX_EVENTS;
110 while (MAX_EVENTS == num_events)
111 {
112 unsigned int i;
113
114 /* update event masks */
115 num_events = epoll_wait (daemon->epoll_upgrade_fd,
116 events,
117 MAX_EVENTS,
118 0);
119 if (-1 == num_events)
120 {
121 const int err = MHD_socket_get_error_ ();
122 if (MHD_SCKT_ERR_IS_EINTR_ (err))
123 return MHD_SC_OK;
124#ifdef HAVE_MESSAGES
125 MHD_DLOG (daemon,
126 MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR,
127 _ ("Call to epoll_wait failed: %s\n"),
128 MHD_socket_strerr_ (err));
129#endif
130 return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR;
131 }
132 for (i = 0; i < (unsigned int) num_events; i++)
133 {
134 struct UpgradeEpollHandle *const ueh = events[i].data.ptr;
135 struct MHD_UpgradeResponseHandle *const urh = ueh->urh;
136 bool new_err_state = false;
137
138 if (urh->clean_ready)
139 continue;
140
141 /* Update ueh state based on what is ready according to epoll() */
142 if (0 != (events[i].events & EPOLLIN))
143 ueh->celi |= MHD_EPOLL_STATE_READ_READY;
144 if (0 != (events[i].events & EPOLLOUT))
145 ueh->celi |= MHD_EPOLL_STATE_WRITE_READY;
146 if (0 != (events[i].events & EPOLLHUP))
148
149 if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) &&
150 (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) )
151 {
152 /* Process new error state only one time
153 * and avoid continuously marking this connection
154 * as 'ready'. */
155 ueh->celi |= MHD_EPOLL_STATE_ERROR;
156 new_err_state = true;
157 }
158
159 if (! urh->in_eready_list)
160 {
161 if (new_err_state ||
162 is_urh_ready (urh))
163 {
164 EDLL_insert (daemon->eready_urh_head,
165 daemon->eready_urh_tail,
166 urh);
167 urh->in_eready_list = true;
168 }
169 }
170 }
171 }
172 prev = daemon->eready_urh_tail;
173 while (NULL != (pos = prev))
174 {
175 prev = pos->prevE;
176 MHD_upgrade_response_handle_process_ (pos);
177 if (! is_urh_ready (pos))
178 {
179 EDLL_remove (daemon->eready_urh_head,
180 daemon->eready_urh_tail,
181 pos);
182 pos->in_eready_list = false;
183 }
184 /* Finished forwarding? */
185 if ( (0 == pos->in_buffer_size) &&
186 (0 == pos->out_buffer_size) &&
187 (0 == pos->in_buffer_used) &&
188 (0 == pos->out_buffer_used) )
189 {
190 MHD_connection_finish_forward_ (pos->connection);
191 pos->clean_ready = true;
192 /* If 'pos->was_closed' already was set to true, connection
193 * will be moved immediately to cleanup list. Otherwise
194 * connection will stay in suspended list until 'pos' will
195 * be marked with 'was_closed' by application. */
196 MHD_request_resume (&pos->connection->request);
197 }
198 }
199
200 return MHD_SC_OK;
201}
202
203
204#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
205
206
215enum MHD_StatusCode
216MHD_daemon_epoll_ (struct MHD_Daemon *daemon,
217 bool may_block)
218{
219#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
220 static const char *const upgrade_marker = "upgrade_ptr";
221#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
222 struct MHD_Connection *pos;
223 struct MHD_Connection *prev;
224 struct epoll_event events[MAX_EVENTS];
225 struct epoll_event event;
226 int timeout_ms;
227 MHD_UNSIGNED_LONG_LONG timeout_ll;
228 int num_events;
229 unsigned int i;
230 MHD_socket ls;
231#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
232 bool run_upgraded = false;
233#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
234
235 if (-1 == daemon->epoll_fd)
236 return MHD_SC_EPOLL_FD_INVALID; /* we're down! */
237 if (daemon->shutdown)
238 return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
239 if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
240 (! daemon->was_quiesced) &&
241 (daemon->connections < daemon->global_connection_limit) &&
242 (! daemon->listen_socket_in_epoll) &&
243 (! daemon->at_limit) )
244 {
245 event.events = EPOLLIN;
246 event.data.ptr = daemon;
247 if (0 != epoll_ctl (daemon->epoll_fd,
248 EPOLL_CTL_ADD,
249 ls,
250 &event))
251 {
252#ifdef HAVE_MESSAGES
253 MHD_DLOG (daemon,
254 MHD_SC_EPOLL_CTL_ADD_FAILED,
255 _ ("Call to epoll_ctl failed: %s\n"),
257#endif
258 return MHD_SC_EPOLL_CTL_ADD_FAILED;
259 }
260 daemon->listen_socket_in_epoll = true;
261 }
262 if ( (daemon->was_quiesced) &&
263 (daemon->listen_socket_in_epoll) )
264 {
265 if (0 != epoll_ctl (daemon->epoll_fd,
266 EPOLL_CTL_DEL,
267 ls,
268 NULL))
269 MHD_PANIC ("Failed to remove listen FD from epoll set.\n");
270 daemon->listen_socket_in_epoll = false;
271 }
272
273#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
274 if ( (! daemon->upgrade_fd_in_epoll) &&
275 (-1 != daemon->epoll_upgrade_fd) )
276 {
277 event.events = EPOLLIN | EPOLLOUT;
278 event.data.ptr = (void *) upgrade_marker;
279 if (0 != epoll_ctl (daemon->epoll_fd,
280 EPOLL_CTL_ADD,
281 daemon->epoll_upgrade_fd,
282 &event))
283 {
284#ifdef HAVE_MESSAGES
285 MHD_DLOG (daemon,
286 MHD_SC_EPOLL_CTL_ADD_FAILED,
287 _ ("Call to epoll_ctl failed: %s\n"),
289#endif
290 return MHD_SC_EPOLL_CTL_ADD_FAILED;
291 }
292 daemon->upgrade_fd_in_epoll = true;
293 }
294#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
295 if ( (daemon->listen_socket_in_epoll) &&
296 ( (daemon->connections == daemon->global_connection_limit) ||
297 (daemon->at_limit) ||
298 (daemon->was_quiesced) ) )
299 {
300 /* we're at the connection limit, disable listen socket
301 for event loop for now */
302 if (0 != epoll_ctl (daemon->epoll_fd,
303 EPOLL_CTL_DEL,
304 ls,
305 NULL))
306 MHD_PANIC (_ ("Failed to remove listen FD from epoll set.\n"));
307 daemon->listen_socket_in_epoll = false;
308 }
309
310 if ( (! daemon->disallow_suspend_resume) &&
312 may_block = false;
313
314 if (may_block)
315 {
316 if (MHD_SC_OK == /* FIXME: distinguish between NO_TIMEOUT and errors */
318 &timeout_ll))
319 {
320 if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
321 timeout_ms = INT_MAX;
322 else
323 timeout_ms = (int) timeout_ll;
324 }
325 else
326 timeout_ms = -1;
327 }
328 else
329 timeout_ms = 0;
330
331 /* Reset. New value will be set when connections are processed. */
332 /* Note: Used mostly for uniformity here as same situation is
333 * signaled in epoll mode by non-empty eready DLL. */
334 daemon->data_already_pending = false;
335
336 /* drain 'epoll' event queue; need to iterate as we get at most
337 MAX_EVENTS in one system call here; in practice this should
338 pretty much mean only one round, but better an extra loop here
339 than unfair behavior... */
340 num_events = MAX_EVENTS;
341 while (MAX_EVENTS == num_events)
342 {
343 /* update event masks */
344 num_events = epoll_wait (daemon->epoll_fd,
345 events,
346 MAX_EVENTS,
347 timeout_ms);
348 if (-1 == num_events)
349 {
350 const int err = MHD_socket_get_error_ ();
351 if (MHD_SCKT_ERR_IS_EINTR_ (err))
352 return MHD_SC_OK;
353#ifdef HAVE_MESSAGES
354 MHD_DLOG (daemon,
355 MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR,
356 _ ("Call to epoll_wait failed: %s\n"),
357 MHD_socket_strerr_ (err));
358#endif
359 return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR;
360 }
361 for (i = 0; i < (unsigned int) num_events; i++)
362 {
363 /* First, check for the values of `ptr` that would indicate
364 that this event is not about a normal connection. */
365 if (NULL == events[i].data.ptr)
366 continue; /* shutdown signal! */
367#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
368 if (upgrade_marker == events[i].data.ptr)
369 {
370 /* activity on an upgraded connection, we process
371 those in a separate epoll() */
372 run_upgraded = true;
373 continue;
374 }
375#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
376 if (daemon->epoll_itc_marker == events[i].data.ptr)
377 {
378 /* It's OK to clear ITC here as all external
379 conditions will be processed later. */
380 MHD_itc_clear_ (daemon->itc);
381 continue;
382 }
383 if (daemon == events[i].data.ptr)
384 {
385 /* Check for error conditions on listen socket. */
386 /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */
387 if (0 == (events[i].events & (EPOLLERR | EPOLLHUP)))
388 {
389 unsigned int series_length = 0;
390 /* Run 'accept' until it fails or daemon at limit of connections.
391 * Do not accept more then 10 connections at once. The rest will
392 * be accepted on next turn (level trigger is used for listen
393 * socket). */
394 while ( (MHD_SC_OK ==
395 MHD_accept_connection_ (daemon)) &&
396 (series_length < 10) &&
397 (daemon->connections < daemon->global_connection_limit) &&
398 (! daemon->at_limit) )
399 series_length++;
400 }
401 continue;
402 }
403 /* this is an event relating to a 'normal' connection,
404 remember the event and if appropriate mark the
405 connection as 'eready'. */
406 pos = events[i].data.ptr;
407 /* normal processing: update read/write data */
408 if (0 != (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
409 {
410 pos->epoll_state |= MHD_EPOLL_STATE_ERROR;
411 if (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
412 {
413 EDLL_insert (daemon->eready_head,
414 daemon->eready_tail,
415 pos);
416 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
417 }
418 }
419 else
420 {
421 if (0 != (events[i].events & EPOLLIN))
422 {
423 pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
426 pos->request.read_buffer_offset) ) &&
427 (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
428 {
429 EDLL_insert (daemon->eready_head,
430 daemon->eready_tail,
431 pos);
432 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
433 }
434 }
435 if (0 != (events[i].events & EPOLLOUT))
436 {
437 pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
439 (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
440 {
441 EDLL_insert (daemon->eready_head,
442 daemon->eready_tail,
443 pos);
444 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
445 }
446 }
447 }
448 }
449 }
450
451#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
452 if (run_upgraded)
453 run_epoll_for_upgrade (daemon); /* FIXME: return value? */
454#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
455
456 /* process events for connections */
457 prev = daemon->eready_tail;
458 while (NULL != (pos = prev))
459 {
460 prev = pos->prevE;
462 0 != (pos->epoll_state
464 0 != (pos->epoll_state
466 0 != (pos->epoll_state
469 (pos->epoll_state & (MHD_EPOLL_STATE_SUSPENDED
471 {
473 (0 == (pos->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ||
475 (0 == (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) ) ||
477 {
478 EDLL_remove (daemon->eready_head,
479 daemon->eready_tail,
480 pos);
481 pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
482 }
483 }
484 }
485
486 /* Finally, handle timed-out connections; we need to do this here
487 as the epoll mechanism won't call the 'MHD_request_handle_idle_()' on everything,
488 as the other event loops do. As timeouts do not get an explicit
489 event, we need to find those connections that might have timed out
490 here.
491
492 Connections with custom timeouts must all be looked at, as we
493 do not bother to sort that (presumably very short) list. */prev = daemon->manual_timeout_tail;
494 while (NULL != (pos = prev))
495 {
496 prev = pos->prevX;
498 }
499 /* Connections with the default timeout are sorted by prepending
500 them to the head of the list whenever we touch the connection;
501 thus it suffices to iterate from the tail until the first
502 connection is NOT timed out */
503 prev = daemon->normal_timeout_tail;
504 while (NULL != (pos = prev))
505 {
506 prev = pos->prevX;
508 if (MHD_REQUEST_CLOSED != pos->request.state)
509 break; /* sorted by timeout, no need to visit the rest! */
510 }
511 return MHD_SC_OK;
512}
513
514
515#endif
516
517/* end of daemon_epoll.c */
#define MHD_connection_finish_forward_(conn)
Definition connection.h:165
enum MHD_StatusCode MHD_accept_connection_(struct MHD_Daemon *daemon)
functions to add connection to our active set
int MHD_connection_call_handlers_(struct MHD_Connection *con, bool read_ready, bool write_ready, bool force_close)
bool MHD_request_handle_idle_(struct MHD_Request *request)
function to call event handlers based on event mask
complete upgrade socket forwarding operation in TLS mode
non-public functions provided by daemon_epoll.c
enum MHD_StatusCode MHD_daemon_get_timeout(struct MHD_Daemon *daemon, MHD_UNSIGNED_LONG_LONG *timeout)
@ MHD_EPOLL_STATE_SUSPENDED
Definition internal.h:621
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition internal.h:611
@ MHD_EPOLL_STATE_READ_READY
Definition internal.h:600
@ MHD_EPOLL_STATE_WRITE_READY
Definition internal.h:606
@ MHD_EPOLL_STATE_ERROR
Definition internal.h:626
#define EDLL_insert(head, tail, element)
Definition internal.h:1829
#define MHD_PANIC(msg)
Definition internal.h:69
#define EDLL_remove(head, tail, element)
Definition internal.h:1847
@ MHD_REQUEST_CLOSED
Definition internal.h:321
#define MHD_socket_strerr_(err)
#define MHD_socket_last_strerr_()
#define MHD_socket_get_error_()
#define MHD_SCKT_ERR_IS_EINTR_(err)
#define NULL
#define _(String)
Definition mhd_options.h:42
MHD internal shared structures.
@ MHD_EVENT_LOOP_INFO_READ
Definition internal.h:246
@ MHD_EVENT_LOOP_INFO_WRITE
Definition internal.h:251
@ MHD_EVENT_LOOP_INFO_CLEANUP
Definition internal.h:261
int MHD_socket
Definition microhttpd.h:207
#define MHD_UNSIGNED_LONG_LONG
Definition microhttpd.h:311
void * data
#define MHD_INVALID_SOCKET
Definition microhttpd.h:208
void MHD_request_resume(struct MHD_Request *request)
bool MHD_resume_suspended_connections_(struct MHD_Daemon *daemon)
implementation of MHD_request_resume()
struct MHD_Connection * prevX
Definition internal.h:670
bool tls_read_ready
Definition internal.h:769
struct MHD_Request request
Definition internal.h:717
struct MHD_Connection * prev
Definition internal.h:656
struct MHD_Daemon * daemon
Definition internal.h:675
bool data_already_pending
Definition internal.h:1500
bool at_limit
Definition internal.h:1483
bool was_quiesced
Definition internal.h:1505
unsigned int connections
Definition internal.h:1361
struct MHD_itc_ itc
Definition internal.h:1410
struct MHD_Connection * manual_timeout_tail
Definition internal.h:1150
volatile bool shutdown
Definition internal.h:1526
bool disallow_suspend_resume
Definition internal.h:1468
MHD_socket listen_socket
Definition internal.h:1377
struct MHD_Connection * normal_timeout_tail
Definition internal.h:1135
unsigned int global_connection_limit
Definition internal.h:1351
size_t read_buffer_size
Definition internal.h:474
size_t read_buffer_offset
Definition internal.h:480
enum MHD_RequestEventLoopInfo event_loop_info
Definition internal.h:559
enum MHD_REQUEST_STATE state
Definition internal.h:549
function to process upgrade activity (over TLS)