aboutsummaryrefslogtreecommitdiff
path: root/src/lib/daemon_poll.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/daemon_poll.c')
-rw-r--r--src/lib/daemon_poll.c453
1 files changed, 453 insertions, 0 deletions
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 */