Quellcode durchsuchen

more work on mhd2 API implementation

Christian Grothoff vor 8 Jahren
Ursprung
Commit
f6e1ee426f

+ 163 - 10
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,
+
+  
 };
 
 
@@ -630,11 +777,18 @@ 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);
 

+ 855 - 99
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 */

+ 134 - 0
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;
+}

+ 178 - 0
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 */

+ 60 - 0
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;
+    }
+}
+
+

+ 28 - 2
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
 }
 

+ 127 - 0
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;
+}
+
+

+ 156 - 0
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 */

+ 29 - 0
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 */

+ 601 - 3
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
@@ -323,9 +916,15 @@ 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;
   

+ 36 - 0
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;
+}

+ 103 - 6
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 */
 
 

+ 181 - 0
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;
+}

+ 1 - 1
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;

+ 1 - 1
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 */