libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 260dd93fdabe89e8c715286c20e23124a79c2b54
parent d4a325b3edd24ba5ac1957898fd089b3a810a84f
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue,  1 Mar 2011 15:09:14 +0000

enable poll more broadly, fix timeout, use pipe instead of signal

Diffstat:
MChangeLog | 5+++++
Msrc/daemon/daemon.c | 380++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/daemon/internal.h | 5+++++
Msrc/examples/minimal_example.c | 5++++-
Msrc/include/microhttpd.h | 2+-
5 files changed, 237 insertions(+), 160 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,8 @@ +Tue Mar 1 13:58:04 CET 2011 + Allow use of 'poll' in combination with the external select mode. + Avoid using pthread signals (SIGALRM), use pipe instead. + Corrected timeout calculation (s vs. ms). -CG + Wed Feb 23 14:21:44 CET 2011 Removing useless code pointed out by Eivind Sarto. -CG diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -80,14 +80,6 @@ #endif #endif -#ifdef __SYMBIAN32__ -static void pthread_kill (int, int) { - // Symbian doesn't have signals. The user of the library is required to - // run it in an external select loop. - abort(); -} -#endif // __SYMBIAN32__ - /** * Default implementation of the panic function */ @@ -572,45 +564,54 @@ MHD_handle_connection (void *data) fd_set es; int max; struct timeval tv; + struct timeval *tvp; unsigned int timeout; + time_t now; #ifdef HAVE_POLL_H struct MHD_Pollfd mp; - struct pollfd p; + struct pollfd p[2]; #endif timeout = con->daemon->connection_timeout; while ((!con->daemon->shutdown) && (con->socket_fd != -1)) { - tv.tv_usec = 0; - if ( (timeout > (time (NULL) - con->last_activity)) || - (timeout == 0) ) + tvp = NULL; + if (timeout > 0) { - /* in case we are missing the SIGALRM, keep going after - at most 1s; see http://lists.gnu.org/archive/html/libmicrohttpd/2009-10/msg00013.html */ - tv.tv_sec = 1; - if ((con->state == MHD_CONNECTION_NORMAL_BODY_UNREADY) || - (con->state == MHD_CONNECTION_CHUNKED_BODY_UNREADY)) - { - /* do not block (we're waiting for our callback to succeed) */ - tv.tv_sec = 0; - } + now = time (NULL); + if (now - con->last_activity > timeout) + tv.tv_sec = 0; + else + tv.tv_sec = timeout - (now - con->last_activity); + tvp = &tv; } - else + if ((con->state == MHD_CONNECTION_NORMAL_BODY_UNREADY) || + (con->state == MHD_CONNECTION_CHUNKED_BODY_UNREADY)) { + /* do not block (we're waiting for our callback to succeed) */ tv.tv_sec = 0; + tv.tv_usec = 0; + tvp = &tv; } #ifdef HAVE_POLL_H - if (0 == (con->daemon->options & MHD_USE_POLL)) { + if (0 == (con->daemon->options & MHD_USE_POLL)) + { #else - { + { #endif - /* use select */ - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - max = 0; - MHD_connection_get_fdset (con, &rs, &ws, &es, &max); - num_ready = SELECT (max + 1, &rs, &ws, &es, &tv); - if (num_ready < 0) { + /* use select */ + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + if (-1 != con->daemon->wpipe[0]) + { + max = con->daemon->wpipe[0]; + FD_SET (con->daemon->wpipe[0], &rs); + } + else + max = 0; + MHD_connection_get_fdset (con, &rs, &ws, &es, &max); + num_ready = SELECT (max + 1, &rs, &ws, &es, tvp); + if (num_ready < 0) { if (errno == EINTR) continue; #if HAVE_MESSAGES @@ -618,52 +619,54 @@ MHD_handle_connection (void *data) STRERROR (errno)); #endif break; - } - /* call appropriate connection handler if necessary */ - if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &rs))) - con->read_handler (con); - if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws))) - con->write_handler (con); - if (con->socket_fd != -1) - con->idle_handler (con); - } + } + /* call appropriate connection handler if necessary */ + if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &rs))) + con->read_handler (con); + if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws))) + con->write_handler (con); + if (con->socket_fd != -1) + con->idle_handler (con); + } #ifdef HAVE_POLL_H else - { - /* use poll */ - memset(&mp, 0, sizeof (struct MHD_Pollfd)); - MHD_connection_get_pollfd(con, &mp); - memset(&p, 0, sizeof (struct pollfd)); - p.fd = mp.fd; - if (mp.events & MHD_POLL_ACTION_IN) - p.events |= POLLIN; - if (mp.events & MHD_POLL_ACTION_OUT) - p.events |= POLLOUT; - /* in case we are missing the SIGALRM, keep going after - at most 1s */ - if (poll (&p, 1, 1000) < 0) { - if (errno == EINTR) - continue; + { + /* use poll */ + memset(&mp, 0, sizeof (struct MHD_Pollfd)); + MHD_connection_get_pollfd(con, &mp); + memset(&p, 0, sizeof (p)); + p[0].fd = mp.fd; + if (mp.events & MHD_POLL_ACTION_IN) + p[0].events |= POLLIN; + if (mp.events & MHD_POLL_ACTION_OUT) + p[0].events |= POLLOUT; + p[1].fd = con->daemon->wpipe[0]; + p[1].events |= POLLIN; + + if (poll (p, 2, (tvp == NULL) ? -1 : (tv.tv_sec * 1000)) < 0) + { + if (errno == EINTR) + continue; #if HAVE_MESSAGES - MHD_DLOG (con->daemon, "Error during poll: `%s'\n", - STRERROR (errno)); + MHD_DLOG (con->daemon, "Error during poll: `%s'\n", + STRERROR (errno)); #endif - break; - } - if ( (con->socket_fd != -1) && - (0 != (p.revents & POLLIN)) ) - con->read_handler (con); - if ( (con->socket_fd != -1) && - (0 != (p.revents & POLLOUT)) ) - con->write_handler (con); - if (con->socket_fd != -1) - con->idle_handler (con); - if ( (con->socket_fd != -1) && - (0 != (p.revents & (POLLERR | POLLHUP))) ) - MHD_connection_close (con, MHD_REQUEST_TERMINATED_WITH_ERROR); - } + break; + } + if ( (con->socket_fd != -1) && + (0 != (p[0].revents & POLLIN)) ) + con->read_handler (con); + if ( (con->socket_fd != -1) && + (0 != (p[0].revents & POLLOUT)) ) + con->write_handler (con); + if (con->socket_fd != -1) + con->idle_handler (con); + if ( (con->socket_fd != -1) && + (0 != (p[0].revents & (POLLERR | POLLHUP))) ) + MHD_connection_close (con, MHD_REQUEST_TERMINATED_WITH_ERROR); + } #endif - } + } if (con->socket_fd != -1) { #if DEBUG_CLOSE @@ -1085,7 +1088,7 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) prev->next = pos->next; if (0 != (pos->daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { - pthread_kill (pos->pid, SIGALRM); + if (0 != (rc = pthread_join (pos->pid, &unused))) { #if HAVE_MESSAGES @@ -1148,7 +1151,7 @@ MHD_get_timeout (struct MHD_Daemon *daemon, unsigned MHD_LONG_LONG *timeout) return MHD_NO; /* no connections */ now = time (NULL); /* start with conservative estimate */ - earliest_deadline = now + dto; + earliest_deadline = now + dto + 1; while (pos != NULL) { if (earliest_deadline > pos->last_activity + dto) @@ -1163,7 +1166,7 @@ MHD_get_timeout (struct MHD_Daemon *daemon, unsigned MHD_LONG_LONG *timeout) if (earliest_deadline < now) *timeout = 0; else - *timeout = (earliest_deadline - now); + *timeout = 1000 * (1 + earliest_deadline - now); return MHD_YES; } @@ -1185,6 +1188,7 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) fd_set es; int max; struct timeval timeout; + struct timeval *tv; unsigned MHD_LONG_LONG ltimeout; int ds; @@ -1195,8 +1199,13 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) FD_ZERO (&rs); FD_ZERO (&ws); FD_ZERO (&es); - max = 0; - + if (-1 != daemon->wpipe[0]) + { + max = daemon->wpipe[0]; + FD_SET (daemon->wpipe[0], &rs); + } + else + max = 0; if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { /* single-threaded, go over everything */ @@ -1217,26 +1226,21 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) FD_SET (max, &rs); } - /* in case we are missing the SIGALRM, keep going after - at most 1s; see http://lists.gnu.org/archive/html/libmicrohttpd/2009-10/msg00013.html */ - timeout.tv_usec = 0; - timeout.tv_sec = 1; + tv = NULL; if (may_block == MHD_NO) { timeout.tv_usec = 0; timeout.tv_sec = 0; + tv = &timeout; } - else + else if (MHD_YES == MHD_get_timeout (daemon, &ltimeout)) { /* ltimeout is in ms */ - if ( (MHD_YES == MHD_get_timeout (daemon, &ltimeout)) && - (ltimeout < 1000) ) - { - timeout.tv_usec = ltimeout * 1000; - timeout.tv_sec = 0; - } + timeout.tv_usec = (ltimeout % 1000) * 1000; + timeout.tv_sec = ltimeout / 1000; + tv = &timeout; } - num_ready = SELECT (max + 1, &rs, &ws, &es, &timeout); + num_ready = SELECT (max + 1, &rs, &ws, &es, tv); if (daemon->shutdown == MHD_YES) return MHD_NO; @@ -1279,38 +1283,93 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) return MHD_YES; } + /** - * Poll for new connection. Used only with THREAD_PER_CONNECTION + * Poll for new connection. * * @param daemon daemon to run poll loop for + * @param may_block YES if blocking, NO if non-blocking + * @return MHD_NO on serious errors, MHD_YES on success */ static int -MHD_poll (struct MHD_Daemon *daemon) +MHD_poll (struct MHD_Daemon *daemon, + int may_block) { #ifdef HAVE_POLL_H - struct pollfd p; - - if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) - return MHD_NO; - p.fd = daemon->socket_fd; - p.events = POLLIN; - p.revents = 0; + unsigned int num_connections; + struct MHD_Connection *pos; - if (poll(&p, 1, 1000) < 0) { - if (errno == EINTR) - return MHD_YES; + num_connections = 0; + pos = daemon->connections; + while (pos != NULL) + { + num_connections++; + pos = pos->next; + } + { + struct pollfd p[2 + num_connections]; + struct MHD_Pollfd mp; + unsigned MHD_LONG_LONG ltimeout; + unsigned int i; + int timeout; + + p[0].fd = daemon->socket_fd; + p[0].events = POLLIN; + p[0].revents = 0; + p[1].fd = daemon->wpipe[0]; + p[1].events = POLLIN; + p[1].revents = 0; + if (may_block == MHD_NO) + timeout = 0; + else if (MHD_YES != MHD_get_timeout (daemon, &ltimeout)) + timeout = -1; + else + timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout; + + i = 0; + pos = daemon->connections; + while (pos != NULL) + { + memset(&mp, 0, sizeof (struct MHD_Pollfd)); + MHD_connection_get_pollfd (pos, &mp); + memset(&p, 0, sizeof (p)); + p[2+i].fd = mp.fd; + if (mp.events & MHD_POLL_ACTION_IN) + p[2+i].events |= POLLIN; + if (mp.events & MHD_POLL_ACTION_OUT) + p[2+i].events |= POLLOUT; + i++; + pos = pos->next; + } + if (poll (p, 2 + num_connections, timeout) < 0) { + if (errno == EINTR) + return MHD_YES; #if HAVE_MESSAGES - MHD_DLOG (daemon, "poll failed: %s\n", STRERROR (errno)); + MHD_DLOG (daemon, "poll failed: %s\n", STRERROR (errno)); #endif - return MHD_NO; + return MHD_NO; + } + /* handle shutdown cases */ + if (daemon->shutdown == MHD_YES) + return MHD_NO; + if (daemon->socket_fd < 0) + return MHD_YES; + if (0 != (p[0].revents & POLLIN)) + MHD_accept_connection (daemon); + i = 0; + pos = daemon->connections; + while (pos != NULL) + { + if (0 != (p[2+i].revents & POLLIN)) + pos->read_handler (pos); + if (0 != (p[2+i].revents & POLLOUT)) + pos->write_handler (pos); + if (pos->socket_fd != -1) + pos->idle_handler (pos); + i++; + pos = pos->next; + } } - /* handle shutdown cases */ - if (daemon->shutdown == MHD_YES) - return MHD_NO; - if (daemon->socket_fd < 0) - return MHD_YES; - if (0 != (p.revents & POLLIN)) - MHD_accept_connection (daemon); return MHD_YES; #else return MHD_NO; @@ -1335,7 +1394,10 @@ MHD_run (struct MHD_Daemon *daemon) & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))) return MHD_NO; - MHD_select (daemon, MHD_NO); + if ((daemon->options & MHD_USE_POLL) == 0) + MHD_select (daemon, MHD_NO); + else + MHD_poll (daemon, MHD_NO); MHD_cleanup_connections (daemon); return MHD_YES; } @@ -1357,7 +1419,7 @@ MHD_select_thread (void *cls) if ((daemon->options & MHD_USE_POLL) == 0) MHD_select (daemon, MHD_YES); else - MHD_poll(daemon); + MHD_poll (daemon, MHD_YES); MHD_cleanup_connections (daemon); } return NULL; @@ -1725,7 +1787,7 @@ MHD_start_daemon_va (unsigned int options, } #endif retVal->socket_fd = -1; - retVal->options = (enum MHD_OPTION)options; + retVal->options = (enum MHD_OPTION) options; retVal->port = port; retVal->apc = apc; retVal->apc_cls = apc_cls; @@ -1735,6 +1797,37 @@ MHD_start_daemon_va (unsigned int options, retVal->pool_size = MHD_POOL_SIZE_DEFAULT; retVal->unescape_callback = &MHD_http_unescape; retVal->connection_timeout = 0; /* no timeout */ + retVal->wpipe[0] = -1; + retVal->wpipe[1] = -1; + if ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || + (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) + { + if (0 != pipe (retVal->wpipe)) + { +#if HAVE_MESSAGES + FPRINTF(stderr, + "Failed to create control pipe: %s\n", + STRERROR (errno)); +#endif + free (retVal); + return NULL; + } +#ifndef WINDOWS + if ( (0 == (options & MHD_USE_POLL)) && + (retVal->wpipe[0] >= FD_SETSIZE) ) + { +#if HAVE_MESSAGES + FPRINTF(stderr, + "file descriptor for control pipe exceeds maximum value\n"); +#endif + close (retVal->wpipe[0]); + close (retVal->wpipe[1]); + free (retVal); + return NULL; + } +#endif + } + #ifdef DAUTH_SUPPORT retVal->digest_auth_rand_size = 0; retVal->digest_auth_random = NULL; @@ -1809,22 +1902,6 @@ MHD_start_daemon_va (unsigned int options, } #endif - /* poll support currently only works with MHD_USE_THREAD_PER_CONNECTION */ - if ( (0 != (options & MHD_USE_POLL)) && - (0 == (options & MHD_USE_THREAD_PER_CONNECTION)) ) - { -#if HAVE_MESSAGES - MHD_DLOG (retVal, - "MHD poll support only works with MHD_USE_THREAD_PER_CONNECTION\n"); -#endif -#if DAUTH_SUPPORT - free (retVal->nnc); - pthread_mutex_destroy (&retVal->nnc_lock); -#endif - free (retVal); - return NULL; - } - /* Thread pooling currently works only with internal select thread model */ if ( (0 == (options & MHD_USE_SELECT_INTERNALLY)) && (retVal->worker_pool_size > 0) ) @@ -2172,12 +2249,15 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) int fd; unsigned int i; int rc; + char c; if (daemon == NULL) return; daemon->shutdown = MHD_YES; fd = daemon->socket_fd; daemon->socket_fd = -1; + if (daemon->wpipe[1] != -1) + write (daemon->wpipe[1], "e", 1); /* Prepare workers for shutdown */ for (i = 0; i < daemon->worker_pool_size; ++i) @@ -2202,8 +2282,6 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) /* Signal workers to stop and clean them up */ for (i = 0; i < daemon->worker_pool_size; ++i) - pthread_kill (daemon->worker_pool[i].pid, SIGALRM); - for (i = 0; i < daemon->worker_pool_size; ++i) { if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused))) { @@ -2221,7 +2299,6 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) ((0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) && (0 == daemon->worker_pool_size))) { - pthread_kill (daemon->pid, SIGALRM); if (0 != (rc = pthread_join (daemon->pid, &unused))) { #if HAVE_MESSAGES @@ -2272,6 +2349,15 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) pthread_mutex_destroy (&daemon->nnc_lock); #endif pthread_mutex_destroy (&daemon->per_ip_connection_mutex); + + if (daemon->wpipe[1] != -1) + { + /* just to be sure, remove the one char we + wrote into the pipe */ + (void) read (daemon->wpipe[0], &c, 1); + close (daemon->wpipe[0]); + close (daemon->wpipe[1]); + } free (daemon); } @@ -2330,18 +2416,6 @@ MHD_get_version (void) return PACKAGE_VERSION; } -#ifndef WINDOWS - -static struct sigaction sig; - -static struct sigaction old; - -static void -sigalrmHandler (int sig) -{ -} -#endif - #ifdef __GNUC__ #define ATTRIBUTE_CONSTRUCTOR __attribute__ ((constructor)) #define ATTRIBUTE_DESTRUCTOR __attribute__ ((destructor)) @@ -2355,22 +2429,14 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL; #endif /** - * Initialize the signal handler for SIGALRM - * and do other setup work. + * Initialize do setup work. */ void ATTRIBUTE_CONSTRUCTOR MHD_init () { mhd_panic = &mhd_panic_std; mhd_panic_cls = NULL; -#ifndef WINDOWS - /* make sure SIGALRM does not kill us */ - memset (&sig, 0, sizeof (struct sigaction)); - memset (&old, 0, sizeof (struct sigaction)); - sig.sa_flags = SA_NODEFER; - sig.sa_handler = &sigalrmHandler; - sigaction (SIGALRM, &sig, &old); -#else +#ifdef WINDOWS plibc_init ("GNU", "libmicrohttpd"); #endif #if HTTPS_SUPPORT @@ -2388,9 +2454,7 @@ void ATTRIBUTE_DESTRUCTOR MHD_fini () if (0 != pthread_mutex_destroy(&MHD_gnutls_init_mutex)) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); #endif -#ifndef WINDOWS - sigaction (SIGALRM, &old, &sig); -#else +#ifdef WINDOWS plibc_shutdown (); #endif } diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -857,6 +857,11 @@ struct MHD_Daemon int socket_fd; /** + * Pipe we use to signal shutdown. + */ + int wpipe[2]; + + /** * Are we shutting down? */ int shutdown; diff --git a/src/examples/minimal_example.c b/src/examples/minimal_example.c @@ -67,7 +67,10 @@ main (int argc, char *const *argv) printf ("%s PORT\n", argv[0]); return 1; } - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_POLL, + // MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + // MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | MHD_USE_POLL, + // MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, atoi (argv[1]), NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 5, diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -106,7 +106,7 @@ extern "C" /** * Current version of the library. */ -#define MHD_VERSION 0x00090702 +#define MHD_VERSION 0x00090703 /** * MHD-internal return code for "YES".