GNU libmicrohttpd  0.9.62
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
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 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
203 
204 
213 enum MHD_StatusCode
214 MHD_daemon_epoll_ (struct MHD_Daemon *daemon,
215  bool may_block)
216 {
217 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
218  static const char * const upgrade_marker = "upgrade_ptr";
219 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
220  struct MHD_Connection *pos;
221  struct MHD_Connection *prev;
222  struct epoll_event events[MAX_EVENTS];
223  struct epoll_event event;
224  int timeout_ms;
225  MHD_UNSIGNED_LONG_LONG timeout_ll;
226  int num_events;
227  unsigned int i;
228  MHD_socket ls;
229 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
230  bool run_upgraded = false;
231 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
232 
233  if (-1 == daemon->epoll_fd)
234  return MHD_SC_EPOLL_FD_INVALID; /* we're down! */
235  if (daemon->shutdown)
236  return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
237  if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
238  (! daemon->was_quiesced) &&
239  (daemon->connections < daemon->global_connection_limit) &&
240  (! daemon->listen_socket_in_epoll) &&
241  (! daemon->at_limit) )
242  {
243  event.events = EPOLLIN;
244  event.data.ptr = daemon;
245  if (0 != epoll_ctl (daemon->epoll_fd,
246  EPOLL_CTL_ADD,
247  ls,
248  &event))
249  {
250 #ifdef HAVE_MESSAGES
251  MHD_DLOG (daemon,
252  MHD_SC_EPOLL_CTL_ADD_FAILED,
253  _("Call to epoll_ctl failed: %s\n"),
255 #endif
256  return MHD_SC_EPOLL_CTL_ADD_FAILED;
257  }
258  daemon->listen_socket_in_epoll = true;
259  }
260  if ( (daemon->was_quiesced) &&
261  (daemon->listen_socket_in_epoll) )
262  {
263  if (0 != epoll_ctl (daemon->epoll_fd,
264  EPOLL_CTL_DEL,
265  ls,
266  NULL))
267  MHD_PANIC ("Failed to remove listen FD from epoll set\n");
268  daemon->listen_socket_in_epoll = false;
269  }
270 
271 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
272  if ( (! daemon->upgrade_fd_in_epoll) &&
273  (-1 != daemon->epoll_upgrade_fd) )
274  {
275  event.events = EPOLLIN | EPOLLOUT;
276  event.data.ptr = (void *) upgrade_marker;
277  if (0 != epoll_ctl (daemon->epoll_fd,
278  EPOLL_CTL_ADD,
279  daemon->epoll_upgrade_fd,
280  &event))
281  {
282 #ifdef HAVE_MESSAGES
283  MHD_DLOG (daemon,
284  MHD_SC_EPOLL_CTL_ADD_FAILED,
285  _("Call to epoll_ctl failed: %s\n"),
287 #endif
288  return MHD_SC_EPOLL_CTL_ADD_FAILED;
289  }
290  daemon->upgrade_fd_in_epoll = true;
291  }
292 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
293  if ( (daemon->listen_socket_in_epoll) &&
294  ( (daemon->connections == daemon->global_connection_limit) ||
295  (daemon->at_limit) ||
296  (daemon->was_quiesced) ) )
297  {
298  /* we're at the connection limit, disable listen socket
299  for event loop for now */
300  if (0 != epoll_ctl (daemon->epoll_fd,
301  EPOLL_CTL_DEL,
302  ls,
303  NULL))
304  MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
305  daemon->listen_socket_in_epoll = false;
306  }
307 
308  if ( (! daemon->disallow_suspend_resume) &&
309  (MHD_resume_suspended_connections_ (daemon)) )
310  may_block = false;
311 
312  if (may_block)
313  {
314  if (MHD_SC_OK == /* FIXME: distinguish between NO_TIMEOUT and errors */
315  MHD_daemon_get_timeout (daemon,
316  &timeout_ll))
317  {
318  if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
319  timeout_ms = INT_MAX;
320  else
321  timeout_ms = (int) timeout_ll;
322  }
323  else
324  timeout_ms = -1;
325  }
326  else
327  timeout_ms = 0;
328 
329  /* Reset. New value will be set when connections are processed. */
330  /* Note: Used mostly for uniformity here as same situation is
331  * signaled in epoll mode by non-empty eready DLL. */
332  daemon->data_already_pending = false;
333 
334  /* drain 'epoll' event queue; need to iterate as we get at most
335  MAX_EVENTS in one system call here; in practice this should
336  pretty much mean only one round, but better an extra loop here
337  than unfair behavior... */
338  num_events = MAX_EVENTS;
339  while (MAX_EVENTS == num_events)
340  {
341  /* update event masks */
342  num_events = epoll_wait (daemon->epoll_fd,
343  events,
344  MAX_EVENTS,
345  timeout_ms);
346  if (-1 == num_events)
347  {
348  const int err = MHD_socket_get_error_ ();
349  if (MHD_SCKT_ERR_IS_EINTR_ (err))
350  return MHD_SC_OK;
351 #ifdef HAVE_MESSAGES
352  MHD_DLOG (daemon,
353  MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR,
354  _("Call to epoll_wait failed: %s\n"),
355  MHD_socket_strerr_ (err));
356 #endif
357  return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR;
358  }
359  for (i=0;i<(unsigned int) num_events;i++)
360  {
361  /* First, check for the values of `ptr` that would indicate
362  that this event is not about a normal connection. */
363  if (NULL == events[i].data.ptr)
364  continue; /* shutdown signal! */
365 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
366  if (upgrade_marker == events[i].data.ptr)
367  {
368  /* activity on an upgraded connection, we process
369  those in a separate epoll() */
370  run_upgraded = true;
371  continue;
372  }
373 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
374  if (daemon->epoll_itc_marker == events[i].data.ptr)
375  {
376  /* It's OK to clear ITC here as all external
377  conditions will be processed later. */
378  MHD_itc_clear_ (daemon->itc);
379  continue;
380  }
381  if (daemon == events[i].data.ptr)
382  {
383  /* Check for error conditions on listen socket. */
384  /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */
385  if (0 == (events[i].events & (EPOLLERR | EPOLLHUP)))
386  {
387  unsigned int series_length = 0;
388  /* Run 'accept' until it fails or daemon at limit of connections.
389  * Do not accept more then 10 connections at once. The rest will
390  * be accepted on next turn (level trigger is used for listen
391  * socket). */
392  while ( (MHD_SC_OK ==
393  MHD_accept_connection_ (daemon)) &&
394  (series_length < 10) &&
395  (daemon->connections < daemon->global_connection_limit) &&
396  (! daemon->at_limit) )
397  series_length++;
398  }
399  continue;
400  }
401  /* this is an event relating to a 'normal' connection,
402  remember the event and if appropriate mark the
403  connection as 'eready'. */
404  pos = events[i].data.ptr;
405  /* normal processing: update read/write data */
406  if (0 != (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
407  {
408  pos->epoll_state |= MHD_EPOLL_STATE_ERROR;
409  if (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
410  {
411  EDLL_insert (daemon->eready_head,
412  daemon->eready_tail,
413  pos);
414  pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
415  }
416  }
417  else
418  {
419  if (0 != (events[i].events & EPOLLIN))
420  {
421  pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
424  (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
425  {
426  EDLL_insert (daemon->eready_head,
427  daemon->eready_tail,
428  pos);
429  pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
430  }
431  }
432  if (0 != (events[i].events & EPOLLOUT))
433  {
434  pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
436  (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
437  {
438  EDLL_insert (daemon->eready_head,
439  daemon->eready_tail,
440  pos);
441  pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
442  }
443  }
444  }
445  }
446  }
447 
448 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
449  if (run_upgraded)
450  run_epoll_for_upgrade (daemon); /* FIXME: return value? */
451 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
452 
453  /* process events for connections */
454  prev = daemon->eready_tail;
455  while (NULL != (pos = prev))
456  {
457  prev = pos->prevE;
459  0 != (pos->epoll_state & MHD_EPOLL_STATE_READ_READY),
460  0 != (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY),
461  0 != (pos->epoll_state & MHD_EPOLL_STATE_ERROR));
464  {
466  0 == (pos->epoll_state & MHD_EPOLL_STATE_READ_READY) ) ||
468  0 == (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY) ) ||
470  {
471  EDLL_remove (daemon->eready_head,
472  daemon->eready_tail,
473  pos);
474  pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
475  }
476  }
477  }
478 
479  /* Finally, handle timed-out connections; we need to do this here
480  as the epoll mechanism won't call the 'MHD_request_handle_idle_()' on everything,
481  as the other event loops do. As timeouts do not get an explicit
482  event, we need to find those connections that might have timed out
483  here.
484 
485  Connections with custom timeouts must all be looked at, as we
486  do not bother to sort that (presumably very short) list. */
487  prev = daemon->manual_timeout_tail;
488  while (NULL != (pos = prev))
489  {
490  prev = pos->prevX;
492  }
493  /* Connections with the default timeout are sorted by prepending
494  them to the head of the list whenever we touch the connection;
495  thus it suffices to iterate from the tail until the first
496  connection is NOT timed out */
497  prev = daemon->normal_timeout_tail;
498  while (NULL != (pos = prev))
499  {
500  prev = pos->prevX;
502  if (MHD_REQUEST_CLOSED != pos->request.state)
503  break; /* sorted by timeout, no need to visit the rest! */
504  }
505  return MHD_SC_OK;
506 }
507 #endif
508 
509 /* end of daemon_epoll.c */
struct MHD_Request request
Definition: internal.h:714
#define MHD_PANIC(msg)
Definition: internal.h:68
bool data_already_pending
Definition: internal.h:1497
if(daemon->resuming)
void * data
Definition: microhttpd.h:2709
MHD_mutex_lock_chk_ & daemon
struct MHD_Connection * prevX
Definition: internal.h:667
size_t read_buffer_offset
Definition: internal.h:477
unsigned int global_connection_limit
Definition: internal.h:1348
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:507
#define MHD_socket_strerr_(err)
Definition: mhd_sockets.h:525
#define EDLL_insert(head, tail, element)
Definition: internal.h:1829
struct MHD_Connection * prev
non-public functions provided by daemon_epoll.c
int MHD_socket
Definition: microhttpd.h:181
internal shared structures
MHD_socket listen_socket
Definition: internal.h:1374
#define MHD_UNSIGNED_LONG_LONG
Definition: microhttpd.h:277
struct MHD_Daemon * daemon
Definition: internal.h:672
enum MHD_REQUEST_STATE state
Definition: internal.h:546
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:532
bool was_quiesced
Definition: internal.h:1502
enum MHD_StatusCode MHD_daemon_get_timeout(struct MHD_Daemon *daemon, MHD_UNSIGNED_LONG_LONG *timeout)
size_t read_buffer_size
Definition: internal.h:471
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:182
complete upgrade socket forwarding operation in TLS mode
enum MHD_RequestEventLoopInfo event_loop_info
Definition: internal.h:556
#define EDLL_remove(head, tail, element)
Definition: internal.h:1847
struct MHD_Connection * manual_timeout_tail
Definition: internal.h:1147
function to call event handlers based on event mask
enum MHD_StatusCode MHD_accept_connection_(struct MHD_Daemon *daemon)
#define NULL
Definition: reason_phrase.c:30
unsigned int connections
Definition: internal.h:1358
int MHD_connection_call_handlers_(struct MHD_Connection *con, bool read_ready, bool write_ready, bool force_close)
void MHD_request_resume(struct MHD_Request *request)
bool tls_read_ready
Definition: internal.h:766
struct MHD_itc_ itc
Definition: internal.h:1407
#define MHD_connection_finish_forward_(conn)
Definition: connection.h:156
struct MHD_Connection * normal_timeout_tail
Definition: internal.h:1132
bool at_limit
Definition: internal.h:1480
#define _(String)
Definition: mhd_options.h:42
bool disallow_suspend_resume
Definition: internal.h:1465
volatile bool shutdown
Definition: internal.h:1523
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:613
bool MHD_request_handle_idle_(struct MHD_Request *request)
function to process upgrade activity (over TLS)
functions to add connection to our active set
implementation of MHD_request_resume()