| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- /*
- This file is part of libmicrohttpd
- Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- /**
- * @file lib/daemon_poll.c
- * @brief functions to run poll-based event loop
- * @author Christian Grothoff
- */
- #include "internal.h"
- #include "connection_add.h"
- #include "connection_call_handlers.h"
- #include "connection_finish_forward.h"
- #include "daemon_poll.h"
- #include "upgrade_process.h"
- #include "request_resume.h"
- #ifdef HAVE_POLL
- #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- /**
- * Set required 'event' members in 'pollfd' elements,
- * assuming that @a p[0].fd is MHD side of socketpair
- * and @a p[1].fd is TLS connected socket.
- *
- * @param urh upgrade handle to watch for
- * @param p pollfd array to update
- */
- static void
- urh_update_pollfd (struct MHD_UpgradeResponseHandle *urh,
- struct pollfd p[2])
- {
- p[0].events = 0;
- p[1].events = 0;
- if (urh->in_buffer_used < urh->in_buffer_size)
- p[0].events |= POLLIN;
- if (0 != urh->out_buffer_used)
- p[0].events |= POLLOUT;
- /* Do not monitor again for errors if error was detected before as
- * error state is remembered. */
- if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) &&
- ((0 != urh->in_buffer_size) ||
- (0 != urh->out_buffer_size) ||
- (0 != urh->out_buffer_used)))
- p[0].events |= MHD_POLL_EVENTS_ERR_DISC;
- if (urh->out_buffer_used < urh->out_buffer_size)
- p[1].events |= POLLIN;
- if (0 != urh->in_buffer_used)
- p[1].events |= POLLOUT;
- /* Do not monitor again for errors if error was detected before as
- * error state is remembered. */
- if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) &&
- ((0 != urh->out_buffer_size) ||
- (0 != urh->in_buffer_size) ||
- (0 != urh->in_buffer_used)))
- p[1].events |= MHD_POLL_EVENTS_ERR_DISC;
- }
- /**
- * Set @a p to watch for @a urh.
- *
- * @param urh upgrade handle to watch for
- * @param p pollfd array to set
- */
- static void
- urh_to_pollfd (struct MHD_UpgradeResponseHandle *urh,
- struct pollfd p[2])
- {
- p[0].fd = urh->connection->socket_fd;
- p[1].fd = urh->mhd.socket;
- urh_update_pollfd (urh,
- p);
- }
- /**
- * Update ready state in @a urh based on pollfd.
- * @param urh upgrade handle to update
- * @param p 'poll()' processed pollfd.
- */
- static void
- urh_from_pollfd (struct MHD_UpgradeResponseHandle *urh,
- struct pollfd p[2])
- {
- /* Reset read/write ready, preserve error state. */
- urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
- urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
- if (0 != (p[0].revents & POLLIN))
- urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
- if (0 != (p[0].revents & POLLOUT))
- urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
- if (0 != (p[0].revents & POLLHUP))
- urh->app.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY;
- if (0 != (p[0].revents & MHD_POLL_REVENTS_ERRROR))
- urh->app.celi |= MHD_EPOLL_STATE_ERROR;
- if (0 != (p[1].revents & POLLIN))
- urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
- if (0 != (p[1].revents & POLLOUT))
- urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
- if (0 != (p[1].revents & POLLHUP))
- urh->mhd.celi |= MHD_EPOLL_STATE_ERROR;
- if (0 != (p[1].revents & MHD_POLL_REVENTS_ERRROR))
- urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY;
- }
- #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
- /**
- * Process all of our connections and possibly the server
- * socket using poll().
- *
- * @param daemon daemon to run poll loop for
- * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
- * @return #MHD_SC_OK on success
- */
- enum MHD_StatusCode
- MHD_daemon_poll_all_ (struct MHD_Daemon *daemon,
- bool may_block)
- {
- unsigned int num_connections;
- struct MHD_Connection *pos;
- struct MHD_Connection *prev;
- #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- struct MHD_UpgradeResponseHandle *urh;
- struct MHD_UpgradeResponseHandle *urhn;
- #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
- if ( (! daemon->disallow_suspend_resume) &&
- (MHD_resume_suspended_connections_ (daemon)) )
- may_block = false;
- /* count number of connections and thus determine poll set size */
- num_connections = 0;
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- num_connections++;
- #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
- num_connections += 2;
- #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
- {
- MHD_UNSIGNED_LONG_LONG ltimeout;
- unsigned int i;
- int timeout;
- unsigned int poll_server;
- int poll_listen;
- int poll_itc_idx;
- struct pollfd *p;
- MHD_socket ls;
- p = MHD_calloc_ ((2 + num_connections),
- sizeof (struct pollfd));
- if (NULL == p)
- {
- #ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_POLL_MALLOC_FAILURE,
- _ ("Error allocating memory: %s\n"),
- MHD_strerror_ (errno));
- #endif
- return MHD_SC_POLL_MALLOC_FAILURE;
- }
- poll_server = 0;
- poll_listen = -1;
- if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
- (! daemon->was_quiesced) &&
- (daemon->connections < daemon->global_connection_limit) &&
- (! daemon->at_limit) )
- {
- /* only listen if we are not at the connection limit */
- p[poll_server].fd = ls;
- p[poll_server].events = POLLIN;
- p[poll_server].revents = 0;
- poll_listen = (int) poll_server;
- poll_server++;
- }
- poll_itc_idx = -1;
- if (MHD_ITC_IS_VALID_ (daemon->itc))
- {
- p[poll_server].fd = MHD_itc_r_fd_ (daemon->itc);
- p[poll_server].events = POLLIN;
- p[poll_server].revents = 0;
- poll_itc_idx = (int) poll_server;
- poll_server++;
- }
- if (! may_block)
- timeout = 0;
- else if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) ||
- (MHD_SC_OK != /* FIXME: distinguish between NO_TIMEOUT and errors! */
- MHD_daemon_get_timeout (daemon,
- <imeout)) )
- timeout = -1;
- else
- timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout;
- i = 0;
- for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev)
- {
- p[poll_server + i].fd = pos->socket_fd;
- switch (pos->request.event_loop_info)
- {
- case MHD_EVENT_LOOP_INFO_READ:
- p[poll_server + i].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC;
- break;
- case MHD_EVENT_LOOP_INFO_WRITE:
- p[poll_server + i].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC;
- break;
- case MHD_EVENT_LOOP_INFO_BLOCK:
- p[poll_server + i].events |= MHD_POLL_EVENTS_ERR_DISC;
- break;
- case MHD_EVENT_LOOP_INFO_CLEANUP:
- timeout = 0; /* clean up "pos" immediately */
- break;
- }
- i++;
- }
- #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev)
- {
- urh_to_pollfd (urh,
- &(p[poll_server + i]));
- i += 2;
- }
- #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
- if (0 == poll_server + num_connections)
- {
- free (p);
- return MHD_SC_OK;
- }
- if (MHD_sys_poll_ (p,
- poll_server + num_connections,
- timeout) < 0)
- {
- const int err = MHD_socket_get_error_ ();
- if (MHD_SCKT_ERR_IS_EINTR_ (err))
- {
- free (p);
- return MHD_SC_OK;
- }
- #ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_UNEXPECTED_POLL_ERROR,
- _ ("poll failed: %s\n"),
- MHD_socket_strerr_ (err));
- #endif
- free (p);
- return MHD_SC_UNEXPECTED_POLL_ERROR;
- }
- /* Reset. New value will be set when connections are processed. */
- daemon->data_already_pending = false;
- /* handle ITC FD */
- /* do it before any other processing so
- new signals will be processed in next loop */
- if ( (-1 != poll_itc_idx) &&
- (0 != (p[poll_itc_idx].revents & POLLIN)) )
- MHD_itc_clear_ (daemon->itc);
- /* handle shutdown */
- if (daemon->shutdown)
- {
- free (p);
- return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
- }
- i = 0;
- prev = daemon->connections_tail;
- while (NULL != (pos = prev))
- {
- prev = pos->prev;
- /* first, sanity checks */
- if (i >= num_connections)
- break; /* connection list changed somehow, retry later ... */
- if (p[poll_server + i].fd != pos->socket_fd)
- continue; /* fd mismatch, something else happened, retry later ... */
- MHD_connection_call_handlers_ (pos,
- 0 != (p[poll_server + i].revents & POLLIN),
- 0 != (p[poll_server + i].revents
- & POLLOUT),
- 0 != (p[poll_server + i].revents
- & MHD_POLL_REVENTS_ERR_DISC));
- i++;
- }
- #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- for (urh = daemon->urh_tail; NULL != urh; urh = urhn)
- {
- if (i >= num_connections)
- break; /* connection list changed somehow, retry later ... */
- /* Get next connection here as connection can be removed
- * from 'daemon->urh_head' list. */
- urhn = urh->prev;
- /* Check for fd mismatch. FIXME: required for safety? */
- if ((p[poll_server + i].fd != urh->connection->socket_fd) ||
- (p[poll_server + i + 1].fd != urh->mhd.socket))
- break;
- urh_from_pollfd (urh,
- &p[poll_server + i]);
- i += 2;
- MHD_upgrade_response_handle_process_ (urh);
- /* Finished forwarding? */
- if ( (0 == urh->in_buffer_size) &&
- (0 == urh->out_buffer_size) &&
- (0 == urh->in_buffer_used) &&
- (0 == urh->out_buffer_used) )
- {
- /* MHD_connection_finish_forward_() will remove connection from
- * 'daemon->urh_head' list. */
- MHD_connection_finish_forward_ (urh->connection);
- urh->clean_ready = true;
- /* If 'urh->was_closed' already was set to true, connection will be
- * moved immediately to cleanup list. Otherwise connection
- * will stay in suspended list until 'urh' will be marked
- * with 'was_closed' by application. */
- MHD_request_resume (&urh->connection->request);
- }
- }
- #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
- /* handle 'listen' FD */
- if ( (-1 != poll_listen) &&
- (0 != (p[poll_listen].revents & POLLIN)) )
- (void) MHD_accept_connection_ (daemon);
- free (p);
- }
- return MHD_SC_OK;
- }
- /**
- * Process only the listen socket using poll().
- *
- * @param daemon daemon to run poll loop for
- * @param may_block true if blocking, false if non-blocking
- * @return #MHD_SC_OK on success
- */
- enum MHD_StatusCode
- MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon,
- bool may_block)
- {
- struct pollfd p[2];
- int timeout;
- unsigned int poll_count;
- int poll_listen;
- int poll_itc_idx;
- MHD_socket ls;
- memset (&p,
- 0,
- sizeof (p));
- poll_count = 0;
- poll_listen = -1;
- poll_itc_idx = -1;
- if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
- (! daemon->was_quiesced) )
- {
- p[poll_count].fd = ls;
- p[poll_count].events = POLLIN;
- p[poll_count].revents = 0;
- poll_listen = poll_count;
- poll_count++;
- }
- if (MHD_ITC_IS_VALID_ (daemon->itc))
- {
- p[poll_count].fd = MHD_itc_r_fd_ (daemon->itc);
- p[poll_count].events = POLLIN;
- p[poll_count].revents = 0;
- poll_itc_idx = poll_count;
- poll_count++;
- }
- if (! daemon->disallow_suspend_resume)
- (void) MHD_resume_suspended_connections_ (daemon);
- if (! may_block)
- timeout = 0;
- else
- timeout = -1;
- if (0 == poll_count)
- return MHD_SC_OK;
- if (MHD_sys_poll_ (p,
- poll_count,
- timeout) < 0)
- {
- const int err = MHD_socket_get_error_ ();
- if (MHD_SCKT_ERR_IS_EINTR_ (err))
- return MHD_SC_OK;
- #ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_UNEXPECTED_POLL_ERROR,
- _ ("poll failed: %s\n"),
- MHD_socket_strerr_ (err));
- #endif
- return MHD_SC_UNEXPECTED_POLL_ERROR;
- }
- if ( (-1 != poll_itc_idx) &&
- (0 != (p[poll_itc_idx].revents & POLLIN)) )
- MHD_itc_clear_ (daemon->itc);
- /* handle shutdown */
- if (daemon->shutdown)
- return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
- if ( (-1 != poll_listen) &&
- (0 != (p[poll_listen].revents & POLLIN)) )
- (void) MHD_accept_connection_ (daemon);
- return MHD_SC_OK;
- }
- #endif
- /**
- * Do poll()-based processing.
- *
- * @param daemon daemon to run poll()-loop for
- * @param may_block true if blocking, false if non-blocking
- * @return #MHD_SC_OK on success
- */
- enum MHD_StatusCode
- MHD_daemon_poll_ (struct MHD_Daemon *daemon,
- bool may_block)
- {
- #ifdef HAVE_POLL
- if (daemon->shutdown)
- return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
- if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode)
- return MHD_daemon_poll_all_ (daemon,
- may_block);
- return MHD_daemon_poll_listen_socket_ (daemon,
- may_block);
- #else
- /* This code should be dead, as we should have checked
- this earlier... */
- return MHD_SC_POLL_NOT_SUPPORTED;
- #endif
- }
- #ifdef HAVE_POLL
- #ifdef HTTPS_SUPPORT
- /**
- * Process upgraded connection with a poll() loop.
- * We are in our own thread, only processing @a con
- *
- * @param con connection to process
- */
- void
- MHD_daemon_upgrade_connection_with_poll_ (struct MHD_Connection *con)
- {
- struct MHD_UpgradeResponseHandle *urh = con->request.urh;
- struct pollfd p[2];
- memset (p,
- 0,
- sizeof (p));
- p[0].fd = urh->connection->socket_fd;
- p[1].fd = urh->mhd.socket;
- while ( (0 != urh->in_buffer_size) ||
- (0 != urh->out_buffer_size) ||
- (0 != urh->in_buffer_used) ||
- (0 != urh->out_buffer_used) )
- {
- int timeout;
- urh_update_pollfd (urh,
- p);
- if ( (con->tls_read_ready) &&
- (urh->in_buffer_used < urh->in_buffer_size))
- timeout = 0; /* No need to wait if incoming data is already pending in TLS buffers. */
- else
- timeout = -1;
- if (MHD_sys_poll_ (p,
- 2,
- timeout) < 0)
- {
- const int err = MHD_socket_get_error_ ();
- if (MHD_SCKT_ERR_IS_EINTR_ (err))
- continue;
- #ifdef HAVE_MESSAGES
- MHD_DLOG (con->daemon,
- MHD_SC_UNEXPECTED_POLL_ERROR,
- _ ("Error during poll: `%s'\n"),
- MHD_socket_strerr_ (err));
- #endif
- break;
- }
- urh_from_pollfd (urh,
- p);
- MHD_upgrade_response_handle_process_ (urh);
- }
- }
- #endif
- #endif
- /* end of daemon_poll.c */
|