commit f6e1ee426f9caebe49476ec88720d87032a433a6
parent b90f356c47e467407ae407723ddb02161b849f6a
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 7 Feb 2018 23:28:38 +0100
more work on mhd2 API implementation
Diffstat:
15 files changed, 2653 insertions(+), 122 deletions(-)
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
@@ -129,34 +129,181 @@ enum MHD_StatusCode
*/
MHD_SC_DAEMON_STARTED = 10000,
+
+ /**
+ * MHD does not support the requested combination of
+ * EPOLL with thread-per-connection mode.
+ */
+ MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID = 40000,
+
+ /**
+ * MHD does not support quiescing if ITC was disabled
+ * and threads are used.
+ */
+ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 40001,
+
/**
* This build of MHD does not support TLS, but the application
* requested TLS.
*/
- MHD_TLS_DISABLED = 50000,
+ MHD_SC_TLS_DISABLED = 50000,
/**
* The application requested an unsupported TLS backend to be used.
*/
- MHD_TLS_BACKEND_UNSUPPORTED = 50001,
+ MHD_SC_TLS_BACKEND_UNSUPPORTED = 50001,
/**
* The application requested a TLS cipher suite which is not
* supported by the selected backend.
*/
- MHD_TLS_CIPHERS_INVALID = 50002
+ MHD_SC_TLS_CIPHERS_INVALID = 50002
/**
* The application attempted to setup TLS paramters before
* enabling TLS.
*/
- MHD_TLS_BACKEND_UNINITIALIZED = 50003,
+ MHD_SC_TLS_BACKEND_UNINITIALIZED = 50003,
/**
* The selected TLS backend does not yet support this operation.
*/
- MHD_TLS_BACKEND_OPERATION_UNSUPPORTED = 50004,
+ MHD_SC_TLS_BACKEND_OPERATION_UNSUPPORTED = 50004,
+
+ /**
+ * Failed to setup ITC channel.
+ */
+ MHD_SC_ITC_INITIALIZATION_FAILED = 50005,
+
+ /**
+ * File descriptor for ITC channel too large.
+ */
+ MHD_SC_ITC_DESCRIPTOR_TOO_LARGE = 50006,
+
+ /**
+ * The specified value for the NC length is way too large
+ * for this platform (integer overflow on `size_t`).
+ */
+ MHD_SC_DIGEST_AUTH_NC_LENGTH_TOO_BIG = 50007,
+
+ /**
+ * We failed to allocate memory for the specified nonce
+ * counter array. The option was not set.
+ */
+ MHD_SC_DIGEST_AUTH_NC_ALLOCATION_FAILURE = 50008,
+
+ /**
+ * This build of the library does not support
+ * digest authentication.
+ */
+ MHD_SC_DIGEST_AUTH_NOT_SUPPORTED_BY_BUILD = 50009,
+
+ /**
+ * IPv6 requested but not supported by this build.
+ */
+ MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD = 50010,
+
+ /**
+ * We failed to open the listen socket. Maybe the build
+ * supports IPv6, but your kernel does not?
+ */
+ MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET = 50011,
+
+ /**
+ * Specified address family is not supported by this build.
+ */
+ MHD_SC_AF_NOT_SUPPORTED_BY_BUILD = 50012,
+
+ /**
+ * Failed to enable listen address reuse.
+ */
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED = 50013,
+
+ /**
+ * Enabling listen address reuse is not supported by this platform.
+ */
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED = 50014,
+
+ /**
+ * Failed to disable listen address reuse.
+ */
+ MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED = 50015,
+
+ /**
+ * Disabling listen address reuse is not supported by this platform.
+ */
+ MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED = 50016,
+
+ /**
+ * We failed to explicitly enable or disable dual stack for
+ * the IPv6 listen socket. The socket will be used in whatever
+ * the default is the OS gives us.
+ */
+ MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED = 50017,
+
+ /**
+ * On this platform, MHD does not support explicitly configuring
+ * dual stack behavior.
+ */
+ MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED = 50018,
+
+ /**
+ * We failed to bind the listen socket.
+ */
+ MHD_SC_LISTEN_SOCKET_BIND_FAILED = 50019,
+
+ /**
+ * Failed to enable TCP FAST OPEN option.
+ */
+ MHD_SC_FAST_OPEN_FAILURE = 50020,
+ /**
+ * Failed to start listening on listen socket.
+ */
+ MHD_SC_LISTEN_FAILURE = 50021,
+
+ /**
+ * Failed to obtain our listen port via introspection.
+ */
+ MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE = 50022,
+
+ /**
+ * Failed to obtain our listen port via introspection
+ * due to unsupported address family being used.
+ */
+ MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF = 50023,
+
+ /**
+ * We failed to set the listen socket to non-blocking.
+ */
+ MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE = 50024,
+
+ /**
+ * Listen socket value is too large (for use with select()).
+ */
+ MHD_SC_LISTEN_SOCKET_TOO_LARGE = 50025,
+
+ /**
+ * We failed to allocate memory for the thread pool.
+ */
+ MHD_SC_THREAD_POOL_MALLOC_FAILURE = 50026,
+
+ /**
+ * We failed to allocate mutex for thread pool worker.
+ */
+ MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE = 50027,
+
+ /**
+ * We failed to initialize the main thread for listening.
+ */
+ MHD_SC_THREAD_MAIN_LAUNCH_FAILURE = 50030,
+
+ /**
+ * We failed to initialize the threads for the worker pool.
+ */
+ MHD_SC_THREAD_POOL_LAUNCH_FAILURE = 50031,
+
+
};
@@ -631,10 +778,17 @@ MHD_daemon_tcp_fastopen (struct MHD_Daemon *daemon,
enum MHD_AddressFamily
{
/**
+ * Option not given, do not listen at all
+ * (unless listen socket or address specified by
+ * other means).
+ */
+ MHD_AF_NONE = 0,
+
+ /**
* Pick "best" available method automatically.
*/
- MHD_AF_AUTO = 0,
-
+ MHD_AF_AUTO,
+
/**
* Use IPv4.
*/
@@ -662,8 +816,7 @@ enum MHD_AddressFamily
* is specified, MHD will simply not listen on any socket!
*
* @param daemon which instance to configure the TCP port for
- * @param af address family to use, i.e. #AF_INET or #AF_INET6,
- * or #AF_UNSPEC for dual stack
+ * @param af address family to use
* @param port port to use, 0 to bind to a random (free) port
*/
_MHD_EXTERN void
@@ -1194,7 +1347,7 @@ MHD_daemon_digest_auth_random (struct MHD_Daemon *daemon,
* @param daemon daemon to configure
* @param nc_length desired array length
*/
-_MHD_EXTERN void
+_MHD_EXTERN enum MHD_StatusCode
MHD_daemon_digest_auth_nc_length (struct MHD_Daemon *daemon,
size_t nc_length);
diff --git a/src/lib/daemon.c b/src/lib/daemon.c
@@ -19,154 +19,910 @@
/**
* @file lib/daemon.c
- * @brief main functions to create, start, quiesce and destroy a daemon
+ * @brief main functions to start a daemon
* @author Christian Grothoff
*/
#include "internal.h"
+/* ************************* event loops ********************** */
+
+
+
+/* TODO: migrate! */
+
+
+/* ************* Functions for MHD_daemon_start() ************ */
+
/**
- * Logging implementation that logs to a file given
- * as the @a cls.
+ * Set listen socket options to allow port rebinding (or not)
+ * depending on how MHD was configured.
*
- * @param cls a `FILE *` to log to
- * @param sc status code of the event (ignored)
- * @param fm format string (`printf()`-style)
- * @param ap arguments to @a fm
- * @ingroup logging
+ * @param daemon[in,out] the daemon with the listen socket to configure
+ * @return #MHD_SC_OK on success (or non-fatal errors)
*/
-static void
-file_logger (void *cls,
- enum MHD_StatusCode sc,
- const char *fm,
- va_list ap)
+static enum MHD_StatusCode
+configure_listen_reuse (struct MHD_Daemon *daemon)
{
- FILE *f = cls;
+ const MHD_SCKT_OPT_BOOL_ on = 1;
- (void) sc;
- (void) vfprintf (f,
- fm,
- ap);
+ /* Apply the socket options according to
+ listening_address_reuse. */
+ /* FIXME: used to be -1/0/1, now defined as a bool!
+ MISMATCH! */
+ if (0 == daemon->listening_address_reuse)
+ {
+#ifndef MHD_WINSOCK_SOCKETS
+ /* No user requirement, use "traditional" default SO_REUSEADDR
+ * on non-W32 platforms, and do not fail if it doesn't work.
+ * Don't use it on W32, because on W32 it will allow multiple
+ * bind to the same address:port, like SO_REUSEPORT on others. */
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (void*) &on, sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ }
+#endif /* ! MHD_WINSOCK_SOCKETS */
+ return MHD_SC_OK;
+ }
+ if (daemon->listening_address_reuse > 0)
+ {
+ /* User requested to allow reusing listening address:port. */
+#ifndef MHD_WINSOCK_SOCKETS
+ /* Use SO_REUSEADDR on non-W32 platforms, and do not fail if
+ * it doesn't work. */
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (void*)&on, sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
+ }
+#endif /* ! MHD_WINSOCK_SOCKETS */
+ /* Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms.
+ * Fail if SO_REUSEPORT is not defined or setsockopt fails.
+ */
+ /* SO_REUSEADDR on W32 has the same semantics
+ as SO_REUSEPORT on BSD/Linux */
+#if defined(MHD_WINSOCK_SOCKETS) || defined(SO_REUSEPORT)
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+#ifndef MHD_WINSOCK_SOCKETS
+ SO_REUSEPORT,
+#else /* MHD_WINSOCK_SOCKETS */
+ SO_REUSEADDR,
+#endif /* MHD_WINSOCK_SOCKETS */
+ (void *) &on,
+ sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
+ }
+#else /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
+ /* we're supposed to allow address:port re-use, but
+ on this platform we cannot; fail hard */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED,
+ _("Cannot allow listening address reuse: SO_REUSEPORT not defined\n"));
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED;
+#endif /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
+ }
+
+ /* if (daemon->listening_address_reuse < 0) */
+ /* User requested to disallow reusing listening address:port.
+ * Do nothing except for Windows where SO_EXCLUSIVEADDRUSE
+ * is used and Solaris with SO_EXCLBIND.
+ * Fail if MHD was compiled for W32 without SO_EXCLUSIVEADDRUSE
+ * or setsockopt fails.
+ */
+#if (defined(MHD_WINSOCK_SOCKETS) && defined(SO_EXCLUSIVEADDRUSE)) || \
+ (defined(__sun) && defined(SO_EXCLBIND))
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+#ifdef SO_EXCLUSIVEADDRUSE
+ SO_EXCLUSIVEADDRUSE,
+#else /* SO_EXCLBIND */
+ SO_EXCLBIND,
+#endif /* SO_EXCLBIND */
+ (void *) &on,
+ sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED;
+ }
+#elif defined(MHD_WINSOCK_SOCKETS) /* SO_EXCLUSIVEADDRUSE not defined on W32? */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED,
+ _("Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n"));
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED;
+#endif /* MHD_WINSOCK_SOCKETS */
+ return MHD_SC_OK;
}
/**
- * Process escape sequences ('%HH') Updates val in place; the
- * result should be UTF-8 encoded and cannot be larger than the input.
- * The result must also still be 0-terminated.
+ * Open, configure and bind the listen socket (if required).
*
- * @param cls closure (use NULL)
- * @param req handle to request, not used
- * @param val value to unescape (modified in the process)
- * @return length of the resulting val (strlen(val) maybe
- * shorter afterwards due to elimination of escape sequences)
+ * @param daemon[in,out] daemon to open the socket for
+ * @return #MHD_SC_OK on success
*/
-static size_t
-unescape_wrapper (void *cls,
- struct MHD_Request *req,
- char *val)
+static enum MHD_StatusCode
+open_listen_socket (struct MHD_Daemon *daemon)
{
- (void) cls; /* Mute compiler warning. */
- (void) req; /* Mute compiler warning. */
- return MHD_http_unescape (val);
+ enum MHD_StatusCode sc;
+ bool usev6;
+ socklen_t addrlen;
+ struct sockaddr_storage ss;
+ const struct sockaddr *sa;
+
+ if (MHD_INVALID_SOCKET != daemon->listen_fd)
+ return MHD_SC_OK; /* application opened it for us! */
+
+ /* Determine address family */
+ if (MHD_AF_NONE != daemon->address_family)
+ {
+ switch (daemon->address_family)
+ {
+ case MHD_AF_NONE:
+ abort ();
+ case MHD_AF_AUTO:
+#if HAVE_INET6
+ use_v6 = true;
+#else
+ use_v6 = false;
+#endif
+ break;
+ case MHD_AF_INET:
+ use_v6 = false;
+ break;
+ case MHD_AF_INET6:
+ case MHD_AF_DUAL:
+#if HAVE_INET6
+ use_v6 = true;
+ break;
+#else
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD,
+ _("IPv6 not supported by this build\n"));
+#endif
+ return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
+#endif
+ }
+ }
+ else if (0 != daemon->listen_sa_len)
+ {
+ /* we have a listen address, get AF from there! */
+ switch (daemon->listen_sa.ss_family)
+ {
+ case AF_INET:
+ use_v6 = false;
+ break;
+#ifdef AF_INET6
+ case AF_INET6:
+ use_v6 = true;
+ break;
+#endif
+#ifdef AF_UNIX
+ case AF_UNIX:
+ // FIXME: not implemented
+ // (need to change MHD_socket_create_listen_() API!)
+#endif
+ default:
+ return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD;
+ }
+ }
+ else
+ {
+ /* no listening desired, that's OK */
+ return MHD_SC_OK;
+ }
+
+ /* try to open listen socket */
+ try_open_listen_socket:
+ daemon->listen_socket = MHD_socket_create_listen_(use_v6);
+ if ( (MHD_INVALID_SOCKET == daemon->listen_socket) &&
+ (MHD_AF_AUTO == daemon->address_family) &&
+ (use_v6) )
+ {
+ use_v6 = false;
+ goto try_open_listen_socket;
+ }
+ if (MHD_INVALID_SOCKET == daemon->listen_socket)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET,
+ _("Failed to create socket for listening: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
+ }
+
+ if (MHD_SC_OK !=
+ (sc = configure_listen_reuse (daemon)))
+ return sc;
+
+ /* configure for dual stack (or not) */
+ if (use_v6)
+ {
+#if defined IPPROTO_IPV6 && defined 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 anyway... */
+ const MHD_SCKT_OPT_BOOL_ v6_only =
+ (MHD_AF_INET6 == daemon->address_family);
+ if (0 > setsockopt (listen_fd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ (const void *) &v6_only,
+ sizeof (v6_only)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ }
+#else
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED,
+ _("Cannot explicitly setup dual stack behavior on this platform\n"));
+#endif
+#endif
+ }
+
+ /* Determine address to bind to */
+ if (0 != daemon->listen_sa_len)
+ {
+ /* Bind address explicitly given */
+ sa = daemon->listen_sa;
+ addrlen = daemon->listen_sa_len;
+ }
+ else
+ {
+ /* Compute bind address based on port and AF */
+#if HAVE_INET6
+ if (use_v6)
+ {
+#ifdef IN6ADDR_ANY_INIT
+ static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT;
+#endif
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+
+ addrlen = sizeof (struct sockaddr_in6);
+ memset (sin6,
+ 0,
+ sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons (daemon->listen_port);
+#ifdef IN6ADDR_ANY_INIT
+ sin6->sin6_addr = static_in6any;
+#endif
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sin6->sin6_len = sizeof (struct sockaddr_in6);
+#endif
+ }
+ else
+#endif
+ {
+ struct sockaddr_in *sin4 = (struct sockaddr_in *) &ss;
+
+ addrlen = sizeof (struct sockaddr_in);
+ memset (sin4,
+ 0,
+ sizeof (struct sockaddr_in));
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = htons (daemon->listen_port);
+ if (0 != INADDR_ANY)
+ sin4->sin_addr.s_addr = htonl (INADDR_ANY);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sin4->sin_len = sizeof (struct sockaddr_in);
+#endif
+ }
+ sa = (const struct sockaddr *) ss;
+ }
+
+ /* actually do the bind() */
+ if (-1 == bind (daemon->listen_socket,
+ sa,
+ addrlen))
+ {
+#ifdef HAVE_MESSAGES
+ unsigned int port = 0;
+
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ if (addrlen == sizeof (struct sockaddr_in))
+ port = ntohs (((const struct sockaddr_in*)sa)->sin_port);
+ else
+ port = UINT16_MAX + 1; /* indicate size error */
+ break;
+ case AF_INET6:
+ if (addrlen == sizeof (struct sockaddr_in6))
+ port = ntohs (((const struct sockaddr_in6*)sa)->sin_port);
+ else
+ port = UINT16_MAX + 1; /* indicate size error */
+ break;
+ default:
+ port = UINT_MAX; /* AF_UNIX? */
+ break;
+ }
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_SOCKET_BIND_FAILED,
+ _("Failed to bind to port %u: %s\n"),
+ port,
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_SOCKET_BIND_FAILED;
+ }
+
+ /* setup TCP_FASTOPEN */
+#ifdef TCP_FASTOPEN
+ if (MHD_FOM_DISABLE != daemon->fast_open_method)
+ {
+ if (0 != setsockopt (daemon->listen_socket,
+ IPPROTO_TCP,
+ TCP_FASTOPEN,
+ &daemon->fastopen_queue_size,
+ sizeof (daemon->fastopen_queue_size)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_FAST_OPEN_FAILURE,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ if (MHD_FOM_REQUIRE == daemon->fast_open_method)
+ return MHD_SC_FAST_OPEN_FAILURE;
+ }
+ }
+#endif
+
+ /* setup listening */
+ if (0 > listen (daemon->listen_socket,
+ daemon->listen_backlog_size))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_FAILURE,
+ _("Failed to listen for connections: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_FAILURE;
+ }
+ return MHD_SC_OK;
}
-/**
- * Create (but do not yet start) an MHD daemon.
- * Usually, you will want to set various options before
- * starting the daemon with #MHD_daemon_start().
+/**
+ * Obtain the listen port number from the socket (if it
+ * was not explicitly set by us, i.e. if we were given
+ * a listen socket or if the port was 0 and the OS picked
+ * a free one).
*
- * @param cb function to be called for incoming requests
- * @param cb_cls closure for @a cb
- * @return NULL on error
+ * @param daemon[in,out] daemon to obtain the port number for
*/
-struct MHD_Daemon *
-MHD_daemon_create (MHD_RequestCallback cb,
- void *cb_cls)
+static void
+get_listen_port_number (struct MHD_Daemon *daemon)
{
- struct MHD_Daemon *daemon;
-
- MHD_check_global_init_();
- if (NULL == cb)
- return NULL;
- if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
- return NULL;
- memset (daemon,
+ struct sockaddr_storage servaddr;
+ socklen_t addrlen;
+
+ if ( (0 != daemon->port) ||
+ (MHD_INVALID_SOCKET == daemon->listen_socket) )
+ return; /* nothing to be done */
+
+ memset (&servaddr,
0,
- sizeof (struct MHD_Daemon));
- daemon->rc = cb;
- daemon->rc_cls = cb_cls;
- daemon->logger = &file_logger;
- daemon->logger_cls = stderr;
- daemon->unescape_cb = &unescape_wrapper;
- daemon->tls_ciphers = TLS_CIPHERS_DEFAULT;
- daemon->connection_memory_limit_b = MHD_POOL_SIZE_DEFAULT;
- daemon->connection_memory_increment_b = BUF_INC_SIZE_DEFAULT;
-#if ENABLE_DAUTH
- daemon->digest_nc_length = DIGEST_NC_LENGTH_DEFAULT;
-#endif
- daemon->listen_backlog = LISTEN_BACKLOG_DEFAULT;
- daemon->fo_queue_length = FO_QUEUE_LENGTH_DEFAULT;
- daemon->listen_socket = MHD_INVALID_SOCKET;
- return daemon;
+ sizeof (struct sockaddr_storage));
+ addrlen = sizeof (servaddr);
+ if (0 != getsockname (daemon->listen_socket,
+ (struct sockaddr *) &servaddr,
+ &addrlen))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
+ _("Failed to get listen port number: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif /* HAVE_MESSAGES */
+ return;
+ }
+#ifdef MHD_POSIX_SOCKETS
+ if (sizeof (servaddr) < addrlen)
+ {
+ /* should be impossible with `struct sockaddr_storage` */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
+ _("Failed to get listen port number (`struct sockaddr_storage` too small!?)\n"));
+#endif /* HAVE_MESSAGES */
+ return;
+ }
+#endif /* MHD_POSIX_SOCKETS */
+ switch (servaddr.ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s4 = (struct sockaddr_in *) &servaddr;
+
+ daemon->port = ntohs (s4->sin_port);
+ break;
+ }
+#ifdef HAVE_INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &servaddr;
+
+ daemon->port = ntohs(s6->sin6_port);
+ break;
+ }
+#endif /* HAVE_INET6 */
+#ifdef AF_UNIX
+ case AF_UNIX:
+ daemon->port = 0; /* special value for UNIX domain sockets */
+ break;
+#endif
+ default:
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF,
+ _("Unknown address family!\n"));
+#endif
+ daemon->port = 0; /* ugh */
+ break;
+ }
}
/**
- * Start a webserver.
+ * Setup epoll() FD for the daemon and initialize it to listen
+ * on the listen FD.
+ * @remark To be called only from thread that process
+ * daemon's select()/poll()/etc.
*
- * @param daemon daemon to start; you can no longer set
- * options on this daemon after this call!
+ * @param daemon daemon to initialize for epoll()
* @return #MHD_SC_OK on success
- * @ingroup event
*/
-enum MHD_StatusCode
-MHD_daemon_start (struct MHD_Daemon *daemon)
+static enum MHD_StatusCode
+setup_epoll_to_listen (struct MHD_Daemon *daemon)
{
-
- return -1;
+ struct epoll_event event;
+ MHD_socket ls;
+
+ /* FIXME: update function! */
+ daemon->epoll_fd = setup_epoll_fd (daemon);
+ if (-1 == daemon->epoll_fd)
+ return MHD_NO;
+#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
+ if (0 != (MHD_ALLOW_UPGRADE & daemon->options))
+ {
+ daemon->epoll_upgrade_fd = setup_epoll_fd (daemon);
+ if (MHD_INVALID_SOCKET == daemon->epoll_upgrade_fd)
+ return MHD_NO;
+ }
+#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
+ if ( (MHD_INVALID_SOCKET == (ls = daemon->listen_fd)) ||
+ (daemon->was_quiesced) )
+ return MHD_YES; /* non-listening daemon */
+ event.events = EPOLLIN;
+ event.data.ptr = daemon;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ ls,
+ &event))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_NO;
+ }
+ daemon->listen_socket_in_epoll = true;
+ if (MHD_ITC_IS_VALID_(daemon->itc))
+ {
+ event.events = EPOLLIN;
+ event.data.ptr = (void *) epoll_itc_marker;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ MHD_itc_r_fd_ (daemon->itc),
+ &event))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_NO;
+ }
+ }
+ return MHD_SC_OK;
}
+#endif
/**
- * Stop accepting connections from the listening socket. Allows
- * clients to continue processing, but stops accepting new
- * connections. Note that the caller is responsible for closing the
- * returned socket; however, if MHD is run using threads (anything but
- * external select mode), it must not be closed until AFTER
- * #MHD_stop_daemon has been called (as it is theoretically possible
- * that an existing thread is still using it).
+ * Thread that runs the polling loop until the daemon
+ * is explicitly shut down.
*
- * Note that some thread modes require the caller to have passed
- * #MHD_USE_ITC when using this API. If this daemon is
- * in one of those modes and this option was not given to
- * #MHD_start_daemon, this function will return #MHD_INVALID_SOCKET.
+ * @param cls `struct MHD_Deamon` to run select loop in a thread for
+ * @return always 0 (on shutdown)
+ */
+static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
+MHD_polling_thread (void *cls)
+{
+ struct MHD_Daemon *daemon = cls;
+
+ MHD_thread_init_ (&daemon->pid);
+ while (! daemon->shutdown)
+ {
+ switch (daemon->event_loop_syscall)
+ {
+ case MHD_ELS_AUTO:
+ MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style");
+ break;
+ case MHD_ELS_SELECT:
+ MHD_select (daemon,
+ MHD_YES);
+ break;
+ case MHD_ELS_POLL:
+ MHD_poll (daemon,
+ MHD_YES);
+ break;
+ case MHD_ELS_EPOLL:
+#ifdef EPOLL_SUPPORT
+ MHD_epoll (daemon,
+ MHD_YES);
+#else
+ MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier");
+#endif
+ break;
+ }
+ MHD_cleanup_connections (daemon);
+ }
+ /* Resume any pending for resume connections, join
+ * all connection's threads (if any) and finally cleanup
+ * everything. */
+ close_all_connections (daemon);
+
+ return (MHD_THRD_RTRN_TYPE_)0;
+}
+
+
+/**
+ * Setup the thread pool (if needed).
*
- * @param daemon daemon to stop accepting new connections for
- * @return old listen socket on success, #MHD_INVALID_SOCKET if
- * the daemon was already not listening anymore, or
- * was never started
- * @ingroup specialized
+ * @param daemon[in,out] daemon to setup thread pool for
+ * @return #MHD_SC_OK on success
*/
-MHD_socket
-MHD_daemon_quiesce (struct MHD_Daemon *daemon)
+static enum MHD_StatusCode
+setup_thread_pool (struct MHD_Daemon *daemon)
{
- return -1;
+ /* Coarse-grained count of connections per thread (note error
+ * due to integer division). Also keep track of how many
+ * connections are leftover after an equal split. */
+ unsigned int conns_per_thread = daemon->connection_limit
+ / daemon->threading_model;
+ unsigned int leftover_conns = daemon->connection_limit
+ % daemon->threading_model;
+ unsigned int i;
+ enum MHD_StatusCode sc;
+
+ /* Allocate memory for pooled objects */
+ daemon->worker_pool = calloc (daemon->threading_model,
+ sizeof (struct MHD_Daemon));
+ if (NULL == daemon->worker_pool)
+ return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
+
+ /* Start the workers in the pool */
+ for (i = 0; i < daemon->threading_model; i++)
+ {
+ /* 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_INTERNAL_POLLING_THREAD thread model into
+ the worker threads. */
+ d->master = daemon;
+ d->worker_pool_size = 0;
+ d->worker_pool = NULL;
+ /* Divide available connections evenly amongst the threads.
+ * Thread indexes in [0, leftover_conns) each get one of the
+ * leftover connections. */
+ d->connection_limit = conns_per_thread;
+ if (i < leftover_conns)
+ ++d->connection_limit;
+
+ if (! daemon->disable_itc)
+ {
+ if (! MHD_itc_init_ (d->itc))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_INITIALIZATION_FAILED,
+ _("Failed to create worker inter-thread communication channel: %s\n"),
+ MHD_itc_last_strerror_() );
+#endif
+ sc = MHD_SC_ITC_INITIALIZATION_FAILED;
+ goto thread_failed;
+ }
+ if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
+ (! MHD_SCKT_FD_FITS_FDSET_(MHD_itc_r_fd_ (d->itc),
+ NULL)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
+ _("File descriptor for inter-thread communication channel exceeds maximum value\n"));
+#endif
+ MHD_itc_destroy_chk_ (d->itc);
+ sc = MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
+ goto thread_failed;
+ }
+ }
+ else
+ {
+ MHD_itc_set_invalid_ (d->itc);
+ }
+
+#ifdef EPOLL_SUPPORT
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (MHD_SC_OK != (sc = setup_epoll_to_listen (d))) )
+ goto thread_failed;
+#endif
+
+ /* Must init cleanup connection mutex for each worker */
+ if (! MHD_mutex_init_ (&d->cleanup_connection_mutex))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE,
+ _("MHD failed to initialize cleanup connection mutex\n"));
+#endif
+ if (! daemon->disable_itc)
+ MHD_itc_destroy_chk_ (d->itc);
+ sc = MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE;
+ goto thread_failed;
+ }
+
+ /* Spawn the worker thread */
+ if (! MHD_create_named_thread_ (&d->pid,
+ "MHD-worker",
+ daemon->thread_stack_size,
+ &MHD_polling_thread,
+ d))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_THREAD_POOL_LAUNCH_FAILURE,
+ _("Failed to create pool thread: %s\n"),
+ MHD_strerror_ (errno));
+#endif
+ sc = MHD_SC_THREAD_POOL_LAUNCH_FAILURE;
+ /* Free memory for this worker; cleanup below handles
+ * all previously-created workers. */
+ if (! daemon->disable_itc)
+ MHD_itc_destroy_chk_ (d->itc);
+ MHD_mutex_destroy_chk_ (&d->cleanup_connection_mutex);
+ goto thread_failed;
+ }
+ } /* end for() */
+ return MHD_SC_OK;
+
+thread_failed:
+ /* If no worker threads created, then shut down normally. Calling
+ MHD_stop_daemon (as we do below) doesn't work here since it
+ assumes a 0-sized thread pool means we had been in the default
+ MHD_USE_INTERNAL_POLLING_THREAD mode. */
+ if (0 == i)
+ {
+ if (NULL != daemon->worker_pool)
+ {
+ free (daemon->worker_pool);
+ daemon->worker_pool = NULL;
+ }
+ return MHD_SC_THREAD_LAUNCH_FAILURE;
+ }
+ /* Shutdown worker threads we've already created. Pretend
+ as though we had fully initialized our daemon, but
+ with a smaller number of threads than had been
+ requested. */
+ daemon->worker_pool_size = i;
+ daemon->listen_socket = MHD_daemon_quiesce (daemon);
+ return MHD_SC_THREAD_LAUNCH_FAILURE;
}
/**
- * Shutdown and destroy an HTTP daemon.
+ * Start a webserver.
*
- * @param daemon daemon to stop
+ * @param daemon daemon to start; you can no longer set
+ * options on this daemon after this call!
+ * @return #MHD_SC_OK on success
* @ingroup event
*/
-void
-MHD_daemon_destroy (struct MHD_Daemon *daemon)
+enum MHD_StatusCode
+MHD_daemon_start (struct MHD_Daemon *daemon)
{
- free (daemon);
+ enum MHD_StatusCode sc;
+
+ if (MHD_ELS_AUTO == daemon->event_loop_syscall)
+ {
+#if EPOLL_SUPPORT
+ /* We do not support thread-per-connection in combination
+ with epoll, so use poll in this case, otherwise prefer
+ epoll. */
+ if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
+ daemon->event_loop_syscall = MHD_ELS_POLL;
+ else
+ daemon->event_loop_syscall = MHD_ELS_EPOLL;
+#elif HAVE_POLL
+ daemon->event_loop_syscall = MHD_ELS_POLL;
+#else
+ daemon->event_loop_syscall = MHD_ELS_SELECT;
+#endif
+ }
+
+#ifdef EPOLL_SUPPORT
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (0 == daemon->worker_pool_size) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID,
+ _("Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n"));
+#endif
+ return MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID;
+ }
+#endif
+
+ /* Setup ITC */
+ if ( (! daemon->disable_itc) &&
+ (0 == daemon->worker_pool_size) )
+ {
+ if (! MHD_itc_init_ (daemon->itc))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_INITIALIZATION_FAILED,
+ _("Failed to create inter-thread communication channel: %s\n"),
+ MHD_itc_last_strerror_ ());
+#endif
+ return MHD_SC_ITC_INITIALIZATION_FAILED;
+ }
+ if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
+ (! MHD_SCKT_FD_FITS_FDSET_(MHD_itc_r_fd_ (daemon->itc),
+ NULL)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
+ _("File descriptor for inter-thread communication channel exceeds maximum value\n"));
+#endif
+ return MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
+ }
+ }
+
+ if (MHD_SC_OK != (sc = open_listen_socket (daemon)))
+ return sc;
+
+ /* Check listen socket is in range (if we are limited) */
+ if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
+ (! MHD_SCKT_FD_FITS_FDSET_(daemon->listen_socket,
+ NULL)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_SOCKET_TOO_LARGE,
+ _("Socket descriptor larger than FD_SETSIZE: %d > %d\n"),
+ daemon->listen_socket,
+ FD_SETSIZE);
+#endif
+ return MHD_SC_LISTEN_SOCKET_TOO_LARGE;
+ }
+
+ /* set listen socket to non-blocking */
+ if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (! MHD_socket_nonblocking_ (daemon->listen_socket)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE,
+ _("Failed to set nonblocking mode on listening socket: %s\n"),
+ MHD_socket_last_strerr_());
+#endif
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) ||
+ (daemon->worker_pool_size > 0) )
+ {
+ /* Accept must be non-blocking. Multiple children may wake
+ * up to handle a new connection, but only one will win the
+ * race. The others must immediately return. As this is
+ * not possible, we must fail hard here. */
+ return MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE;
+ }
+ }
+
+#ifdef EPOLL_SUPPORT
+ /* Setup epoll */
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (0 == daemon->worker_pool_size) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_SC_OK != (sc = setup_epoll_to_listen (daemon))) )
+ return sc;
+#endif
+
+ /* Setup main listen thread (only if we have no thread pool or
+ external event loop and do have a listen socket) */
+ /* FIXME: why no worker thread if we have no listen socket? */
+ if ( ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) ||
+ (1 == daemon->threading_model) ) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (! MHD_create_named_thread_ (&daemon->pid,
+ (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
+ ? "MHD-listen"
+ : "MHD-single",
+ daemon->thread_stack_size,
+ &MHD_polling_thread,
+ daemon) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_THREAD_MAIN_LAUNCH_FAILURE,
+ _("Failed to create listen thread: %s\n"),
+ MHD_strerror_ (errno));
+#endif
+ return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE;
+ }
+
+ /* Setup worker threads */
+ /* FIXME: why no thread pool if we have no listen socket? */
+ if ( (1 < daemon->threading_model) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_SC_OK != (sc = setup_thread_pool (daemon))) )
+ return sc;
+
+ return MHD_SC_OK;
}
+
/* end of daemon.c */
diff --git a/src/lib/daemon_create.c b/src/lib/daemon_create.c
@@ -0,0 +1,134 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/daemon_create.c
+ * @brief main functions to create a daemon
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Logging implementation that logs to a file given
+ * as the @a cls.
+ *
+ * @param cls a `FILE *` to log to
+ * @param sc status code of the event (ignored)
+ * @param fm format string (`printf()`-style)
+ * @param ap arguments to @a fm
+ * @ingroup logging
+ */
+static void
+file_logger (void *cls,
+ enum MHD_StatusCode sc,
+ const char *fm,
+ va_list ap)
+{
+ FILE *f = cls;
+
+ (void) sc;
+ (void) vfprintf (f,
+ fm,
+ ap);
+}
+
+
+/**
+ * Process escape sequences ('%HH') Updates val in place; the
+ * result should be UTF-8 encoded and cannot be larger than the input.
+ * The result must also still be 0-terminated.
+ *
+ * @param cls closure (use NULL)
+ * @param req handle to request, not used
+ * @param val value to unescape (modified in the process)
+ * @return length of the resulting val (strlen(val) maybe
+ * shorter afterwards due to elimination of escape sequences)
+ */
+static size_t
+unescape_wrapper (void *cls,
+ struct MHD_Request *req,
+ char *val)
+{
+ (void) cls; /* Mute compiler warning. */
+ (void) req; /* Mute compiler warning. */
+ return MHD_http_unescape (val);
+}
+
+
+/**
+ * Create (but do not yet start) an MHD daemon.
+ * Usually, you will want to set various options before
+ * starting the daemon with #MHD_daemon_start().
+ *
+ * @param cb function to be called for incoming requests
+ * @param cb_cls closure for @a cb
+ * @return NULL on error
+ */
+struct MHD_Daemon *
+MHD_daemon_create (MHD_RequestCallback cb,
+ void *cb_cls)
+{
+ struct MHD_Daemon *daemon;
+
+ MHD_check_global_init_();
+ if (NULL == cb)
+ return NULL;
+ if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
+ return NULL;
+ memset (daemon,
+ 0,
+ sizeof (struct MHD_Daemon));
+ daemon->rc = cb;
+ daemon->rc_cls = cb_cls;
+ daemon->logger = &file_logger;
+ daemon->logger_cls = stderr;
+ daemon->unescape_cb = &unescape_wrapper;
+ daemon->tls_ciphers = TLS_CIPHERS_DEFAULT;
+ daemon->connection_memory_limit_b = MHD_POOL_SIZE_DEFAULT;
+ daemon->connection_memory_increment_b = BUF_INC_SIZE_DEFAULT;
+#if ENABLE_DAUTH
+ daemon->digest_nc_length = DIGEST_NC_LENGTH_DEFAULT;
+#endif
+ daemon->listen_backlog = LISTEN_BACKLOG_DEFAULT;
+ daemon->fo_queue_length = FO_QUEUE_LENGTH_DEFAULT;
+ daemon->listen_socket = MHD_INVALID_SOCKET;
+
+ if (! MHD_mutex_init_ (&daemon->cleanup_connection_mutex))
+ {
+ free (daemon);
+ return NULL;
+ }
+ if (! MHD_mutex_init_ (&daemon->per_ip_connection_mutex))
+ {
+ MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+ free (daemon);
+ return NULL;
+ }
+#ifdef DAUTH_SUPPORT
+ if (! MHD_mutex_init_ (&daemon->nnc_lock))
+ {
+ MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+ MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
+ free (daemon);
+ return NULL;
+ }
+#endif
+ return daemon;
+}
diff --git a/src/lib/daemon_destroy.c b/src/lib/daemon_destroy.c
@@ -0,0 +1,178 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/daemon_destroy.c
+ * @brief main functions to destroy a daemon
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+/* TODO: migrate logic below! */
+
+/**
+ * Shutdown and destroy an HTTP daemon.
+ *
+ * @param daemon daemon to stop
+ * @ingroup event
+ */
+void
+MHD_daemon_destroy (struct MHD_Daemon *daemon)
+{
+ MHD_socket fd;
+ unsigned int i;
+
+ if (NULL == daemon)
+ return;
+ daemon->shutdown = true;
+ if (daemon->was_quiesced)
+ fd = MHD_INVALID_SOCKET; /* Do not use FD if daemon was quiesced */
+ else
+ fd = daemon->listen_socket;
+
+ /* FIXME: convert from here to microhttpd2-style API! */
+
+ if (NULL != daemon->worker_pool)
+ { /* Master daemon with worker pool. */
+ mhd_assert (1 < daemon->worker_pool_size);
+ mhd_assert (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD));
+
+ /* Let workers shutdown in parallel. */
+ for (i = 0; i < daemon->worker_pool_size; ++i)
+ {
+ daemon->worker_pool[i].shutdown = true;
+ if (MHD_ITC_IS_VALID_(daemon->worker_pool[i].itc))
+ {
+ if (! MHD_itc_activate_ (daemon->worker_pool[i].itc, "e"))
+ MHD_PANIC (_("Failed to signal shutdown via inter-thread communication channel."));
+ }
+ else
+ mhd_assert (MHD_INVALID_SOCKET != fd);
+ }
+#ifdef HAVE_LISTEN_SHUTDOWN
+ if (MHD_INVALID_SOCKET != fd)
+ {
+ (void) shutdown (fd,
+ SHUT_RDWR);
+ }
+#endif /* HAVE_LISTEN_SHUTDOWN */
+ for (i = 0; i < daemon->worker_pool_size; ++i)
+ {
+ MHD_stop_daemon (&daemon->worker_pool[i]);
+ }
+ free (daemon->worker_pool);
+ mhd_assert (MHD_ITC_IS_INVALID_(daemon->itc));
+#ifdef EPOLL_SUPPORT
+ mhd_assert (-1 == daemon->epoll_fd);
+#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
+ mhd_assert (-1 == daemon->epoll_upgrade_fd);
+#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
+#endif /* EPOLL_SUPPORT */
+ }
+ else
+ { /* Worker daemon or single daemon. */
+ if (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD))
+ { /* Worker daemon or single daemon with internal thread(s). */
+ mhd_assert (0 == daemon->worker_pool_size);
+ if (0 != (MHD_TEST_ALLOW_SUSPEND_RESUME & daemon->options))
+ resume_suspended_connections (daemon);
+
+ if (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD))
+ {
+ /* Separate thread(s) is used for polling sockets. */
+ if (MHD_ITC_IS_VALID_(daemon->itc))
+ {
+ if (! MHD_itc_activate_ (daemon->itc, "e"))
+ MHD_PANIC (_("Failed to signal shutdown via inter-thread communication channel"));
+ }
+ else
+ {
+#ifdef HAVE_LISTEN_SHUTDOWN
+ if (MHD_INVALID_SOCKET != fd)
+ {
+ if (NULL == daemon->master)
+ (void) shutdown (fd,
+ SHUT_RDWR);
+ }
+ else
+#endif /* HAVE_LISTEN_SHUTDOWN */
+ mhd_assert (false); /* Should never happen */
+ }
+
+ if (! MHD_join_thread_ (daemon->pid.handle))
+ {
+ MHD_PANIC (_("Failed to join a thread\n"));
+ }
+ /* close_all_connections() was called in daemon thread. */
+ }
+ }
+ else
+ {
+ /* No internal threads are used for polling sockets. */
+ close_all_connections (daemon);
+ }
+ if (MHD_ITC_IS_VALID_ (daemon->itc))
+ MHD_itc_destroy_chk_ (daemon->itc);
+
+#ifdef EPOLL_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
+ (-1 != daemon->epoll_fd) )
+ MHD_socket_close_chk_ (daemon->epoll_fd);
+#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
+ if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
+ (-1 != daemon->epoll_upgrade_fd) )
+ MHD_socket_close_chk_ (daemon->epoll_upgrade_fd);
+#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
+#endif /* EPOLL_SUPPORT */
+
+ MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex);
+ }
+
+ if (NULL != daemon->master)
+ return;
+ /* Cleanup that should be done only one time in master/single daemon.
+ * Do not perform this cleanup in worker daemons. */
+
+ if (MHD_INVALID_SOCKET != fd)
+ MHD_socket_close_chk_ (fd);
+
+ /* TLS clean up */
+#ifdef HTTPS_SUPPORT
+ if (daemon->have_dhparams)
+ {
+ gnutls_dh_params_deinit (daemon->https_mem_dhparams);
+ daemon->have_dhparams = false;
+ }
+ if (0 != (daemon->options & MHD_USE_TLS))
+ {
+ gnutls_priority_deinit (daemon->priority_cache);
+ if (daemon->x509_cred)
+ gnutls_certificate_free_credentials (daemon->x509_cred);
+ }
+#endif /* HTTPS_SUPPORT */
+
+#ifdef DAUTH_SUPPORT
+ free (daemon->nnc);
+ MHD_mutex_destroy_chk_ (&daemon->nnc_lock);
+#endif
+ MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex);
+ free (daemon);
+}
+
+/* end of daemon_destroy.c */
diff --git a/src/lib/daemon_info.c b/src/lib/daemon_info.c
@@ -0,0 +1,60 @@
+
+/**
+ * Obtain information about the given daemon
+ * (not fully implemented!).
+ *
+ * @param daemon what daemon to get information about
+ * @param info_type what information is desired?
+ * @param ... depends on @a info_type
+ * @return NULL if this information is not available
+ * (or if the @a info_type is unknown)
+ * @ingroup specialized
+ */
+const union MHD_DaemonInfo *
+MHD_get_daemon_info (struct MHD_Daemon *daemon,
+ enum MHD_DaemonInfoType info_type,
+ ...)
+{
+ if (NULL == daemon)
+ return NULL;
+ switch (info_type)
+ {
+ 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->listen_fd;
+#ifdef EPOLL_SUPPORT
+ case MHD_DAEMON_INFO_EPOLL_FD:
+ return (const union MHD_DaemonInfo *) &daemon->epoll_fd;
+#endif
+ case MHD_DAEMON_INFO_CURRENT_CONNECTIONS:
+ if (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD))
+ {
+ /* Assume that MHD_run() in not called in other thread
+ * at the same time. */
+ MHD_cleanup_connections (daemon);
+ }
+ else if (daemon->worker_pool)
+ {
+ unsigned int i;
+ /* Collect the connection information stored in the workers. */
+ daemon->connections = 0;
+ for (i = 0; i < daemon->worker_pool_size; i++)
+ {
+ /* FIXME: next line is thread-safe only if read is atomic. */
+ daemon->connections += daemon->worker_pool[i].connections;
+ }
+ }
+ return (const union MHD_DaemonInfo *) &daemon->connections;
+ case MHD_DAEMON_INFO_FLAGS:
+ return (const union MHD_DaemonInfo *) &daemon->options;
+ case MHD_DAEMON_INFO_BIND_PORT:
+ return (const union MHD_DaemonInfo *) &daemon->port;
+ default:
+ return NULL;
+ }
+}
+
+
diff --git a/src/lib/daemon_options.c b/src/lib/daemon_options.c
@@ -698,14 +698,40 @@ MHD_daemon_digest_auth_random (struct MHD_Daemon *daemon,
* @param daemon daemon to configure
* @param nc_length desired array length
*/
-void
+enum MHD_StatusCode
MHD_daemon_digest_auth_nc_length (struct MHD_Daemon *daemon,
size_t nc_length)
{
#if ENABLE_DAUTH
+ if ( ( (size_t) (nc_length * sizeof (struct MHD_NonceNc))) /
+ sizeof (struct MHD_NonceNc) != nc_length)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Specified value for NC_SIZE too large\n"));
+#endif
+ return MHD_DIGEST_AUTH_NC_LENGTH_TOO_BIG;
+ }
+ if (0 < nc_length)
+ {
+ if (NULL != daemon->nnc)
+ free (daemon->nnc);
+ daemon->nnc = malloc (daemon->nonce_nc_size *
+ sizeof (struct MHD_NonceNc));
+ if (NULL == daemon->nnc)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Failed to allocate memory for nonce-nc map: %s\n"),
+ MHD_strerror_ (errno));
+#endif
+ return MHD_DIGEST_AUTH_NC_ALLOCATION_FAILURE;
+ }
+ }
daemon->digest_nc_length = nc_length;
+ return MHD_SC_OK;
#else
- MHD_PANIC ("digest authentication not supported by this build");
+ return MHD_DIGEST_AUTH_NOT_SUPPORTED_BY_BUILD;
#endif
}
diff --git a/src/lib/daemon_quiesce.c b/src/lib/daemon_quiesce.c
@@ -0,0 +1,127 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/daemon.c
+ * @brief main functions to quiesce a daemon
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Stop accepting connections from the listening socket. Allows
+ * clients to continue processing, but stops accepting new
+ * connections. Note that the caller is responsible for closing the
+ * returned socket; however, if MHD is run using threads (anything but
+ * external select mode), it must not be closed until AFTER
+ * #MHD_stop_daemon has been called (as it is theoretically possible
+ * that an existing thread is still using it).
+ *
+ * Note that some thread modes require the caller to have passed
+ * #MHD_USE_ITC when using this API. If this daemon is
+ * in one of those modes and this option was not given to
+ * #MHD_start_daemon, this function will return #MHD_INVALID_SOCKET.
+ *
+ * @param daemon daemon to stop accepting new connections for
+ * @return old listen socket on success, #MHD_INVALID_SOCKET if
+ * the daemon was already not listening anymore, or
+ * was never started
+ * @ingroup specialized
+ */
+MHD_socket
+MHD_daemon_quiesce (struct MHD_Daemon *daemon)
+{
+ MHD_socket listen_socket;
+
+ if (MHD_INVALID_SOCKET == (listen_socket = daemon->listen_socket))
+ return MHD_INVALID_SOCKET;
+ if ( (daemon->disable_itc) &&
+ (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC,
+ "Using MHD_quiesce_daemon in this mode requires ITC\n");
+#endif
+ return MHD_INVALID_SOCKET;
+ }
+
+ if (NULL != daemon->worker_pool)
+ {
+ unsigned int i;
+
+ for (i = 0; i < daemon->threading_model; i++)
+ {
+ struct MHD_Daemon *worker = &daemon->worker_pool[i];
+
+ worker->was_quiesced = true;
+#ifdef EPOLL_SUPPORT
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (-1 != worker->epoll_fd) &&
+ (worker->listen_socket_in_epoll) )
+ {
+ if (0 != epoll_ctl (worker->epoll_fd,
+ EPOLL_CTL_DEL,
+ listen_socket,
+ NULL))
+ MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
+ worker->listen_socket_in_epoll = false;
+ }
+ else
+#endif
+ if (MHD_ITC_IS_VALID_(worker->itc))
+ {
+ if (! MHD_itc_activate_ (worker->itc,
+ "q"))
+ MHD_PANIC (_("Failed to signal quiesce via inter-thread communication channel"));
+ }
+ }
+ daemon->was_quiesced = true;
+#ifdef EPOLL_SUPPORT
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (-1 != daemon->epoll_fd) &&
+ (daemon->listen_socket_in_epoll) )
+ {
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_DEL,
+ listen_socket,
+ NULL))
+ MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+ daemon->listen_socket_in_epoll = false;
+ }
+#endif
+ }
+
+ if ( (MHD_ITC_IS_VALID_(daemon->itc)) &&
+ (! MHD_itc_activate_ (daemon->itc,
+ "q")) )
+ MHD_PANIC (_("Failed to signal quiesce via inter-thread communication channel"));
+
+ /* FIXME: we might want some bi-directional communication here
+ (in both the thread-pool and single-thread case!)
+ to be sure that the threads have stopped using the listen
+ socket, otherwise there is still the possibility of a race
+ between a thread accept()ing and the caller closing and
+ re-binding the socket. */
+
+ return listen_socket;
+}
+
+
diff --git a/src/lib/init.c b/src/lib/init.c
@@ -0,0 +1,156 @@
+#include "init.h"
+
+
+#ifdef MHD_HTTPS_REQUIRE_GRYPT
+#if defined(HTTPS_SUPPORT) && GCRYPT_VERSION_NUMBER < 0x010600
+#if defined(MHD_USE_POSIX_THREADS)
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#elif defined(MHD_W32_MUTEX_)
+
+static int
+gcry_w32_mutex_init (void **ppmtx)
+{
+ *ppmtx = malloc (sizeof (MHD_mutex_));
+
+ if (NULL == *ppmtx)
+ return ENOMEM;
+ if (!MHD_mutex_init_ ((MHD_mutex_*)*ppmtx))
+ {
+ free (*ppmtx);
+ *ppmtx = NULL;
+ return EPERM;
+ }
+
+ return 0;
+}
+
+
+static int
+gcry_w32_mutex_destroy (void **ppmtx)
+{
+ int res = (MHD_mutex_destroy_ ((MHD_mutex_*)*ppmtx)) ? 0 : EINVAL;
+ free (*ppmtx);
+ return res;
+}
+
+
+static int
+gcry_w32_mutex_lock (void **ppmtx)
+{
+ return MHD_mutex_lock_ ((MHD_mutex_*)*ppmtx) ? 0 : EINVAL;
+}
+
+
+static int
+gcry_w32_mutex_unlock (void **ppmtx)
+{
+ return MHD_mutex_unlock_ ((MHD_mutex_*)*ppmtx) ? 0 : EINVAL;
+}
+
+
+static struct gcry_thread_cbs gcry_threads_w32 = {
+ (GCRY_THREAD_OPTION_USER | (GCRY_THREAD_OPTION_VERSION << 8)),
+ NULL, gcry_w32_mutex_init, gcry_w32_mutex_destroy,
+ gcry_w32_mutex_lock, gcry_w32_mutex_unlock,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+#endif /* defined(MHD_W32_MUTEX_) */
+#endif /* HTTPS_SUPPORT && GCRYPT_VERSION_NUMBER < 0x010600 */
+#endif /* MHD_HTTPS_REQUIRE_GRYPT */
+
+
+#ifndef _AUTOINIT_FUNCS_ARE_SUPPORTED
+
+/**
+ * Track global initialisation
+ */
+volatile int global_init_count = 0;
+#ifdef MHD_MUTEX_STATIC_DEFN_INIT_
+/**
+ * Global initialisation mutex
+ */
+MHD_MUTEX_STATIC_DEFN_INIT_(global_init_mutex_);
+#endif /* MHD_MUTEX_STATIC_DEFN_INIT_ */
+
+
+/**
+ * Check whether global initialisation was performed
+ * and call initialiser if necessary.
+ */
+void
+MHD_check_global_init_ (void)
+{
+#ifdef MHD_MUTEX_STATIC_DEFN_INIT_
+ MHD_mutex_lock_chk_(&global_init_mutex_);
+#endif /* MHD_MUTEX_STATIC_DEFN_INIT_ */
+ if (0 == global_init_count++)
+ MHD_init ();
+#ifdef MHD_MUTEX_STATIC_DEFN_INIT_
+ MHD_mutex_unlock_chk_(&global_init_mutex_);
+#endif /* MHD_MUTEX_STATIC_DEFN_INIT_ */
+}
+
+
+/**
+ * Initialize do setup work.
+ */
+void
+MHD_init(void)
+{
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+ WSADATA wsd;
+#endif /* _WIN32 && ! __CYGWIN__ */
+
+ if (NULL == mhd_panic)
+ mhd_panic = &mhd_panic_std;
+
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+ if (0 != WSAStartup(MAKEWORD(2, 2), &wsd))
+ MHD_PANIC (_("Failed to initialize winsock\n"));
+ mhd_winsock_inited_ = 1;
+ if (2 != LOBYTE(wsd.wVersion) && 2 != HIBYTE(wsd.wVersion))
+ MHD_PANIC (_("Winsock version 2.2 is not available\n"));
+#endif
+#ifdef HTTPS_SUPPORT
+#ifdef MHD_HTTPS_REQUIRE_GRYPT
+#if GCRYPT_VERSION_NUMBER < 0x010600
+#if defined(MHD_USE_POSIX_THREADS)
+ if (0 != gcry_control (GCRYCTL_SET_THREAD_CBS,
+ &gcry_threads_pthread))
+ MHD_PANIC (_("Failed to initialise multithreading in libgcrypt\n"));
+#elif defined(MHD_W32_MUTEX_)
+ if (0 != gcry_control (GCRYCTL_SET_THREAD_CBS,
+ &gcry_threads_w32))
+ MHD_PANIC (_("Failed to initialise multithreading in libgcrypt\n"));
+#endif /* defined(MHD_W32_MUTEX_) */
+ gcry_check_version (NULL);
+#else
+ if (NULL == gcry_check_version ("1.6.0"))
+ MHD_PANIC (_("libgcrypt is too old. MHD was compiled for libgcrypt 1.6.0 or newer\n"));
+#endif
+#endif /* MHD_HTTPS_REQUIRE_GRYPT */
+ gnutls_global_init ();
+#endif /* HTTPS_SUPPORT */
+ MHD_monotonic_sec_counter_init();
+#ifdef HAVE_FREEBSD_SENDFILE
+ MHD_conn_init_static_ ();
+#endif /* HAVE_FREEBSD_SENDFILE */
+}
+
+
+void
+MHD_fini(void)
+{
+#ifdef HTTPS_SUPPORT
+ gnutls_global_deinit ();
+#endif /* HTTPS_SUPPORT */
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+ if (mhd_winsock_inited_)
+ WSACleanup();
+#endif
+ MHD_monotonic_sec_counter_finish();
+}
+
+#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED
+_SET_INIT_AND_DEINIT_FUNCS(MHD_init, MHD_fini);
+#endif /* _AUTOINIT_FUNCS_ARE_SUPPORTED */
diff --git a/src/lib/init.h b/src/lib/init.h
@@ -0,0 +1,29 @@
+
+
+#ifndef INIT_H
+#define INIT_H
+
+/**
+ * Globally initialise library.
+ */
+void
+MHD_init(void);
+
+
+#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED
+/**
+ * Do nothing - global initialisation is
+ * performed by library constructor.
+ */
+#define MHD_check_global_init_() (void)0
+#else /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */
+/**
+ * Check whether global initialisation was performed
+ * and call initialiser if necessary.
+ */
+void
+MHD_check_global_init_ (void);
+#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */
+
+
+#endif /* INIT_H */
diff --git a/src/lib/internal.h b/src/lib/internal.h
@@ -126,6 +126,599 @@ extern void *mhd_panic_cls;
#endif /* ! MHD_STATICSTR_LEN_ */
+
+/**
+ * Ability to use same connection for next request
+ */
+enum MHD_ConnKeepAlive
+{
+ /**
+ * Connection must be closed after sending response.
+ */
+ MHD_CONN_MUST_CLOSE = -1,
+
+ /**
+ * KeelAlive state is not yet determined
+ */
+ MHD_CONN_KEEPALIVE_UNKOWN = 0,
+
+ /**
+ * Connection can be used for serving next request
+ */
+ MHD_CONN_USE_KEEPALIVE = 1
+};
+
+
+/**
+ * Function to receive plaintext data.
+ *
+ * @param conn the connection struct
+ * @param write_to where to write received data
+ * @param max_bytes maximum number of bytes to receive
+ * @return number of bytes written to @a write_to
+ */
+typedef ssize_t
+(*ReceiveCallback) (struct MHD_Connection *conn,
+ void *write_to,
+ size_t max_bytes);
+
+
+/**
+ * Function to transmit plaintext data.
+ *
+ * @param conn the connection struct
+ * @param read_from where to read data to transmit
+ * @param max_bytes maximum number of bytes to transmit
+ * @return number of bytes transmitted
+ */
+typedef ssize_t
+(*TransmitCallback) (struct MHD_Connection *conn,
+ const void *read_from,
+ size_t max_bytes);
+
+
+/**
+ * States in a state machine for a request.
+ *
+ * The main transitions are any-state to #MHD_REQUEST_CLOSED, any
+ * state to state+1, #MHD_REQUEST_FOOTERS_SENT to
+ * #MHD_REQUEST_INIT. #MHD_REQUEST_CLOSED is the terminal state
+ * and #MHD_REQUEST_INIT the initial state.
+ *
+ * Note that transitions for *reading* happen only after the input has
+ * been processed; transitions for *writing* happen after the
+ * respective data has been put into the write buffer (the write does
+ * not have to be completed yet). A transition to
+ * #MHD_REQUEST_CLOSED or #MHD_REQUEST_INIT requires the write
+ * to be complete.
+ */
+enum MHD_REQUEST_STATE
+{
+ /**
+ * Request just started (no headers received).
+ * Waiting for the line with the request type, URL and version.
+ */
+ MHD_REQUEST_INIT = 0,
+
+ /**
+ * 1: We got the URL (and request type and version). Wait for a header line.
+ */
+ MHD_REQUEST_URL_RECEIVED = MHD_REQUEST_INIT + 1,
+
+ /**
+ * 2: We got part of a multi-line request header. Wait for the rest.
+ */
+ MHD_REQUEST_HEADER_PART_RECEIVED = MHD_REQUEST_URL_RECEIVED + 1,
+
+ /**
+ * 3: We got the request headers. Process them.
+ */
+ MHD_REQUEST_HEADERS_RECEIVED = MHD_REQUEST_HEADER_PART_RECEIVED + 1,
+
+ /**
+ * 4: We have processed the request headers. Send 100 continue.
+ */
+ MHD_REQUEST_HEADERS_PROCESSED = MHD_REQUEST_HEADERS_RECEIVED + 1,
+
+ /**
+ * 5: We have processed the headers and need to send 100 CONTINUE.
+ */
+ MHD_REQUEST_CONTINUE_SENDING = MHD_REQUEST_HEADERS_PROCESSED + 1,
+
+ /**
+ * 6: We have sent 100 CONTINUE (or do not need to). Read the message body.
+ */
+ MHD_REQUEST_CONTINUE_SENT = MHD_REQUEST_CONTINUE_SENDING + 1,
+
+ /**
+ * 7: We got the request body. Wait for a line of the footer.
+ */
+ MHD_REQUEST_BODY_RECEIVED = MHD_REQUEST_CONTINUE_SENT + 1,
+
+ /**
+ * 8: We got part of a line of the footer. Wait for the
+ * rest.
+ */
+ MHD_REQUEST_FOOTER_PART_RECEIVED = MHD_REQUEST_BODY_RECEIVED + 1,
+
+ /**
+ * 9: We received the entire footer. Wait for a response to be queued
+ * and prepare the response headers.
+ */
+ MHD_REQUEST_FOOTERS_RECEIVED = MHD_REQUEST_FOOTER_PART_RECEIVED + 1,
+
+ /**
+ * 10: We have prepared the response headers in the writ buffer.
+ * Send the response headers.
+ */
+ MHD_REQUEST_HEADERS_SENDING = MHD_REQUEST_FOOTERS_RECEIVED + 1,
+
+ /**
+ * 11: We have sent the response headers. Get ready to send the body.
+ */
+ MHD_REQUEST_HEADERS_SENT = MHD_REQUEST_HEADERS_SENDING + 1,
+
+ /**
+ * 12: We are ready to send a part of a non-chunked body. Send it.
+ */
+ MHD_REQUEST_NORMAL_BODY_READY = MHD_REQUEST_HEADERS_SENT + 1,
+
+ /**
+ * 13: We are waiting for the client to provide more
+ * data of a non-chunked body.
+ */
+ MHD_REQUEST_NORMAL_BODY_UNREADY = MHD_REQUEST_NORMAL_BODY_READY + 1,
+
+ /**
+ * 14: We are ready to send a chunk.
+ */
+ MHD_REQUEST_CHUNKED_BODY_READY = MHD_REQUEST_NORMAL_BODY_UNREADY + 1,
+
+ /**
+ * 15: We are waiting for the client to provide a chunk of the body.
+ */
+ MHD_REQUEST_CHUNKED_BODY_UNREADY = MHD_REQUEST_CHUNKED_BODY_READY + 1,
+
+ /**
+ * 16: We have sent the response body. Prepare the footers.
+ */
+ MHD_REQUEST_BODY_SENT = MHD_REQUEST_CHUNKED_BODY_UNREADY + 1,
+
+ /**
+ * 17: We have prepared the response footer. Send it.
+ */
+ MHD_REQUEST_FOOTERS_SENDING = MHD_REQUEST_BODY_SENT + 1,
+
+ /**
+ * 18: We have sent the response footer. Shutdown or restart.
+ */
+ MHD_REQUEST_FOOTERS_SENT = MHD_REQUEST_FOOTERS_SENDING + 1,
+
+ /**
+ * 19: This request is to be closed.
+ */
+ MHD_REQUEST_CLOSED = MHD_REQUEST_FOOTERS_SENT + 1,
+
+ /**
+ * 20: This request is finished (only to be freed)
+ */
+ MHD_REQUEST_IN_CLEANUP = MHD_REQUEST_CLOSED + 1,
+
+#ifdef UPGRADE_SUPPORT
+ /**
+ * Request was "upgraded" and socket is now under the
+ * control of the application.
+ */
+ MHD_REQUEST_UPGRADE
+#endif /* UPGRADE_SUPPORT */
+
+};
+
+
+/**
+ * Header or cookie in HTTP request or response.
+ */
+struct MHD_HTTP_Header
+{
+ /**
+ * Headers are kept in a linked list.
+ */
+ struct MHD_HTTP_Header *next;
+
+ /**
+ * The name of the header (key), without the colon.
+ */
+ char *header;
+
+ /**
+ * The value of the header.
+ */
+ char *value;
+
+ /**
+ * Type of the header (where in the HTTP protocol is this header
+ * from).
+ */
+ enum MHD_ValueKind kind;
+
+};
+
+
+/**
+ * State kept for each HTTP request.
+ */
+struct MHD_Request
+{
+
+ /**
+ * Reference to the MHD_Daemon struct.
+ */
+ struct MHD_Daemon *daemon;
+
+ /**
+ * Connection this request is associated with.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Linked list of parsed headers.
+ */
+ struct MHD_HTTP_Header *headers_received;
+
+ /**
+ * Tail of linked list of parsed headers.
+ */
+ struct MHD_HTTP_Header *headers_received_tail;
+
+ /**
+ * The memory pool is created whenever we first read from the TCP
+ * stream and destroyed at the end of each request (and re-created
+ * for the next request). In the meantime, this pointer is NULL.
+ * The pool is used for all request-related data except for the
+ * response (which maybe shared between requests) and the IP
+ * address (which persists across individual requests).
+ */
+ struct MemoryPool *pool;
+
+ /**
+ * We allow the main application to associate some pointer with the
+ * HTTP request, which is passed to each #MHD_AccessHandlerCallback
+ * and some other API calls. Here is where we store it. (MHD does
+ * not know or care what it is).
+ */
+ void *client_context;
+
+ /**
+ * Request method. Should be GET/POST/etc. Allocated in pool.
+ */
+ char *method;
+
+ /**
+ * Requested URL (everything after "GET" only). Allocated
+ * in pool.
+ */
+ const char *url;
+
+ /**
+ * HTTP version string (i.e. http/1.1). Allocated
+ * in pool.
+ */
+ char *version;
+
+ /**
+ * Close connection after sending response?
+ * Functions may change value from "Unknown" or "KeepAlive" to "Must close",
+ * but no functions reset value "Must Close" to any other value.
+ */
+ enum MHD_ConnKeepAlive keepalive;
+
+ /**
+ * Buffer for reading requests. Allocated in pool. Actually one
+ * byte larger than @e read_buffer_size (if non-NULL) to allow for
+ * 0-termination.
+ */
+ char *read_buffer;
+
+ /**
+ * Buffer for writing response (headers only). Allocated
+ * in pool.
+ */
+ char *write_buffer;
+
+ /**
+ * Last incomplete header line during parsing of headers.
+ * Allocated in pool. Only valid if state is
+ * either #MHD_REQUEST_HEADER_PART_RECEIVED or
+ * #MHD_REQUEST_FOOTER_PART_RECEIVED.
+ */
+ char *last;
+
+ /**
+ * Position after the colon on the last incomplete header
+ * line during parsing of headers.
+ * Allocated in pool. Only valid if state is
+ * either #MHD_REQUEST_HEADER_PART_RECEIVED or
+ * #MHD_REQUEST_FOOTER_PART_RECEIVED.
+ */
+ char *colon;
+
+
+ /**
+ * Function used for reading HTTP request stream.
+ */
+ ReceiveCallback recv_cls;
+
+ /**
+ * Function used for writing HTTP response stream.
+ */
+ TransmitCallback send_cls;
+
+#ifdef UPGRADE_SUPPORT
+ /**
+ * If this connection was upgraded, this points to
+ * the upgrade response details such that the
+ * #thread_main_connection_upgrade()-logic can perform the
+ * bi-directional forwarding.
+ */
+ struct MHD_UpgradeResponseHandle *urh;
+#endif /* UPGRADE_SUPPORT */
+
+ /**
+ * Foreign address (of length @e addr_len).
+ */
+ struct sockaddr_storage addr;
+
+ /**
+ * Thread handle for this connection (if we are using
+ * one thread per connection).
+ */
+ MHD_thread_handle_ID_ pid;
+
+ /**
+ * Size of @e read_buffer (in bytes). This value indicates
+ * how many bytes we're willing to read into the buffer;
+ * the real buffer is one byte longer to allow for
+ * adding zero-termination (when needed).
+ */
+ size_t read_buffer_size;
+
+ /**
+ * Position where we currently append data in
+ * @e read_buffer (last valid position).
+ */
+ size_t read_buffer_offset;
+
+ /**
+ * Size of @e write_buffer (in bytes).
+ */
+ size_t write_buffer_size;
+
+ /**
+ * Offset where we are with sending from @e write_buffer.
+ */
+ size_t write_buffer_send_offset;
+
+ /**
+ * Last valid location in write_buffer (where do we
+ * append and up to where is it safe to send?)
+ */
+ size_t write_buffer_append_offset;
+
+ /**
+ * Number of bytes we had in the HTTP header, set once we
+ * pass #MHD_REQUEST_HEADERS_RECEIVED.
+ */
+ size_t header_size;
+
+ /**
+ * How many more bytes of the body do we expect
+ * to read? #MHD_SIZE_UNKNOWN for unknown.
+ */
+ uint64_t remaining_upload_size;
+
+ /**
+ * If we are receiving with chunked encoding, where are we right
+ * now? Set to 0 if we are waiting to receive the chunk size;
+ * otherwise, this is the size of the current chunk. A value of
+ * zero is also used when we're at the end of the chunks.
+ */
+ uint64_t current_chunk_size;
+
+ /**
+ * If we are receiving with chunked encoding, where are we currently
+ * with respect to the current chunk (at what offset / position)?
+ */
+ uint64_t current_chunk_offset;
+
+ /**
+ * Current write position in the actual response
+ * (excluding headers, content only; should be 0
+ * while sending headers).
+ */
+ uint64_t response_write_position;
+
+#if defined(_MHD_HAVE_SENDFILE)
+ enum MHD_resp_sender_
+ {
+ MHD_resp_sender_std = 0,
+ MHD_resp_sender_sendfile
+ } resp_sender;
+#endif /* _MHD_HAVE_SENDFILE */
+
+ /**
+ * Position in the 100 CONTINUE message that
+ * we need to send when receiving http 1.1 requests.
+ */
+ size_t continue_message_write_offset;
+
+ /**
+ * State in the FSM for this request.
+ */
+ enum MHD_REQUEST_STATE state;
+
+ /**
+ * What is this request waiting for?
+ */
+ enum MHD_RequestEventLoopInfo event_loop_info;
+
+ /**
+ * HTTP response code. Only valid if response object
+ * is already set.
+ */
+ unsigned int responseCode;
+
+ /**
+ * Did we ever call the "default_handler" on this request? (this
+ * flag will determine if we call the #MHD_OPTION_NOTIFY_COMPLETED
+ * handler when the request closes down).
+ */
+ bool client_aware;
+
+ /**
+ * Are we currently inside the "idle" handler (to avoid recursively
+ * invoking it).
+ */
+ bool in_idle;
+
+ /**
+ * Are we currently inside the "idle" handler (to avoid recursively
+ * invoking it).
+ */
+ bool in_cleanup;
+
+ /**
+ * Are we receiving with chunked encoding? This will be set to
+ * #MHD_YES after we parse the headers and are processing the body
+ * with chunks. After we are done with the body and we are
+ * processing the footers; once the footers are also done, this will
+ * be set to #MHD_NO again (before the final call to the handler).
+ */
+ bool have_chunked_upload;
+
+ /**
+ * Is the request suspended?
+ */
+ bool suspended;
+
+ /**
+ * Is the request wanting to resume?
+ */
+ bool resuming;
+};
+
+
+/**
+ * State kept per HTTP connection.
+ */
+struct MHD_Connection
+{
+
+#ifdef EPOLL_SUPPORT
+ /**
+ * 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;
+
+ /**
+ * Previous pointer for the DLL describing our IO state.
+ */
+ struct MHD_Connection *prev;
+
+ /**
+ * Next pointer for the XDLL organizing connections by timeout.
+ * This DLL can be either the
+ * 'manual_timeout_head/manual_timeout_tail' or the
+ * 'normal_timeout_head/normal_timeout_tail', depending on whether a
+ * custom timeout is set for the connection.
+ */
+ 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;
+
+ /**
+ * Information about the current request we are processing
+ * on this connection.
+ */
+ struct MHD_Request request;
+
+
+ /**
+ * Set to `true` if the thread has been joined.
+ */
+ bool thread_joined;
+
+ /**
+ * true if #socket_fd is non-blocking, false otherwise.
+ */
+ bool sk_nonblck;
+
+ /**
+ * 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).
+ */
+ bool read_closed;
+
+
+ /**
+ * Length of the foreign address.
+ */
+ socklen_t addr_len;
+
+ /**
+ * Last time this connection had any activity
+ * (reading or writing).
+ */
+ time_t last_activity;
+
+ /**
+ * After how many seconds of inactivity should
+ * this connection time out? Zero for no timeout.
+ */
+ time_t connection_timeout;
+
+ /**
+ * Socket for this connection. Set to #MHD_INVALID_SOCKET if
+ * this connection has died (daemon should clean
+ * up in that case).
+ */
+ MHD_socket socket_fd;
+
+
+#ifdef EPOLL_SUPPORT
+ /**
+ * What is the state of this socket in relation to epoll?
+ */
+ enum MHD_EpollState epoll_state;
+#endif
+
+
+};
+
+
+
+
+
/**
* State kept for each MHD daemon. All connections are kept in two
* doubly-linked lists. The first one reflects the state of the
@@ -324,8 +917,14 @@ struct MHD_Daemon
MHD_socket listen_socket;
/**
+ * Inter-thread communication channel.
+ */
+ struct MHD_itc_ itc;
+
+ /**
* Which threading model do we use? Postive
* numbers indicate the number of worker threads to be used.
+ * Values larger than 1 imply a thread pool.
*/
enum MHD_ThreadingModel threading_model;
@@ -337,7 +936,7 @@ struct MHD_Daemon
/**
* Address family to use when listening.
- * Default is #MHD_AF_AUTO.
+ * Default is #MHD_AF_NONE (do not listen).
*/
enum MHD_AddressFamily listen_af;
@@ -357,8 +956,7 @@ struct MHD_Daemon
/**
* On which port should we listen on? Only effective if we were not
* given a listen socket or a full address via
- * #MHD_daemon_bind_sa(). 0 means not set, which means to default
- * to 80 (http) or 443 (https) respectively.
+ * #MHD_daemon_bind_sa(). 0 means to bind to random free port.
*/
uint16_t listen_port;
diff --git a/src/lib/panic.c b/src/lib/panic.c
@@ -0,0 +1,36 @@
+
+
+/**
+ * Handler for fatal errors.
+ */
+MHD_PanicCallback mhd_panic = NULL;
+
+/**
+ * Closure argument for #mhd_panic.
+ */
+void *mhd_panic_cls = NULL;
+
+
+/**
+ * Sets the global error handler to a different implementation. @a cb
+ * will only be called in the case of typically fatal, serious
+ * internal consistency issues. These issues should only arise in the
+ * case of serious memory corruption or similar problems with the
+ * architecture. While @a cb is allowed to return and MHD will then
+ * try to continue, this is never safe.
+ *
+ * The default implementation that is used if no panic function is set
+ * simply prints an error message and calls `abort()`. Alternative
+ * implementations might call `exit()` or other similar functions.
+ *
+ * @param cb new error handler
+ * @param cls passed to @a cb
+ * @ingroup logging
+ */
+void
+MHD_set_panic_func (MHD_PanicCallback cb,
+ void *cls)
+{
+ mhd_panic = cb;
+ mhd_panic_cls = cls;
+}
diff --git a/src/lib/request.c b/src/lib/request.c
@@ -1,3 +1,31 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+/**
+ * @file requests.c
+ * @brief Methods for managing HTTP requests
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+
/**
* Get all of the headers from the request.
*
@@ -9,11 +37,35 @@
* @return number of entries iterated over
* @ingroup request
*/
-_MHD_EXTERN unsigned int
+unsigned int
MHD_request_get_values (struct MHD_Request *request,
enum MHD_ValueKind kind,
MHD_KeyValueIterator iterator,
- void *iterator_cls);
+ void *iterator_cls)
+{
+ int ret;
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == request)
+ return -1;
+ ret = 0;
+ for (pos = request->headers_received;
+ NULL != pos;
+ pos = pos->next)
+ {
+ if (0 != (pos->kind & kind))
+ {
+ ret++;
+ if ( (NULL != iterator) &&
+ (MHD_YES != iterator (iterator_cls,
+ pos->kind,
+ pos->header,
+ pos->value)) )
+ return ret;
+ }
+ }
+ return ret;
+}
/**
@@ -41,11 +93,36 @@ MHD_request_get_values (struct MHD_Request *request,
* #MHD_YES on success
* @ingroup request
*/
-_MHD_EXTERN enum MHD_Bool
+enum MHD_Bool
MHD_request_set_value (struct MHD_Request *request,
enum MHD_ValueKind kind,
const char *key,
- const char *value);
+ const char *value)
+{
+ struct MHD_HTTP_Header *pos;
+
+ pos = MHD_pool_allocate (request->pool,
+ sizeof (struct MHD_HTTP_Header),
+ MHD_YES);
+ if (NULL == pos)
+ return MHD_NO;
+ pos->header = (char *) key;
+ pos->value = (char *) value;
+ pos->kind = kind;
+ pos->next = NULL;
+ /* append 'pos' to the linked list of headers */
+ if (NULL == request->headers_received_tail)
+ {
+ request->headers_received = pos;
+ request->headers_received_tail = pos;
+ }
+ else
+ {
+ request->headers_received_tail->next = pos;
+ request->headers_received_tail = pos;
+ }
+ return MHD_YES;
+}
/**
@@ -58,11 +135,31 @@ MHD_request_set_value (struct MHD_Request *request,
* @return NULL if no such item was found
* @ingroup request
*/
-_MHD_EXTERN const char *
+const char *
MHD_request_lookup_value (struct MHD_Request *request,
enum MHD_ValueKind kind,
- const char *key);
+ const char *key)
+{
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == request)
+ return NULL;
+ for (pos = request->headers_received;
+ NULL != pos;
+ pos = pos->next)
+ {
+ if ((0 != (pos->kind & kind)) &&
+ ( (key == pos->header) ||
+ ( (NULL != pos->header) &&
+ (NULL != key) &&
+ (MHD_str_equal_caseless_(key,
+ pos->header)))))
+ return pos->value;
+ }
+ return NULL;
+}
+/* end of request.c */
diff --git a/src/lib/version.c b/src/lib/version.c
@@ -0,0 +1,181 @@
+
+
+/**
+ * Obtain the version of this library
+ *
+ * @return static version string, e.g. "0.9.9"
+ * @ingroup specialized
+ */
+const char *
+MHD_get_version (void)
+{
+#ifdef PACKAGE_VERSION
+ return PACKAGE_VERSION;
+#else /* !PACKAGE_VERSION */
+ static char ver[12] = "\0\0\0\0\0\0\0\0\0\0\0";
+ if (0 == ver[0])
+ {
+ int res = MHD_snprintf_(ver,
+ sizeof(ver),
+ "%x.%x.%x",
+ (((int)MHD_VERSION >> 24) & 0xFF),
+ (((int)MHD_VERSION >> 16) & 0xFF),
+ (((int)MHD_VERSION >> 8) & 0xFF));
+ if (0 >= res || sizeof(ver) <= res)
+ return "0.0.0"; /* Can't return real version*/
+ }
+ return ver;
+#endif /* !PACKAGE_VERSION */
+}
+
+
+/**
+ * Get information about supported MHD features.
+ * Indicate that MHD was compiled with or without support for
+ * particular feature. Some features require additional support
+ * by kernel. Kernel support is not checked by this function.
+ *
+ * @param feature type of requested information
+ * @return #MHD_YES if feature is supported by MHD, #MHD_NO if
+ * feature is not supported or feature is unknown.
+ * @ingroup specialized
+ */
+_MHD_EXTERN int
+MHD_is_feature_supported(enum MHD_FEATURE feature)
+{
+ switch(feature)
+ {
+ case MHD_FEATURE_MESSAGES:
+#ifdef HAVE_MESSAGES
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_TLS:
+#ifdef HTTPS_SUPPORT
+ return MHD_YES;
+#else /* ! HTTPS_SUPPORT */
+ return MHD_NO;
+#endif /* ! HTTPS_SUPPORT */
+ case MHD_FEATURE_HTTPS_CERT_CALLBACK:
+#if defined(HTTPS_SUPPORT) && GNUTLS_VERSION_MAJOR >= 3
+ return MHD_YES;
+#else /* !HTTPS_SUPPORT || GNUTLS_VERSION_MAJOR < 3 */
+ return MHD_NO;
+#endif /* !HTTPS_SUPPORT || GNUTLS_VERSION_MAJOR < 3 */
+ case MHD_FEATURE_IPv6:
+#ifdef HAVE_INET6
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_IPv6_ONLY:
+#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_POLL:
+#ifdef HAVE_POLL
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_EPOLL:
+#ifdef EPOLL_SUPPORT
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_SHUTDOWN_LISTEN_SOCKET:
+#ifdef HAVE_LISTEN_SHUTDOWN
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_SOCKETPAIR:
+#ifdef _MHD_ITC_SOCKETPAIR
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_TCP_FASTOPEN:
+#ifdef TCP_FASTOPEN
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_BASIC_AUTH:
+#ifdef BAUTH_SUPPORT
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_DIGEST_AUTH:
+#ifdef DAUTH_SUPPORT
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_POSTPROCESSOR:
+#ifdef HAVE_POSTPROCESSOR
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_HTTPS_KEY_PASSWORD:
+#if defined(HTTPS_SUPPORT) && GNUTLS_VERSION_NUMBER >= 0x030111
+ return MHD_YES;
+#else /* !HTTPS_SUPPORT || GNUTLS_VERSION_NUMBER < 0x030111 */
+ return MHD_NO;
+#endif /* !HTTPS_SUPPORT || GNUTLS_VERSION_NUMBER < 0x030111 */
+ case MHD_FEATURE_LARGE_FILE:
+#if defined(HAVE_PREAD64) || defined(_WIN32)
+ return MHD_YES;
+#elif defined(HAVE_PREAD)
+ return (sizeof(uint64_t) > sizeof(off_t)) ? MHD_NO : MHD_YES;
+#elif defined(HAVE_LSEEK64)
+ return MHD_YES;
+#else
+ return (sizeof(uint64_t) > sizeof(off_t)) ? MHD_NO : MHD_YES;
+#endif
+ case MHD_FEATURE_THREAD_NAMES:
+#if defined(MHD_USE_THREAD_NAME_)
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_UPGRADE:
+#if defined(UPGRADE_SUPPORT)
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_RESPONSES_SHARED_FD:
+#if defined(HAVE_PREAD64) || defined(HAVE_PREAD) || defined(_WIN32)
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_AUTODETECT_BIND_PORT:
+#ifdef MHD_USE_GETSOCKNAME
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_AUTOSUPPRESS_SIGPIPE:
+#if defined(MHD_WINSOCK_SOCKETS) || defined(MHD_socket_nosignal_) || defined (MSG_NOSIGNAL)
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+ case MHD_FEATURE_SENDFILE:
+#ifdef _MHD_HAVE_SENDFILE
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
+
+ }
+ return MHD_NO;
+}
diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c
@@ -468,7 +468,7 @@ MHD_socket_noninheritable_ (MHD_socket sock)
* @return created socket or MHD_INVALID_SOCKET in case of errors
*/
MHD_socket
-MHD_socket_create_listen_ (int use_ipv6)
+MHD_socket_create_listen_ (bool use_ipv6)
{
int domain;
MHD_socket fd;
diff --git a/src/microhttpd/mhd_sockets.h b/src/microhttpd/mhd_sockets.h
@@ -755,6 +755,6 @@ MHD_socket_noninheritable_ (MHD_socket sock);
* @return created socket or MHD_INVALID_SOCKET in case of errors
*/
MHD_socket
-MHD_socket_create_listen_ (int use_ipv6);
+MHD_socket_create_listen_ (bool use_ipv6);
#endif /* ! MHD_SOCKETS_H */