[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r27749 - in libmicrohttpd: . src/include src/microhttpd src
From: |
gnunet |
Subject: |
[GNUnet-SVN] r27749 - in libmicrohttpd: . src/include src/microhttpd src/testcurl |
Date: |
Thu, 4 Jul 2013 17:29:56 +0200 |
Author: grothoff
Date: 2013-07-04 17:29:56 +0200 (Thu, 04 Jul 2013)
New Revision: 27749
Modified:
libmicrohttpd/ChangeLog
libmicrohttpd/src/include/microhttpd.h
libmicrohttpd/src/microhttpd/connection.c
libmicrohttpd/src/microhttpd/connection.h
libmicrohttpd/src/microhttpd/daemon.c
libmicrohttpd/src/microhttpd/internal.h
libmicrohttpd/src/microhttpd/memorypool.c
libmicrohttpd/src/testcurl/Makefile.am
libmicrohttpd/src/testcurl/perf_get.c
libmicrohttpd/src/testcurl/perf_get_concurrent.c
libmicrohttpd/src/testcurl/test_get.c
libmicrohttpd/src/testcurl/test_quiesce.c
libmicrohttpd/src/testcurl/test_start_stop.c
Log:
Adding support for using epoll for the MHD event loop
Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog 2013-07-04 15:27:59 UTC (rev 27748)
+++ libmicrohttpd/ChangeLog 2013-07-04 15:29:56 UTC (rev 27749)
@@ -6,6 +6,35 @@
because the application queued a response immediately ---
reserve that behavior for PUT/POST. -CG
+Tue Jun 25 15:08:30 CEST 2013
+ Added option 'MHD_USE_DUAL_STACK' to support a single
+ daemon for IPv4 and IPv6 without the application having
+ to do the binding. -CG
+
+Mon Jun 24 22:33:34 CEST 2013
+ Finished integration with epoll, including benchmarking and
+ documentation. -CG
+
+Sun Jun 23 15:28:13 CEST 2013
+ Added option 'MHD_USE_PIPE_FOR_SHUTDOWN' to cleanly support
+ 'MHD_quiesce_daemon' with thread pools and per-connection
+ threads (we then need a pipe for shutdown, but if
+ 'MHD_quiesce_daemon' is not used, we do not want to
+ require the use of a pipe; introducing the pipe after
+ the threads have been started can also fail, so the
+ application needs to tell us early on). -CG
+
+Sat Jun 22 20:24:17 CEST 2013
+ Removed locking calls for thread modes that do not need them.
+ Reorganized way to obtain connection's event loop state.
+ Added sorted XDLL for connections with default timeout to
+ avoid having to loop over all connections to determine current
+ timeout (custom per-connection timeouts are in another list
+ which is iterated each time). -CG
+
+Fri Jun 21 20:55:48 CEST 2013
+ Preparing build system and tests for epoll support. -CG
+
Tue May 21 14:34:36 CEST 2013
Improving configure tests for OpenSSL and spdylay to
avoid build errors in libmicrospdy code if those libraries
Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/include/microhttpd.h 2013-07-04 15:29:56 UTC (rev
27749)
@@ -356,8 +356,11 @@
MHD_USE_SELECT_INTERNALLY = 8,
/**
- * Run using the IPv6 protocol (otherwise, MHD will
- * just support IPv4).
+ * Run using the IPv6 protocol (otherwise, MHD will just support
+ * IPv4). If you want MHD to support IPv4 and IPv6 using a single
+ * socket, pass MHD_USE_DUAL_STACK, otherwise, if you only pass
+ * this option, MHD will try to bind to IPv6-only (resulting in
+ * no IPv4 support).
*/
MHD_USE_IPv6 = 16,
@@ -428,8 +431,13 @@
* specify it), if 'MHD_USE_NO_LISTEN_SOCKET' is specified. In
* "external" select mode, this option is always simply ignored.
*/
- MHD_USE_PIPE_FOR_SHUTDOWN = 1024
+ MHD_USE_PIPE_FOR_SHUTDOWN = 1024,
+ /**
+ * Use a single socket for IPv4 and IPv6.
+ */
+ MHD_USE_DUAL_STACK = MHD_USE_IPv6 | 2048
+
};
Modified: libmicrohttpd/src/microhttpd/connection.c
===================================================================
--- libmicrohttpd/src/microhttpd/connection.c 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/microhttpd/connection.c 2013-07-04 15:29:56 UTC (rev
27749)
@@ -318,8 +318,9 @@
daemon = connection->daemon;
SHUTDOWN (connection->socket_fd,
- (connection->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
+ (MHD_YES == connection->read_closed) ? SHUT_WR : SHUT_RDWR);
connection->state = MHD_CONNECTION_CLOSED;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
if ( (NULL != daemon->notify_completed) &&
(MHD_YES == connection->client_aware) )
daemon->notify_completed (daemon->notify_completed_cls,
@@ -745,8 +746,8 @@
kind = MHD_FOOTER_KIND;
off = 0;
}
- must_add_close = ( (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED) &&
- (connection->read_closed == MHD_YES) &&
+ must_add_close = ( (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) &&
+ (MHD_YES == connection->read_closed) &&
(0 == strcasecmp (connection->version,
MHD_HTTP_VERSION_1_1)) &&
(NULL == MHD_get_response_header (connection->response,
@@ -853,87 +854,16 @@
/**
- * Add "fd" to the "fd_set". If "fd" is
- * greater than "*max", set "*max" to fd.
+ * Update the 'event_loop_info' field of this connection based on the state
+ * that the connection is now in. May also close the connection or
+ * perform other updates to the connection if needed to prepare for
+ * the next round of the event loop.
*
- * @param fd file descriptor to add to the set
- * @param set set to modify
- * @param max_fd maximum value to potentially update
+ * @param connection connetion to get poll set for
*/
static void
-add_to_fd_set (int fd,
- fd_set *set,
- int *max_fd)
+MHD_connection_update_event_loop_info (struct MHD_Connection *connection)
{
- FD_SET (fd, set);
- if ( (NULL != max_fd) &&
- (fd > *max_fd) )
- *max_fd = fd;
-}
-
-
-/**
- * Obtain the select sets for this connection. The given
- * sets (and the maximum) are updated and must have
- * already been initialized.
- *
- * @param connection connetion to get select sets for
- * @param read_fd_set read set to initialize
- * @param write_fd_set write set to initialize
- * @param except_fd_set except set to initialize (never changed)
- * @param max_fd where to store largest FD put into any set
- * @return MHD_YES on success
- */
-int
-MHD_connection_get_fdset (struct MHD_Connection *connection,
- fd_set *read_fd_set,
- fd_set *write_fd_set,
- fd_set *except_fd_set,
- int *max_fd)
-{
- int ret;
- struct MHD_Pollfd p;
-
- /* we use the 'poll fd' as a convenient way to re-use code
- when determining the select sets */
- memset (&p, 0, sizeof(struct MHD_Pollfd));
- ret = MHD_connection_get_pollfd (connection, &p);
- if ( (MHD_YES == ret) && (p.fd >= 0) ) {
- if (0 != (p.events & MHD_POLL_ACTION_IN))
- add_to_fd_set(p.fd, read_fd_set, max_fd);
- if (0 != (p.events & MHD_POLL_ACTION_OUT))
- add_to_fd_set(p.fd, write_fd_set, max_fd);
- }
- return ret;
-}
-
-
-/**
- * Obtain the pollfd for this connection
- *
- * @param connection connetion to get poll set for
- * @param p where to store the polling information
- * @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 (NULL == connection->pool)
- connection->pool = MHD_pool_create (connection->daemon->pool_size);
- if (NULL == connection->pool)
- {
- CONNECTION_CLOSE_ERROR (connection,
- "Failed to create memory pool!\n");
- return MHD_YES;
- }
- fd = connection->socket_fd;
- p->fd = fd;
- if (-1 == fd)
- return MHD_YES;
while (1)
{
#if DEBUG_STATES
@@ -945,9 +875,9 @@
#if HTTPS_SUPPORT
case MHD_TLS_CONNECTION_INIT:
if (0 == gnutls_record_get_direction (connection->tls_session))
- p->events |= MHD_POLL_ACTION_IN;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
else
- p->events |= MHD_POLL_ACTION_OUT;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
#endif
case MHD_CONNECTION_INIT:
@@ -955,16 +885,9 @@
case MHD_CONNECTION_HEADER_PART_RECEIVED:
/* while reading headers, we always grow the
read buffer if needed, no size-check required */
- if ((connection->read_closed) &&
- (0 == connection->read_buffer_offset))
+ if ( (connection->read_buffer_offset ==
connection->read_buffer_size) &&
+ (MHD_NO == try_grow_read_buffer (connection)) )
{
- CONNECTION_CLOSE_ERROR (connection,
- "Connection buffer to small for
request\n");
- continue;
- }
- if ((connection->read_buffer_offset == connection->read_buffer_size)
- && (MHD_NO == try_grow_read_buffer (connection)))
- {
transmit_error_response (connection,
(connection->url != NULL)
? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
@@ -972,18 +895,19 @@
REQUEST_TOO_BIG);
continue;
}
- if (MHD_NO == connection->read_closed)
- p->events |= MHD_POLL_ACTION_IN;
+ if (MHD_NO == connection->read_closed)
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ else
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
break;
case MHD_CONNECTION_HEADERS_RECEIVED:
- /* we should never get here */
EXTRA_CHECK (0);
break;
case MHD_CONNECTION_HEADERS_PROCESSED:
EXTRA_CHECK (0);
break;
case MHD_CONNECTION_CONTINUE_SENDING:
- p->events |= MHD_POLL_ACTION_OUT;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_CONTINUE_SENT:
if (connection->read_buffer_offset == connection->read_buffer_size)
@@ -1010,9 +934,11 @@
continue;
}
}
- if ((connection->read_buffer_offset < connection->read_buffer_size)
- && (MHD_NO == connection->read_closed))
- p->events |= MHD_POLL_ACTION_IN;
+ if ( (connection->read_buffer_offset < connection->read_buffer_size)
&&
+ (MHD_NO == connection->read_closed) )
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ else
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
break;
case MHD_CONNECTION_BODY_RECEIVED:
case MHD_CONNECTION_FOOTER_PART_RECEIVED:
@@ -1024,50 +950,49 @@
NULL);
continue;
}
- p->events |= MHD_POLL_ACTION_IN;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
/* transition to FOOTERS_RECEIVED
happens in read handler */
break;
case MHD_CONNECTION_FOOTERS_RECEIVED:
- /* no socket action, wait for client
- to provide response */
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
break;
case MHD_CONNECTION_HEADERS_SENDING:
/* headers in buffer, keep writing */
- p->events |= MHD_POLL_ACTION_OUT;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_HEADERS_SENT:
EXTRA_CHECK (0);
break;
case MHD_CONNECTION_NORMAL_BODY_READY:
- p->events |= MHD_POLL_ACTION_OUT;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_NORMAL_BODY_UNREADY:
- /* not ready, no socket action */
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
break;
case MHD_CONNECTION_CHUNKED_BODY_READY:
- p->events |= MHD_POLL_ACTION_OUT;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
- /* not ready, no socket action */
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
break;
case MHD_CONNECTION_BODY_SENT:
EXTRA_CHECK (0);
break;
case MHD_CONNECTION_FOOTERS_SENDING:
- p->events |= MHD_POLL_ACTION_OUT;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_FOOTERS_SENT:
EXTRA_CHECK (0);
break;
case MHD_CONNECTION_CLOSED:
- return MHD_YES; /* do nothing, not even reading */
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ return; /* do nothing, not even reading */
default:
EXTRA_CHECK (0);
}
break;
}
- return MHD_YES;
}
@@ -1888,10 +1813,41 @@
/**
+ * Update the 'last_activity' field of the connection to the current time
+ * and move the connection to the head of the 'normal_timeout' list if
+ * the timeout for the connection uses the default value.
+ *
+ * @param connection the connection that saw some activity
+ */
+static void
+update_last_activity (struct MHD_Connection *connection)
+{
+ struct MHD_Daemon *daemon = connection->daemon;
+
+ connection->last_activity = MHD_monotonic_time();
+ if (connection->connection_timeout != daemon->connection_timeout)
+ return; /* custom timeout, no need to move it in DLL */
+
+ /* move connection to head of timeout list (by remove + add operation) */
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ XDLL_insert (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+}
+
+
+
+/**
* This function handles a particular connection when it has been
- * determined that there is data to be read off a socket. All
- * implementations (multithreaded, external select, internal select)
- * call this function to handle reads.
+ * determined that there is data to be read off a socket.
*
* @param connection connection to handle
* @return always MHD_YES (we should continue to process the
@@ -1900,7 +1856,7 @@
int
MHD_connection_handle_read (struct MHD_Connection *connection)
{
- connection->last_activity = MHD_monotonic_time();
+ update_last_activity (connection);
if (connection->state == MHD_CONNECTION_CLOSED)
return MHD_YES;
/* make sure "read" has a reasonable number of bytes
@@ -1953,9 +1909,7 @@
/**
* This function was created to handle writes to sockets when it has
- * been determined that the socket can be written to. All
- * implementations (multithreaded, external select, internal select)
- * call this function
+ * been determined that the socket can be written to.
*
* @param connection connection to handle
* @return always MHD_YES (we should continue to process the
@@ -1966,7 +1920,8 @@
{
struct MHD_Response *response;
int ret;
- connection->last_activity = MHD_monotonic_time();
+
+ update_last_activity (connection);
while (1)
{
#if DEBUG_STATES
@@ -2111,9 +2066,7 @@
/**
* This function was created to handle per-connection processing that
- * has to happen even if the socket cannot be read or written to. All
- * implementations (multithreaded, external select, internal select)
- * call this function.
+ * has to happen even if the socket cannot be read or written to.
*
* @param connection connection to handle
* @return MHD_YES if we should continue to process the
@@ -2122,7 +2075,7 @@
int
MHD_connection_handle_idle (struct MHD_Connection *connection)
{
- struct MHD_Daemon *daemon;
+ struct MHD_Daemon *daemon = connection->daemon;
unsigned int timeout;
const char *end;
int rend;
@@ -2131,18 +2084,18 @@
while (1)
{
#if DEBUG_STATES
- MHD_DLOG (connection->daemon, "%s: state: %s\n",
+ MHD_DLOG (daemon, "%s: state: %s\n",
__FUNCTION__, MHD_state_to_string (connection->state));
#endif
switch (connection->state)
{
case MHD_CONNECTION_INIT:
line = get_next_header_line (connection);
- if (line == NULL)
+ if (NULL == line)
{
- if (connection->state != MHD_CONNECTION_INIT)
+ if (MHD_CONNECTION_INIT != connection->state)
continue;
- if (connection->read_closed)
+ if (MHD_YES == connection->read_closed)
{
CONNECTION_CLOSE_ERROR (connection,
NULL);
@@ -2159,9 +2112,9 @@
line = get_next_header_line (connection);
if (line == NULL)
{
- if (connection->state != MHD_CONNECTION_URL_RECEIVED)
+ if (MHD_CONNECTION_URL_RECEIVED != connection->state)
continue;
- if (connection->read_closed)
+ if (MHD_YES == connection->read_closed)
{
CONNECTION_CLOSE_ERROR (connection,
NULL);
@@ -2189,7 +2142,7 @@
{
if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED)
continue;
- if (connection->read_closed)
+ if (MHD_YES == connection->read_closed)
{
CONNECTION_CLOSE_ERROR (connection,
NULL);
@@ -2232,7 +2185,7 @@
/* force close, in case client still tries to upload... */
connection->read_closed = MHD_YES;
}
- connection->state = (connection->remaining_upload_size == 0)
+ connection->state = (0 == connection->remaining_upload_size)
? MHD_CONNECTION_FOOTERS_RECEIVED : MHD_CONNECTION_CONTINUE_SENT;
continue;
case MHD_CONNECTION_CONTINUE_SENDING:
@@ -2269,7 +2222,7 @@
{
if (connection->state != MHD_CONNECTION_BODY_RECEIVED)
continue;
- if (connection->read_closed)
+ if (MHD_YES == connection->read_closed)
{
CONNECTION_CLOSE_ERROR (connection,
NULL);
@@ -2297,7 +2250,7 @@
{
if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED)
continue;
- if (connection->read_closed)
+ if (MHD_YES == connection->read_closed)
{
CONNECTION_CLOSE_ERROR (connection,
NULL);
@@ -2421,11 +2374,10 @@
( (end != NULL) && (0 == strcasecmp (end, "close")) ) );
MHD_destroy_response (connection->response);
connection->response = NULL;
- if (connection->daemon->notify_completed != NULL)
- connection->daemon->notify_completed (connection->daemon->
- notify_completed_cls,
- connection,
- &connection->client_context,
+ if (daemon->notify_completed != NULL)
+ daemon->notify_completed (daemon->notify_completed_cls,
+ connection,
+ &connection->client_context,
MHD_REQUEST_TERMINATED_COMPLETED_OK);
connection->client_aware = MHD_NO;
end =
@@ -2478,21 +2430,26 @@
MHD_destroy_response (connection->response);
connection->response = NULL;
}
- daemon = connection->daemon;
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ if (connection->connection_timeout == daemon->connection_timeout)
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ else
+ XDLL_remove (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ connection);
DLL_remove (daemon->connections_head,
daemon->connections_tail,
connection);
DLL_insert (daemon->cleanup_head,
daemon->cleanup_tail,
connection);
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to release cleanup mutex\n");
return MHD_NO;
default:
EXTRA_CHECK (0);
@@ -2507,6 +2464,32 @@
MHD_connection_close (connection,
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
return MHD_YES;
}
+ MHD_connection_update_event_loop_info (connection);
+ switch (connection->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ if (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY))
+ EDLL_insert (daemon->eready_head,
+ daemon->eready_tail,
+ connection);
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ if (0 != (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))
+ EDLL_insert (daemon->eready_head,
+ daemon->eready_tail,
+ connection);
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ /* we should look at this connection again in the next iteration
+ of the event loop, as we're waiting on the application */
+ EDLL_insert (daemon->eready_head,
+ daemon->eready_tail,
+ connection);
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* This connection is finished, nothing left to do */
+ break;
+ }
return MHD_YES;
}
@@ -2582,13 +2565,37 @@
...)
{
va_list ap;
+ struct MHD_Daemon *daemon;
+ daemon = connection->daemon;
switch (option)
{
case MHD_CONNECTION_OPTION_TIMEOUT:
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ if (connection->connection_timeout == daemon->connection_timeout)
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ else
+ XDLL_remove (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ connection);
va_start (ap, option);
connection->connection_timeout = va_arg (ap, unsigned int);
va_end (ap);
+ if (connection->connection_timeout == daemon->connection_timeout)
+ XDLL_insert (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ else
+ XDLL_insert (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ connection);
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to release cleanup mutex\n");
return MHD_YES;
default:
return MHD_NO;
Modified: libmicrohttpd/src/microhttpd/connection.h
===================================================================
--- libmicrohttpd/src/microhttpd/connection.h 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/microhttpd/connection.h 2013-07-04 15:29:56 UTC (rev
27749)
@@ -31,38 +31,6 @@
/**
- * Obtain the select sets for this connection. The given
- * sets (and the maximum) are updated and must have
- * already been initialized.
- *
- * @param connection connetion to get select sets for
- * @param read_fd_set read set to initialize
- * @param write_fd_set write set to initialize
- * @param except_fd_set except set to initialize (never changed)
- * @param max_fd where to store largest FD put into any set
- * @return MHD_YES on success
- */
-int
-MHD_connection_get_fdset (struct MHD_Connection *connection,
- fd_set * read_fd_set,
- 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).
- *
- * @param connection connetion to get poll set for
- * @param p where to store the polling information
- */
-int
-MHD_connection_get_pollfd (struct MHD_Connection *connection,
- struct MHD_Pollfd *p);
-
-
-/**
* Set callbacks for this connection to those for HTTP.
*
* @param connection connection to initialize
Modified: libmicrohttpd/src/microhttpd/daemon.c
===================================================================
--- libmicrohttpd/src/microhttpd/daemon.c 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/microhttpd/daemon.c 2013-07-04 15:29:56 UTC (rev
27749)
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- (C) 2007, 2008, 2009, 2010, 2011, 2012 Daniel Pittman and Christian Grothoff
+ (C) 2007-2013 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
@@ -39,7 +39,6 @@
#if HTTPS_SUPPORT
#include "connection_https.h"
-#include <gnutls/gnutls.h>
#include <gcrypt.h>
#endif
@@ -178,7 +177,7 @@
* @param daemon handle to daemon where lock is
*/
static void
-MHD_ip_count_lock(struct MHD_Daemon *daemon)
+MHD_ip_count_lock (struct MHD_Daemon *daemon)
{
if (0 != pthread_mutex_lock(&daemon->per_ip_connection_mutex))
{
@@ -193,7 +192,7 @@
* @param daemon handle to daemon where lock is
*/
static void
-MHD_ip_count_unlock(struct MHD_Daemon *daemon)
+MHD_ip_count_unlock (struct MHD_Daemon *daemon)
{
if (0 != pthread_mutex_unlock(&daemon->per_ip_connection_mutex))
{
@@ -212,7 +211,7 @@
* @return -1, 0 or 1 depending on result of compare
*/
static int
-MHD_ip_addr_compare(const void *a1, const void *a2)
+MHD_ip_addr_compare (const void *a1, const void *a2)
{
return memcmp (a1, a2, offsetof (struct MHD_IPCount, count));
}
@@ -227,9 +226,9 @@
* @return MHD_YES on success and MHD_NO otherwise (e.g., invalid address type)
*/
static int
-MHD_ip_addr_to_key(const struct sockaddr *addr,
- socklen_t addrlen,
- struct MHD_IPCount *key)
+MHD_ip_addr_to_key (const struct sockaddr *addr,
+ socklen_t addrlen,
+ struct MHD_IPCount *key)
{
memset(key, 0, sizeof(*key));
@@ -268,9 +267,9 @@
* Also returns MHD_NO if fails to allocate memory.
*/
static int
-MHD_ip_limit_add(struct MHD_Daemon *daemon,
- const struct sockaddr *addr,
- socklen_t addrlen)
+MHD_ip_limit_add (struct MHD_Daemon *daemon,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
{
struct MHD_IPCount *key;
void **nodep;
@@ -332,9 +331,9 @@
* @param addrlen number of bytes in addr
*/
static void
-MHD_ip_limit_del(struct MHD_Daemon *daemon,
- const struct sockaddr *addr,
- socklen_t addrlen)
+MHD_ip_limit_del (struct MHD_Daemon *daemon,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
{
struct MHD_IPCount search_key;
struct MHD_IPCount *found_key;
@@ -392,7 +391,11 @@
{
int res;
- connection->tls_read_ready = MHD_NO;
+ if (MHD_YES == connection->tls_read_ready)
+ {
+ connection->daemon->num_tls_read_ready--;
+ connection->tls_read_ready = MHD_NO;
+ }
res = gnutls_record_recv (connection->tls_session, other, i);
if ( (GNUTLS_E_AGAIN == res) ||
(GNUTLS_E_INTERRUPTED == res) )
@@ -404,12 +407,15 @@
{
/* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
disrupted); set errno to something caller will interpret
- correctly as a hard error*/
+ correctly as a hard error */
errno = EPIPE;
return res;
}
if (res == i)
- connection->tls_read_ready = MHD_YES;
+ {
+ connection->tls_read_ready = MHD_YES;
+ connection->daemon->num_tls_read_ready++;
+ }
return res;
}
@@ -515,6 +521,26 @@
/**
+ * Add "fd" to the "fd_set". If "fd" is
+ * greater than "*max", set "*max" to fd.
+ *
+ * @param fd file descriptor to add to the set
+ * @param set set to modify
+ * @param max_fd maximum value to potentially update
+ */
+static void
+add_to_fd_set (int fd,
+ fd_set *set,
+ int *max_fd)
+{
+ FD_SET (fd, set);
+ if ( (NULL != max_fd) &&
+ (fd > *max_fd) )
+ *max_fd = fd;
+}
+
+
+/**
* Obtain the select sets for this daemon.
*
* @param daemon daemon to get sets from
@@ -546,6 +572,20 @@
|| (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|| (0 != (daemon->options & MHD_USE_POLL)))
return MHD_NO;
+#if EPOLL_SUPPORT
+ if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+ {
+ /* we're in epoll mode, use the epoll FD as a stand-in for
+ the entire event set */
+
+ if (daemon->epoll_fd >= FD_SETSIZE)
+ return MHD_NO; /* poll fd too big, fail hard */
+ FD_SET (daemon->epoll_fd, read_fd_set);
+ if ((*max_fd) < daemon->epoll_fd)
+ *max_fd = daemon->epoll_fd;
+ return MHD_YES;
+ }
+#endif
fd = daemon->socket_fd;
if (-1 != fd)
{
@@ -555,11 +595,23 @@
*max_fd = fd;
}
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- if (MHD_YES != MHD_connection_get_fdset (pos,
- read_fd_set,
- write_fd_set,
- except_fd_set, max_fd))
- return MHD_NO;
+ {
+ switch (pos->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ add_to_fd_set (pos->socket_fd, read_fd_set, max_fd);
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ add_to_fd_set (pos->socket_fd, write_fd_set, max_fd);
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ /* not in any FD set */
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* this should never happen */
+ break;
+ }
+ }
#if DEBUG_CONNECT
MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd);
#endif
@@ -581,14 +633,12 @@
int num_ready;
fd_set rs;
fd_set ws;
- 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[1];
#endif
@@ -607,14 +657,6 @@
tv.tv_usec = 0;
tvp = &tv;
}
- if ( (MHD_CONNECTION_NORMAL_BODY_UNREADY == con->state) ||
- (MHD_CONNECTION_CHUNKED_BODY_UNREADY == con->state) )
- {
- /* do not block (we're waiting for our callback to succeed) */
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- tvp = &tv;
- }
#if HTTPS_SUPPORT
if (MHD_YES == con->tls_read_ready)
{
@@ -629,10 +671,25 @@
/* 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, tvp);
+ switch (con->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ add_to_fd_set (con->socket_fd, &rs, &max);
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ add_to_fd_set (con->socket_fd, &ws, &max);
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* how did we get here!? */
+ goto exit;
+ }
+ num_ready = SELECT (max + 1, &rs, &ws, NULL, tvp);
if (num_ready < 0)
{
if (EINTR == errno)
@@ -648,7 +705,7 @@
/* call appropriate connection handler if necessary */
if ( (FD_ISSET (con->socket_fd, &rs))
#if HTTPS_SUPPORT
- || (MHD_YES == con->tls_read_ready)
+ || (MHD_YES == con->tls_read_ready)
#endif
)
con->read_handler (con);
@@ -661,16 +718,26 @@
else
{
/* 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;
- if (poll (p,
- 1,
+ memset (&p, 0, sizeof (p));
+ p[0].fd = con->socket_fd;
+ switch (con->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ p[0].events |= POLLIN;
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ p[0].events |= POLLOUT;
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* how did we get here!? */
+ goto exit;
+ }
+ if (poll (p, 1,
(NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
{
if (EINTR == errno)
@@ -732,15 +799,23 @@
void *other,
size_t i)
{
+ ssize_t ret;
+
if ( (-1 == connection->socket_fd) ||
(MHD_CONNECTION_CLOSED == connection->state) )
{
errno = ENOTCONN;
return -1;
}
- if (0 != (connection->daemon->options & MHD_USE_SSL))
- return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
- return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
+ ret = RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
+#if EPOLL_SUPPORT
+ if (ret < i)
+ {
+ /* partial read --- no longer read-ready */
+ connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
+ }
+#endif
+ return ret;
}
@@ -757,12 +832,13 @@
const void *other,
size_t i)
{
+ ssize_t ret;
#if LINUX
int fd;
off_t offset;
off_t left;
- ssize_t ret;
#endif
+
if ( (-1 == connection->socket_fd) ||
(MHD_CONNECTION_CLOSED == connection->state) )
{
@@ -786,7 +862,16 @@
fd,
&offset,
(size_t) left)))
- return ret;
+ {
+#if EPOLL_SUPPORT
+ if (ret < left)
+ {
+ /* partial write --- no longer write-ready */
+ connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ }
+#endif
+ return ret;
+ }
if ( (EINTR == errno) || (EAGAIN == errno) )
return 0;
if ( (EINVAL == errno) || (EBADF == errno) )
@@ -797,7 +882,15 @@
http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html
*/
}
#endif
- return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
+ ret = SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
+#if EPOLL_SUPPORT
+ if (ret < i)
+ {
+ /* partial write --- no longer write-ready */
+ connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ }
+#endif
+ return ret;
}
@@ -890,13 +983,31 @@
{
struct MHD_Connection *connection;
int res_thread_create;
+ unsigned int i;
#if OSX
static int on = 1;
-#endif
+#endif
+ if (NULL != daemon->worker_pool)
+ {
+ /* have a pool, try to find a pool with capacity; we use the
+ socket as the initial offset into the pool for load
+ balancing */
+ for (i=0;i<daemon->worker_pool_size;i++)
+ if (0 < daemon->worker_pool[(i + client_socket) %
daemon->worker_pool_size].max_connections)
+ return MHD_add_connection (&daemon->worker_pool[(i + client_socket) %
daemon->worker_pool_size],
+ client_socket,
+ addr, addrlen);
+ /* all pools are at their connection limit, must refuse */
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
+ return MHD_NO;
+ }
+
#ifndef WINDOWS
if ( (client_socket >= FD_SETSIZE) &&
- (0 == (daemon->options & MHD_USE_POLL)) )
+ (0 == (daemon->options & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
@@ -905,7 +1016,8 @@
FD_SETSIZE);
#endif
SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
return MHD_NO;
}
#endif
@@ -925,7 +1037,8 @@
"Server reached connection limit (closing inbound
connection)\n");
#endif
SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
return MHD_NO;
}
@@ -940,9 +1053,10 @@
#endif
#endif
SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
- return MHD_YES;
+ return MHD_NO;
}
#if OSX
@@ -963,13 +1077,29 @@
STRERROR (errno));
#endif
SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
return MHD_NO;
}
memset (connection, 0, sizeof (struct MHD_Connection));
+ connection->pool = MHD_pool_create (daemon->pool_size);
+ if (NULL == connection->pool)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Error allocating memory: %s\n",
+ STRERROR (errno));
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ free (connection);
+ return MHD_NO;
+ }
+
connection->connection_timeout = daemon->connection_timeout;
- connection->pool = NULL;
if (NULL == (connection->addr = malloc (addrlen)))
{
#if HAVE_MESSAGES
@@ -978,8 +1108,10 @@
STRERROR (errno));
#endif
SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
+ MHD_pool_destroy (connection->pool);
free (connection);
return MHD_NO;
}
@@ -988,7 +1120,7 @@
connection->socket_fd = client_socket;
connection->daemon = daemon;
connection->last_activity = MHD_monotonic_time();
-
+
/* set default connection handlers */
MHD_set_http_callbacks_ (connection);
connection->recv_cls = &recv_param_adapter;
@@ -1050,7 +1182,8 @@
daemon->cred_type);
#endif
SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
free (connection->addr);
free (connection);
@@ -1065,16 +1198,22 @@
(gnutls_push_func)
&send_param_adapter);
if (daemon->https_mem_trust)
- gnutls_certificate_server_set_request(connection->tls_session,
GNUTLS_CERT_REQUEST);
+ gnutls_certificate_server_set_request (connection->tls_session,
+ GNUTLS_CERT_REQUEST);
}
#endif
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ XDLL_insert (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
DLL_insert (daemon->connections_head,
daemon->connections_tail,
connection);
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
/* attempt to create handler thread */
@@ -1088,27 +1227,58 @@
MHD_DLOG (daemon, "Failed to create a thread: %s\n",
STRERROR (res_thread_create));
#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- MHD_ip_limit_del (daemon, addr, addrlen);
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- DLL_remove (daemon->connections_head,
- daemon->connections_tail,
- connection);
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
- free (connection->addr);
- free (connection);
- return MHD_NO;
+ goto cleanup;
}
}
- daemon->max_connections--;
+#if EPOLL_SUPPORT
+ if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+ {
+ struct epoll_event event;
+
+ event.events = EPOLLIN | EPOLLOUT | EPOLLET;
+ event.data.ptr = connection;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ client_socket,
+ &event))
+ {
+#if HAVE_MESSAGES
+ if (0 != (daemon->options & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Call to epoll_ctl failed: %s\n",
+ STRERROR (errno));
+#endif
+ goto cleanup;
+ }
+ daemon->listen_socket_in_epoll = MHD_YES;
+
+ }
+#endif
+ daemon->max_connections--;
return MHD_YES;
+#if HTTPS_SUPPORT || EPOLL_SUPPORT
+ cleanup:
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ if (0 != CLOSE (client_socket))
+ MHD_PANIC ("close failed\n");
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+ MHD_pool_destroy (connection->pool);
+ free (connection->addr);
+ free (connection);
+ return MHD_NO;
+#endif
}
@@ -1118,7 +1288,11 @@
* accept policy callback.
*
* @param daemon handle with the listen socket
- * @return MHD_YES on success
+ * @return MHD_YES on success (connections denied by policy or due
+ * to 'out of memory' and similar errors) are still considered
+ * successful as far as MHD_accept_connection is concerned);
+ * a return code of MHD_NO only refers to the actual
+ * 'accept' system call.
*/
static int
MHD_accept_connection (struct MHD_Daemon *daemon)
@@ -1143,14 +1317,9 @@
s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC);
need_fcntl = MHD_NO;
#else
- s = -1;
+ s = ACCEPT (fd, addr, &addrlen);
need_fcntl = MHD_YES;
#endif
- if (-1 == s)
- {
- s = ACCEPT (fd, addr, &addrlen);
- need_fcntl = MHD_YES;
- }
if ((-1 == s) || (addrlen <= 0))
{
#if HAVE_MESSAGES
@@ -1163,11 +1332,13 @@
if (-1 != s)
{
SHUTDOWN (s, SHUT_RDWR);
- CLOSE (s);
+ if (0 != CLOSE (s))
+ MHD_PANIC ("close failed\n");
/* just in case */
}
return MHD_NO;
}
+#if !HAVE_ACCEPT4
if (MHD_YES == need_fcntl)
{
/* make socket non-inheritable */
@@ -1198,13 +1369,15 @@
}
#endif
}
+#endif
#if HAVE_MESSAGES
#if DEBUG_CONNECT
MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
#endif
#endif
- return MHD_add_connection (daemon, s,
+ (void) MHD_add_connection (daemon, s,
addr, addrlen);
+ return MHD_YES;
}
@@ -1222,10 +1395,9 @@
void *unused;
int rc;
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
while (NULL != (pos = daemon->cleanup_head))
{
DLL_remove (daemon->cleanup_head,
@@ -1245,22 +1417,39 @@
gnutls_deinit (pos->tls_session);
#endif
MHD_ip_limit_del (daemon, (struct sockaddr*)pos->addr, pos->addr_len);
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (-1 != daemon->epoll_fd) )
+ {
+ /* epoll documentation suggests that closing a FD
+ automatically removes it from the epoll set; however,
+ this is not true as if we fail to do manually remove it,
+ we are still seeing an event for this fd in epoll,
+ causing grief (use-after-free...) --- at least on my
+ system. */
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_DEL,
+ pos->socket_fd,
+ NULL))
+ MHD_PANIC ("Failed to remove FD from epoll set\n");
+ }
+#endif
if (NULL != pos->response)
{
MHD_destroy_response (pos->response);
pos->response = NULL;
}
- if (-1 != pos->socket_fd)
- CLOSE (pos->socket_fd);
+ if ( (-1 != pos->socket_fd) &&
+ (0 != CLOSE (pos->socket_fd)) )
+ MHD_PANIC ("close failed\n");
if (NULL != pos->addr)
free (pos->addr);
free (pos);
daemon->max_connections++;
}
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to release cleanup mutex\n");
}
@@ -1292,17 +1481,20 @@
#endif
return MHD_NO;
}
+
+#if HTTPS_SUPPORT
+ if (0 != daemon->num_tls_read_ready)
+ {
+ /* if there is any TLS connection with data ready for
+ reading, we must not block in the event loop */
+ *timeout = 0;
+ return MHD_YES;
+ }
+#endif
+
have_timeout = MHD_NO;
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ for (pos = daemon->manual_timeout_head; NULL != pos; pos = pos->nextX)
{
-#if HTTPS_SUPPORT
- if (MHD_YES == pos->tls_read_ready)
- {
- earliest_deadline = 0;
- have_timeout = MHD_YES;
- break;
- }
-#endif
if (0 != pos->connection_timeout)
{
if ( (! have_timeout) ||
@@ -1316,6 +1508,22 @@
have_timeout = MHD_YES;
}
}
+ /* normal timeouts are sorted, so we only need to look at the 'head' */
+ pos = daemon->normal_timeout_head;
+ if ( (NULL != pos) &&
+ (0 != pos->connection_timeout) )
+ {
+ if ( (! have_timeout) ||
+ (earliest_deadline > pos->last_activity + pos->connection_timeout) )
+ earliest_deadline = pos->last_activity + pos->connection_timeout;
+#if HTTPS_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_SSL)) &&
+ (0 != gnutls_record_check_pending (pos->tls_session)) )
+ earliest_deadline = 0;
+#endif
+ have_timeout = MHD_YES;
+ }
+
if (MHD_NO == have_timeout)
return MHD_NO;
now = MHD_monotonic_time();
@@ -1356,10 +1564,23 @@
struct MHD_Connection *pos;
struct MHD_Connection *next;
+#if EPOLL_SUPPORT
+ if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+ {
+ /* we're in epoll mode, the epoll FD stands for
+ the entire event set! */
+ if (daemon->epoll_fd >= FD_SETSIZE)
+ return MHD_NO; /* poll fd too big, fail hard */
+ if (FD_ISSET (daemon->epoll_fd, read_fd_set))
+ return MHD_run (daemon);
+ return MHD_YES;
+ }
+#endif
+
/* select connection thread handling type */
if ( (-1 != (ds = daemon->socket_fd)) &&
(FD_ISSET (ds, read_fd_set)) )
- MHD_accept_connection (daemon);
+ (void) MHD_accept_connection (daemon);
/* drain signaling pipe to avoid spinning select */
if ( (-1 != daemon->wpipe[0]) &&
(FD_ISSET (daemon->wpipe[0], read_fd_set)) )
@@ -1373,18 +1594,30 @@
{
next = pos->next;
ds = pos->socket_fd;
- if (ds != -1)
- {
- if ( (FD_ISSET (ds, read_fd_set))
+ if (-1 == ds)
+ continue;
+ switch (pos->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ if ( (FD_ISSET (ds, read_fd_set))
#if HTTPS_SUPPORT
|| (MHD_YES == pos->tls_read_ready)
#endif
)
- pos->read_handler (pos);
- if (FD_ISSET (ds, write_fd_set))
- pos->write_handler (pos);
- pos->idle_handler (pos);
- }
+ pos->read_handler (pos);
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ if (FD_ISSET (ds, write_fd_set))
+ pos->write_handler (pos);
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ /* only idle handler */
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* should never happen */
+ break;
+ }
+ pos->idle_handler (pos);
}
}
return MHD_YES;
@@ -1502,11 +1735,11 @@
/* 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 ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) ||
+ (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) )
+ num_connections++;
{
struct pollfd p[2 + num_connections];
- struct MHD_Pollfd mp;
MHD_UNSIGNED_LONG_LONG ltimeout;
unsigned int i;
int timeout;
@@ -1544,13 +1777,22 @@
i = 0;
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
{
- memset(&mp, 0, sizeof (struct MHD_Pollfd));
- MHD_connection_get_pollfd (pos, &mp);
- p[poll_server+i].fd = mp.fd;
- if (mp.events & MHD_POLL_ACTION_IN)
- p[poll_server+i].events |= POLLIN;
- if (mp.events & MHD_POLL_ACTION_OUT)
- p[poll_server+i].events |= POLLOUT;
+ p[poll_server+i].fd = pos->socket_fd;
+ switch (pos->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ p[poll_server+i].events |= POLLIN;
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ p[poll_server+i].events |= POLLOUT;
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ /* not in poll */
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* should never happen */
+ break;
+ }
i++;
}
if (0 == poll_server + num_connections)
@@ -1574,24 +1816,44 @@
while (NULL != (pos = next))
{
next = pos->next;
- /* first, sanity checks */
- if (i >= num_connections)
- break; /* connection list changed somehow, retry later ... */
- MHD_connection_get_pollfd (pos, &mp);
- if (p[poll_server+i].fd != mp.fd)
- break; /* fd mismatch, something else happened, retry later ... */
-
- /* normal handling */
- if (0 != (p[poll_server+i].revents & POLLIN))
- pos->read_handler (pos);
- if (0 != (p[poll_server+i].revents & POLLOUT))
- pos->write_handler (pos);
- pos->idle_handler (pos);
- i++;
+ switch (pos->event_loop_info)
+ {
+ case MHD_EVENT_LOOP_INFO_READ:
+ /* first, sanity checks */
+ if (i >= num_connections)
+ break; /* connection list changed somehow, retry later ... */
+ if (p[poll_server+i].fd != pos->socket_fd)
+ break; /* fd mismatch, something else happened, retry later ... */
+ /* normal handling */
+ if (0 != (p[poll_server+i].revents & POLLIN))
+ pos->read_handler (pos);
+ pos->idle_handler (pos);
+ i++;
+ break;
+ case MHD_EVENT_LOOP_INFO_WRITE:
+ /* first, sanity checks */
+ if (i >= num_connections)
+ break; /* connection list changed somehow, retry later ... */
+ if (p[poll_server+i].fd != pos->socket_fd)
+ break; /* fd mismatch, something else happened, retry later ... */
+ /* normal handling */
+ if (0 != (p[poll_server+i].revents & POLLOUT))
+ pos->write_handler (pos);
+ pos->idle_handler (pos);
+ i++;
+ break;
+ case MHD_EVENT_LOOP_INFO_BLOCK:
+ pos->idle_handler (pos);
+ break;
+ case MHD_EVENT_LOOP_INFO_CLEANUP:
+ /* should never happen */
+ break;
+ }
}
+ /* handle 'listen' FD */
if ( (-1 != poll_listen) &&
(0 != (p[poll_listen].revents & POLLIN)) )
- MHD_accept_connection (daemon);
+ (void) MHD_accept_connection (daemon);
}
return MHD_YES;
}
@@ -1651,7 +1913,7 @@
return MHD_NO;
if ( (-1 != poll_listen) &&
(0 != (p[poll_listen].revents & POLLIN)) )
- MHD_accept_connection (daemon);
+ (void) MHD_accept_connection (daemon);
return MHD_YES;
}
#endif
@@ -1681,7 +1943,205 @@
}
+#if EPOLL_SUPPORT
+
/**
+ * How many events to we process at most per epoll call? Trade-off
+ * between required stack-size and number of system calls we have to
+ * make; 128 should be way enough to avoid more than one system call
+ * for most scenarios, and still be moderate in stack size
+ * consumption. Embedded systems might want to choose a smaller value
+ * --- but why use 'epoll' on such a system in the first place?
+ */
+#define MAX_EVENTS 128
+
+
+/**
+ * Do 'epoll'-based processing (this function is allowed to
+ * block).
+ *
+ * @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_epoll (struct MHD_Daemon *daemon,
+ int may_block)
+{
+ struct MHD_Connection *pos;
+ struct MHD_Connection *next;
+ struct epoll_event events[MAX_EVENTS];
+ struct epoll_event event;
+ int timeout_ms;
+ MHD_UNSIGNED_LONG_LONG timeout_ll;
+ int num_events;
+ unsigned int i;
+
+ if (-1 == daemon->epoll_fd)
+ return MHD_NO; /* we're down! */
+ if (MHD_YES == daemon->shutdown)
+ return MHD_NO;
+ if ( (-1 != daemon->socket_fd) &&
+ (0 != daemon->max_connections) &&
+ (MHD_NO == daemon->listen_socket_in_epoll) )
+ {
+ event.events = EPOLLIN;
+ event.data.ptr = daemon;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ daemon->socket_fd,
+ &event))
+ {
+#if HAVE_MESSAGES
+ if (0 != (daemon->options & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Call to epoll_ctl failed: %s\n",
+ STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ daemon->listen_socket_in_epoll = MHD_YES;
+ }
+ if ( (MHD_YES == daemon->listen_socket_in_epoll) &&
+ (0 == daemon->max_connections) )
+ {
+ /* we're at the connection limit, disable listen socket
+ for event loop for now */
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_DEL,
+ daemon->socket_fd,
+ NULL))
+ MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+ daemon->listen_socket_in_epoll = MHD_NO;
+ }
+ if (MHD_YES == may_block)
+ {
+ if (MHD_YES == MHD_get_timeout (daemon,
+ &timeout_ll))
+ {
+ if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
+ timeout_ms = INT_MAX;
+ else
+ timeout_ms = (int) timeout_ll;
+ }
+ else
+ timeout_ms = -1;
+ }
+ else
+ timeout_ms = 0;
+
+ /* drain 'epoll' event queue; need to iterate as we get at most
+ MAX_EVENTS in one system call here; in practice this should
+ pretty much mean only one round, but better an extra loop here
+ than unfair behavior... */
+ num_events = MAX_EVENTS;
+ while (MAX_EVENTS == num_events)
+ {
+ /* update event masks */
+ num_events = epoll_wait (daemon->epoll_fd,
+ events, MAX_EVENTS, timeout_ms);
+ if (-1 == num_events)
+ {
+ if (EINTR == errno)
+ return MHD_YES;
+#if HAVE_MESSAGES
+ if (0 != (daemon->options & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Call to epoll_wait failed: %s\n",
+ STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ for (i=0;i<num_events;i++)
+ {
+ if (NULL == events[i].data.ptr)
+ continue; /* shutdown signal! */
+ if (daemon != events[i].data.ptr)
+ {
+ /* this is an event relating to a 'normal' connection,
+ remember the event and if appropriate mark the
+ connection as 'eready'. */
+ pos = events[i].data.ptr;
+ if (0 != (events[i].events & EPOLLIN))
+ {
+ pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
+ if ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) &&
+ (0 == (pos->epoll_state &
MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
+ {
+ EDLL_insert (daemon->eready_head,
+ daemon->eready_tail,
+ pos);
+ pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ }
+ }
+ if (0 != (events[i].events & EPOLLOUT))
+ {
+ pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
+ if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) &&
+ (0 == (pos->epoll_state &
MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
+ {
+ EDLL_insert (daemon->eready_head,
+ daemon->eready_tail,
+ pos);
+ pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ }
+ }
+ }
+ else /* must be listen socket */
+ {
+ /* run 'accept' until it fails or we are not allowed to take
+ on more connections */
+ while ( (MHD_YES == MHD_accept_connection (daemon)) &&
+ (0 != daemon->max_connections) ) ;
+ }
+ }
+ }
+
+ /* process events for connections */
+ while (NULL != (pos = daemon->eready_tail))
+ {
+ EDLL_remove (daemon->eready_head,
+ daemon->eready_tail,
+ pos);
+ pos->epoll_state -= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ if (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info)
+ pos->read_handler (pos);
+ if (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info)
+ pos->write_handler (pos);
+ pos->idle_handler (pos);
+ }
+ /* Finally, handle timed-out connections; we need to do this here
+ as the epoll mechanism won't call the 'idle_handler' on everything,
+ as the other event loops do. As timeouts do not get an explicit
+ event, we need to find those connections that might have timed out
+ here.
+
+ Connections with custom timeouts must all be looked at, as we
+ do not bother to sort that (presumably very short) list. */
+ next = daemon->manual_timeout_head;
+ while (NULL != (pos = next))
+ {
+ next = pos->nextX;
+ pos->idle_handler (pos);
+ }
+ /* Connections with the default timeout are sorted by prepending
+ them to the head of the list whenever we touch the connection;
+ thus it sufficies to iterate from the tail until the first
+ connection is NOT timed out */
+ next = daemon->normal_timeout_tail;
+ while (NULL != (pos = next))
+ {
+ next = pos->prevX;
+ pos->idle_handler (pos);
+ if (MHD_CONNECTION_CLOSED != pos->state)
+ break; /* sorted by timeout, no need to visit the rest! */
+ }
+ return MHD_YES;
+}
+#endif
+
+
+/**
* Run webserver operations (without blocking unless
* in client callbacks). This method should be called
* by clients in combination with MHD_get_fdset
@@ -1702,10 +2162,14 @@
(0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
(0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
return MHD_NO;
- if (0 == (daemon->options & MHD_USE_POLL))
+ if (0 != (daemon->options & MHD_USE_POLL))
+ MHD_poll (daemon, MHD_NO);
+#if EPOLL_SUPPORT
+ else if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+ MHD_epoll (daemon, MHD_NO);
+#endif
+ else
MHD_select (daemon, MHD_NO);
- else
- MHD_poll (daemon, MHD_NO);
MHD_cleanup_connections (daemon);
return MHD_YES;
}
@@ -1725,10 +2189,14 @@
while (MHD_YES != daemon->shutdown)
{
- if (0 == (daemon->options & MHD_USE_POLL))
+ if (0 != (daemon->options & MHD_USE_POLL))
+ MHD_poll (daemon, MHD_YES);
+#if EPOLL_SUPPORT
+ else if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+ MHD_epoll (daemon, MHD_YES);
+#endif
+ else
MHD_select (daemon, MHD_YES);
- else
- MHD_poll (daemon, MHD_YES);
MHD_cleanup_connections (daemon);
}
return NULL;
@@ -1784,10 +2252,50 @@
int ret;
ret = daemon->socket_fd;
+ if (-1 == ret)
+ return -1;
+ if ( (-1 == daemon->wpipe[1]) &&
+ (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Using MHD_quiesce_daemon in this mode requires
MHD_USE_PIPE_FOR_SHUTDOWN\n");
+#endif
+ return -1;
+ }
+
if (NULL != daemon->worker_pool)
- for (i = 0; i < daemon->worker_pool_size; i++)
- daemon->worker_pool[i].socket_fd = -1;
+ for (i = 0; i < daemon->worker_pool_size; i++)
+ {
+ daemon->worker_pool[i].socket_fd = -1;
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (-1 != daemon->worker_pool[i].epoll_fd) &&
+ (MHD_YES == daemon->worker_pool[i].listen_socket_in_epoll) )
+ {
+ if (0 != epoll_ctl (daemon->worker_pool[i].epoll_fd,
+ EPOLL_CTL_DEL,
+ ret,
+ NULL))
+ MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+ daemon->worker_pool[i].listen_socket_in_epoll = MHD_NO;
+ }
+#endif
+ }
daemon->socket_fd = -1;
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (-1 != daemon->epoll_fd) &&
+ (MHD_YES == daemon->listen_socket_in_epoll) )
+ {
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_DEL,
+ ret,
+ NULL))
+ MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+ daemon->listen_socket_in_epoll = MHD_NO;
+ }
+#endif
return ret;
}
@@ -2157,7 +2665,55 @@
}
+#if EPOLL_SUPPORT
/**
+ * Setup epoll FD for the daemon and initialize it to listen
+ * on the listen FD.
+ *
+ * @param daemon daemon to initialize for epoll
+ * @return MHD_YES on success, MHD_NO on failure
+ */
+static int
+setup_epoll_to_listen (struct MHD_Daemon *daemon)
+{
+ struct epoll_event event;
+
+ daemon->epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
+ if (-1 == daemon->epoll_fd)
+ {
+#if HAVE_MESSAGES
+ if (0 != (daemon->options & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Call to epoll_create1 failed: %s\n",
+ STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ if (-1 == daemon->socket_fd)
+ return MHD_YES; /* non-listening daemon */
+ event.events = EPOLLIN;
+ event.data.ptr = daemon;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ daemon->socket_fd,
+ &event))
+ {
+#if HAVE_MESSAGES
+ if (0 != (daemon->options & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Call to epoll_ctl failed: %s\n",
+ STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ daemon->listen_socket_in_epoll = MHD_YES;
+ return MHD_YES;
+}
+#endif
+
+
+
+/**
* Start a webserver on the given port.
*
* @param flags combination of MHD_FLAG values
@@ -2209,6 +2765,9 @@
if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
return NULL;
memset (daemon, 0, sizeof (struct MHD_Daemon));
+#if EPOLL_SUPPORT
+ daemon->epoll_fd = -1;
+#endif
/* try to open listen socket */
#if HTTPS_SUPPORT
if (0 != (flags & MHD_USE_SSL))
@@ -2236,10 +2795,12 @@
daemon->custom_error_log_cls = stderr;
#endif
#ifdef HAVE_LISTEN_SHUTDOWN
- use_pipe = (0 != (daemon->options & MHD_USE_NO_LISTEN_SOCKET));
+ use_pipe = (0 != (daemon->options & (MHD_USE_NO_LISTEN_SOCKET |
MHD_USE_PIPE_FOR_SHUTDOWN)));
#else
use_pipe = 1; /* yes, must use pipe to signal shutdown */
#endif
+ if (0 == (flags & (MHD_USE_SELECT_INTERNALLY |
MHD_USE_THREAD_PER_CONNECTION)))
+ use_pipe = 0; /* useless if we are using 'external' select */
if ( (use_pipe) &&
(0 != PIPE (daemon->wpipe)) )
{
@@ -2259,8 +2820,10 @@
MHD_DLOG (daemon,
"file descriptor for control pipe exceeds maximum value\n");
#endif
- CLOSE (daemon->wpipe[0]);
- CLOSE (daemon->wpipe[1]);
+ if (0 != CLOSE (daemon->wpipe[0]))
+ MHD_PANIC ("close failed\n");
+ if (0 != CLOSE (daemon->wpipe[1]))
+ MHD_PANIC ("close failed\n");
free (daemon);
return NULL;
}
@@ -2291,7 +2854,7 @@
#ifdef DAUTH_SUPPORT
if (daemon->nonce_nc_size > 0)
{
- if ( ( (size_t) (daemon->nonce_nc_size * sizeof(struct MHD_NonceNc))) /
+ if ( ( (size_t) (daemon->nonce_nc_size * sizeof (struct MHD_NonceNc))) /
sizeof(struct MHD_NonceNc) != daemon->nonce_nc_size)
{
#if HAVE_MESSAGES
@@ -2305,7 +2868,7 @@
free (daemon);
return NULL;
}
- daemon->nnc = malloc (daemon->nonce_nc_size * sizeof(struct
MHD_NonceNc));
+ daemon->nnc = malloc (daemon->nonce_nc_size * sizeof (struct
MHD_NonceNc));
if (NULL == daemon->nnc)
{
#if HAVE_MESSAGES
@@ -2359,6 +2922,32 @@
goto free_and_fail;
}
#endif
+#if EPOLL_SUPPORT
+ if ( (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (0 == daemon->worker_pool_size) &&
+ (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
+ {
+ if (0 != (flags & MHD_USE_THREAD_PER_CONNECTION))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Combining MHD_USE_THREAD_PER_CONNECTION and
MHD_USE_EPOLL_LINUX_ONLY is not supported.\n");
+#endif
+ goto free_and_fail;
+ }
+ if (MHD_YES != setup_epoll_to_listen (daemon))
+ goto free_and_fail;
+ }
+#else
+ if (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "epoll is not supported on this platform by this build.\n");
+#endif
+ goto free_and_fail;
+ }
+#endif
if ( (-1 == daemon->socket_fd) &&
(0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
{
@@ -2377,10 +2966,11 @@
#endif
goto free_and_fail;
}
- if ((SETSOCKOPT (socket_fd,
- SOL_SOCKET,
- SO_REUSEADDR,
- &on, sizeof (on)) < 0) && ((flags & MHD_USE_DEBUG) != 0))
+ if ( (0 > SETSOCKOPT (socket_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ &on, sizeof (on))) &&
+ (0 != (flags & MHD_USE_DEBUG)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
@@ -2423,27 +3013,33 @@
}
daemon->socket_fd = socket_fd;
- if (0 != (flags & MHD_USE_IPv6))
+ if ( (0 != (flags & MHD_USE_IPv6)) &&
+ (0 == (flags & MHD_USE_DUAL_STACK)) )
{
#ifdef IPPROTO_IPV6
#ifdef IPV6_V6ONLY
/* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see
"IPPROTO_IPV6 Socket Options"
(http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
and may also be missing on older POSIX systems; good luck if you
have any of those,
- your IPv6 socket may then also bind against IPv4... */
+ your IPv6 socket may then also bind against IPv4 anyway... */
#ifndef WINDOWS
const int on = 1;
- setsockopt (socket_fd,
- IPPROTO_IPV6, IPV6_V6ONLY,
- &on, sizeof (on));
#else
const char on = 1;
- setsockopt (socket_fd,
- IPPROTO_IPV6, IPV6_V6ONLY,
- &on, sizeof (on));
#endif
+ if ( (0 > SETSOCKOPT (socket_fd,
+ IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof (on))) &&
+ (0 != (flags & MHD_USE_DEBUG)) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "setsockopt failed: %s\n",
+ STRERROR (errno));
#endif
+ }
#endif
+#endif
}
if (-1 == BIND (socket_fd, servaddr, addrlen))
{
@@ -2454,19 +3050,35 @@
(unsigned int) port,
STRERROR (errno));
#endif
- CLOSE (socket_fd);
+ if (0 != CLOSE (socket_fd))
+ MHD_PANIC ("close failed\n");
goto free_and_fail;
}
-
- if (LISTEN (socket_fd, 20) < 0)
+#if EPOLL_SUPPORT
+ if (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY))
{
+ int sk_flags = fcntl (socket_fd, F_GETFL);
+ if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
+ {
+ MHD_DLOG (daemon,
+ "Failed to make listen socket non-blocking: %s\n",
+ STRERROR (errno));
+ if (0 != CLOSE (socket_fd))
+ MHD_PANIC ("close failed\n");
+ goto free_and_fail;
+ }
+ }
+#endif
+ if (LISTEN (socket_fd, 32) < 0)
+ {
#if HAVE_MESSAGES
if (0 != (flags & MHD_USE_DEBUG))
MHD_DLOG (daemon,
"Failed to listen for connections: %s\n",
STRERROR (errno));
#endif
- CLOSE (socket_fd);
+ if (0 != CLOSE (socket_fd))
+ MHD_PANIC ("close failed\n");
goto free_and_fail;
}
}
@@ -2476,7 +3088,7 @@
}
#ifndef WINDOWS
if ( (socket_fd >= FD_SETSIZE) &&
- (0 == (flags & MHD_USE_POLL)) )
+ (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY)) ) )
{
#if HAVE_MESSAGES
if ((flags & MHD_USE_DEBUG) != 0)
@@ -2485,7 +3097,8 @@
socket_fd,
FD_SETSIZE);
#endif
- CLOSE (socket_fd);
+ if (0 != CLOSE (socket_fd))
+ MHD_PANIC ("close failed\n");
goto free_and_fail;
}
#endif
@@ -2496,8 +3109,9 @@
MHD_DLOG (daemon,
"MHD failed to initialize IP connection limit mutex\n");
#endif
- if (-1 != socket_fd)
- CLOSE (socket_fd);
+ if ( (-1 != socket_fd) &&
+ (0 != CLOSE (socket_fd)) )
+ MHD_PANIC ("close failed\n");
goto free_and_fail;
}
if (0 != pthread_mutex_init (&daemon->cleanup_connection_mutex, NULL))
@@ -2507,8 +3121,9 @@
"MHD failed to initialize IP connection limit mutex\n");
#endif
pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
- if (-1 != socket_fd)
- CLOSE (socket_fd);
+ if ( (-1 != socket_fd) &&
+ (0 != CLOSE (socket_fd)) )
+ MHD_PANIC ("close failed\n");
goto free_and_fail;
}
@@ -2520,8 +3135,9 @@
MHD_DLOG (daemon,
"Failed to initialize TLS support\n");
#endif
- if (-1 != socket_fd)
- CLOSE (socket_fd);
+ if ( (-1 != socket_fd) &&
+ (0 != CLOSE (socket_fd)) )
+ MHD_PANIC ("close failed\n");
pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
goto free_and_fail;
@@ -2541,8 +3157,9 @@
#endif
pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
- if (-1 != socket_fd)
- CLOSE (socket_fd);
+ if ( (-1 != socket_fd) &&
+ (0 != CLOSE (socket_fd)) )
+ MHD_PANIC ("close failed\n");
goto free_and_fail;
}
if ( (daemon->worker_pool_size > 0) &&
@@ -2596,8 +3213,8 @@
{
/* Create copy of the Daemon object for each worker */
struct MHD_Daemon *d = &daemon->worker_pool[i];
+
memcpy (d, daemon, sizeof (struct MHD_Daemon));
-
/* Adjust pooling params for worker daemons; note that memcpy()
has already copied MHD_USE_SELECT_INTERNALLY thread model into
the worker threads. */
@@ -2611,7 +3228,11 @@
d->max_connections = conns_per_thread;
if (i < leftover_conns)
++d->max_connections;
-
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (MHD_YES != setup_epoll_to_listen (d)) )
+ goto thread_failed;
+#endif
/* Must init cleanup connection mutex for each worker */
if (0 != pthread_mutex_init (&d->cleanup_connection_mutex, NULL))
{
@@ -2623,7 +3244,8 @@
}
/* Spawn the worker thread */
- if (0 != (res_thread_create = create_thread (&d->pid, daemon,
&MHD_select_thread, d)))
+ if (0 != (res_thread_create =
+ create_thread (&d->pid, daemon, &MHD_select_thread, d)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
@@ -2646,8 +3268,9 @@
MHD_USE_SELECT_INTERNALLY mode. */
if (0 == i)
{
- if (-1 != socket_fd)
- CLOSE (socket_fd);
+ if ( (-1 != socket_fd) &&
+ (0 != CLOSE (socket_fd)) )
+ MHD_PANIC ("close failed\n");
pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
if (NULL != daemon->worker_pool)
@@ -2666,6 +3289,8 @@
free_and_fail:
/* clean up basic memory state in 'daemon' and return NULL to
indicate failure */
+ if (-1 != daemon->epoll_fd)
+ close (daemon->epoll_fd);
#ifdef DAUTH_SUPPORT
free (daemon->nnc);
pthread_mutex_destroy (&daemon->nnc_lock);
@@ -2680,6 +3305,37 @@
/**
+ * Close the given connection, remove it from all of its
+ * DLLs and move it into the cleanup queue.
+ *
+ * @param pos connection to move to cleanup
+ */
+static void
+close_connection (struct MHD_Connection *pos)
+{
+ struct MHD_Daemon *daemon = pos->daemon;
+
+ MHD_connection_close (pos,
+ MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+ if (pos->connection_timeout == pos->daemon->connection_timeout)
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ pos);
+ else
+ XDLL_remove (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ pos);
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ pos);
+ pos->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ DLL_insert (daemon->cleanup_head,
+ daemon->cleanup_tail,
+ pos);
+}
+
+
+/**
* Close all connections for the daemon; must only be called after
* all of the threads have been joined and there is no more concurrent
* activity on the connection lists.
@@ -2695,45 +3351,59 @@
/* first, make sure all threads are aware of shutdown; need to
traverse DLLs in peace... */
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ for (pos = daemon->connections_head; NULL != pos; pos = pos->nextX)
SHUTDOWN (pos->socket_fd,
(pos->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
- if (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+ MHD_PANIC ("Failed to release cleanup mutex\n");
- /* now, collect threads */
+ /* now, collect threads from thread pool */
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
while (NULL != (pos = daemon->connections_head))
{
- if (0 != (rc = pthread_join (pos->pid, &unused)))
- {
- MHD_PANIC ("Failed to join a thread\n");
- }
+ if (0 != (rc = pthread_join (pos->pid, &unused)))
+ MHD_PANIC ("Failed to join a thread\n");
pos->thread_joined = MHD_YES;
}
}
/* now that we're alone, move everyone to cleanup */
while (NULL != (pos = daemon->connections_head))
+ close_connection (pos);
+ MHD_cleanup_connections (daemon);
+}
+
+
+#if EPOLL_SUPPORT
+/**
+ * Shutdown 'epoll' event loop by adding 'wpipe' to its event set.
+ *
+ * @param daemon daemon of which the epoll instance must be signalled
+ */
+static void
+epoll_shutdown (struct MHD_Daemon *daemon)
+{
+ struct epoll_event event;
+
+ if (-1 == daemon->wpipe[1])
{
- MHD_connection_close (pos,
- MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
- DLL_remove (daemon->connections_head,
- daemon->connections_tail,
- pos);
- DLL_insert (daemon->cleanup_head,
- daemon->cleanup_tail,
- pos);
+ /* wpipe was required in this mode, how could this happen? */
+ MHD_PANIC ("Internal error\n");
}
- MHD_cleanup_connections (daemon);
+ event.events = EPOLLOUT;
+ event.data.ptr = NULL;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ daemon->wpipe[1],
+ &event))
+ MHD_PANIC ("Failed to add wpipe to epoll set to signal termination\n");
}
+#endif
/**
@@ -2762,6 +3432,12 @@
{
daemon->worker_pool[i].shutdown = MHD_YES;
daemon->worker_pool[i].socket_fd = -1;
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (-1 != daemon->worker_pool[i].epoll_fd) &&
+ (-1 == fd) )
+ epoll_shutdown (&daemon->worker_pool[i]);
+#endif
}
}
if (-1 != daemon->wpipe[1])
@@ -2777,6 +3453,13 @@
(void) SHUTDOWN (fd, SHUT_RDWR);
}
#endif
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (-1 != daemon->epoll_fd) &&
+ (-1 == fd) )
+ epoll_shutdown (daemon);
+#endif
+
#if DEBUG_CLOSE
#if HAVE_MESSAGES
MHD_DLOG (daemon, "MHD listen socket shutdown\n");
@@ -2790,12 +3473,13 @@
/* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to
check */
for (i = 0; i < daemon->worker_pool_size; ++i)
{
- if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused)))
- {
- MHD_PANIC ("Failed to join a thread\n");
- }
- close_all_connections (&daemon->worker_pool[i]);
+ if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused)))
+ MHD_PANIC ("Failed to join a thread\n");
+ close_all_connections (&daemon->worker_pool[i]);
pthread_mutex_destroy
(&daemon->worker_pool[i].cleanup_connection_mutex);
+ if ( (-1 != daemon->worker_pool[i].epoll_fd) &&
+ (0 != CLOSE (daemon->worker_pool[i].epoll_fd)) )
+ MHD_PANIC ("close failed\n");
}
free (daemon->worker_pool);
}
@@ -2813,8 +3497,9 @@
}
}
close_all_connections (daemon);
- if (-1 != fd)
- (void) CLOSE (fd);
+ if ( (-1 != fd) &&
+ (0 != CLOSE (fd)) )
+ MHD_PANIC ("close failed\n");
/* TLS clean up */
#if HTTPS_SUPPORT
@@ -2825,6 +3510,12 @@
gnutls_certificate_free_credentials (daemon->x509_cred);
}
#endif
+#if EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+ (-1 != daemon->epoll_fd) &&
+ (0 != CLOSE (daemon->epoll_fd)) )
+ MHD_PANIC ("close failed\n");
+#endif
#ifdef DAUTH_SUPPORT
free (daemon->nnc);
@@ -2835,8 +3526,10 @@
if (-1 != daemon->wpipe[1])
{
- (void) CLOSE (daemon->wpipe[0]);
- (void) CLOSE (daemon->wpipe[1]);
+ if (0 != CLOSE (daemon->wpipe[0]))
+ MHD_PANIC ("close failed\n");
+ if (0 != CLOSE (daemon->wpipe[1]))
+ MHD_PANIC ("close failed\n");
}
free (daemon);
}
@@ -2858,8 +3551,16 @@
{
switch (infoType)
{
+ case MHD_DAEMON_INFO_KEY_SIZE:
+ return NULL; /* no longer supported */
+ case MHD_DAEMON_INFO_MAC_KEY_SIZE:
+ return NULL; /* no longer supported */
case MHD_DAEMON_INFO_LISTEN_FD:
return (const union MHD_DaemonInfo *) &daemon->socket_fd;
+#if EPOLL_SUPPORT
+ case MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY:
+ return (const union MHD_DaemonInfo *) &daemon->epoll_fd;
+#endif
default:
return NULL;
};
Modified: libmicrohttpd/src/microhttpd/internal.h
===================================================================
--- libmicrohttpd/src/microhttpd/internal.h 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/microhttpd/internal.h 2013-07-04 15:29:56 UTC (rev
27749)
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- (C) 2007, 2008, 2009, 2010, 2011, 2012 Daniel Pittman and Christian Grothoff
+ (C) 2007-2013 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
@@ -32,7 +32,11 @@
#if HTTPS_SUPPORT
#include <gnutls/gnutls.h>
#endif
+#if EPOLL_SUPPORT
+#include <sys/epoll.h>
+#endif
+
/**
* Should we perform additional sanity checks at runtime (on our internal
* invariants)? This may lead to aborts, but can be useful for debugging.
@@ -69,63 +73,67 @@
#define MHD_PANIC(msg) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL)
#endif
+
/**
- * Events we care about with respect to poll/select
- * for file descriptors.
+ * State of the socket with respect to epoll (bitmask).
*/
-enum MHD_PollActions
+enum MHD_EpollState
{
+
/**
- * No event interests us.
+ * The socket is not involved with a defined state in epoll right
+ * now.
*/
- MHD_POLL_ACTION_NOTHING = 0,
+ MHD_EPOLL_STATE_UNREADY = 0,
/**
- * We would like to read.
+ * epoll told us that data was ready for reading, and we did
+ * not consume all of it yet.
*/
- MHD_POLL_ACTION_IN = 1,
+ MHD_EPOLL_STATE_READ_READY = 1,
/**
- * We would like to write.
- */
- MHD_POLL_ACTION_OUT = 2
+ * epoll told us that space was available for writing, and we did
+ * not consume all of it yet.
+ */
+ MHD_EPOLL_STATE_WRITE_READY = 2,
+
+ /**
+ * Is this connection currently in the 'eready' EDLL?
+ */
+ MHD_EPOLL_STATE_IN_EREADY_EDLL = 4
+
};
/**
- * State of the socket with respect to epoll.
+ * What is this connection waiting for?
*/
-enum MHD_EpollState
+enum MHD_ConnectionEventLoopInfo
{
+ /**
+ * We are waiting to be able to read.
+ */
+ MHD_EVENT_LOOP_INFO_READ = 0,
/**
- *
+ * We are waiting to be able to write.
*/
- MHD_EPOLL_STATE_NOTHING = 0
+ MHD_EVENT_LOOP_INFO_WRITE = 1,
+ /**
+ * We are waiting for the application to provide data.
+ */
+ MHD_EVENT_LOOP_INFO_BLOCK = 2,
-
+ /**
+ * We are finished and are awaiting cleanup.
+ */
+ MHD_EVENT_LOOP_INFO_CLEANUP = 3
};
/**
- * 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;
-};
-
-
-/**
* Maximum length of a nonce in digest authentication. 32(MD5 Hex) +
* 8(Timestamp Hex) + 1(NULL); hence 41 should suffice, but Opera
* (already) takes more (see Mantis #1633), so we've increased the
@@ -477,17 +485,39 @@
struct MHD_Connection
{
+#if EPOLL_SUPPORT
/**
- * This is a doubly-linked list.
+ * Next pointer for the EDLL listing connections that are epoll-ready.
*/
+ struct MHD_Connection *nextE;
+
+ /**
+ * Previous pointer for the EDLL listing connections that are epoll-ready.
+ */
+ struct MHD_Connection *prevE;
+#endif
+
+ /**
+ * Next pointer for the DLL describing our IO state.
+ */
struct MHD_Connection *next;
/**
- * This is a doubly-linked list.
+ * Previous pointer for the DLL describing our IO state.
*/
struct MHD_Connection *prev;
/**
+ * Next pointer for the XDLL organizing connections by timeout.
+ */
+ struct MHD_Connection *nextX;
+
+ /**
+ * Previous pointer for the XDLL organizing connections by timeout.
+ */
+ struct MHD_Connection *prevX;
+
+ /**
* Reference to the MHD_Daemon struct.
*/
struct MHD_Daemon *daemon;
@@ -667,11 +697,10 @@
int socket_fd;
/**
- * Has this socket been closed for reading (i.e.
- * other side closed the connection)? If so,
- * we must completely close the connection once
- * we are done sending our response (and stop
- * trying to read from this socket).
+ * Has this socket been closed for reading (i.e. other side closed
+ * the connection)? If so, we must completely close the connection
+ * once we are done sending our response (and stop trying to read
+ * from this socket).
*/
int read_closed;
@@ -680,12 +709,24 @@
*/
int thread_joined;
+#if EPOLL_SUPPORT
/**
+ * What is the state of this socket in relation to epoll?
+ */
+ enum MHD_EpollState epoll_state;
+#endif
+
+ /**
* State in the FSM for this connection.
*/
enum MHD_CONNECTION_STATE state;
/**
+ * What is this connection waiting for?
+ */
+ enum MHD_ConnectionEventLoopInfo event_loop_info;
+
+ /**
* HTTP response code. Only valid if response object
* is already set.
*/
@@ -797,7 +838,11 @@
/**
- * State kept for each MHD daemon.
+ * State kept for each MHD daemon. All connections are kept in two
+ * doubly-linked lists. The first one reflects the state of the
+ * connection in terms of what operations we are waiting for (read,
+ * write, locally blocked, cleanup) whereas the second is about its
+ * timeout state (default or custom).
*/
struct MHD_Daemon
{
@@ -813,7 +858,7 @@
void *default_handler_cls;
/**
- * Tail of doubly-linked list of our current, active connections.
+ * Head of doubly-linked list of our current, active connections.
*/
struct MHD_Connection *connections_head;
@@ -823,7 +868,7 @@
struct MHD_Connection *connections_tail;
/**
- * Tail of doubly-linked list of connections to clean up.
+ * Head of doubly-linked list of connections to clean up.
*/
struct MHD_Connection *cleanup_head;
@@ -832,11 +877,55 @@
*/
struct MHD_Connection *cleanup_tail;
+#if EPOLL_SUPPORT
/**
- * Function to call to check if we should
- * accept or reject an incoming request.
- * May be NULL.
+ * Head of EDLL of connections ready for processing (in epoll mode).
*/
+ struct MHD_Connection *eready_head;
+
+ /**
+ * Tail of EDLL of connections ready for processing (in epoll mode)
+ */
+ struct MHD_Connection *eready_tail;
+#endif
+
+ /**
+ * Head of the XDLL of ALL connections with a default ('normal')
+ * timeout, sorted by timeout (earliest at the tail, most recently
+ * used connection at the head). MHD can just look at the tail of
+ * this list to determine the timeout for all of its elements;
+ * whenever there is an event of a connection, the connection is
+ * moved back to the tail of the list.
+ *
+ * All connections by default start in this list; if a custom
+ * timeout that does not match 'connection_timeout' is set, they
+ * are moved to the 'manual_timeout_head'-XDLL.
+ */
+ struct MHD_Connection *normal_timeout_head;
+
+ /**
+ * Tail of the XDLL of ALL connections with a default timeout,
+ * sorted by timeout (earliest timeout at the tail).
+ */
+ struct MHD_Connection *normal_timeout_tail;
+
+ /**
+ * Head of the XDLL of ALL connections with a non-default/custom
+ * timeout, unsorted. MHD will do a O(n) scan over this list to
+ * determine the current timeout.
+ */
+ struct MHD_Connection *manual_timeout_head;
+
+ /**
+ * Tail of the XDLL of ALL connections with a non-default/custom
+ * timeout, unsorted.
+ */
+ struct MHD_Connection *manual_timeout_tail;
+
+ /**
+ * Function to call to check if we should accept or reject an
+ * incoming request. May be NULL.
+ */
MHD_AcceptPolicyCallback apc;
/**
@@ -942,7 +1031,20 @@
*/
int socket_fd;
+#if EPOLL_SUPPORT
/**
+ * File descriptor associated with our epoll loop.
+ */
+ int epoll_fd;
+
+ /**
+ * MHD_YES if the listen socket is in the 'epoll' set,
+ * MHD_NO if not.
+ */
+ int listen_socket_in_epoll;
+#endif
+
+ /**
* Pipe we use to signal shutdown, unless
* 'HAVE_LISTEN_SHUTDOWN' is defined AND we have a listen
* socket (which we can then 'shutdown' to stop listening).
@@ -1018,6 +1120,14 @@
*/
const char *https_mem_trust;
+ /**
+ * For how many connections do we have 'tls_read_ready' set to MHD_YES?
+ * Used to avoid O(n) traversal over all connections when determining
+ * event-loop timeout (as it needs to be zero if there is any connection
+ * which might have ready data within TLS).
+ */
+ unsigned int num_tls_read_ready;
+
#endif
#ifdef DAUTH_SUPPORT
@@ -1099,10 +1209,93 @@
(element)->prev = NULL; } while (0)
+
/**
+ * Insert an element at the head of a XDLL. Assumes that head, tail and
+ * element are structs with prevX and nextX fields.
+ *
+ * @param head pointer to the head of the XDLL
+ * @param tail pointer to the tail of the XDLL
+ * @param element element to insert
+ */
+#define XDLL_insert(head,tail,element) do { \
+ (element)->nextX = (head); \
+ (element)->prevX = NULL; \
+ if ((tail) == NULL) \
+ (tail) = element; \
+ else \
+ (head)->prevX = element; \
+ (head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a XDLL. Assumes
+ * that head, tail and element are structs
+ * with prevX and nextX fields.
+ *
+ * @param head pointer to the head of the XDLL
+ * @param tail pointer to the tail of the XDLL
+ * @param element element to remove
+ */
+#define XDLL_remove(head,tail,element) do { \
+ if ((element)->prevX == NULL) \
+ (head) = (element)->nextX; \
+ else \
+ (element)->prevX->nextX = (element)->nextX; \
+ if ((element)->nextX == NULL) \
+ (tail) = (element)->prevX; \
+ else \
+ (element)->nextX->prevX = (element)->prevX; \
+ (element)->nextX = NULL; \
+ (element)->prevX = NULL; } while (0)
+
+
+/**
+ * Insert an element at the head of a EDLL. Assumes that head, tail and
+ * element are structs with prevE and nextE fields.
+ *
+ * @param head pointer to the head of the EDLL
+ * @param tail pointer to the tail of the EDLL
+ * @param element element to insert
+ */
+#define EDLL_insert(head,tail,element) do { \
+ (element)->nextE = (head); \
+ (element)->prevE = NULL; \
+ if ((tail) == NULL) \
+ (tail) = element; \
+ else \
+ (head)->prevE = element; \
+ (head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a EDLL. Assumes
+ * that head, tail and element are structs
+ * with prevE and nextE fields.
+ *
+ * @param head pointer to the head of the EDLL
+ * @param tail pointer to the tail of the EDLL
+ * @param element element to remove
+ */
+#define EDLL_remove(head,tail,element) do { \
+ if ((element)->prevE == NULL) \
+ (head) = (element)->nextE; \
+ else \
+ (element)->prevE->nextE = (element)->nextE; \
+ if ((element)->nextE == NULL) \
+ (tail) = (element)->prevE; \
+ else \
+ (element)->nextE->prevE = (element)->prevE; \
+ (element)->nextE = NULL; \
+ (element)->prevE = NULL; } while (0)
+
+
+/**
* Equivalent to time(NULL) but tries to use some sort of monotonic
* clock that isn't affected by someone setting the system real time
* clock.
+ *
+ * @return 'current' time
*/
time_t MHD_monotonic_time(void);
Modified: libmicrohttpd/src/microhttpd/memorypool.c
===================================================================
--- libmicrohttpd/src/microhttpd/memorypool.c 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/microhttpd/memorypool.c 2013-07-04 15:29:56 UTC (rev
27749)
@@ -90,10 +90,13 @@
pool = malloc (sizeof (struct MemoryPool));
if (pool == NULL)
- return NULL;
+ return NULL;
#ifdef MAP_ANONYMOUS
- pool->memory = MMAP (NULL, max, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (max <= 32 * 1024)
+ pool->memory = MAP_FAILED;
+ else
+ pool->memory = MMAP (NULL, max, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#else
pool->memory = MAP_FAILED;
#endif
Modified: libmicrohttpd/src/testcurl/Makefile.am
===================================================================
--- libmicrohttpd/src/testcurl/Makefile.am 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/testcurl/Makefile.am 2013-07-04 15:29:56 UTC (rev
27749)
@@ -28,8 +28,8 @@
check_PROGRAMS = \
test_start_stop \
+ test_quiesce \
test_get \
- test_quiesce \
test_get_sendfile \
test_urlparse \
test_put \
@@ -52,6 +52,8 @@
$(CURL_FORK_TEST) \
perf_get $(PERF_GET_CONCURRENT)
+
+
if HAVE_POSTPROCESSOR
check_PROGRAMS += \
test_post \
Modified: libmicrohttpd/src/testcurl/perf_get.c
===================================================================
--- libmicrohttpd/src/testcurl/perf_get.c 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/testcurl/perf_get.c 2013-07-04 15:29:56 UTC (rev
27749)
@@ -219,7 +219,8 @@
}
curl_easy_cleanup (c);
}
- stop (poll_flag ? "internal poll" : "internal select");
+ stop (poll_flag == MHD_USE_POLL ? "internal poll" :
+ poll_flag == MHD_USE_EPOLL_LINUX_ONLY ? "internal epoll" : "internal
select");
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 4;
@@ -278,7 +279,8 @@
}
curl_easy_cleanup (c);
}
- stop (poll_flag ? "thread with poll" : "thread with select");
+ stop ((poll_flag & MHD_USE_POLL) ? "thread with poll" :
+ (poll_flag & MHD_USE_EPOLL_LINUX_ONLY) ? "thread with epoll" : "thread
with select");
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 64;
@@ -337,7 +339,8 @@
}
curl_easy_cleanup (c);
}
- stop (poll_flag ? "thread pool with poll" : "thread pool with select");
+ stop (0 != (poll_flag & MHD_USE_POLL) ? "thread pool with poll" :
+ 0 != (poll_flag & MHD_USE_EPOLL_LINUX_ONLY) ? "thread pool with epoll"
: "thread pool with select");
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 64;
@@ -507,6 +510,10 @@
errorCount += testMultithreadedGet (port++, MHD_USE_POLL);
errorCount += testMultithreadedPoolGet (port++, MHD_USE_POLL);
#endif
+#if EPOLL_SUPPORT
+ errorCount += testInternalGet (port++, MHD_USE_EPOLL_LINUX_ONLY);
+ errorCount += testMultithreadedPoolGet (port++, MHD_USE_EPOLL_LINUX_ONLY);
+#endif
MHD_destroy_response (response);
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
Modified: libmicrohttpd/src/testcurl/perf_get_concurrent.c
===================================================================
--- libmicrohttpd/src/testcurl/perf_get_concurrent.c 2013-07-04 15:27:59 UTC
(rev 27748)
+++ libmicrohttpd/src/testcurl/perf_get_concurrent.c 2013-07-04 15:29:56 UTC
(rev 27749)
@@ -336,6 +336,10 @@
errorCount += testMultithreadedGet (port++, MHD_USE_POLL);
errorCount += testMultithreadedPoolGet (port++, MHD_USE_POLL);
#endif
+#if EPOLL_SUPPORT
+ errorCount += testInternalGet (port++, MHD_USE_EPOLL_LINUX_ONLY);
+ errorCount += testMultithreadedPoolGet (port++, MHD_USE_EPOLL_LINUX_ONLY);
+#endif
MHD_destroy_response (response);
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
Modified: libmicrohttpd/src/testcurl/test_get.c
===================================================================
--- libmicrohttpd/src/testcurl/test_get.c 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/testcurl/test_get.c 2013-07-04 15:29:56 UTC (rev
27749)
@@ -145,6 +145,7 @@
return 0;
}
+
static int
testMultithreadedGet (int poll_flag)
{
@@ -194,6 +195,7 @@
return 0;
}
+
static int
testMultithreadedPoolGet (int poll_flag)
{
@@ -244,6 +246,7 @@
return 0;
}
+
static int
testExternalGet ()
{
@@ -366,6 +369,7 @@
return 0;
}
+
static int
testUnknownPortGet (int poll_flag)
{
@@ -506,6 +510,11 @@
errorCount += testUnknownPortGet (MHD_USE_POLL);
errorCount += testStopRace (MHD_USE_POLL);
#endif
+#if EPOLL_SUPPORT
+ errorCount += testInternalGet (MHD_USE_EPOLL_LINUX_ONLY);
+ errorCount += testMultithreadedPoolGet (MHD_USE_EPOLL_LINUX_ONLY);
+ errorCount += testUnknownPortGet (MHD_USE_EPOLL_LINUX_ONLY);
+#endif
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
Modified: libmicrohttpd/src/testcurl/test_quiesce.c
===================================================================
--- libmicrohttpd/src/testcurl/test_quiesce.c 2013-07-04 15:27:59 UTC (rev
27748)
+++ libmicrohttpd/src/testcurl/test_quiesce.c 2013-07-04 15:29:56 UTC (rev
27749)
@@ -31,6 +31,8 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#ifndef WINDOWS
#include <unistd.h>
@@ -38,8 +40,8 @@
#endif
static int oneone;
-static int done;
+
struct CBC
{
char *buf;
@@ -47,6 +49,7 @@
size_t size;
};
+
static size_t
copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
{
@@ -59,6 +62,7 @@
return size * nmemb;
}
+
static int
ahc_echo (void *cls,
struct MHD_Connection *connection,
@@ -91,24 +95,28 @@
return ret;
}
-void request_completed (void *cls, struct MHD_Connection *connection,
- void **con_cls, enum MHD_RequestTerminationCode code)
+
+static void
+request_completed (void *cls, struct MHD_Connection *connection,
+ void **con_cls, enum MHD_RequestTerminationCode code)
{
int *done = (int *)cls;
*done = 1;
}
-void ServeOneRequest(int fd)
+
+static void
+ServeOneRequest(int fd)
{
struct MHD_Daemon *d;
fd_set rs;
fd_set ws;
fd_set es;
int max;
- struct CURLMsg *msg;
time_t start;
struct timeval tv;
int done = 0;
+
d = MHD_start_daemon (MHD_USE_DEBUG,
1082, NULL, NULL, &ahc_echo, "GET",
MHD_OPTION_LISTEN_SOCKET, fd,
@@ -140,9 +148,12 @@
_exit(0);
}
-CURL *setupCURL(void *cbc)
+
+static CURL *
+setupCURL (void *cbc)
{
CURL *c;
+
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
@@ -162,6 +173,7 @@
return c;
}
+
static int
testGet (int type, int pool_count, int poll_flag)
{
@@ -171,18 +183,17 @@
struct CBC cbc;
CURLcode errornum;
int fd;
- pid_t pid;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
if (pool_count > 0) {
- d = MHD_start_daemon (type | MHD_USE_DEBUG | poll_flag,
+ d = MHD_start_daemon (type | MHD_USE_DEBUG | MHD_USE_PIPE_FOR_SHUTDOWN |
poll_flag,
11080, NULL, NULL, &ahc_echo, "GET",
MHD_OPTION_THREAD_POOL_SIZE, pool_count,
MHD_OPTION_END);
} else {
- d = MHD_start_daemon (type | MHD_USE_DEBUG | poll_flag,
+ d = MHD_start_daemon (type | MHD_USE_DEBUG | MHD_USE_PIPE_FOR_SHUTDOWN |
poll_flag,
11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
}
if (d == NULL)
@@ -211,13 +222,13 @@
return 8;
}
- fd = MHD_quiesce_daemon(d);
+ fd = MHD_quiesce_daemon (d);
+ if (fork() == 0)
+ {
+ ServeOneRequest (fd);
+ _exit(1);
+ }
- if (fork() == 0) {
- ServeOneRequest(fd);
- _exit(1);
- }
-
cbc.pos = 0;
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
@@ -225,25 +236,28 @@
"curl_easy_perform failed: `%s'\n",
curl_easy_strerror (errornum));
curl_easy_cleanup (c);
+ MHD_stop_daemon (d);
return 2;
}
waitpid(-1, NULL, 0);
- if (cbc.pos != strlen ("/hello_world")) {
- fprintf(stderr, "%s\n", cbc.buf);
- curl_easy_cleanup (c);
- MHD_stop_daemon (d);
- close(fd);
- return 4;
- }
- if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) {
- fprintf(stderr, "%s\n", cbc.buf);
- curl_easy_cleanup (c);
- MHD_stop_daemon (d);
- close(fd);
- return 8;
- }
+ if (cbc.pos != strlen ("/hello_world"))
+ {
+ fprintf(stderr, "%s\n", cbc.buf);
+ curl_easy_cleanup (c);
+ MHD_stop_daemon (d);
+ close(fd);
+ return 4;
+ }
+ if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+ {
+ fprintf(stderr, "%s\n", cbc.buf);
+ curl_easy_cleanup (c);
+ MHD_stop_daemon (d);
+ close(fd);
+ return 8;
+ }
/* at this point, the forked server quit, and the new
* server has quiesced, so new requests should fail
@@ -408,6 +422,10 @@
errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION, 0, MHD_USE_POLL);
errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 4, MHD_USE_POLL);
#endif
+#if EPOLL_SUPPORT
+ errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 0,
MHD_USE_EPOLL_LINUX_ONLY);
+ errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 4,
MHD_USE_EPOLL_LINUX_ONLY);
+#endif
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
Modified: libmicrohttpd/src/testcurl/test_start_stop.c
===================================================================
--- libmicrohttpd/src/testcurl/test_start_stop.c 2013-07-04 15:27:59 UTC
(rev 27748)
+++ libmicrohttpd/src/testcurl/test_start_stop.c 2013-07-04 15:29:56 UTC
(rev 27749)
@@ -110,6 +110,10 @@
errorCount += testMultithreadedGet (MHD_USE_POLL);
errorCount += testMultithreadedPoolGet (MHD_USE_POLL);
#endif
+#if EPOLL_SUPPORT
+ errorCount += testInternalGet (MHD_USE_EPOLL_LINUX_ONLY);
+ errorCount += testMultithreadedPoolGet (MHD_USE_EPOLL_LINUX_ONLY);
+#endif
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
return errorCount != 0; /* 0 == pass */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r27749 - in libmicrohttpd: . src/include src/microhttpd src/testcurl,
gnunet <=