libmicrohttpd

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

commit a952ad938c6ede2cbb537e7181ac5f0ff643828c
parent 90d6f460958d62b6086e639d07e75597c8e2573c
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 11 Nov 2009 11:55:34 +0000

[libmicrohttpd] large file descriptors
From: 
John Muth <muth@parascale.com>
  To: 
libmicrohttpd@gnu.org
  Date: 
Yesterday 18:35:25

Hi Christian,

We are using libmicro http in our application. Our application runs on Linux
and uses a large number of file descriptors (> 1024). Unfortunately the
Linux fdset's only have room for 1024 fd's.

I made changes to the 0.4.4 source to support poll instead of select so we
could get around the file descriptor issue. I've attached the diffs if you
are interested.

-John Muth
Parascale


Diffstat:
MAUTHORS | 1+
MChangeLog | 4++++
Mdoc/microhttpd.texi | 9+++++++++
Msrc/daemon/connection.c | 40++++++++++++++++++++++++++++++++--------
Msrc/daemon/connection.h | 8++++++++
Msrc/daemon/daemon.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/daemon/internal.h | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/include/microhttpd.h | 9++++++++-
8 files changed, 213 insertions(+), 32 deletions(-)

diff --git a/AUTHORS b/AUTHORS @@ -21,6 +21,7 @@ David Carvalho <andaris@gmail.com> David Reiss <dreiss@facebook.com> Mika Raento <mikie@iki.fi> Mike Crowe <mac@mcrowe.com> +John Muth <muth@parascale.com> Documentation contributions also came from: Marco Maggi <marco.maggi-ipsu@poste.it> diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,7 @@ +Wed Nov 11 12:54:16 CET 2009 + Adding support for poll (alternative to select allowing + for more than FD_SETSIZE parallel connections). -JM + Wed Oct 28 20:26:00 CET 2009 Releasing libmicrohttpd 0.4.4. -CG diff --git a/doc/microhttpd.texi b/doc/microhttpd.texi @@ -244,6 +244,15 @@ standard, but of course in violation of the ``be as liberal as possible in what you accept'' norm. It is recommended to turn this @strong{ON} if you are testing clients against @mhd{}, and @strong{OFF} in production. + +@item MHD_USE_POLL +@cindex FD_SETSIZE +@cindex poll +@cindex select +Use poll instead of select. This allows sockets with descriptors +@code{>= FD_SETSIZE}. This option only works in conjunction with +@code{MHD_USE_THREAD_PER_CONNECTION} (at this point). + @end table @end deftp diff --git a/src/daemon/connection.c b/src/daemon/connection.c @@ -702,6 +702,29 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, fd_set * write_fd_set, fd_set * except_fd_set, int *max_fd) { + int ret; + struct MHD_Pollfd p; + + memset(&p, 0, sizeof(struct MHD_Pollfd)); + ret = MHD_connection_get_pollfd(connection, &p); + if ( (ret == MHD_YES) && (p.fd >= 0) ) { + if (0 != (p.events & MHD_POLL_ACTION_IN)) + do_fd_set(p.fd, read_fd_set, max_fd); + if (0 != (p.events & MHD_POLL_ACTION_OUT)) + do_fd_set(p.fd, write_fd_set, max_fd); + } + return ret; +} + +/** + * Obtain the pollfd for this connection + * + * @return MHD_YES on success. If return MHD_YES and p->fd < 0, this + * connection is not waiting for any read or write events + */ +int +MHD_connection_get_pollfd(struct MHD_Connection *connection, struct MHD_Pollfd *p) +{ int fd; if (connection->pool == NULL) @@ -715,6 +738,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, return MHD_NO; } fd = connection->socket_fd; + p->fd = fd; if (fd == -1) return MHD_YES; while (1) @@ -750,7 +774,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, continue; } if (MHD_NO == connection->read_closed) - do_fd_set (fd, read_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_IN; break; case MHD_CONNECTION_HEADERS_RECEIVED: /* we should never get here */ @@ -760,7 +784,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, EXTRA_CHECK (0); break; case MHD_CONNECTION_CONTINUE_SENDING: - do_fd_set (fd, write_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_OUT; break; case MHD_CONNECTION_CONTINUE_SENT: if (connection->read_buffer_offset == connection->read_buffer_size) @@ -789,7 +813,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, } if ((connection->read_buffer_offset < connection->read_buffer_size) && (MHD_NO == connection->read_closed)) - do_fd_set (fd, read_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_IN; break; case MHD_CONNECTION_BODY_RECEIVED: case MHD_CONNECTION_FOOTER_PART_RECEIVED: @@ -800,7 +824,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, connection->state = MHD_CONNECTION_CLOSED; continue; } - do_fd_set (fd, read_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_IN; /* transition to FOOTERS_RECEIVED happens in read handler */ break; @@ -810,19 +834,19 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, break; case MHD_CONNECTION_HEADERS_SENDING: /* headers in buffer, keep writing */ - do_fd_set (fd, write_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_OUT; break; case MHD_CONNECTION_HEADERS_SENT: EXTRA_CHECK (0); break; case MHD_CONNECTION_NORMAL_BODY_READY: - do_fd_set (fd, write_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_OUT; break; case MHD_CONNECTION_NORMAL_BODY_UNREADY: /* not ready, no socket action */ break; case MHD_CONNECTION_CHUNKED_BODY_READY: - do_fd_set (fd, write_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_OUT; break; case MHD_CONNECTION_CHUNKED_BODY_UNREADY: /* not ready, no socket action */ @@ -831,7 +855,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, EXTRA_CHECK (0); break; case MHD_CONNECTION_FOOTERS_SENDING: - do_fd_set (fd, write_fd_set, max_fd); + p->events |= MHD_POLL_ACTION_OUT; break; case MHD_CONNECTION_FOOTERS_SENT: EXTRA_CHECK (0); diff --git a/src/daemon/connection.h b/src/daemon/connection.h @@ -40,6 +40,14 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, fd_set * write_fd_set, fd_set * except_fd_set, int *max_fd); +/** + * Obtain the pollfd for this connection. The poll interface allows large + * file descriptors. Select goes stupid when the fd overflows fdset (which + * is fixed). + */ +int MHD_connection_get_pollfd(struct MHD_Connection *connection, + struct MHD_Pollfd *p); + void MHD_set_http_calbacks (struct MHD_Connection *connection); int MHD_connection_handle_read (struct MHD_Connection *connection); diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -36,6 +36,8 @@ #include "gnutls_global.h" #endif +#include <poll.h> + /** * Default connection limit. */ @@ -437,7 +439,8 @@ MHD_get_fdset (struct MHD_Daemon *daemon, if ((daemon == NULL) || (read_fd_set == NULL) || (write_fd_set == NULL) || (except_fd_set == NULL) || (max_fd == NULL) || (-1 == (fd = daemon->socket_fd)) || (daemon->shutdown == MHD_YES) - || ((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0)) + || ((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) + || ((daemon->options & MHD_USE_POLL) != 0)) return MHD_NO; FD_SET (fd, read_fd_set); @@ -463,7 +466,7 @@ MHD_get_fdset (struct MHD_Daemon *daemon, /** * Main function of the thread that handles an individual - * connection. + * connection when MHD_USE_THREAD_PER_CONNECTION. */ static void * MHD_handle_connection (void *data) @@ -477,17 +480,13 @@ MHD_handle_connection (void *data) struct timeval tv; unsigned int timeout; unsigned int now; + struct MHD_Pollfd mp; + struct pollfd p; if (con == NULL) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); timeout = con->daemon->connection_timeout; - while ((!con->daemon->shutdown) && (con->socket_fd != -1)) - { - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - max = 0; - MHD_connection_get_fdset (con, &rs, &ws, &es, &max); + while ((!con->daemon->shutdown) && (con->socket_fd != -1)) { now = time (NULL); tv.tv_usec = 0; if (timeout > (now - con->last_activity)) @@ -506,24 +505,63 @@ MHD_handle_connection (void *data) { tv.tv_sec = 0; } - num_ready = SELECT (max + 1, &rs, &ws, &es, &tv); - if (num_ready < 0) - { + if (0 == (con->daemon->options & MHD_USE_POLL)) { + /* 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) { + if (errno == EINTR) + continue; +#if HAVE_MESSAGES + MHD_DLOG (con->daemon, "Error during select (%d): `%s'\n", max, + 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); + } 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; #if HAVE_MESSAGES - MHD_DLOG (con->daemon, "Error during select (%d): `%s'\n", max, + MHD_DLOG (con->daemon, "Error during poll: `%s'\n", 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); + 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); + } } if (con->socket_fd != -1) { @@ -622,7 +660,8 @@ MHD_accept_connection (struct MHD_Daemon *daemon) return MHD_NO; } #ifndef WINDOWS - if (s >= FD_SETSIZE) + if ( (s >= FD_SETSIZE) && + (0 == daemon->options & MHD_USE_POLL) ) { #if HAVE_MESSAGES MHD_DLOG (daemon, @@ -986,6 +1025,38 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) } /** + * Poll for new connection. Used only with THREAD_PER_CONNECTION + */ +static int +MHD_poll (struct MHD_Daemon *daemon) +{ + 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; + + if (poll(&p, 1, 0) < 0) { + if (errno == EINTR) + return MHD_YES; +#if HAVE_MESSAGES + MHD_DLOG (daemon, "poll failed: %s\n", STRERROR (errno)); +#endif + 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.revents & POLLIN)) + MHD_accept_connection (daemon); + return MHD_YES; +} + +/** * Run webserver operations (without blocking unless * in client callbacks). This method should be called * by clients in combination with MHD_get_fdset @@ -1017,7 +1088,10 @@ MHD_select_thread (void *cls) struct MHD_Daemon *daemon = cls; while (daemon->shutdown == MHD_NO) { - MHD_select (daemon, MHD_YES); + if ((daemon->options & MHD_USE_POLL) == 0) + MHD_select (daemon, MHD_YES); + else + MHD_poll(daemon); MHD_cleanup_connections (daemon); } return NULL; @@ -1206,6 +1280,17 @@ MHD_start_daemon_va (unsigned int options, } } + /* 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 + fprintf (stderr, + "MHD poll support only works with MHD_USE_THREAD_PER_CONNECTION\n"); +#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)) @@ -1253,7 +1338,8 @@ MHD_start_daemon_va (unsigned int options, return NULL; } #ifndef WINDOWS - if (socket_fd >= FD_SETSIZE) + if ( (socket_fd >= FD_SETSIZE) && + (0 == (options & MHD_USE_POLL)) ) { #if HAVE_MESSAGES if ((options & MHD_USE_DEBUG) != 0) diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -49,8 +49,50 @@ * Error handler */ extern MHD_PanicCallback mhd_panic; + +/** + * Closure argument for "mhd_panic". + */ extern void *mhd_panic_cls; +/** + * Events we care about with respect to poll/select + * for file descriptors. + */ +enum MHD_PollActions + { + /** + * No event interests us. + */ + MHD_POLL_ACTION_NOTHING = 0, + + /** + * We would like to read. + */ + MHD_POLL_ACTION_IN = 1, + + /** + * We would like to write. + */ + MHD_POLL_ACTION_OUT = 2 + }; + + +/** + * Socket descriptor and events we care about. + */ +struct MHD_Pollfd { + /** + * Socket descriptor. + */ + int fd; + + /** + * Which events do we care about for this socket? + */ + enum MHD_PollActions events; +}; + #if HAVE_MESSAGES /** diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -292,7 +292,14 @@ enum MHD_FLAG * recommended to turn this ON if you are testing clients against * MHD, and OFF in production. */ - MHD_USE_PEDANTIC_CHECKS = 32 + MHD_USE_PEDANTIC_CHECKS = 32, + + /** + * Use poll instead of select. This allows sockets with fd >= FD_SETSIZE. + * This option only works in conjunction with MHD_USE_THREAD_PER_CONNECTION + * (at this point). + */ + MHD_USE_POLL = 64 }; /**