GNU libmicrohttpd  0.9.72
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 
55 static bool
56 is_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 
101 static enum MHD_StatusCode
102 run_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 
215 enum MHD_StatusCode
216 MHD_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 */
317  MHD_daemon_get_timeout (daemon,
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;
425  (pos->request.read_buffer_size >
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 */
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
void MHD_connection_finish_forward_(struct MHD_Connection *connection) MHD_NONNULL(1)
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)
Definition: mhd_sockets.h:542
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:549
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:523
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:634
#define NULL
Definition: reason_phrase.c:30
#define _(String)
Definition: mhd_options.h:42
internal shared structures
@ MHD_EVENT_LOOP_INFO_READ
Definition: internal.h:234
@ MHD_EVENT_LOOP_INFO_WRITE
Definition: internal.h:239
@ MHD_EVENT_LOOP_INFO_CLEANUP
Definition: internal.h:249
int MHD_socket
Definition: microhttpd.h:196
#define MHD_UNSIGNED_LONG_LONG
Definition: microhttpd.h:299
void * data
Definition: microhttpd.h:3053
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:197
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)