Просмотр исходного кода

MHD_start_daemon(): added port autodetection if '0' (autoselect) is used for port number,
Added new MHD_FEATURE value MHD_FEATURE_AUTODETECT_BIND_PORT.

Evgeny Grin (Karlson2k) 8 лет назад
Родитель
Сommit
da9f4fd6f2
3 измененных файлов с 219 добавлено и 5 удалено
  1. 113 0
      configure.ac
  2. 16 3
      src/include/microhttpd.h
  3. 90 2
      src/microhttpd/daemon.c

+ 113 - 0
configure.ac

@@ -756,6 +756,119 @@ AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
 #endif
    ])
 
+AC_CHECK_DECLS([getsockname],
+  [
+   AC_CHECK_FUNCS([getsockname], 
+     [
+      AC_CACHE_CHECK([[whether getsockname() is usable]], [[mhc_cv_getsockname_usable]],
+        [
+         AC_RUN_IFELSE(
+           [
+            AC_LANG_SOURCE(
+             [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+
+static void zr_mem(void *ptr, socklen_t size)
+{ char *mem = ptr; while(size--) {mem[0] = 0; mem++;} }
+
+int main(void)
+{
+  const socklen_t c_addr_size = (socklen_t)sizeof(struct sockaddr_in);
+  struct sockaddr_in sa;
+  socklen_t addr_size;
+  int ret = 1;
+#if !defined(_WIN32) || defined(__CYGWIN__)
+  int sckt;
+  const int invld_sckt = -1;
+#else
+  SOCKET sckt;
+  const SOCKET invld_sckt = INVALID_SOCKET;
+  WSADATA wsa_data;
+
+  if (0 != WSAStartup(MAKEWORD(2, 2), &wsa_data) || MAKEWORD(2, 2) != wsa_data.wVersion)
+    return 20;
+#endif
+
+  sckt = socket (PF_INET, SOCK_STREAM, 0);
+  if (invld_sckt != sckt)
+  {
+    zr_mem(&sa, c_addr_size);
+    sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    sa.sin_len = c_addr_size;
+#endif
+    if (0 == bind (sckt, (struct sockaddr *)&sa, c_addr_size))
+    {
+      if (0 == listen (sckt, 1))
+      {
+        addr_size = c_addr_size;
+        if (0 == getsockname (sckt, (struct sockaddr  *)&sa, &addr_size))
+        {
+          if (c_addr_size >= addr_size)
+          {
+            if (0 != ntohs(sa.sin_port))
+            { ret = 0;
+            } else ret = 7;
+          } else ret = 6;
+        } else ret = 5;
+      } else ret = 4;
+    } else ret = 3;
+  } else ret = 2;
+#if !defined(_WIN32) || defined(__CYGWIN__)
+  close (sckt);
+#else
+  closesocket (sckt);
+  WSACleanup();
+#endif
+  return ret;
+}
+             ]]
+            )
+           ],
+           [[mhc_cv_getsockname_usable='yes']],
+           [[mhc_cv_getsockname_usable='no']],
+           [[mhc_cv_getsockname_usable='assuming yes']]
+         )
+        ]
+      )
+      AS_VAR_IF([[mhc_cv_getsockname_usable]], [["no"]], [:],
+        [AC_DEFINE([[MHD_USE_GETSOCKNAME]], [[1]], [Define if you have usable `getsockname' function.])])
+     ]
+   )
+  ], [],
+  [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+  ]]
+)
 
 # Check for inter-thread signaling type
 AC_ARG_ENABLE([[itc]],

+ 16 - 3
src/include/microhttpd.h

@@ -2095,7 +2095,11 @@ typedef int
  * Start a webserver on the given port.
  *
  * @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to (in host byte order)
+ * @param port port to bind to (in host byte order),
+ *        use '0' to bind to random free port,
+ *        ignored if MHD_OPTION_SOCK_ADDR or
+ *        MHD_OPTION_LISTEN_SOCKET is provided
+ *        or MHD_USE_NO_LISTEN_SOCKET is specified
  * @param apc callback to call to check which clients
  *        will be allowed to connect; you can pass NULL
  *        in which case connections from any IP will be
@@ -2121,7 +2125,11 @@ MHD_start_daemon_va (unsigned int flags,
  * #MHD_start_daemon_va.
  *
  * @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to
+ * @param port port to bind to (in host byte order),
+ *        use '0' to bind to random free port,
+ *        ignored if MHD_OPTION_SOCK_ADDR or
+ *        MHD_OPTION_LISTEN_SOCKET is provided
+ *        or MHD_USE_NO_LISTEN_SOCKET is specified
  * @param apc callback to call to check which clients
  *        will be allowed to connect; you can pass NULL
  *        in which case connections from any IP will be
@@ -3425,7 +3433,12 @@ enum MHD_FEATURE
    * It's always safe to use same file FD in multiple responses if MHD
    * is run in any single thread mode.
    */
-  MHD_FEATURE_RESPONSES_SHARED_FD = 18
+  MHD_FEATURE_RESPONSES_SHARED_FD = 18,
+
+  /**
+   * Get whether MHD support automatic detection of bind port number.
+   */
+  MHD_FEATURE_AUTODETECT_BIND_PORT = 19
 };
 
 

+ 90 - 2
src/microhttpd/daemon.c

@@ -4346,7 +4346,11 @@ unescape_wrapper (void *cls,
  * #MHD_start_daemon_va.
  *
  * @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to
+ * @param port port to bind to (in host byte order),
+ *        use '0' to bind to random free port,
+ *        ignored if MHD_OPTION_SOCK_ADDR or
+ *        MHD_OPTION_LISTEN_SOCKET is provided
+ *        or MHD_USE_NO_LISTEN_SOCKET is specified
  * @param apc callback to call to check which clients
  *        will be allowed to connect; you can pass NULL
  *        in which case connections from any IP will be
@@ -5082,7 +5086,11 @@ setup_epoll_to_listen (struct MHD_Daemon *daemon)
  * Start a webserver on the given port.
  *
  * @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to (in host byte order)
+ * @param port port to bind to (in host byte order),
+ *        use '0' to bind to random free port,
+ *        ignored if MHD_OPTION_SOCK_ADDR or
+ *        MHD_OPTION_LISTEN_SOCKET is provided
+ *        or MHD_USE_NO_LISTEN_SOCKET is specified
  * @param apc callback to call to check which clients
  *        will be allowed to connect; you can pass NULL
  *        in which case connections from any IP will be
@@ -5621,6 +5629,80 @@ MHD_start_daemon_va (unsigned int flags,
       listen_fd = daemon->listen_fd;
     }
 
+  if ( (0 == daemon->port) &&
+       (0 == (*pflags & MHD_USE_NO_LISTEN_SOCKET)) )
+    { /* Get port number. */
+      struct sockaddr *realaddr;
+#ifdef MHD_POSIX_SOCKETS
+      socklen_t alloc_len;
+#endif /* MHD_POSIX_SOCKETS */
+#ifdef HAVE_INET6
+      if (0 != (*pflags & MHD_USE_IPv6))
+        {
+          memset (&servaddr6,
+                  0,
+                  sizeof (struct sockaddr_in6));
+          servaddr6.sin6_family = AF_INET6;
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+          servaddr6.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* HAVE_SOCKADDR_IN_SIN_LEN */
+          addrlen = (socklen_t) sizeof (struct sockaddr_in6);
+          realaddr = (struct sockaddr *) &servaddr6;
+        }
+      else
+#else  /* ! HAVE_INET6 */
+      if (1)
+#endif /* ! HAVE_INET6 */
+        {
+          memset (&servaddr4,
+                  0,
+                  sizeof (struct sockaddr_in));
+          servaddr4.sin_family = AF_INET;
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+          servaddr4.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_SOCKADDR_IN_SIN_LEN */
+          addrlen = (socklen_t) sizeof (struct sockaddr_in);
+          realaddr = (struct sockaddr *) &servaddr4;
+        }
+#ifdef MHD_POSIX_SOCKETS
+      alloc_len = addrlen;
+#endif /* MHD_POSIX_SOCKETS */
+      if (0 != getsockname (listen_fd, realaddr, &addrlen))
+        {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("Failed to get listen port number: %s\n"),
+                MHD_socket_last_strerr_ ());
+#endif /* HAVE_MESSAGES */
+        }
+#ifdef MHD_POSIX_SOCKETS
+      else if (alloc_len < addrlen)
+        {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("Failed to get listen port number due to small buffer\n"));
+#endif /* HAVE_MESSAGES */
+        }
+#endif /* MHD_POSIX_SOCKETS */
+      else
+        {
+#ifdef HAVE_INET6
+          if (0 != (*pflags & MHD_USE_IPv6))
+            {
+              mhd_assert (AF_INET6 == servaddr6.sin6_family);
+              daemon->port = ntohs(servaddr6.sin6_port);
+            }
+          else
+#else  /* ! HAVE_INET6 */
+          if (1)
+#endif /* ! HAVE_INET6 */
+            {
+              mhd_assert (AF_INET == servaddr4.sin_family);
+              daemon->port = ntohs(servaddr4.sin_port);
+            }
+        }
+    }
+
   if ( (MHD_INVALID_SOCKET != listen_fd) &&
        (! MHD_socket_nonblocking_ (listen_fd)) )
     {
@@ -6424,6 +6506,12 @@ MHD_is_feature_supported(enum MHD_FEATURE feature)
       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
     }
   return MHD_NO;