GNU libmicrohttpd 0.9.77
Loading...
Searching...
No Matches
daemon_poll.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*/
24#include "internal.h"
25#include "connection_add.h"
28#include "daemon_poll.h"
29#include "upgrade_process.h"
30#include "request_resume.h"
31
32
33#ifdef HAVE_POLL
34
35
36#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
37
46static void
47urh_update_pollfd (struct MHD_UpgradeResponseHandle *urh,
48 struct pollfd p[2])
49{
50 p[0].events = 0;
51 p[1].events = 0;
52
53 if (urh->in_buffer_used < urh->in_buffer_size)
54 p[0].events |= POLLIN;
55 if (0 != urh->out_buffer_used)
56 p[0].events |= POLLOUT;
57
58 /* Do not monitor again for errors if error was detected before as
59 * error state is remembered. */
60 if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) &&
61 ((0 != urh->in_buffer_size) ||
62 (0 != urh->out_buffer_size) ||
63 (0 != urh->out_buffer_used)))
64 p[0].events |= MHD_POLL_EVENTS_ERR_DISC;
65
66 if (urh->out_buffer_used < urh->out_buffer_size)
67 p[1].events |= POLLIN;
68 if (0 != urh->in_buffer_used)
69 p[1].events |= POLLOUT;
70
71 /* Do not monitor again for errors if error was detected before as
72 * error state is remembered. */
73 if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) &&
74 ((0 != urh->out_buffer_size) ||
75 (0 != urh->in_buffer_size) ||
76 (0 != urh->in_buffer_used)))
77 p[1].events |= MHD_POLL_EVENTS_ERR_DISC;
78}
79
80
87static void
88urh_to_pollfd (struct MHD_UpgradeResponseHandle *urh,
89 struct pollfd p[2])
90{
91 p[0].fd = urh->connection->socket_fd;
92 p[1].fd = urh->mhd.socket;
93 urh_update_pollfd (urh,
94 p);
95}
96
97
103static void
104urh_from_pollfd (struct MHD_UpgradeResponseHandle *urh,
105 struct pollfd p[2])
106{
107 /* Reset read/write ready, preserve error state. */
108 urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
109 urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
110
111 if (0 != (p[0].revents & POLLIN))
112 urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
113 if (0 != (p[0].revents & POLLOUT))
114 urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
115 if (0 != (p[0].revents & POLLHUP))
117 if (0 != (p[0].revents & MHD_POLL_REVENTS_ERRROR))
118 urh->app.celi |= MHD_EPOLL_STATE_ERROR;
119 if (0 != (p[1].revents & POLLIN))
120 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
121 if (0 != (p[1].revents & POLLOUT))
122 urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
123 if (0 != (p[1].revents & POLLHUP))
124 urh->mhd.celi |= MHD_EPOLL_STATE_ERROR;
125 if (0 != (p[1].revents & MHD_POLL_REVENTS_ERRROR))
127}
128
129
130#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
131
132
141enum MHD_StatusCode
142MHD_daemon_poll_all_ (struct MHD_Daemon *daemon,
143 bool may_block)
144{
145 unsigned int num_connections;
146 struct MHD_Connection *pos;
147 struct MHD_Connection *prev;
148#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
149 struct MHD_UpgradeResponseHandle *urh;
150 struct MHD_UpgradeResponseHandle *urhn;
151#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
152
153 if ( (! daemon->disallow_suspend_resume) &&
155 may_block = false;
156
157 /* count number of connections and thus determine poll set size */
158 num_connections = 0;
159 for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
160 num_connections++;
161#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
162 for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
163 num_connections += 2;
164#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
165 {
166 MHD_UNSIGNED_LONG_LONG ltimeout;
167 unsigned int i;
168 int timeout;
169 unsigned int poll_server;
170 int poll_listen;
171 int poll_itc_idx;
172 struct pollfd *p;
173 MHD_socket ls;
174
175 p = MHD_calloc_ ((2 + num_connections),
176 sizeof (struct pollfd));
177 if (NULL == p)
178 {
179#ifdef HAVE_MESSAGES
180 MHD_DLOG (daemon,
181 MHD_SC_POLL_MALLOC_FAILURE,
182 _ ("Error allocating memory: %s\n"),
183 MHD_strerror_ (errno));
184#endif
185 return MHD_SC_POLL_MALLOC_FAILURE;
186 }
187 poll_server = 0;
188 poll_listen = -1;
189 if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
190 (! daemon->was_quiesced) &&
191 (daemon->connections < daemon->global_connection_limit) &&
192 (! daemon->at_limit) )
193 {
194 /* only listen if we are not at the connection limit */
195 p[poll_server].fd = ls;
196 p[poll_server].events = POLLIN;
197 p[poll_server].revents = 0;
198 poll_listen = (int) poll_server;
199 poll_server++;
200 }
201 poll_itc_idx = -1;
202 if (MHD_ITC_IS_VALID_ (daemon->itc))
203 {
204 p[poll_server].fd = MHD_itc_r_fd_ (daemon->itc);
205 p[poll_server].events = POLLIN;
206 p[poll_server].revents = 0;
207 poll_itc_idx = (int) poll_server;
208 poll_server++;
209 }
210 if (! may_block)
211 timeout = 0;
212 else if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) ||
213 (MHD_SC_OK != /* FIXME: distinguish between NO_TIMEOUT and errors! */
215 &ltimeout)) )
216 timeout = -1;
217 else
218 timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout;
219
220 i = 0;
221 for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev)
222 {
223 p[poll_server + i].fd = pos->socket_fd;
224 switch (pos->request.event_loop_info)
225 {
227 p[poll_server + i].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC;
228 break;
230 p[poll_server + i].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC;
231 break;
233 p[poll_server + i].events |= MHD_POLL_EVENTS_ERR_DISC;
234 break;
236 timeout = 0; /* clean up "pos" immediately */
237 break;
238 }
239 i++;
240 }
241#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
242 for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev)
243 {
244 urh_to_pollfd (urh,
245 &(p[poll_server + i]));
246 i += 2;
247 }
248#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
249 if (0 == poll_server + num_connections)
250 {
251 free (p);
252 return MHD_SC_OK;
253 }
254 if (MHD_sys_poll_ (p,
255 poll_server + num_connections,
256 timeout) < 0)
257 {
258 const int err = MHD_socket_get_error_ ();
259 if (MHD_SCKT_ERR_IS_EINTR_ (err))
260 {
261 free (p);
262 return MHD_SC_OK;
263 }
264#ifdef HAVE_MESSAGES
265 MHD_DLOG (daemon,
266 MHD_SC_UNEXPECTED_POLL_ERROR,
267 _ ("poll failed: %s\n"),
268 MHD_socket_strerr_ (err));
269#endif
270 free (p);
271 return MHD_SC_UNEXPECTED_POLL_ERROR;
272 }
273
274 /* Reset. New value will be set when connections are processed. */
275 daemon->data_already_pending = false;
276
277 /* handle ITC FD */
278 /* do it before any other processing so
279 new signals will be processed in next loop */
280 if ( (-1 != poll_itc_idx) &&
281 (0 != (p[poll_itc_idx].revents & POLLIN)) )
282 MHD_itc_clear_ (daemon->itc);
283
284 /* handle shutdown */
285 if (daemon->shutdown)
286 {
287 free (p);
288 return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
289 }
290 i = 0;
291 prev = daemon->connections_tail;
292 while (NULL != (pos = prev))
293 {
294 prev = pos->prev;
295 /* first, sanity checks */
296 if (i >= num_connections)
297 break; /* connection list changed somehow, retry later ... */
298 if (p[poll_server + i].fd != pos->socket_fd)
299 continue; /* fd mismatch, something else happened, retry later ... */
301 0 != (p[poll_server + i].revents & POLLIN),
302 0 != (p[poll_server + i].revents
303 & POLLOUT),
304 0 != (p[poll_server + i].revents
305 & MHD_POLL_REVENTS_ERR_DISC));
306 i++;
307 }
308#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
309 for (urh = daemon->urh_tail; NULL != urh; urh = urhn)
310 {
311 if (i >= num_connections)
312 break; /* connection list changed somehow, retry later ... */
313
314 /* Get next connection here as connection can be removed
315 * from 'daemon->urh_head' list. */
316 urhn = urh->prev;
317 /* Check for fd mismatch. FIXME: required for safety? */
318 if ((p[poll_server + i].fd != urh->connection->socket_fd) ||
319 (p[poll_server + i + 1].fd != urh->mhd.socket))
320 break;
321 urh_from_pollfd (urh,
322 &p[poll_server + i]);
323 i += 2;
324 MHD_upgrade_response_handle_process_ (urh);
325 /* Finished forwarding? */
326 if ( (0 == urh->in_buffer_size) &&
327 (0 == urh->out_buffer_size) &&
328 (0 == urh->in_buffer_used) &&
329 (0 == urh->out_buffer_used) )
330 {
331 /* MHD_connection_finish_forward_() will remove connection from
332 * 'daemon->urh_head' list. */
333 MHD_connection_finish_forward_ (urh->connection);
334 urh->clean_ready = true;
335 /* If 'urh->was_closed' already was set to true, connection will be
336 * moved immediately to cleanup list. Otherwise connection
337 * will stay in suspended list until 'urh' will be marked
338 * with 'was_closed' by application. */
339 MHD_request_resume (&urh->connection->request);
340 }
341 }
342#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
343 /* handle 'listen' FD */
344 if ( (-1 != poll_listen) &&
345 (0 != (p[poll_listen].revents & POLLIN)) )
346 (void) MHD_accept_connection_ (daemon);
347
348 free (p);
349 }
350 return MHD_SC_OK;
351}
352
353
361enum MHD_StatusCode
362MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon,
363 bool may_block)
364{
365 struct pollfd p[2];
366 int timeout;
367 unsigned int poll_count;
368 int poll_listen;
369 int poll_itc_idx;
370 MHD_socket ls;
371
372 memset (&p,
373 0,
374 sizeof (p));
375 poll_count = 0;
376 poll_listen = -1;
377 poll_itc_idx = -1;
378 if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
379 (! daemon->was_quiesced) )
380
381 {
382 p[poll_count].fd = ls;
383 p[poll_count].events = POLLIN;
384 p[poll_count].revents = 0;
385 poll_listen = poll_count;
386 poll_count++;
387 }
388 if (MHD_ITC_IS_VALID_ (daemon->itc))
389 {
390 p[poll_count].fd = MHD_itc_r_fd_ (daemon->itc);
391 p[poll_count].events = POLLIN;
392 p[poll_count].revents = 0;
393 poll_itc_idx = poll_count;
394 poll_count++;
395 }
396
397 if (! daemon->disallow_suspend_resume)
398 (void) MHD_resume_suspended_connections_ (daemon);
399
400 if (! may_block)
401 timeout = 0;
402 else
403 timeout = -1;
404 if (0 == poll_count)
405 return MHD_SC_OK;
406 if (MHD_sys_poll_ (p,
407 poll_count,
408 timeout) < 0)
409 {
410 const int err = MHD_socket_get_error_ ();
411
412 if (MHD_SCKT_ERR_IS_EINTR_ (err))
413 return MHD_SC_OK;
414#ifdef HAVE_MESSAGES
415 MHD_DLOG (daemon,
416 MHD_SC_UNEXPECTED_POLL_ERROR,
417 _ ("poll failed: %s\n"),
418 MHD_socket_strerr_ (err));
419#endif
420 return MHD_SC_UNEXPECTED_POLL_ERROR;
421 }
422 if ( (-1 != poll_itc_idx) &&
423 (0 != (p[poll_itc_idx].revents & POLLIN)) )
424 MHD_itc_clear_ (daemon->itc);
425
426 /* handle shutdown */
427 if (daemon->shutdown)
428 return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
429 if ( (-1 != poll_listen) &&
430 (0 != (p[poll_listen].revents & POLLIN)) )
431 (void) MHD_accept_connection_ (daemon);
432 return MHD_SC_OK;
433}
434
435
436#endif
437
438
446enum MHD_StatusCode
448 bool may_block)
449{
450#ifdef HAVE_POLL
451 if (daemon->shutdown)
452 return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
453 if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode)
454 return MHD_daemon_poll_all_ (daemon,
455 may_block);
456 return MHD_daemon_poll_listen_socket_ (daemon,
457 may_block);
458#else
459 /* This code should be dead, as we should have checked
460 this earlier... */
461 return MHD_SC_POLL_NOT_SUPPORTED;
462#endif
463}
464
465
466#ifdef HAVE_POLL
467#ifdef HTTPS_SUPPORT
474void
475MHD_daemon_upgrade_connection_with_poll_ (struct MHD_Connection *con)
476{
477 struct MHD_UpgradeResponseHandle *urh = con->request.urh;
478 struct pollfd p[2];
479
480 memset (p,
481 0,
482 sizeof (p));
483 p[0].fd = urh->connection->socket_fd;
484 p[1].fd = urh->mhd.socket;
485
486 while ( (0 != urh->in_buffer_size) ||
487 (0 != urh->out_buffer_size) ||
488 (0 != urh->in_buffer_used) ||
489 (0 != urh->out_buffer_used) )
490 {
491 int timeout;
492
493 urh_update_pollfd (urh,
494 p);
495
496 if ( (con->tls_read_ready) &&
497 (urh->in_buffer_used < urh->in_buffer_size))
498 timeout = 0; /* No need to wait if incoming data is already pending in TLS buffers. */
499 else
500 timeout = -1;
501
502 if (MHD_sys_poll_ (p,
503 2,
504 timeout) < 0)
505 {
506 const int err = MHD_socket_get_error_ ();
507
508 if (MHD_SCKT_ERR_IS_EINTR_ (err))
509 continue;
510#ifdef HAVE_MESSAGES
511 MHD_DLOG (con->daemon,
512 MHD_SC_UNEXPECTED_POLL_ERROR,
513 _ ("Error during poll: `%s'\n"),
514 MHD_socket_strerr_ (err));
515#endif
516 break;
517 }
518 urh_from_pollfd (urh,
519 p);
520 MHD_upgrade_response_handle_process_ (urh);
521 }
522}
523
524
525#endif
526#endif
527
528/* end of daemon_poll.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)
function to call event handlers based on event mask
complete upgrade socket forwarding operation in TLS mode
enum MHD_StatusCode MHD_daemon_poll_(struct MHD_Daemon *daemon, bool may_block)
non-public functions provided by daemon_poll.c
enum MHD_StatusCode MHD_daemon_get_timeout(struct MHD_Daemon *daemon, MHD_UNSIGNED_LONG_LONG *timeout)
@ 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
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition mhd_compat.c:98
#define MHD_strerror_(errnum)
Definition mhd_compat.h:44
#define MHD_socket_strerr_(err)
#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
@ MHD_EVENT_LOOP_INFO_BLOCK
Definition internal.h:256
int MHD_socket
Definition microhttpd.h:207
#define MHD_UNSIGNED_LONG_LONG
Definition microhttpd.h:311
int fd
#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()
MHD_socket socket_fd
Definition internal.h:752
bool tls_read_ready
Definition internal.h:769
struct MHD_Request request
Definition internal.h:717
struct MHD_Connection * next
Definition internal.h:651
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
struct MHD_Connection * connections_head
Definition internal.h:1155
unsigned int connections
Definition internal.h:1361
struct MHD_itc_ itc
Definition internal.h:1410
volatile bool shutdown
Definition internal.h:1526
bool disallow_suspend_resume
Definition internal.h:1468
MHD_socket listen_socket
Definition internal.h:1377
enum MHD_ThreadingMode threading_mode
Definition internal.h:1417
unsigned int global_connection_limit
Definition internal.h:1351
struct MHD_Connection * connections_tail
Definition internal.h:1160
enum MHD_RequestEventLoopInfo event_loop_info
Definition internal.h:559
function to process upgrade activity (over TLS)