Răsfoiți Sursa

Initial draft M2 (WIP)

Evgeny Grin (Karlson2k) 1 an în urmă
părinte
comite
41f2a26f62
97 a modificat fișierele cu 15500 adăugiri și 1798 ștergeri
  1. 1 1
      Makefile.am
  2. 581 102
      configure.ac
  3. 8 8
      m4/mhd_shutdown_socket_trigger.m4
  4. 6 6
      src/Makefile.am
  5. 1 1
      src/examples/Makefile.am
  6. 3 0
      src/incl_priv/.gitignore
  7. 3 0
      src/incl_priv/Makefile.am
  8. 233 34
      src/incl_priv/mhd_sys_options.h
  9. 19 19
      src/include/Makefile.am
  10. 0 3
      src/include/mhd_future.h
  11. 420 147
      src/include/microhttpd2.h
  12. 2 0
      src/include/microhttpd2_generated_response_options.h
  13. 40 50
      src/include/microhttpd2_portability.h
  14. 126 0
      src/mhd2/Makefile.am
  15. 168 0
      src/mhd2/action.c
  16. 39 39
      src/mhd2/autoinit_funcs.h
  17. 1 1
      src/mhd2/compat_calloc.c
  18. 2 2
      src/mhd2/compat_calloc.h
  19. 982 0
      src/mhd2/daemon_add_conn.c
  20. 83 0
      src/mhd2/daemon_add_conn.h
  21. 112 0
      src/mhd2/daemon_create.c
  22. 51 0
      src/mhd2/daemon_funcs.c
  23. 51 0
      src/mhd2/daemon_funcs.h
  24. 53 0
      src/mhd2/daemon_logger.c
  25. 133 0
      src/mhd2/daemon_logger.h
  26. 57 0
      src/mhd2/daemon_logger_default.c
  27. 60 0
      src/mhd2/daemon_logger_default.h
  28. 2777 0
      src/mhd2/daemon_start.c
  29. 303 0
      src/mhd2/data_process.c
  30. 44 0
      src/mhd2/data_process.h
  31. 604 0
      src/mhd2/events_process.c
  32. 55 0
      src/mhd2/events_process.h
  33. 179 0
      src/mhd2/http_method.h
  34. 58 0
      src/mhd2/http_prot_ver.h
  35. 207 0
      src/mhd2/http_status_str.c
  36. 46 0
      src/mhd2/http_status_str.h
  37. 293 0
      src/mhd2/mhd_action.h
  38. 81 0
      src/mhd2/mhd_assert.h
  39. 537 0
      src/mhd2/mhd_connection.h
  40. 900 0
      src/mhd2/mhd_daemon.h
  41. 290 0
      src/mhd2/mhd_dlinked_list.h
  42. 105 0
      src/mhd2/mhd_iovec.h
  43. 45 0
      src/mhd2/mhd_itc.c
  44. 343 0
      src/mhd2/mhd_itc.h
  45. 26 26
      src/mhd2/mhd_itc_types.h
  46. 85 0
      src/mhd2/mhd_lib_init.c
  47. 66 0
      src/mhd2/mhd_lib_init.h
  48. 76 0
      src/mhd2/mhd_lib_init_impl.h
  49. 243 0
      src/mhd2/mhd_locks.h
  50. 41 30
      src/mhd2/mhd_mempool.c
  51. 9 11
      src/mhd2/mhd_mempool.h
  52. 48 101
      src/mhd2/mhd_mono_clock.c
  53. 9 26
      src/mhd2/mhd_mono_clock.h
  54. 110 0
      src/mhd2/mhd_panic.c
  55. 105 0
      src/mhd2/mhd_panic.h
  56. 2 1
      src/mhd2/mhd_public_api.h
  57. 106 0
      src/mhd2/mhd_recv.c
  58. 56 0
      src/mhd2/mhd_recv.h
  59. 115 0
      src/mhd2/mhd_reply.h
  60. 376 0
      src/mhd2/mhd_request.h
  61. 324 0
      src/mhd2/mhd_response.h
  62. 119 0
      src/mhd2/mhd_socket_error.h
  63. 252 0
      src/mhd2/mhd_sockets_funcs.c
  64. 87 0
      src/mhd2/mhd_sockets_funcs.h
  65. 334 0
      src/mhd2/mhd_sockets_macros.h
  66. 44 0
      src/mhd2/mhd_str_macros.h
  67. 76 0
      src/mhd2/mhd_str_types.h
  68. 139 137
      src/mhd2/mhd_threads.c
  69. 545 0
      src/mhd2/mhd_threads.h
  70. 83 0
      src/mhd2/mhd_tristate.h
  71. 124 0
      src/mhd2/response_add_header.c
  72. 368 0
      src/mhd2/response_from.c
  73. 36 0
      src/mhd2/response_from.h
  74. 85 0
      src/mhd2/response_funcs.c
  75. 51 0
      src/mhd2/response_funcs.h
  76. 3 2
      src/mhd2/response_options.h
  77. 82 18
      src/mhd2/response_set_options.c
  78. 5 5
      src/mhd2/sys_base_types.h
  79. 52 0
      src/mhd2/sys_errno.h
  80. 45 0
      src/mhd2/sys_file_fd.h
  81. 56 0
      src/mhd2/sys_ip_headers.h
  82. 40 0
      src/mhd2/sys_null_macro.h
  83. 91 0
      src/mhd2/sys_poll.h
  84. 49 0
      src/mhd2/sys_sendfile.h
  85. 131 0
      src/mhd2/sys_sockets_headers.h
  86. 2 1
      src/mhd2/sys_sockets_types.h
  87. 49 0
      src/mhd2/sys_thread_entry_type.h
  88. 51 0
      src/mhd2/w32_lib_res.rc
  89. 51 0
      src/mhd2/w32_lib_res.rc.in
  90. 0 72
      src/microhttpd/mhd_itc.c
  91. 0 387
      src/microhttpd/mhd_itc.h
  92. 0 566
      src/microhttpd/mhd_threads.h
  93. 1 1
      src/testcurl/https/Makefile.am
  94. 6 0
      src/tests/Makefile.am
  95. 180 0
      src/tests/basic/Makefile.am
  96. 363 0
      src/tests/basic/test_basic_checks.c
  97. 1 1
      src/tools/Makefile.am

+ 1 - 1
Makefile.am

@@ -3,7 +3,7 @@ ACLOCAL_AMFLAGS = -I m4
 SUBDIRS = contrib m4 src .
 
 if BUILD_DOC
-SUBDIRS += doc
+# SUBDIRS += doc
 endif
 
 

+ 581 - 102
configure.ac

@@ -23,10 +23,10 @@
 #
 AC_PREREQ([2.64])
 LT_PREREQ([2.4.0])
-AC_INIT([GNU libmicrohttpd],[1.0.1],[[email protected]])
+AC_INIT([GNU libmicrohttpd],[1.99.0],[[email protected]])
 AC_CONFIG_AUX_DIR([build-aux])
 MHD_AUX_DIR='build-aux' # Must be set to the same value as in the previous line
-AC_CONFIG_HEADERS([MHD_config.h])
+AC_CONFIG_HEADERS([src/incl_priv/mhd_config.h])
 AC_CONFIG_MACRO_DIR([m4])
 m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl
 
@@ -46,7 +46,7 @@ AC_SUBST([PACKAGE_VERSION_MAJOR])
 AC_SUBST([PACKAGE_VERSION_MINOR])
 AC_SUBST([PACKAGE_VERSION_SUBMINOR])
 AC_SUBST([MHD_W32_DLL_SUFF])
-AC_CONFIG_FILES([src/microhttpd/microhttpd_dll_res.rc])
+AC_CONFIG_FILES([src/mhd2/w32_lib_res.rc])
 
 MHD_LIB_CPPFLAGS=""
 MHD_LIB_CFLAGS=""
@@ -485,7 +485,7 @@ AC_CHECK_HEADERS_ONCE([stdio.h string.h stdint.h stdarg.h errno.h limits.h fcntl
 
 # Check for basic optional headers
 AC_CHECK_HEADERS([stddef.h stdlib.h inttypes.h sys/types.h sys/stat.h unistd.h \
-                  sys/uio.h crtdefs.h malloc.h], [], [], [AC_INCLUDES_DEFAULT])
+                  sys/uio.h crtdefs.h malloc.h io.h], [], [], [AC_INCLUDES_DEFAULT])
 
 # Check for clock-specific optional headers
 AC_CHECK_HEADERS([sys/time.h time.h], [], [], [AC_INCLUDES_DEFAULT])
@@ -1801,7 +1801,7 @@ AS_IF([test "x$USE_THREADS" = "xposix"],
    [AC_DEFINE([MHD_USE_W32_THREADS],[1],[define to use W32 threads])])])
 AM_CONDITIONAL([USE_POSIX_THREADS], [test "x$USE_THREADS" = "xposix"])
 AM_CONDITIONAL([USE_W32_THREADS], [test "x$USE_THREADS" = "xw32"])
-AM_CONDITIONAL([USE_THREADS], [test "x$USE_THREADS" != "xnone"])
+AM_CONDITIONAL([MHD_USE_THREADS], [test "x$USE_THREADS" != "xnone"])
 AM_CONDITIONAL([DISABLE_THREADS], [test "x$USE_THREADS" = "xnone"])
 AC_MSG_RESULT([$USE_THREADS])
 
@@ -2595,14 +2595,14 @@ AC_CACHE_CHECK([whether socket value is a signed type],[mhd_cv_socket_signed],
 
 /* Keep in sync with microhttpd.h */
 #if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
-typedef int MHD_socket;
+typedef int MHD_Socket;
 #else /* defined(_WIN32) && ! defined(_SYS_TYPES_FD_SET) */
-typedef SOCKET MHD_socket;
+typedef SOCKET MHD_Socket;
 #endif /* defined(_WIN32) && ! defined(_SYS_TYPES_FD_SET) */
 
 int main(void)
 {
-  int test_arr[2 - 5*(!!(0 < ((MHD_socket)-1)))];
+  int test_arr[2 - 5*(!!(0 < ((MHD_Socket)-1)))];
   test_arr[1] = 0;
   return test_arr[1];
 }
@@ -2710,14 +2710,14 @@ AS_VAR_IF([mhd_cv_macro___func___avail], ["yes"],
 )
 AC_CACHE_CHECK([[whether __builtin_bswap32() is available]],
   [[mhd_cv_func___builtin_bswap32_avail]], [dnl
-  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include<stdint.h>]], [[uint32_t a = 1; uint32_t b = __builtin_bswap32(a); a = b; (void) a;]])],
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include<stdint.h>]], [[uint_fast32_t a = 1; uint_fast32_t b = __builtin_bswap32(a); a = b; (void) a;]])],
     [[mhd_cv_func___builtin_bswap32_avail="yes"]],[[mhd_cv_func___builtin_bswap32_avail="no"]])
 ])
 AS_IF([[test "x$mhd_cv_func___builtin_bswap32_avail" = "xyes"]],
   [AC_DEFINE([[MHD_HAVE___BUILTIN_BSWAP32]], [[1]], [Define to 1 if you have __builtin_bswap32() builtin function])])
 AC_CACHE_CHECK([[whether __builtin_bswap64() is available]],
   [[mhd_cv_func___builtin_bswap64_avail]], [dnl
-  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include<stdint.h>]], [[uint64_t a = 1; uint64_t b = __builtin_bswap64(a); a = b; (void) a;]])],
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include<stdint.h>]], [[uint_fast64_t a = 1; uint_fast64_t b = __builtin_bswap64(a); a = b; (void) a;]])],
     [[mhd_cv_func___builtin_bswap64_avail="yes"]], [[mhd_cv_func___builtin_bswap64_avail="no"]])
 ])
 AS_IF([[test "x$mhd_cv_func___builtin_bswap64_avail" = "xyes"]],
@@ -2795,70 +2795,163 @@ AM_CONDITIONAL([HEAVY_TESTS],[test "x$use_heavy_tests" = "xyes"])
 AM_CONDITIONAL([VHEAVY_TESTS],[test "x$use_vheavy_tests" = "xyes"])
 AM_CONDITIONAL([TESTS_STRESS_OS],[false])
 
-AC_ARG_ENABLE([[poll]],
-  [AS_HELP_STRING([[--enable-poll[=ARG]]], [enable poll support (yes, no, auto) [auto]])],
-    [enable_poll=${enableval}],
-    [enable_poll='auto']
-  )
-
-AS_IF([test "$enable_poll" != "no"],
+AC_ARG_ENABLE([[select]],
   [
-    AS_IF([test "$os_is_native_w32" != "yes"],
+    AS_HELP_STRING([[--enable-select[=ARG]]], [enable 'select()' support (yes, no, auto) [auto]])
+  ],[],[enable_select='auto']
+)
+AC_CACHE_CHECK([for select() function],[mhd_cv_func_select],
+  [
+    AS_IF([test "x$os_is_native_w32" = "xyes"],
+      [mhd_cv_func_select="yes"],
       [
-        AC_CHECK_HEADERS([poll.h],
+        AC_LINK_IFELSE(
           [
-            MHD_CHECK_FUNC([poll],
-              [[
+            AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SELECTLIB_H
+#include <selectLib.h>
+#endif
+
+int main(void)
+{
+  int res;
+  fd_set rfds;
+  fd_set wfds;
+  fd_set efds;
+  struct timeval tv_to;
+
+  tv_to.tv_sec = 1;
+  tv_to.tv_usec = 0;
+  FD_ZERO(&rfds);
+  FD_ZERO(&wfds);
+  FD_ZERO(&efds);
+  FD_SET(1, &wfds);
+
+  res = select(2, &rfds, &wfds, &efds, &tv_to);
+  if (res < 0)
+    return 3;
+
+  FD_CLR(1, &efds);
+  if (FD_ISSET(1, &efds)
+    return 4;
+
+  return 0;
+}
+              ]]
+            )
+          ],
+          [mhd_cv_func_select="yes"],
+          [mhd_cv_func_select="no"]
+        )
+      ]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_func_select],["yes"],
+  [
+    AC_DEFINE([[HAVE_SELECT]],[[1]],[Define to '1' if select() is supported on your platform])
+    AS_IF([test "x$enable_select" != "xno"],
+      [
+        enable_select="yes"
+        AC_DEFINE([[MHD_USE_SELECT]],[[1]],[Define to '1' to enable use of select() system call])
+      ]
+    )
+  ],
+  [
+    AS_VAR_IF([enable_select],["yes"],
+      [AC_MSG_ERROR([Support for select() was explicitly requested but cannot be enabled on this platform.])]
+    )
+  ]
+)
+AM_CONDITIONAL([MHD_USE_SELECT],[test "x${enable_select}" = "xyes"])
+
+AC_ARG_ENABLE([[poll]],
+  [
+    AS_HELP_STRING([[--enable-poll[=ARG]]], [enable 'poll()' support (yes, no, auto) [auto]])
+  ],[],[enable_poll='auto']
+)
+AS_IF([test "$os_is_native_w32" != "yes"],
+  [
+    AC_CHECK_HEADERS([poll.h],[AC_INCLUDES_DEFAULT])
+    MHD_CHECK_FUNC([poll],
+      [[
 #include <poll.h>
-              ]],
-              [[
+      ]],
+      [[
   struct pollfd fds[2];
 
   fds[0].fd = 0;
   fds[0].events = POLLIN;
   if (0 > poll(fds, 1, 0))
     return 2;
-              ]],
-              [have_poll='yes'], [have_poll='no']
-            )
-          ], [], [AC_INCLUDES_DEFAULT]
-        )
-      ],
-      [
-        MHD_CHECK_FUNC([WSAPoll],
-          [[
+      ]],
+      [have_poll='yes'],[have_poll='no']
+    )
+  ],
+  [
+    MHD_CHECK_FUNC([WSAPoll],
+      [[
 #include <winsock2.h>
-          ]],
-          [[
+      ]],
+      [[
   WSAPOLLFD fda[2];
   WSAPoll(fda, 2, 0);
-          ]],
-          [
-            have_poll='yes'
-            AC_DEFINE([HAVE_POLL],[1])
-          ],
-          [have_poll='no']
-        )
+      ]],
+      [have_poll='yes'],[have_poll='no']
+    )
+  ]
+)
+AS_VAR_IF([have_poll],["yes"],
+  [
+    AC_DEFINE([[HAVE_POLL]],[[1]],[Define to '1' if poll() is supported on your platform])
+    AC_CHECK_DECLS([POLLRDNORM,POLLIN,POLLRDBAND,POLLWRNORM,POLLOUT,POLLWRBAND,POLLPRI],[],[],[[
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+      ]]
+    )
+    AS_IF([test "x$enable_poll" != "xno"],
+      [
+        enable_poll="yes"
+        AC_DEFINE([[MHD_USE_POLL]],[[1]],[Define to '1' to enable use of select() system call])
       ]
     )
-    AS_IF([test "$enable_poll" = "yes" && test "$have_poll" != "yes"],
-      [AC_MSG_ERROR([[Support for poll was explicitly requested but cannot be enabled on this platform.]])])
-    enable_poll="$have_poll"
+  ],
+  [
+    AS_VAR_IF([enable_poll],["yes"],
+      [AC_MSG_ERROR([Support for poll() was explicitly requested but cannot be enabled on this platform.])]
+    )
   ]
 )
+AM_CONDITIONAL([MHD_USE_POLL],[test "x${enable_poll}" = "xyes"])
 
 AC_ARG_ENABLE([[epoll]],
-  [AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]])],
-    [enable_epoll=${enableval}],
-    [enable_epoll='auto']
-  )
-
+  [
+    AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]])
+  ],[],[enable_epoll='auto']
+)
 AS_IF([test "$enable_epoll" != "no"],
   [
     AX_HAVE_EPOLL
     AS_IF([test "${ax_cv_have_epoll}" = "yes"],
       [
-        AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support])
+        AC_DEFINE([[HAVE_EPOLL]],[[1]],[Define to '1' if epoll is supported on your platform])
+        AC_DEFINE([[MHD_USE_EPOLL]],[[1]],[Define to '1' to enable 'epoll' functionality])
         enable_epoll='yes'
       ],
       [
@@ -2870,8 +2963,7 @@ AS_IF([test "$enable_epoll" != "no"],
     )
   ]
 )
-
-AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]])
+AM_CONDITIONAL([MHD_USE_EPOLL], [[test "x${enable_epoll}" = "xyes"]])
 
 AS_IF([test "x$enable_epoll" = "xyes"],
   [
@@ -2964,8 +3056,8 @@ static struct timeval test_var;
 )
 AC_DEFINE_UNQUOTED([SIZEOF_STRUCT_TIMEVAL_TV_SEC], [$mhd_cv_size_timeval_tv_sec],
   [The size of `tv_sec' member of `struct timeval', as computed by sizeof])
-AC_CHECK_SIZEOF([int64_t], [], [[#include <stdint.h>]])
-AC_CHECK_SIZEOF([uint64_t], [], [[#include <stdint.h>]])
+AC_CHECK_SIZEOF([int_fast64_t], [], [[#include <stdint.h>]])
+AC_CHECK_SIZEOF([uint_fast64_t], [], [[#include <stdint.h>]])
 AC_CHECK_SIZEOF([int], [], [[#include <stdint.h>]])
 AC_CHECK_SIZEOF([unsigned int], [], [[#include <stdint.h>]])
 AC_CHECK_SIZEOF([unsigned long long], [], [[#include <stdint.h>]])
@@ -3384,7 +3476,7 @@ int main(void)
       [
         use_itc='eventfd'
         enable_itc="$use_itc"
-        AC_DEFINE([[_MHD_ITC_EVENTFD]], [[1]], [Define to use eventFD for inter-thread communication])
+        AC_DEFINE([[MHD_ITC_EVENTFD_]], [[1]], [Define to use eventFD for inter-thread communication])
       ],
       [
         AS_VAR_IF([[enable_itc]], [["eventfd"]], [AC_MSG_ERROR([[eventfd(2) is not usable, consider using other type of inter-thread communication]])])
@@ -3418,7 +3510,7 @@ AC_INCLUDES_DEFAULT
     AS_VAR_IF([[mhd_cv_pipe_usable]], [["yes"]], [
       use_itc='pipe'
       enable_itc="$use_itc"
-      AC_DEFINE([[_MHD_ITC_PIPE]], [[1]], [Define to use pipe for inter-thread communication])
+      AC_DEFINE([[MHD_ITC_PIPE_]], [[1]], [Define to use pipe for inter-thread communication])
       MHD_CHECK_LINK_RUN([[whether pipe2(2) is usable]],[[mhd_cv_pipe2_usable]],
         [
           # Cross-compiling
@@ -3452,42 +3544,69 @@ AC_INCLUDES_DEFAULT
   ])
 ])
 
-AS_IF([[test "x$enable_itc" = "xsocketpair" || test "x$enable_itc" = "xauto"]], [
-  AS_VAR_IF([[os_is_native_w32]], [["yes"]], [[mhd_cv_socketpair_usable='yes']], [
-    AC_CACHE_CHECK([[whether socketpair(3) is usable]], [[mhd_cv_socketpair_usable]], [
-      AC_LINK_IFELSE([
-        AC_LANG_PROGRAM([
-AC_INCLUDES_DEFAULT
+AS_IF([[test "x$enable_itc" = "xsocketpair" || test "x$enable_itc" = "xauto"]],
+  [
+    AS_IF([test "x${os_is_native_w32}" = "xyes"],
+      [mhd_cv_socketpair_usable='yes'],
+      [test "x${mhd_cv_func_socketpair}" = "xyes"],
+      [
+        AC_CACHE_CHECK([[whether socketpair(3) is usable]], [[mhd_cv_socketpair_usable]],
+          [
+            AC_LINK_IFELSE([
+                AC_LANG_PROGRAM(
+                  [[
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
-        ], [[
-          int arr[2];
-          int res;
-#if defined(AF_LOCAL)
-          res = socketpair(AF_LOCAL, SOCK_STREAM, 0, arr);
-#elif defined(AF_UNIX)
-          res = socketpair(AF_UNIX, SOCK_STREAM, 0, arr);
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+                  ]], [[
+              int arr[2];
+              int res;
+#if defined(AF_UNIX)
+              res = socketpair(AF_UNIX, SOCK_STREAM, 0, arr);
+#elif defined(AF_LOCAL)
+              res = socketpair(AF_LOCAL, SOCK_STREAM, 0, arr);
 #else
 #error AF_LOCAL and AF_UNIX are both undefined
-          choke me now;
+              choke me now;
 #endif
-          if (res != 0) return 1
-        ]])
-      ], [[mhd_cv_socketpair_usable='yes']], [[mhd_cv_socketpair_usable='no']])
-    ])
-  ])
-  AS_VAR_IF([[mhd_cv_socketpair_usable]], [["yes"]], [
-    use_itc='socketpair'
-    enable_itc="$use_itc"
-    AC_DEFINE([[_MHD_ITC_SOCKETPAIR]], [[1]], [Define to use socketpair for inter-thread communication])
-  ], [
-    AS_VAR_IF([[enable_itc]], [["socketpair"]], [AC_MSG_ERROR([[socketpair(3) is not usable, consider using other type of inter-thread communication]])])
-  ])
-])
+              if (res != 0) return 1;
+              close (arr[1]);
+              close (arr[0]);
+                  ]]
+                )
+              ],
+              [[mhd_cv_socketpair_usable='yes']],
+              [[mhd_cv_socketpair_usable='no']]
+            )
+          ]
+        )
+      ],
+      [AS_UNSET([mhd_cv_socketpair_usable])]
+    )
+
+    AS_VAR_IF([[mhd_cv_socketpair_usable]], [["yes"]],
+      [
+        use_itc='socketpair'
+        enable_itc="$use_itc"
+        AC_DEFINE([[MHD_ITC_SOCKETPAIR_]], [[1]], [Define to use socketpair for inter-thread communication])
+      ],
+      [
+        AS_VAR_IF([[enable_itc]], [["socketpair"]], [
+          AC_MSG_ERROR([[socketpair(3) is not usable, consider using other type of inter-thread communication]])]
+        )
+      ]
+    )
+  ]
+)
 
 AS_IF([[test -z "$use_itc"]], [AC_MSG_ERROR([[cannot find usable type of inter-thread communication]])])
 
@@ -3929,6 +4048,313 @@ int main(void) {return test_intern_func();}
   ]
 )
 CFLAGS="${CFLAGS_ac} ${user_CFLAGS} ${errattr_CFLAGS}"
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((warn_unused_result))],[mhd_cv_cc_attr_warn_unused_res],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((warn_unused_result)) int
+test_func(void) {return 0; }
+
+int main(void) { return test_func(); }
+          ]])
+      ],
+      [mhd_cv_cc_attr_warn_unused_res="yes"],[mhd_cv_cc_attr_warn_unused_res="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_warn_unused_res],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_WARN_UNUSED_RES],[1],
+      [Define to '1' if your compiler supports __attribute__ ((warn_unused_result))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((returns_nonnull))],[mhd_cv_cc_attr_ret_nonnull],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((returns_nonnull)) int *
+test_func(void) {
+  static int i = 0;
+  return &i;
+}
+
+int main(void) {
+  return *(test_func());
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_ret_nonnull="yes"],[mhd_cv_cc_attr_ret_nonnull="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_ret_nonnull],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_RET_NONNULL],[1],
+      [Define to '1' if your compiler supports __attribute__ ((returns_nonnull))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((nonnull))],[mhd_cv_cc_attr_nonnull],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((nonnull)) int
+test_func(int *p) {return 0 == *p;}
+
+int main(void) {
+  int i = 0;
+  return test_func(&i) ? 0 : 5;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_nonnull="yes"],[mhd_cv_cc_attr_nonnull="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_nonnull],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_NONNULL],[1],
+      [Define to '1' if your compiler supports __attribute__ ((nonnull))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((nonnull(NUM)))],[mhd_cv_cc_attr_nonnull_num],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((nonnull(2))) int
+test_func(int *p1, int *p2) {
+  return (((int *)(void *)0) == p1) && (0 == *p2);
+}
+
+int main(void) {
+  int i = 0;
+  return test_func( (int *)(void *)0, &i) ? 0 : 5;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_nonnull_num="yes"],[mhd_cv_cc_attr_nonnull_num="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_nonnull_num],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_NONNULL_NUM],[1],
+      [Define to '1' if your compiler supports __attribute__ ((nonnull(NUM)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_only,NUM)))],[mhd_cv_cc_attr_access_read],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((access (read_only,1))) int
+test_func(int *p) {return 0 == *p;}
+
+int main(void) {
+  int i = 0;
+  return test_func(&i) ? 0 : 5;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_access_read="yes"],[mhd_cv_cc_attr_access_read="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_access_read],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_ACCESS_READ],[1],
+      [Define to '1' if your compiler supports __attribute__ ((access (read_only,NUM)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (write_only,NUM)))],[mhd_cv_cc_attr_access_write],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((access (write_only,1))) void
+test_func(int *p) {*p = 0;}
+
+int main(void) {
+  int i = 1;
+  test_func(&i);
+  return i;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_access_write="yes"],[mhd_cv_cc_attr_access_write="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_access_write],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_ACCESS_WRITE],[1],
+      [Define to '1' if your compiler supports __attribute__ ((access (write_only,NUM)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_write,NUM)))],[mhd_cv_cc_attr_access_read_write],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((access (read_write,1))) void
+test_func(int *p) {*p = *p - 1;}
+
+int main(void) {
+  int i = 1;
+  test_func(&i);
+  return i;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_access_read_write="yes"],[mhd_cv_cc_attr_access_read_write="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_access_read_write],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_ACCESS_READ_WRITE],[1],
+      [Define to '1' if your compiler supports __attribute__ ((access (read_write,NUM)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_only,NUM,NUM_SIZE)))],[mhd_cv_cc_attr_access_read_size],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((access (read_only,2,1))) int
+test_func(unsigned int num, int *arr) {return 2 == arr[num - 1];}
+
+int main(void) {
+  int arr[4] = {5, 4, 3, 2};
+  return test_func(4, arr) ? 0 : 5;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_access_read_size="yes"],[mhd_cv_cc_attr_access_read_size="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_access_read_size],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_ACCESS_READ_SIZE],[1],
+      [Define to '1' if your compiler supports __attribute__ ((access (read_only,NUM,NUM_SIZE)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (write_only,NUM,NUM_SIZE)))],[mhd_cv_cc_attr_access_write_size],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((access (write_only,2,1))) void
+test_func(unsigned int num, int *arr) {arr[num-1] = 0;}
+
+int main(void) {
+  int arr[4] = {5, 4, 3, 2};
+  test_func(4, arr);
+  return arr[3];
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_access_write_size="yes"],[mhd_cv_cc_attr_access_write_size="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_access_write_size],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_ACCESS_WRITE_SIZE],[1],
+      [Define to '1' if your compiler supports __attribute__ ((access (write_only,NUM,NUM_SIZE)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_write,NUM,NUM_SIZE)))],[mhd_cv_cc_attr_access_read_write_size],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((access (read_write,2,1))) void
+test_func(unsigned int num, int *arr) {arr[num-1] = arr[1] - 4;}
+
+int main(void) {
+  int arr[4] = {5, 4, 3, 2};
+  test_func(4, arr);
+  return arr[3];
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_access_read_write_size="yes"],[mhd_cv_cc_attr_access_read_write_size="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_access_read_write_size],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_ACCESS_READ_WRITE_SIZE],[1],
+      [Define to '1' if your compiler supports __attribute__ ((access (read_write,NUM,NUM_SIZE)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((fd_arg_read (NUM)))],[mhd_cv_cc_attr_fd_arg_read],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#ifndef _CRT_NONSTDC_NO_WARNINGS
+#define _CRT_NONSTDC_NO_WARNINGS 1
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+static __attribute__ ((fd_arg_read (1))) int
+test_func(int fd)
+{
+  int data_in;
+  int read_size;
+  read_size = (int) read(fd, &data_in, sizeof(data_in));
+  if (read_size < (int) sizeof(data_in)) return 4;
+  return data_in;
+}
+
+int main(void) {
+  int fd = open("test.txt", O_RDONLY);
+  int res = test_func(fd);
+  close (fd);
+  return res;
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_fd_arg_read="yes"],[mhd_cv_cc_attr_fd_arg_read="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_fd_arg_read],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_FD_ARG_READ],[1],
+      [Define to '1' if your compiler supports __attribute__ ((fd_arg_read (NUM)))]
+    )
+  ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__ ((null_terminated_string_arg (NUM)))],[mhd_cv_cc_attr_null_term_str],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static __attribute__ ((null_terminated_string_arg (1))) int
+test_func(const char *str) {return (0 == str[0]) ? 5 : 0;}
+
+int main(void) {
+  return test_func("test");
+}
+          ]])
+      ],
+      [mhd_cv_cc_attr_null_term_str="yes"],[mhd_cv_cc_attr_null_term_str="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_null_term_str],["yes"],
+  [
+    AC_DEFINE([HAVE_ATTR_NULL_TERM_STR],[1],
+      [Define to '1' if your compiler supports __attribute__ ((null_terminated_string_arg (NUM)))]
+    )
+  ]
+)
 AC_CACHE_CHECK([whether $CC supports __attribute__((enum_extensibility (closed)))],[mhd_cv_cc_attr_enum_extns_closed],
   [
     SAVE_ac_c_werror_flag="$ac_c_werror_flag"
@@ -3992,6 +4418,52 @@ AS_VAR_IF([mhd_cv_cc_attr_flag_enum],["yes"],
     )
   ]
 )
+AC_CACHE_CHECK([[whether $CC supports array[static N] with fixed N as a function parameter]],[mhd_cv_cc_func_param_arr_static_fixed],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static int
+test_func(int arr[static 4]) { return arr[2] - 4; }
+
+int main(void) {
+  int arr[4] = {5, 4, 3, 2};
+  return test_func(arr);
+}
+          ]])
+      ],
+      [mhd_cv_cc_func_param_arr_static_fixed="yes"],[mhd_cv_cc_func_param_arr_static_fixed="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_func_param_arr_static_fixed],["yes"],
+  [
+    AC_DEFINE([HAVE_FUNC_PARAM_ARR_STATIC_FIXED],[1],
+      [[Define to '1' if your compiler supports 'array[static N]' with fixed N as function parameter]]
+    )
+  ]
+)
+AC_CACHE_CHECK([[whether $CC supports array[static N] with variable N as a function parameter]],[mhd_cv_cc_func_param_arr_static_var],
+  [
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+static int
+test_func(int num, int arr[static num]) { return arr[num-2] - 4; }
+
+int main(void) {
+  int arr[4] = {5, 4, 3, 2};
+  return test_func(4, arr);
+}
+          ]])
+      ],
+      [mhd_cv_cc_func_param_arr_static_var="yes"],[mhd_cv_cc_func_param_arr_static_var="no"]
+    )
+  ]
+)
+AS_VAR_IF([mhd_cv_cc_func_param_arr_static_var],["yes"],
+  [
+    AC_DEFINE([HAVE_FUNC_PARAM_ARR_STATIC_VAR],[1],
+      [[Define to '1' if your compiler supports 'array[static N]' with variable N as a function parameter]]
+    )
+  ]
+)
 CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
 
 # libcurl (required for testing)
@@ -4099,10 +4571,10 @@ AS_VAR_IF([[found_sendfile]], [["no"]],
 static void empty_func(void)
 {
 /* Check for declaration */
+#ifndef sendfile
   (void)sendfile;
+#endif
 }
-/* Declare again to check form match */
-ssize_t sendfile(int, int, off_t*, size_t);
         ]],
         [[
           int fd1=0, fd2=2;
@@ -4155,11 +4627,10 @@ AS_VAR_IF([[found_sendfile]], [["no"]],
 static void empty_func(void)
 {
 /* Check for declaration */
+#ifndef sendfile
   (void)sendfile;
+#endif
 }
-/* Declare again to check form match */
-int sendfile(int, int, off_t, size_t,
-             struct sf_hdtr*, off_t*, int);
        ]],
        [[
          int fd1=0, fd2=1;
@@ -4167,7 +4638,7 @@ int sendfile(int, int, off_t, size_t,
          size_t s = 5;
          off_t r1;
          int r2;
-         r2 = sendfile (fd1, fd2, o, s, (void*)0, &r1, 0);
+         r2 = sendfile (fd1, fd2, o, s, (struct sf_hdtr *)0, &r1, 0);
          if (r2)
            empty_func();
        ]]
@@ -4203,18 +4674,17 @@ AS_VAR_IF([[found_sendfile]], [["no"]],
 static void empty_func(void)
 {
 /* Check for declaration */
+#ifndef sendfile
   (void)sendfile;
+#endif
 }
-/* Declare again to check form match */
-int sendfile(int, int, off_t, off_t*,
-             struct sf_hdtr *, int);
        ]],
        [[
          int fd=0, s=1;
          off_t o = 0;
          off_t l = 5;
          int r;
-         r = sendfile (fd, s, o, &l, (void*)0, 0);
+         r = sendfile (fd, s, o, &l, (struct sf_hdtr *)0, 0);
          if (r)
            empty_func();
        ]]
@@ -4233,7 +4703,7 @@ int sendfile(int, int, off_t, off_t*,
 
 AS_VAR_IF([[found_sendfile]], [["no"]],
   [
-   AC_MSG_CHECKING([[for Solaris-style sendfile(3)]])
+   AC_MSG_CHECKING([[for old Solaris-style sendfile(3)]])
    SAVE_LIBS="$LIBS"
    LIBS="-lsendfile $LIBS"
    AC_LINK_IFELSE(
@@ -4253,11 +4723,10 @@ AS_VAR_IF([[found_sendfile]], [["no"]],
 static void empty_func(void)
 {
 /* Check for declaration */
+#ifndef sendfile
   (void)sendfile;
+#endif
 }
-/* Declare again to check form match */
-ssize_t sendfile(int out_fd, int in_fd,
-                 off_t *off, size_t len);
        ]],
        [[
          int fd1=0, fd2=1;
@@ -4271,7 +4740,8 @@ ssize_t sendfile(int out_fd, int in_fd,
       )
      ],
      [
-       AC_DEFINE([HAVE_SOLARIS_SENDFILE], [1], [Define to 1 if you have Solaris-style sendfile(3).])
+       # Solaris uses the same format as GNU/Linux. Old versions need libsendfile.
+       AC_DEFINE([HAVE_LINUX_SENDFILE], [1], [Define to 1 if you have linux-style sendfile(2).])
        found_sendfile="yes, Solaris-style"
        MHD_LIBDEPS="-lsendfile $MHD_LIBDEPS"
        MHD_LIBDEPS_PKGCFG="-lsendfile $MHD_LIBDEPS_PKGCFG"
@@ -4291,6 +4761,9 @@ ssize_t sendfile(int out_fd, int in_fd,
          ]],
          [[
   off64_t f_offset = (off64_t) 0;
+#ifndef sendfile64
+  (void)sendfile64;
+#endif
   if (0 > sendfile64 (0, 1, &f_offset, 1))
     return 3;
          ]]
@@ -4820,7 +5293,7 @@ AS_VAR_IF([enable_dauth], ["yes"],
 #include <stdint.h>
               ]],
               [[
-  static int arr[((int) 2) - 4 * (int)(${enable_dauth_def_max_nc} != ((uint32_t)${enable_dauth_def_max_nc}))];
+  static int arr[((int) 2) - 4 * (int)(${enable_dauth_def_max_nc} != ((uint_fast32_t)${enable_dauth_def_max_nc}))];
   (void) arr;
               ]]
             )
@@ -5175,6 +5648,7 @@ AC_CACHE_CHECK([[for calloc()]], [[mhd_cv_have_func_calloc]],
 )
 AS_VAR_IF([[mhd_cv_have_func_calloc]], [["yes"]],
   [AC_DEFINE([[HAVE_CALLOC]], [[1]], [Define to 1 if you have the usable `calloc' function.])])
+AM_CONDITIONAL([HAVE_SYS_CALLOC], [[test "x${mhd_cv_have_func_calloc}" = "xyes"]])
 
 # Some systems have IPv6 disabled in kernel at run-time
 AS_IF([[test "x${mhd_cv_have_inet6}" = "xyes" && test "x${cross_compiling}" = "xno"]],
@@ -5267,6 +5741,8 @@ int main(void)
 AS_VAR_IF([mhd_cv_ipv6_for_testing],["yes"],
 	[AC_DEFINE([[USE_IPV6_TESTING]], [[1]], [Define to 1 if your kernel supports IPv6 and IPv6 is enabled and useful for testing.])]
 )
+AM_CONDITIONAL([USE_IPV6_TESTING], [[test "x$mhd_cv_ipv6_for_testing" = "xyes"]])
+
 
 AS_VAR_IF([enable_tools],["yes"],
   [
@@ -6713,9 +7189,11 @@ doc/doxygen/Makefile
 doc/examples/Makefile
 m4/Makefile
 src/Makefile
+src/incl_priv/Makefile
 src/include/Makefile
-src/lib/Makefile
-src/microhttpd/Makefile
+src/mhd2/Makefile
+src/tests/Makefile
+src/tests/basic/Makefile
 src/microhttpd_ws/Makefile
 src/examples/Makefile
 src/tools/Makefile
@@ -6775,7 +7253,8 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary:
   Threading lib:     ${USE_THREADS}
   Inter-thread comm: ${use_itc}
   Shutdown of listening socket triggers select: ${mhd_cv_host_shtdwn_trgr_select}
-  poll support:      ${enable_poll=no}
+  select() support:  ${enable_select}
+  poll() support:    ${enable_poll=no}
   epoll support:     ${enable_epoll=no}
   sendfile used:     ${found_sendfile}
   HTTPS support:     ${MSG_HTTPS}

+ 8 - 8
m4/mhd_shutdown_socket_trigger.m4

@@ -107,14 +107,14 @@ AC_DEFUN([_MHD_RUN_CHECK_SOCKET_SHUTDOWN_TRIGGER],[dnl
 #  ifdef HAVE_NETINET_TCP_H
 #    include <netinet/tcp.h>
 #  endif
-   typedef int MHD_socket;
+   typedef int MHD_Socket;
 #  define MHD_INVALID_SOCKET (-1)
 #  define MHD_POSIX_SOCKETS 1
 #else
 #  include <winsock2.h>
 #  include <ws2tcpip.h>
 #  include <windows.h>
-   typedef SOCKET MHD_socket;
+   typedef SOCKET MHD_Socket;
 #  define MHD_INVALID_SOCKET (INVALID_SOCKET)
 #  define MHD_WINSOCK_SOCKETS 1
 #endif
@@ -176,7 +176,7 @@ static void* select_thrd_func(void* param)
 #endif
   fd_set rs;
   struct timeval tmot = {0, 0};
-  MHD_socket fd = *((MHD_socket*)param);
+  MHD_Socket fd = *((MHD_Socket*)param);
 
   FD_ZERO(&rs);
   FD_SET(fd, &rs);
@@ -196,10 +196,10 @@ static void* select_thrd_func(void* param)
 }
 
 
-static MHD_socket create_socket(void)
+static MHD_Socket create_socket(void)
 { return socket (AF_INET, SOCK_STREAM, 0); }
 
-static void close_socket(MHD_socket fd)
+static void close_socket(MHD_Socket fd)
 {
 #ifdef MHD_POSIX_SOCKETS
   close(fd);
@@ -208,10 +208,10 @@ static void close_socket(MHD_socket fd)
 #endif
 }
 
-static MHD_socket
+static MHD_Socket
 create_socket_listen(int port)
 {
-  MHD_socket fd;
+  MHD_Socket fd;
   struct sockaddr_in sock_addr;
   fd = create_socket();
   if (MHD_INVALID_SOCKET == fd)
@@ -240,7 +240,7 @@ create_socket_listen(int port)
 static long long test_run_select(int timeout_millsec, int use_shutdown, long long delay_before_shutdown)
 {
   pthread_t select_thrd;
-  MHD_socket fd;
+  MHD_Socket fd;
 #ifdef HAVE_GETTIMEOFDAY
   struct timeval start, stop;
 #else

+ 6 - 6
src/Makefile.am

@@ -1,25 +1,25 @@
 # This Makefile.am is in the public domain
 
-SUBDIRS = include microhttpd .
+SUBDIRS = include incl_priv mhd2 tests .
 
 if RUN_LIBCURL_TESTS
-SUBDIRS += testcurl
+#SUBDIRS += testcurl
 if RUN_ZZUF_TESTS
-SUBDIRS += testzzuf
+#SUBDIRS += testzzuf
 endif
 endif
 
 # Finally (last!) also build experimental lib...
 if HAVE_EXPERIMENTAL
-SUBDIRS += microhttpd_ws lib
+SUBDIRS += microhttpd_ws
 endif
 
 if BUILD_EXAMPLES
-SUBDIRS += examples
+#SUBDIRS += examples
 endif
 
 if BUILD_TOOLS
-SUBDIRS += tools
+#SUBDIRS += tools
 endif
 
 EXTRA_DIST = \

+ 1 - 1
src/examples/Makefile.am

@@ -44,7 +44,7 @@ noinst_PROGRAMS += \
   websocket_chatserver_example
 endif
 
-if MHD_HAVE_EPOLL
+if MHD_USE_EPOLL
 noinst_PROGRAMS += \
   suspend_resume_epoll
 endif

+ 3 - 0
src/incl_priv/.gitignore

@@ -0,0 +1,3 @@
+/stamp-h1
+/mhd_config.h.in
+/mhd_config.h

+ 3 - 0
src/incl_priv/Makefile.am

@@ -0,0 +1,3 @@
+# This Makefile.am is in the public domain
+
+noinst_HEADERS = mhd_sys_options.h

+ 233 - 34
src/include/mhd_sys_options.h → src/incl_priv/mhd_sys_options.h

@@ -34,7 +34,11 @@
 #ifndef MHD_SYS_OPTIONS_H
 #define MHD_SYS_OPTIONS_H 1
 
-#include "MHD_config.h"
+#ifndef HAVE_CONFIG_H
+#error HAVE_CONFIG_H must be defind
+#endif
+
+#include "mhd_config.h"
 
 /**
  * Macro to make it easy to mark text for translation. Note that
@@ -89,6 +93,9 @@
 #  define MHD_VISIBILITY_INTERNAL /* empty */
 #endif
 
+/* To be used with internal non-static functions */
+#define MHD_INTERNAL MHD_VISIBILITY_INTERNAL
+
 #ifdef HAVE_ATTR_PURE
 #  define MHD_FN_PURE_ __attribute__((pure))
 #else
@@ -101,35 +108,209 @@
 #  define MHD_FN_CONST_ MHD_FN_PURE_
 #endif
 
-/* To be used with internal non-static functions */
-#define MHD_INTERNAL MHD_VISIBILITY_INTERNAL
+#ifdef HAVE_ATTR_WARN_UNUSED_RES
+#  define MHD_FN_MUST_CHECK_RESULT_ __attribute__ ((warn_unused_result))
+#else
+#  define MHD_FN_MUST_CHECK_RESULT_       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_RET_NONNULL
+#  define MHD_FN_RETURNS_NONNULL_ __attribute__ ((returns_nonnull))
+#else
+#  define MHD_FN_RETURNS_NONNULL_       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_NONNULL_NUM
+#  define MHD_FN_PAR_NONNULL_(param_num) __attribute__ ((nonnull (param_num)))
+#else
+#  define MHD_FN_PAR_NONNULL_(param_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_NONNULL
+#  define MHD_FN_PAR_NONNULL_ALL_ __attribute__ ((nonnull))
+#else
+#  define MHD_FN_PAR_NONNULL_ALL_       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_ACCESS_READ
+#  define MHD_FN_PAR_IN_(param_num) \
+        __attribute__ ((access (read_only,param_num)))
+#else
+#  define MHD_FN_PAR_IN_(param_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_ACCESS_READ_SIZE
+#  define MHD_FN_PAR_IN_SIZE_(param_num,size_num) \
+        __attribute__ ((access (read_only,param_num,size_num)))
+#else
+#  define MHD_FN_PAR_IN_SIZE_(param_num,size_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_ACCESS_READ_WRITE
+#  define MHD_FN_PAR_OUT_(param_num) \
+        __attribute__ ((access (write_only,param_num)))
+#else
+#  define MHD_FN_PAR_OUT_(param_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_ACCESS_WRITE
+#  define MHD_FN_PAR_OUT_SIZE_(param_num,size_num) \
+        __attribute__ ((access (write_only,param_num,size_num)))
+#else
+#  define MHD_FN_PAR_OUT_SIZE_(param_num,size_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_ACCESS_READ_WRITE
+#  define MHD_FN_PAR_INOUT_(param_num) \
+        __attribute__ ((access (read_write,param_num)))
+#else
+#  define MHD_FN_PAR_INOUT_(param_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_ACCESS_READ_WRITE_SIZE
+#  define MHD_FN_PAR_INOUT_SIZE_(param_num,size_num) \
+        __attribute__ ((access (read_write,param_num,size_num)))
+#else
+#  define MHD_FN_PAR_INOUT_SIZE_(param_num,size_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_FD_ARG_READ
+#  define MHD_FN_PAR_FD_READ_(param_num) \
+        __attribute__ ((fd_arg_read (param_num)))
+#else
+#  define MHD_FN_PAR_FD_READ_(param_num)       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_NULL_TERM_STR
+#  define MHD_FN_PAR_CSTR_(param_num) \
+        __attribute__ ((null_terminated_string_arg (param_num)))
+#else
+#  define MHD_FN_PAR_CSTR_(param_num)       /* empty */
+#endif
+
+#ifdef HAVE_FUNC_PARAM_ARR_STATIC_FIXED
+#  define MHD_FN_PAR_FIX_ARR_SIZE_(size)  static size
+#else
+#  define MHD_FN_PAR_FIX_ARR_SIZE_(size)  size
+#endif
+
+#ifdef HAVE_FUNC_PARAM_ARR_STATIC_VAR
+#  define MHD_FN_PAR_DYN_ARR_SIZE_(size)  static size
+#else
+#  define MHD_FN_PAR_DYN_ARR_SIZE_(size)  1
+#endif
+
+#ifdef HAVE_ATTR_ENUM_EXTNS_CLOSED
+#  define MHD_FIXED_ENUM_ __attribute__((enum_extensibility (closed)))
+#else
+#  define MHD_FIXED_ENUM_       /* empty */
+#endif
+
+#ifdef HAVE_ATTR_FLAG_ENUM
+#  define MHD_FLAGS_ENUM_ __attribute__((flag_enum))
+#else
+#  define MHD_FLAGS_ENUM_       /* empty */
+#endif /* MHD_FLAGS_ENUM_ */
+
+#define MHD_FIXED_FLAGS_ENUM_ MHD_FIXED_ENUM_ MHD_FLAGS_ENUM_
+
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FIXED_ENUM_APP_SET_ /* empty */ /* handle unknown values set by the app */
+#else
+#  define MHD_FIXED_ENUM_APP_SET_ MHD_FIXED_ENUM_
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_
+#else
+#  define MHD_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FIXED_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_ /* handle unknown values set by the app */
+#else
+#  define MHD_FIXED_FLAGS_ENUM_APP_SET_ MHD_FIXED_FLAGS_ENUM_
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FIXED_ENUM_MHD_SET_ MHD_FIXED_ENUM_
+#else
+#  define MHD_FIXED_ENUM_MHD_SET_ /* empty */ /* enum can be extended in next MHD versions */
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_
+#else
+#  define MHD_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_
+#endif
 
-#ifdef HAVE_MACRO_VARIADIC
-#  define MHD_USE_VARARG_MACROS 1
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FIXED_FLAGS_ENUM_MHD_SET_ MHD_FIXED_FLAGS_ENUM_
+#else
+#  define MHD_FIXED_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_ /* enum can be extended in next MHD versions */
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FIXED_ENUM_MHD_APP_SET_ /* empty */ /* handle unknown values set by the app */
+#else
+#  define MHD_FIXED_ENUM_MHD_APP_SET_ /* empty */ /* enum can be extended in next MHD versions */
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_
+#else
+#  define MHD_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_
+#endif
+
+#ifdef BUILDING_MHD_LIB
+#  define MHD_FIXED_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ /* handle unknown values set by the app */
+#else
+#  define MHD_FIXED_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ /* enum can be extended in next MHD versions */
+#endif
+
+/**
+ * Automatic string with the name of the current function
+ */
+#if defined(HAVE___FUNC__)
+#  define MHD_FUNC_       __func__
+#  define MHD_HAVE_MHD_FUNC_ 1
+#elif defined(HAVE___FUNCTION__)
+#  define MHD_FUNC_       __FUNCTION__
+#  define MHD_HAVE_MHD_FUNC_ 1
+#elif defined(HAVE___PRETTY_FUNCTION__)
+#  define MHD_FUNC_       __PRETTY_FUNCTION__
+#  define MHD_HAVE_MHD_FUNC_ 1
+#else
+#  define MHD_FUNC_       "**name unavailable**"
+#  ifdef MHD_HAVE_MHD_FUNC_
+#    undef MHD_HAVE_MHD_FUNC_
+#  endif /* MHD_HAVE_MHD_FUNC_ */
 #endif
 
 /* Some platforms (FreeBSD, Solaris, W32) allow to override
    default FD_SETSIZE by defining it before including
    headers. */
 #ifdef FD_SETSIZE
-/* FD_SETSIZE defined in command line or in MHD_config.h */
+/* FD_SETSIZE defined in command line or in mhd_config.h */
 #elif defined(_WIN32) || defined(__CYGWIN__)
 /* Platform with WinSock and without overridden FD_SETSIZE */
-#define FD_SETSIZE 2048 /* Override default small value (64) */
+#  ifdef _WIN64
+#    define FD_SETSIZE 4096 /* Override default small value (64) */
+#  else
+#    define FD_SETSIZE 1024 /* Override default small value (64) */
+#  endif
 #else  /* !FD_SETSIZE && !W32 */
 /* System default value of FD_SETSIZE is used */
-#define _MHD_FD_SETSIZE_IS_DEFAULT 1
+#  define MHD_FD_SETSIZE_IS_DEFAULT_ 1
 #endif /* !FD_SETSIZE && !W32 */
 
 #if defined(HAVE_LINUX_SENDFILE) || defined(HAVE_FREEBSD_SENDFILE) || \
-  defined(HAVE_DARWIN_SENDFILE) || defined(HAVE_SOLARIS_SENDFILE)
+  defined(HAVE_DARWIN_SENDFILE)
 /* Have any supported sendfile() function. */
-#define _MHD_HAVE_SENDFILE
-#endif /* HAVE_LINUX_SENDFILE || HAVE_FREEBSD_SENDFILE ||
-          HAVE_DARWIN_SENDFILE || HAVE_SOLARIS_SENDFILE */
-#if defined(HAVE_LINUX_SENDFILE) || defined(HAVE_SOLARIS_SENDFILE)
-#define MHD_LINUX_SOLARIS_SENDFILE 1
-#endif /* HAVE_LINUX_SENDFILE || HAVE_SOLARIS_SENDFILE */
+#  define MHD_USE_SENDFILE 1
+#endif /* HAVE_LINUX_SENDFILE || HAVE_FREEBSD_SENDFILE
+          || HAVE_DARWIN_SENDFILE */
 
 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
 #  ifndef MHD_USE_THREADS
@@ -137,6 +318,20 @@
 #  endif
 #endif /* MHD_USE_POSIX_THREADS || MHD_USE_W32_THREADS */
 
+/**
+ * Macro to drop 'const' qualifier from pointer.
+ * Try to avoid compiler warning.
+ * To be used *only* to deal with broken external APIs, which require non-const
+ * pointer to unmodifiable data.
+ * Must not be used to transform pointers for internal MHD needs.
+ */
+#ifdef HAVE_UINTPTR_T
+#  define mhd_DROP_CONST(ptr)  ((void *) ((uintptr_t) ((const void *) (ptr))))
+#else
+#  define mhd_DROP_CONST(ptr)  ((void *) ((const void *) (ptr)))
+#endif
+
+
 #if defined(OS390)
 #define _OPEN_THREADS
 #define _OPEN_SYS_SOCK_IPV6
@@ -162,6 +357,12 @@
 #  endif /* !WIN32_LEAN_AND_MEAN */
 #endif /* _WIN32 && ! __CYGWIN__ */
 
+#if defined(__MINGW32__)
+#  ifdef __USE_MINGW_ANSI_STDIO
+#    define __USE_MINGW_ANSI_STDIO 0 /* Force use native printf, the code is well-adapted */
+#  endif
+#endif
+
 #if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS)
 #define RESTRICT __restrict__
 #endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */
@@ -177,6 +378,14 @@
 #define __STDC_WANT_LIB_EXT1__ 1
 #endif /* HAVE_C11_GMTIME_S */
 
+#if ! defined(_DEBUG) && ! defined(NDEBUG)
+#  ifndef DEBUG /* Used by some toolchains */
+#    define NDEBUG 1 /* Use NDEBUG by default */
+#  else  /* DEBUG */
+#    define _DEBUG 1 /* Non-standart macro */
+#  endif /* DEBUG */
+#endif /* !_DEBUG && !NDEBUG */
+
 #if defined(MHD_FAVOR_FAST_CODE) && defined(MHD_FAVOR_SMALL_CODE)
 #error \
   MHD_FAVOR_FAST_CODE and MHD_FAVOR_SMALL_CODE are both defined. Cannot favor speed and size at the same time.
@@ -236,25 +445,6 @@
         __pragma(runtime_checks("c", restore))
 #endif /* _MSC_FULL_VER */
 
-/**
- * Automatic string with the name of the current function
- */
-#if defined(HAVE___FUNC__)
-#define MHD_FUNC_       __func__
-#define MHD_HAVE_MHD_FUNC_ 1
-#elif defined(HAVE___FUNCTION__)
-#define MHD_FUNC_       __FUNCTION__
-#define MHD_HAVE_MHD_FUNC_ 1
-#elif defined(HAVE___PRETTY_FUNCTION__)
-#define MHD_FUNC_       __PRETTY_FUNCTION__
-#define MHD_HAVE_MHD_FUNC_ 1
-#else
-#define MHD_FUNC_       "**name unavailable**"
-#ifdef MHD_HAVE_MHD_FUNC_
-#undef MHD_HAVE_MHD_FUNC_
-#endif /* MHD_HAVE_MHD_FUNC_ */
-#endif
-
 /* Un-define some HAVE_DECL_* macro if they equal zero.
    This should allow safely use #ifdef in the code.
    Define HAS_DECL_* macros only if matching HAVE_DECL_* macro
@@ -339,4 +529,13 @@
 #  define MHD_DAUTH_DEF_MAX_NC_ 1000
 #endif /* ! MHD_DAUTH_DEF_MAX_NC_ */
 
+/* Eclipse parse compatibility */
+#ifdef __CDT_PARSER__
+#  undef MHD_NORETURN_
+#  define MHD_NORETURN_ __attribute__((__noreturn__))
+#endif
+
+/* Avoid interference with third-party headers */
+#undef HAVE_CONFIG_H
+
 #endif /* MHD_SYS_OPTIONS_H */

+ 19 - 19
src/include/Makefile.am

@@ -1,25 +1,25 @@
 # This Makefile.am is in the public domain
 SUBDIRS = .
 
-microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h: d_options.rec options-generator
-	rm -f microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h
-	./options-generator daemon > microhttpd2_generated_daemon_options.h
-	chmod -w $@
-microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h: r_options.rec options-generator
-	rm -f microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h
-	./options-generator response > microhttpd2_generated_response_options.h
-	chmod -w $@
-
-microhttpd2.h: microhttpd2_preamble.h.in microhttpd2_inline_daemon_documentation.h.in microhttpd2_inline_response_documentation.h.in microhttpd2_main.h.in microhttpd2_postamble.h.in
-	rm -f $@
-	cat $^ >$@
-	chmod -w $@
-
-noinst_PROGRAMS = \
-  options-generator
-
-options_generator_SOURCES = \
- options-generator.c
+#microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h: d_options.rec options-generator
+#	rm -f microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h
+#	./options-generator daemon > microhttpd2_generated_daemon_options.h
+#	chmod -w $@
+#microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h: r_options.rec options-generator
+#	rm -f microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h
+#	./options-generator response > microhttpd2_generated_response_options.h
+#	chmod -w $@
+
+#microhttpd2.h: microhttpd2_preamble.h.in microhttpd2_inline_daemon_documentation.h.in microhttpd2_inline_response_documentation.h.in microhttpd2_main.h.in microhttpd2_postamble.h.in
+#	rm -f $@
+#	cat $^ >$@
+#	chmod -w $@
+
+#noinst_PROGRAMS = \
+#  options-generator
+
+#options_generator_SOURCES = \
+# options-generator.c
 
 mhd2includedir = $(includedir)/mhd2
 

+ 0 - 3
src/include/mhd_future.h

@@ -111,9 +111,6 @@
 #ifndef MHD_FN_RETURNS_NONNULL_
 #  define MHD_FN_RETURNS_NONNULL_       /* empty */
 #endif /* ! MHD_FN_RETURNS_NONNULL_ */
-#ifndef MHD_FN_WARN_UNUSED_RESULT_
-#  define MHD_FN_WARN_UNUSED_RESULT_    /* empty */
-#endif /* ! MHD_FN_WARN_UNUSED_RESULT_ */
 
 
 /**

Fișier diff suprimat deoarece este prea mare
+ 420 - 147
src/include/microhttpd2.h


+ 2 - 0
src/include/microhttpd2_generated_response_options.h

@@ -46,6 +46,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption
    * Response still use HTTP/1.1 version in header, but always close the connection after sending the response and do not use chunked encoding for the response.
    * You can also set the #MHD_R_O_HTTP_1_0_SERVER flag to force HTTP/1.0 version in the response.
    * Responses are still compatible with HTTP/1.1.
+   *
    * This option can be used to communicate with some broken client, which does not implement HTTP/1.1 features, but advertises HTTP/1.1 support.
    */
   MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT = 80
@@ -58,6 +59,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption
    * Chunked encoding will not be used for the response.
    * Due to backward compatibility, responses still can be used with HTTP/1.1 clients.
    * This option can be used to emulate HTTP/1.0 server (for response part only as chunked encoding in requests (if any) is processed by MHD).
+   *
    * With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections).
    */
   MHD_R_O_HTTP_1_0_SERVER = 81

+ 40 - 50
src/include/microhttpd2_portability.h

@@ -319,11 +319,11 @@ typedef SOCKET MHD_Socket;
       * 'pragma push/pop' support was added in GCC 4.6.0 */
 #    define MHD_WARN_PUSH_ _Pragma("GCC diagnostic push")
 #    define MHD_WARN_POP_  _Pragma("GCC diagnostic pop")
-#    define MHD_WARN_INGORE_(warn) \
+#    define MHD_WARN_IGNORE_(warn) \
         _Pragma(MHD_MACRO_STR_(GCC diagnostic ignored MHD_MACRO_STR__(warn)))
 #    ifdef MHD_USE_VARARG_MACROS_EXT
 #      define MHD_NOWARN_VARIADIC_MACROS_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wvariadic-macros)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wvariadic-macros)
 #      define MHD_RESTORE_WARN_VARIADIC_MACROS_ MHD_WARN_POP_
 #    endif
 #    ifdef MHD_USE_COMPOUND_LITERALS_EXT
@@ -331,43 +331,43 @@ typedef SOCKET MHD_Socket;
 #      define MHD_RESTORE_WARN_COMPOUND_LITERALS_       /* empty */
 #    endif
 #    define MHD_NOWARN_UNUSED_FUNC_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wunused-function)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wunused-function)
 #    define MHD_RESTORE_WARN_UNUSED_FUNC_ MHD_WARN_POP_
 #  elif MHD_CLANG_MINV (3,1)
 #    define MHD_WARN_PUSH_ _Pragma("clang diagnostic push")
 #    define MHD_WARN_POP_  _Pragma("clang diagnostic pop")
-#    define MHD_WARN_INGORE_(warn) \
+#    define MHD_WARN_IGNORE_(warn) \
         _Pragma(MHD_MACRO_STR_(clang diagnostic ignored MHD_MACRO_STR__(warn)))
 #    ifdef MHD_USE_VARARG_MACROS_EXT
 #      define MHD_NOWARN_VARIADIC_MACROS_ \
         MHD_WARN_PUSH_ \
-        MHD_WARN_INGORE_ (-Wvariadic-macros) \
-        MHD_WARN_INGORE_ (-Wc++98-compat-pedantic)
+        MHD_WARN_IGNORE_ (-Wvariadic-macros) \
+        MHD_WARN_IGNORE_ (-Wc++98-compat-pedantic)
 #      define MHD_RESTORE_WARN_VARIADIC_MACROS_ MHD_WARN_POP_
 #    else  /* ! MHD_USE_VARARG_MACROS_EXT */
 #      define MHD_NOWARN_VARIADIC_MACROS_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wc++98-compat-pedantic)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wc++98-compat-pedantic)
 #      define MHD_RESTORE_WARN_VARIADIC_MACROS_ MHD_WARN_POP_
 #    endif
 #    ifdef MHD_USE_CPP_INIT_LIST
 #      define MHD_NOWARN_CPP_INIT_LIST_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wc++98-compat)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wc++98-compat)
 #      define MHD_RESTORE_WARN_CPP_INIT_LIST_ MHD_WARN_POP_
 #    endif
 #    ifdef MHD_USE_COMPOUND_LITERALS_EXT
 #      define MHD_NOWARN_COMPOUND_LITERALS_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wc99-extensions)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wc99-extensions)
 #      define MHD_RESTORE_WARN_COMPOUND_LITERALS_ MHD_WARN_POP_
 #    endif
 #    define MHD_NOWARN_UNUSED_FUNC_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wunused-function)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wunused-function)
 #    define MHD_RESTORE_WARN_UNUSED_FUNC_ MHD_WARN_POP_
 #  elif MHD_MSC_MINV (1500)
 #    define MHD_WARN_PUSH_ __pragma(warning(push))
 #    define MHD_WARN_POP_  __pragma(warning(pop))
-#    define MHD_WARN_INGORE_(warn)      __pragma(warning(disable:warn))
+#    define MHD_WARN_IGNORE_(warn)      __pragma(warning(disable:warn))
 #    define MHD_NOWARN_UNUSED_FUNC_ \
-        MHD_WARN_PUSH_ MHD_WARN_INGORE_ (4514)
+        MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (4514)
 #    define MHD_RESTORE_WARN_UNUSED_FUNC_ MHD_WARN_POP_
 #  endif
 #endif /*!  MHD_NO__PRAGMA */
@@ -378,8 +378,8 @@ typedef SOCKET MHD_Socket;
 #ifndef MHD_WARN_POP_
 #  define MHD_WARN_POP_         /* empty */
 #endif
-#ifndef MHD_WARN_INGORE_
-#  define MHD_WARN_INGORE_(ignored)     /* empty */
+#ifndef MHD_WARN_IGNORE_
+#  define MHD_WARN_IGNORE_(ignored)     /* empty */
 #endif
 #ifndef MHD_NOWARN_VARIADIC_MACROS_
 #  define MHD_NOWARN_VARIADIC_MACROS_   /* empty */
@@ -485,14 +485,18 @@ typedef SOCKET MHD_Socket;
 #endif /* !MHD_DEPR_FUNC_ */
 
 #ifdef __has_attribute
-#  if __has_attribute (enum_extensibility)
+#  ifndef MHD_FIXED_ENUM_
+#    if __has_attribute (enum_extensibility)
 /* Enum will not be extended */
-#    define MHD_FIXED_ENUM_ __attribute__((enum_extensibility (closed)))
-#  endif /* enum_extensibility */
-#  if __has_attribute (flag_enum)
+#      define MHD_FIXED_ENUM_ __attribute__((enum_extensibility (closed)))
+#    endif /* enum_extensibility */
+#  endif
+#  ifndef MHD_FLAGS_ENUM_
+#    if __has_attribute (flag_enum)
 /* Enum is a bitmap */
-#    define MHD_FLAGS_ENUM_ __attribute__((flag_enum))
-#  endif /* flag_enum */
+#      define MHD_FLAGS_ENUM_ __attribute__((flag_enum))
+#    endif /* flag_enum */
+#  endif
 #endif /* __has_attribute */
 
 #ifndef MHD_FIXED_ENUM_
@@ -592,6 +596,16 @@ typedef SOCKET MHD_Socket;
 #      endif
 #    endif /* const && !MHD_FN_CONST_ */
 
+/* Override detected value of MHD_FN_RETURNS_NONNULL_ by defining it before
+ * including the header */
+#    if __has_attribute (returns_nonnull) && \
+  ! defined(MHD_FN_RETURNS_NONNULL_)
+/**
+ * MHD_FN_RETURNS_NONNULL_ indicates that function never returns NULL.
+ */
+#      define MHD_FN_RETURNS_NONNULL_ __attribute__ ((returns_nonnull))
+#    endif /* returns_nonnull && !MHD_FN_RETURNS_NONNULL_ */
+
 /* Override detected value of MHD_FN_MUST_CHECK_RESULT_ by defining it before
  * including the header */
 #    if __has_attribute (warn_unused_result) && \
@@ -629,7 +643,7 @@ typedef SOCKET MHD_Socket;
 #    if __has_attribute (access)
 
 /* Override detected value of MHD_FN_PAR_IN_ by defining it before
-  * including the header */
+ * including the header */
 #      if ! defined(MHD_FN_PAR_IN_)
 /**
  * MHD_FN_PAR_IN_ indicates function parameter points to data
@@ -704,7 +718,7 @@ typedef SOCKET MHD_Socket;
 #    if __has_attribute (fd_arg_read) && \
   ! defined(MHD_FN_PAR_FD_READ_)
 /**
- * MHD_FN_PAR_IN_ indicates function parameter is file descriptor that
+ * MHD_FN_PAR_FD_READ_ indicates function parameter is file descriptor that
  * must be in open state and available for reading
  */
 #      define MHD_FN_PAR_FD_READ_(param_num) \
@@ -716,34 +730,13 @@ typedef SOCKET MHD_Socket;
 #    if __has_attribute (null_terminated_string_arg) && \
   ! defined(MHD_FN_PAR_CSTR_)
 /**
- * MHD_FN_PAR_IN_ indicates function parameter is file descriptor that
+ * MHD_FN_PAR_CSTR_ indicates function parameter is file descriptor that
  * must be in open state and available for reading
  */
 #      define MHD_FN_PAR_CSTR_(param_num) \
         __attribute__ ((null_terminated_string_arg (param_num)))
 #    endif /* null_terminated_string_arg && !MHD_FN_PAR_CSTR_ */
 
-/* Override detected value of MHD_FN_RETURNS_NONNULL_ by defining it before
- * including the header */
-#    if __has_attribute (returns_nonnull) && \
-  ! defined(MHD_FN_RETURNS_NONNULL_)
-/**
- * MHD_FN_RETURNS_NONNULL_ indicates that function never returns NULL.
- */
-#      define MHD_FN_RETURNS_NONNULL_ __attribute__ ((returns_nonnull))
-#    endif /* returns_nonnull && !MHD_FN_RETURNS_NONNULL_ */
-
-/* Override detected value of MHD_FN_WARN_UNUSED_RESULT_ by defining it before
- * including the header */
-#    if __has_attribute (warn_unused_result) && \
-  ! defined(MHD_FN_WARN_UNUSED_RESULT_)
-/**
- * MHD_FN_WARN_UNUSED_RESULT_ that function return value should not be ignored
- */
-#      define MHD_FN_WARN_UNUSED_RESULT_ \
-        __attribute__ ((warn_unused_result))
-#    endif /* warn_unused_result && !MHD_FN_WARN_UNUSED_RESULT_ */
-
 #  endif /* __has_attribute */
 #endif /* ! MHD_NO_FUNC_ATTRIBUTES */
 
@@ -780,6 +773,9 @@ typedef SOCKET MHD_Socket;
 #ifndef MHD_FN_PURE_
 #  define MHD_FN_PURE_        /* empty */
 #endif /* ! MHD_FN_PURE_ */
+#ifndef MHD_FN_RETURNS_NONNULL_
+#  define MHD_FN_RETURNS_NONNULL_       /* empty */
+#endif /* ! MHD_FN_RETURNS_NONNULL_ */
 #ifndef MHD_FN_MUST_CHECK_RESULT_
 #  define MHD_FN_MUST_CHECK_RESULT_   /* empty */
 #endif /* ! MHD_FN_MUST_CHECK_RESULT_ */
@@ -813,9 +809,3 @@ typedef SOCKET MHD_Socket;
 #ifndef MHD_FN_PAR_CSTR_
 #  define MHD_FN_PAR_CSTR_(param_num)   /* empty */
 #endif /* ! MHD_FN_PAR_CSTR_ */
-#ifndef MHD_FN_RETURNS_NONNULL_
-#  define MHD_FN_RETURNS_NONNULL_       /* empty */
-#endif /* ! MHD_FN_RETURNS_NONNULL_ */
-#ifndef MHD_FN_WARN_UNUSED_RESULT_
-#  define MHD_FN_WARN_UNUSED_RESULT_    /* empty */
-#endif /* ! MHD_FN_WARN_UNUSED_RESULT_ */

+ 126 - 0
src/mhd2/Makefile.am

@@ -0,0 +1,126 @@
+# This Makefile.am is in the public domain
+
+AM_CPPFLAGS = \
+  -I$(srcdir)/../incl_priv \
+  -I$(srcdir)/../include \
+  $(CPPFLAGS_ac)
+
+AM_CFLAGS = $(CFLAGS_ac)
+
+if USE_COVERAGE
+  AM_CFLAGS += --coverage
+endif
+
+AM_LDFLAGS = $(LDFLAGS_ac)
+
+AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
+
+lib_LTLIBRARIES = \
+  libmicrohttpd2.la
+
+noinst_DATA =
+MOSTLYCLEANFILES =
+
+libmicrohttpd2_la_SOURCES = \
+  $(CONFIG_HEADER) \
+  autoinit_funcs.h \
+  sys_null_macro.h          sys_base_types.h          sys_bool_type.h \
+  sys_sockets_types.h       sys_sockets_headers.h     sys_ip_headers.h \
+  sys_errno.h               sys_malloc.h \
+  sys_poll.h \
+  sys_sendfile.h \
+  compat_calloc.h \
+  mhd_assert.h \
+  mhd_tristate.h \
+  mhd_socket_types.h        mhd_sockets_macros.h \
+  mhd_sockets_funcs.c       mhd_sockets_funcs.h \
+  mhd_iovec.h \
+  mhd_panic.c               mhd_panic.h \
+  mhd_lib_init.c            mhd_lib_init_impl.h       mhd_lib_init.h \
+  mhd_dlinked_list.h \
+  mhd_locks.h \
+  mhd_itc.c                 mhd_itc.h                 mhd_itc_types.h \
+  mhd_threads.c             mhd_threads.h             sys_thread_entry_type.h \
+  mhd_mono_clock.c          mhd_mono_clock.h \
+  mhd_mempool.c             mhd_mempool.h \
+  mhd_daemon.h              daemon_funcs.c            daemon_funcs.h \
+  mhd_public_api.h \
+  events_process.c          events_process.h \
+  daemon_logger.c           daemon_logger.h \
+  daemon_logger_default.c   daemon_logger_default.h \
+  daemon_options.h          daemon_set_options.c \
+  daemon_create.c \
+  daemon_start.c \
+  daemon_add_conn.c         daemon_add_conn.h \
+  mhd_connection.h          mhd_request.h             mhd_reply.h \
+  response_from.c           response_from.h
+
+
+if ! HAVE_SYS_CALLOC
+libmicrohttpd2_la_SOURCES += \
+  compat_calloc.c
+endif
+
+
+libmicrohttpd2_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+libmicrohttpd2_la_CFLAGS = \
+  $(AM_CFLAGS) $(MHD_LIB_CFLAGS) $(MHD_TLS_LIB_CFLAGS)
+libmicrohttpd2_la_LDFLAGS = \
+  $(AM_LDFLAGS) $(MHD_LIB_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS) \
+  $(W32_MHD_LIB_LDFLAGS) \
+  -export-dynamic -no-undefined \
+  -version-info @LIB_VERSION_CURRENT@:@LIB_VERSION_REVISION@:@LIB_VERSION_AGE@
+libmicrohttpd2_la_LIBADD = \
+  $(MHD_LIBDEPS) $(MHD_TLS_LIBDEPS)
+
+
+AM_V_RC = $(am__v_RC_@AM_V@)
+am__v_RC_ = $(am__v_RC_@AM_DEFAULT_V@)
+am__v_RC_0 = @echo "  RC      " $@;
+am__v_RC_1 = 
+
+# General rule is not required, but keep it just in case
+# Note: windres does not understand '-isystem' flag, so all
+# possible '-isystem' flags are replaced by simple '-I' flags.
+.rc.lo:
+	$(AM_V_RC) RC_ALL_CPPFLAGS=`echo ' $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) ' | $(SED) -e 's/ -isystem / -I/g'`; \
+          $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_ALL_CPPFLAGS} $< -o $@
+
+# Note: windres does not understand '-isystem' flag, so all
+# possible '-isystem' flags are replaced by simple '-I' flags.
+libmicrohttpd2_la-w32_lib_res.lo: $(builddir)/w32_lib_res.rc
+	$(AM_V_RC) RC_ALL_CPPFLAGS=`echo ' $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) ' | $(SED) -e 's/ -isystem / -I/g'`; \
+          $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_ALL_CPPFLAGS} $(builddir)/w32_lib_res.rc -o $@
+
+if HAVE_W32
+MHD_DLL_RES_LO = libmicrohttpd2_la-w32_lib_res.lo
+else
+MHD_DLL_RES_LO = 
+endif
+
+EXTRA_libmicrohttpd2_la_DEPENDENCIES = $(MHD_DLL_RES_LO)
+libmicrohttpd2_la_LIBADD += $(MHD_DLL_RES_LO)
+
+#TESTS = $(check_PROGRAMS)
+
+update-po-POTFILES.in: $(top_srcdir)/po/POTFILES.in
+
+$(top_srcdir)/po/POTFILES.in: $(srcdir)/Makefile.am
+	@echo "Creating $@"
+	@echo  src/include/microhttpd2.h > "$@" && \
+	for src in $(am__libmicrohttpd2_la_SOURCES_DIST) ; do \
+	  echo "$(subdir)/$$src" >> "$@" ; \
+	done
+
+.PHONY: update-po-POTFILES.in
+
+
+$(CONFIG_HEADER): $(builddir)/../incl_priv/mhd_config.h.in $(top_srcdir)/configure $(top_builddir)/config.status
+	@echo "cd $(srcdir)/../incl_priv && $(MAKE) $(AM_MAKEFLAGS) mhd_config.h" && \
+	  $(am__cd) $(srcdir)/../incl_priv && $(MAKE) $(AM_MAKEFLAGS) mhd_config.h
+
+$(builddir)/../incl_priv/mhd_config.h.in: $(top_srcdir)/configure.ac
+	@echo "cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh" && \
+	  $(am__cd) $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh

+ 168 - 0
src/mhd2/action.c

@@ -0,0 +1,168 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/action.c
+ * @brief  The definition of the MHD_action_*() and MHD_upload_action_*()
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_action.h"
+#include "mhd_request.h"
+#include "mhd_public_api.h"
+
+
+MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_
+const struct MHD_Action *
+MHD_action_suspend (struct MHD_Request *request)
+{
+  struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
+  head_act->act = mhd_ACTION_SUSPEND;
+  return head_act;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) const struct MHD_Action *
+MHD_action_from_response (struct MHD_Request *request,
+                          struct MHD_Response *response)
+{
+  struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
+  if (mhd_ACTION_NO_ACTION != head_act->act)
+    return NULL;
+  if (NULL == response)
+    return (const struct MHD_Action *) NULL;
+
+  head_act->act = mhd_ACTION_RESPONSE;
+  head_act->data.response = response;
+
+  return head_act;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) const struct MHD_Action *
+MHD_action_process_upload (struct MHD_Request *request,
+                           size_t large_buffer_size,
+                           MHD_UploadCallback uc_full,
+                           void *uc_full_cls,
+                           MHD_UploadCallback uc_inc,
+                           void *uc_inc_cls)
+{
+  struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
+  if (mhd_ACTION_NO_ACTION != head_act->act)
+    return NULL;
+  if (0 == large_buffer_size)
+  {
+    if (NULL != uc_full)
+      return NULL;
+    if (NULL == uc_inc)
+      return NULL;
+  }
+  else
+  {
+    if (NULL == uc_full)
+      return NULL;
+  }
+
+  head_act->act = mhd_ACTION_UPLOAD;
+  head_act->data.upload.large_buffer_size = large_buffer_size;
+  head_act->data.upload.full.cb = uc_full;
+  head_act->data.upload.full.cls = uc_full_cls;
+  head_act->data.upload.inc.cb = uc_inc;
+  head_act->data.upload.inc.cls = uc_inc_cls;
+
+  return head_act;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) struct MHD_Action *
+MHD_action_post_processor (struct MHD_Request *request,
+                           size_t pp_buffer_size,
+                           size_t pp_stream_limit,
+                           enum MHD_HTTP_PostEncoding enc,
+                           MHD_PostDataReader reader,
+                           void *reader_cls,
+                           MHD_PostDataFinished done_cb,
+                           void *done_cb_cls)
+{
+  struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
+  if (mhd_ACTION_NO_ACTION != head_act->act)
+    return NULL;
+  if (NULL == done_cb)
+    return NULL;
+
+  head_act->act = mhd_ACTION_POST_PROCESS;
+  head_act->data.post_process.pp_buffer_size = pp_buffer_size;
+  head_act->data.post_process.pp_stream_limit = pp_stream_limit;
+  head_act->data.post_process.enc = enc;
+  head_act->data.post_process.reader = reader;
+  head_act->data.post_process.reader_cls = reader_cls;
+  head_act->data.post_process.done_cb = done_cb;
+  head_act->data.post_process.done_cb_cls = done_cb_cls;
+
+  return head_act;
+}
+
+
+MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_
+const struct MHD_UploadAction *
+MHD_upload_action_suspend (struct MHD_Request *request)
+{
+  struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
+  if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+    return NULL;
+
+  upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
+
+  return upl_act;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) const struct MHD_UploadAction *
+MHD_upload_action_from_response (struct MHD_Request *request,
+                                 struct MHD_Response *response)
+{
+  struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
+  if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+    return NULL;
+
+  upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
+  upl_act->data.response = response;
+
+  return upl_act;
+}
+
+
+MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ const struct MHD_UploadAction *
+MHD_upload_action_continue (struct MHD_Request *request)
+{
+  struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
+  if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+    return NULL;
+
+  upl_act->act = mhd_UPLOAD_ACTION_CONTINUE;
+
+  return upl_act;
+}

+ 39 - 39
src/include/autoinit_funcs.h → src/mhd2/autoinit_funcs.h

@@ -85,13 +85,13 @@
   (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100)
 
 #define GNUC_SET_INIT_AND_DEINIT(FI,FD) \
-  void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void);  \
-  void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \
-  void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void)   \
-  { (void) (FI) (); } \
-  void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void)  \
-  { (void) (FD) (); } \
-  struct _GNUC_dummy_str_ ## FI {int i;}
+        void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void);  \
+        void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \
+        void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void)   \
+        { (void) (FI) (); } \
+        void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void)  \
+        { (void) (FD) (); } \
+        struct _GNUC_dummy_str_ ## FI {int i;}
 
 #define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT (FI,FD)
 #define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
@@ -144,7 +144,7 @@
 /* Internal variable prefix (can be any) */
 #define W32_INITHELPERVARNAME(f) _initHelperDummy_ ## f
 #define W32_INITHELPERVARNAMEDECORSTR(f) \
-  W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f))
+        W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f))
 
 /* Declare section (segment), put variable pointing to init function to chosen segment,
    force linker to always include variable to avoid omitting by optimiser */
@@ -152,10 +152,10 @@
    void __cdecl FuncName(void) */
 /* "extern" with initialisation value means that variable is declared AND defined. */
 #define W32_VFPTR_IN_SEG(S,F) \
-  __pragma (section (S,long,read)) \
-  __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \
-  W32_INITVARDECL __declspec(allocate (S))void \
-    (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F
+        __pragma(section (S,long,read)) \
+        __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \
+        W32_INITVARDECL __declspec(allocate (S))void \
+        (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F
 
 /* Sections (segments) for pointers to initialisers/deinitialisers */
 
@@ -181,10 +181,10 @@
 /* Startup process is aborted if initialiser returns non-zero */
 /* "extern" with initialisation value means that variable is declared AND defined. */
 #define W32_IFPTR_IN_SEG(S,F) \
-  __pragma (section (S,long,read)) \
-  __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \
-  W32_INITVARDECL __declspec(allocate (S))int \
-    (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F
+        __pragma(section (S,long,read)) \
+        __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \
+        W32_INITVARDECL __declspec(allocate (S))int \
+        (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F
 
 /* Unsafe sections (segments) for pointers to initialisers with
    "int" return type */
@@ -229,9 +229,9 @@
   ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) || \
   defined(AUTOINIT_FUNCS_FORCE_EARLY_INIT)) && \
   ! defined(AUTOINIT_FUNCS_FORCE_LATE_INIT)
-#define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY(F)
+#define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY (F)
 #else
-#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE(F)
+#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE (F)
 #endif
 
 #endif /* ! _USRDLL || ! AUTOINIT_FUNCS_DECLARE_STATIC_REG
@@ -243,13 +243,13 @@
 #include <stdlib.h> /* required for atexit() */
 
 #define W32_SET_INIT_AND_DEINIT(FI,FD) \
-  void __cdecl _W32_init_helper_ ## FI (void);    \
-  void __cdecl _W32_deinit_helper_ ## FD (void); \
-  void __cdecl _W32_init_helper_ ## FI (void)     \
-  { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \
-  void __cdecl _W32_deinit_helper_ ## FD (void)  \
-  { (void) (FD) (); } \
-  W32_REGISTER_INIT (_W32_init_helper_ ## FI)
+        void __cdecl _W32_init_helper_ ## FI (void);    \
+        void __cdecl _W32_deinit_helper_ ## FD (void); \
+        void __cdecl _W32_init_helper_ ## FI (void)     \
+        { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \
+        void __cdecl _W32_deinit_helper_ ## FD (void)  \
+        { (void) (FD) (); } \
+        W32_REGISTER_INIT (_W32_init_helper_ ## FI)
 #else  /* _USRDLL */
 
 #ifndef WIN32_LEAN_AND_MEAN
@@ -262,22 +262,22 @@
    and rename DllMain to usr_DllMain */
 #ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN
 #define W32_SET_INIT_AND_DEINIT(FI,FD) \
-  BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
-  BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused)  \
-  { (void) hinst; (void) unused; \
-    if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \
-    else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \
-    return TRUE; \
-  } struct _W32_dummy_strc_ ## FI {int i;}
+        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
+        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused)  \
+        { (void) hinst; (void) unused; \
+          if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \
+          else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \
+          return TRUE; \
+        } struct _W32_dummy_strc_ ## FI {int i;}
 #else  /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
 #define W32_SET_INIT_AND_DEINIT(FI,FD) \
-  BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
-  BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
-  BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused)  \
-  { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \
-    else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \
-    return usr_DllMain (hinst,reason,unused); \
-  } struct _W32_dummy_strc_ ## FI {int i;}
+        BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
+        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
+        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused)  \
+        { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \
+          else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \
+          return usr_DllMain (hinst,reason,unused); \
+        } struct _W32_dummy_strc_ ## FI {int i;}
 #endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
 #endif /* _USRDLL */
 

+ 1 - 1
src/mhd2/compat_calloc.c

@@ -40,7 +40,7 @@
 #endif /* __GNUC__ >= 5 */
 
 MHD_INTERNAL void *
-MHD_calloc_ (size_t nelem, size_t elsize)
+mhd_calloc (size_t nelem, size_t elsize)
 {
   size_t alloc_size;
   void *ptr;

+ 2 - 2
src/mhd2/compat_calloc.h

@@ -43,7 +43,7 @@
 #    include <string.h>
 #  endif
 
-#  define MHD_calloc_ calloc
+#  define mhd_calloc calloc
 #else
 
 #  include "sys_base_types.h" /* for size_t, NULL */
@@ -58,7 +58,7 @@
  *         the NULL pointer on failure.
  */
 MHD_INTERNAL void *
-MHD_calloc_ (size_t nelem, size_t elsize);
+mhd_calloc (size_t nelem, size_t elsize);
 
 #endif /* ! HAVE_CALLOC */
 

+ 982 - 0
src/mhd2/daemon_add_conn.c

@@ -0,0 +1,982 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_add_conn.c
+ * @brief  The implementations of MHD functions for adding new connections
+ * @author Karlson2k (Evgeny Grin)
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ *
+ * @warning Imported from MHD1 with minimal changes
+ * TODO:
+ * + Rewrite,
+ * + add per IP limit,
+ * + add app policy for new conn,
+ */
+
+#include "mhd_sys_options.h"
+
+#include "daemon_add_conn.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "sys_sockets_types.h"
+#include "sys_sockets_headers.h"
+
+#include "compat_calloc.h"
+
+#include "mhd_panic.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+#include "mhd_public_api.h"
+#include "daemon_logger.h"
+#include "sys_sockets_headers.h"
+#include "sys_ip_headers.h"
+#include "mhd_sockets_macros.h"
+#include "mhd_sockets_funcs.h"
+#include "mhd_mono_clock.h"
+#include "mhd_mempool.h"
+#include "events_process.h"
+
+#include "response_from.h"
+
+
+/**
+ * Set initial internal states for the connection to start reading and
+ * processing incoming data.
+ * @param c the connection to process
+ */
+static void
+connection_set_initial_state (struct MHD_Connection *restrict c)
+{
+  size_t read_buf_size;
+
+  mhd_assert (MHD_CONNECTION_INIT == c->state);
+
+  c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN;
+  c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+
+  memset (&c->rq, 0, sizeof(c->rq));
+  memset (&c->rp, 0, sizeof(c->rp));
+
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+  mhd_DLINKEDL_INIT_LINKS (c, all_conn);
+  mhd_DLINKEDL_INIT_LINKS (c, proc_ready);
+  mhd_DLINKEDL_INIT_LINKS (c, by_timeout);
+  // TODO: set all other pointers manually
+#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
+
+  c->write_buffer = NULL;
+  c->write_buffer_size = 0;
+  c->write_buffer_send_offset = 0;
+  c->write_buffer_append_offset = 0;
+
+  c->continue_message_write_offset = 0;
+
+  c->read_buffer_offset = 0;
+  read_buf_size = c->daemon->conns.cfg.mem_pool_size / 2;
+  c->read_buffer
+    = MHD_pool_allocate (c->pool,
+                         read_buf_size,
+                         false);
+  c->read_buffer_size = read_buf_size;
+}
+
+
+static void
+notify_app_conn (struct MHD_Daemon *restrict daemon,
+                 struct MHD_Connection *restrict connection,
+                 bool closed)
+{
+  (void) daemon, (void) connection, (void) closed;
+  // TODO: implement
+}
+
+
+static void
+connection_clean_destroy (struct MHD_Connection *restrict c,
+                          struct MHD_Daemon *restrict d)
+{
+  mhd_assert (c->daemon == d);
+
+#if defined(MHD_USE_THREADS)
+  if (mhd_D_HAS_THREADS (d) &&
+      (! c->thread_joined) &&
+      (! mhd_thread_handle_ID_join_thread (c->tid)) )
+    MHD_PANIC ("Failed to join a thread.");
+#endif
+
+  // TODO: upgrade support
+
+  MHD_pool_destroy (c->pool);
+
+  // TODO: TLS deinit
+
+
+  /* clean up the connection */
+  notify_app_conn (d,c, true);
+
+  // TODO: per IP limit
+
+  /* Connection must not be in 'process ready' list */
+  mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready));
+
+#ifdef MHD_USE_EPOLL
+  if (mhd_D_IS_USING_EPOLL (d))
+  {
+    /* epoll documentation suggests that closing a FD
+       automatically removes it from the epoll set, but it
+       happens only if all FD copies are closed as well.
+       To avoid problems with new events with pointers to closed
+       connection, remove FD manually */
+    if (0 != epoll_ctl (d->events.data.epoll.e_fd,
+                        EPOLL_CTL_DEL,
+                        c->socket_fd,
+                        NULL))
+      MHD_PANIC ("Failed to remove FD from epoll set.\n");
+  }
+#endif
+  if (NULL != c->rp.response)
+  {
+    mhd_response_dec_use_count (c->rp.response);
+    c->rp.response = NULL;
+  }
+  if (MHD_INVALID_SOCKET != c->socket_fd)
+    (void) mhd_socket_close (c->socket_fd);
+  if (NULL != c->addr)
+    free (c->addr);
+  free (c);
+}
+
+
+/**
+ * Free resources associated with all closed connections.
+ * (destroy responses, free buffers, etc.).  All closed
+ * connections are kept in the "cleanup" doubly-linked list.
+ * @remark To be called only from thread that
+ * process daemon's select()/poll()/etc.
+ *
+ * @param daemon daemon to clean up
+ */
+static void
+MHD_cleanup_connections (struct MHD_Daemon *daemon) // FIXME: Remove??
+{
+  struct MHD_Connection *pos;
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type));
+
+  for (pos = mhd_DLINKEDL_GET_FIRST_D (&(daemon->conns.to_clean));
+       NULL != pos; pos = mhd_DLINKEDL_GET_FIRST_D (&(daemon->conns.to_clean)))
+  {
+    mhd_DLINKEDL_DEL_D (&(daemon->conns.to_clean), pos, all_conn);
+
+    connection_clean_destroy (pos, daemon);
+
+    daemon->conns.count--;
+    daemon->conns.block_new = false;
+  }
+}
+
+
+/**
+ * Do basic preparation work on the new incoming connection.
+ *
+ * This function do all preparation that is possible outside main daemon
+ * thread.
+ * @remark Could be called from any thread.
+ *
+ * @param daemon daemon that manages the connection
+ * @param client_socket socket to manage (MHD will expect
+ *        to receive an HTTP request from this socket next).
+ * @param addr IP address of the client
+ * @param addrlen number of bytes in @a addr
+ * @param external_add indicate that socket has been added externally
+ * @param non_blck indicate that socket in non-blocking mode
+ * @param sk_spipe_supprs indicate that the @a client_socket has
+ *                         set SIGPIPE suppression
+ * @param sk_is_nonip _MHD_YES if this is not a TCP/IP socket
+ * @return pointer to the connection on success, NULL if this daemon could
+ *        not handle the connection (i.e. malloc failed, etc).
+ *        The socket will be closed in case of error; 'errno' is
+ *        set to indicate further details about the error.
+ */
+static enum MHD_StatusCode
+new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
+                         MHD_Socket client_socket,
+                         const struct sockaddr_storage *restrict addr,
+                         size_t addrlen,
+                         bool external_add,
+                         bool non_blck,
+                         bool sk_spipe_supprs,
+                         enum mhd_Tristate sk_is_nonip,
+                         struct MHD_Connection **restrict conn_out)
+{
+  struct MHD_Connection *connection;
+  *conn_out = NULL;
+
+  if (NULL == (connection = mhd_calloc (1, sizeof (struct MHD_Connection))))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE,
+                 "Failed to allocate memory for the new connection");
+    mhd_socket_close (client_socket);
+    return MHD_SC_CONNECTION_MALLOC_FAILURE;
+  }
+
+  if (! external_add)
+  {
+    connection->sk_corked = mhd_T_NO;
+    connection->sk_nodelay = mhd_T_NO;
+  }
+  else
+  {
+    connection->sk_corked = mhd_T_MAYBE;
+    connection->sk_nodelay = mhd_T_MAYBE;
+  }
+
+  if (0 < addrlen)
+  {
+    if (NULL == (connection->addr = malloc (addrlen)))
+    {
+      MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE,
+                   "Failed to allocate memory for the new connection");
+      mhd_socket_close (client_socket);
+      free (connection);
+      return MHD_SC_CONNECTION_MALLOC_FAILURE;
+    }
+    memcpy (connection->addr,
+            addr,
+            addrlen);
+  }
+  else
+    connection->addr = NULL;
+  connection->addr_len = addrlen;
+  connection->socket_fd = client_socket;
+  connection->sk_nonblck = non_blck;
+  connection->is_nonip = sk_is_nonip;
+  connection->sk_spipe_suppress = sk_spipe_supprs;
+#ifdef MHD_USE_THREADS
+  mhd_thread_handle_ID_set_invalid (&connection->tid);
+#endif /* MHD_USE_THREADS */
+  connection->daemon = daemon;
+  connection->connection_timeout_ms = daemon->conns.cfg.timeout;
+  connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+  if (0 != connection->connection_timeout_ms)
+    connection->last_activity = MHD_monotonic_msec_counter ();
+
+  // TODO: init TLS
+  *conn_out = connection;
+
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Finally insert the new connection to the list of connections
+ * served by the daemon and start processing.
+ * @remark To be called only from thread that process
+ * daemon's select()/poll()/etc.
+ *
+ * @param daemon daemon that manages the connection
+ * @param connection the newly created connection
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+static enum MHD_StatusCode
+new_connection_process_ (struct MHD_Daemon *restrict daemon,
+                         struct MHD_Connection *restrict connection)
+{
+  enum MHD_StatusCode res;
+  mhd_assert (connection->daemon == daemon);
+
+  res = MHD_SC_OK;
+  /* Allocate memory pool in the processing thread so
+   * intensively used memory area is allocated in "good"
+   * (for the thread) memory region. It is important with
+   * NUMA and/or complex cache hierarchy. */
+  connection->pool = MHD_pool_create (daemon->conns.cfg.mem_pool_size);
+  if (NULL == connection->pool)
+  { /* 'pool' creation failed */
+    MHD_LOG_MSG (daemon, MHD_SC_POOL_MALLOC_FAILURE, \
+                 "Failed to allocate memory for the connection memory pool.");
+    res = MHD_SC_POOL_MALLOC_FAILURE;
+  }
+  else
+  { /* 'pool' creation succeed */
+
+    if (daemon->conns.block_new)
+    { /* Connections limit */
+      MHD_LOG_MSG (daemon, MHD_SC_LIMIT_CONNECTIONS_REACHED, \
+                   "Server reached connection limit. " \
+                   "Closing inbound connection.");
+      res = MHD_SC_LIMIT_CONNECTIONS_REACHED;
+    }
+    else
+    { /* Have space for new connection */
+      mhd_assert (daemon->conns.count < daemon->conns.cfg.count_limit);
+      daemon->conns.count++;
+      daemon->conns.block_new =
+        (daemon->conns.count >= daemon->conns.cfg.count_limit);
+      mhd_DLINKEDL_INS_LAST (&(daemon->conns), connection, all_conn);
+      if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != daemon->wmode_int)
+        mhd_DLINKEDL_INS_FIRST_D (&(daemon->conns.def_timeout), \
+                                  connection, by_timeout);
+
+      connection_set_initial_state (connection);
+
+      notify_app_conn (daemon, connection, false);
+
+#ifdef MHD_USE_THREADS
+      if (mhd_DAEMON_TYPE_LISTEN_ONLY == daemon->threading.d_type)
+      {
+        mhd_assert ((mhd_POLL_TYPE_SELECT == daemon->events.poll_type) || \
+                    (mhd_POLL_TYPE_POLL == daemon->events.poll_type));
+        if (! mhd_create_named_thread (&connection->tid,
+                                       "MHD-connection",
+                                       daemon->threading.cfg.stack_size,
+                                       &mhd_worker_connection,
+                                       connection))
+        {
+#ifdef EAGAIN
+          if (EAGAIN == errno)
+          {
+            MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED,
+                         "Failed to create a new thread because it would "
+                         "have exceeded the system limit on the number of "
+                         "threads or no system resources available.");
+            res = MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED;
+          }
+          else
+#endif /* EAGAIN */
+          if (1)
+          {
+            MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE,
+                         "Failed to create a thread.");
+            res = MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE;
+          }
+        }
+        else               /* New thread has been created successfully */
+          return MHD_SC_OK;  /* *** Function success exit point *** */
+      }
+      else
+#else  /* ! MHD_USE_THREADS */
+      if (1)
+#endif /* ! MHD_USE_THREADS */
+      { /* No 'thread-per-connection' */
+#ifdef MHD_USE_THREADS
+        connection->tid = daemon->threading.tid;
+#endif /* MHD_USE_THREADS */
+#ifdef MHD_USE_EPOLL
+        if (mhd_POLL_TYPE_EPOLL == daemon->events.poll_type)
+        {
+          struct epoll_event event;
+
+          event.events = EPOLLIN | EPOLLOUT | EPOLLET;
+          event.data.ptr = connection;
+          if (0 != epoll_ctl (daemon->epoll_fd,
+                              EPOLL_CTL_ADD,
+                              connection->socket_fd,
+                              &event))
+          {
+            MHD_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED,
+                         "Failed to add connection socket .");
+            res = MHD_SC_EPOLL_CTL_ADD_FAILED;
+          }
+          else
+          {
+            if (0) // TODO: implement turbo
+            {
+              connection->sk_ready = mhd_SOCKET_NET_STATE_RECV_READY
+                                     | mhd_SOCKET_NET_STATE_SEND_READY;
+              mhd_DLINKEDL_INS_LAST (&(daemon->events), connection, proc_ready);
+            }
+            return MHD_SC_OK;  /* *** Function success exit point *** */
+          }
+        }
+        else /* No 'epoll' */
+#endif /* MHD_USE_EPOLL */
+        return MHD_SC_OK;    /* *** Function success exit point *** */
+      }
+
+      /* ** Below is a cleanup path ** */
+      mhd_assert (MHD_SC_OK != res);
+      notify_app_conn (daemon, connection, true);
+
+      if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != daemon->wmode_int)
+        mhd_DLINKEDL_DEL_D (&(daemon->conns.def_timeout), \
+                            connection, by_timeout);
+
+      mhd_DLINKEDL_DEL (&(daemon->conns), connection, all_conn);
+      daemon->conns.count--;
+      daemon->conns.block_new = false;
+    }
+    MHD_pool_destroy (connection->pool);
+  }
+  /* Free resources allocated before the call of this functions */
+
+  // TODO: TLS support
+
+  // TODO: per IP limit
+
+  if (NULL != connection->addr)
+    free (connection->addr);
+  (void) mhd_socket_close (connection->socket_fd);
+  free (connection);
+  mhd_assert (MHD_SC_OK != res);
+  return res;  /* *** Function failure exit point *** */
+}
+
+
+/**
+ * The given client socket will be managed (and closed!) by MHD after
+ * this call and must no longer be used directly by the application
+ * afterwards.
+ *
+ * @param daemon daemon that manages the connection
+ * @param client_socket socket to manage (MHD will expect
+ *        to receive an HTTP request from this socket next).
+ * @param addr IP address of the client
+ * @param addrlen number of bytes in @a addr
+ * @param external_add perform additional operations needed due
+ *        to the application calling us directly
+ * @param non_blck indicate that socket in non-blocking mode
+ * @param sk_spipe_supprs indicate that the @a client_socket has
+ *                         set SIGPIPE suppression
+ * @param sk_is_nonip _MHD_YES if this is not a TCP/IP socket
+ * @return #MHD_YES on success, #MHD_NO if this daemon could
+ *        not handle the connection (i.e. malloc failed, etc).
+ *        The socket will be closed in any case; 'errno' is
+ *        set to indicate further details about the error.
+ */
+static enum MHD_StatusCode
+internal_add_connection (struct MHD_Daemon *daemon,
+                         MHD_Socket client_socket,
+                         const struct sockaddr_storage *addr,
+                         size_t addrlen,
+                         bool external_add,
+                         bool non_blck,
+                         bool sk_spipe_supprs,
+                         enum mhd_Tristate sk_is_nonip)
+{
+  struct MHD_Connection *connection;
+  enum MHD_StatusCode res;
+
+  /* Direct add to master daemon could never happen. */
+  mhd_assert (! mhd_D_HAS_WORKERS (daemon));
+
+  if (! mhd_FD_FITS_DAEMON (d, client_socket))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE, \
+                 "New connection socket descriptor value is too large for " \
+                 "the daemon configuration.");
+    (void) mhd_socket_close (client_socket);
+    return MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE;
+  }
+
+  if ((! non_blck) &&
+      ((mhd_POLL_TYPE_EPOLL == daemon->events.poll_type) ||
+       (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == daemon->wmode_int)))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_NONBLOCKING_REQUIRED, \
+                 "The daemon configuration requires non-blocking sockets, "
+                 "the new socket has not been added.");
+    (void) mhd_socket_close (client_socket);
+    return MHD_SC_NONBLOCKING_REQUIRED;
+  }
+  res = new_connection_prepare_ (daemon,
+                                 client_socket,
+                                 addr, addrlen,
+                                 external_add,
+                                 non_blck,
+                                 sk_spipe_supprs,
+                                 sk_is_nonip,
+                                 &connection);
+  if (MHD_SC_OK != res)
+    return res;
+
+  if (external_add) // TODO: support thread-unsafe
+  {
+    mhd_assert (0 && "Not implemented yet");
+#if 0 // TODO: support externally added
+    /* Connection is added externally and MHD is thread safe mode. */
+    MHD_mutex_lock_chk_ (&daemon->new_connections_mutex);
+    DLL_insert (daemon->new_connections_head,
+                daemon->new_connections_tail,
+                connection);
+    daemon->have_new = true;
+    MHD_mutex_unlock_chk_ (&daemon->new_connections_mutex);
+
+    /* The rest of connection processing must be handled in
+     * the daemon thread. */
+    if ((mhd_ITC_IS_VALID (daemon->itc)) &&
+        (! mhd_itc_activate (daemon->itc, "n")))
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _ ("Failed to signal new connection via inter-thread " \
+                   "communication channel.\n"));
+#endif
+    }
+    return MHD_YES;
+#endif
+  }
+
+  return new_connection_process_ (daemon, connection);
+}
+
+
+#if 0 // TODO: implement
+static void
+new_connections_list_process_ (struct MHD_Daemon *daemon)
+{
+  struct MHD_Connection *local_head;
+  struct MHD_Connection *local_tail;
+  mhd_assert (daemon->events.act_req);
+  // mhd_assert (MHD_D_IS_THREAD_SAFE_ (daemon));
+
+  /* Detach DL-list of new connections from the daemon for
+   * following local processing. */
+  MHD_mutex_lock_chk_ (&daemon->new_connections_mutex);
+  mhd_assert (NULL != daemon->new_connections_head);
+  local_head = daemon->new_connections_head;
+  local_tail = daemon->new_connections_tail;
+  daemon->new_connections_head = NULL;
+  daemon->new_connections_tail = NULL;
+  daemon->have_new = false;
+  MHD_mutex_unlock_chk_ (&daemon->new_connections_mutex);
+  (void) local_head; /* Mute compiler warning */
+
+  /* Process new connections in FIFO order. */
+  do
+  {
+    struct MHD_Connection *c;   /**< Currently processed connection */
+
+    c = local_tail;
+    DLL_remove (local_head,
+                local_tail,
+                c);
+    mhd_assert (daemon == c->daemon);
+    if (MHD_NO == new_connection_process_ (daemon, c))
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _ ("Failed to start serving new connection.\n"));
+#endif
+      (void) 0;
+    }
+  } while (NULL != local_tail);
+
+}
+
+
+#endif
+
+
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_ (4) MHD_EXTERN_ enum MHD_StatusCode
+MHD_daemon_add_connection (struct MHD_Daemon *daemon,
+                           MHD_Socket client_socket,
+                           size_t addrlen,
+                           const struct sockaddr *addr,
+                           void *connection_cntx)
+{
+  bool sk_nonbl;
+  bool sk_spipe_supprs;
+  struct sockaddr_storage addrstorage;
+  // TODO: global daemon lock for external events
+  (void) connection_cntx; // FIXME: is it really needed? Where it is used?
+
+  if ((! mhd_D_HAS_THREADS (daemon)) &&
+      (daemon->conns.block_new))
+    MHD_cleanup_connections (daemon);
+
+  if (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type)
+      && daemon->conns.block_new)
+    return MHD_SC_LIMIT_CONNECTIONS_REACHED;
+
+  if (0 != addrlen)
+  {
+    if (AF_INET == addr->sa_family)
+    {
+      if (sizeof(struct sockaddr_in) > addrlen)
+      {
+        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+                     "MHD_add_connection() has been called with " \
+                     "incorrect 'addrlen' value.");
+        return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
+      }
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+      if ((0 != addr->sa_len) &&
+          (sizeof(struct sockaddr_in) > (size_t) addr->sa_len) )
+      {
+        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+                     "MHD_add_connection() has been called with " \
+                     "non-zero value of 'sa_len' member of " \
+                     "'struct sockaddr' which does not match 'sa_family'.");
+        return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
+      }
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+    }
+#ifdef HAVE_INET6
+    if (AF_INET6 == addr->sa_family)
+    {
+      if (sizeof(struct sockaddr_in6) > addrlen)
+      {
+        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+                     "MHD_add_connection() has been called with " \
+                     "incorrect 'addrlen' value.");
+        return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
+      }
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+      if ((0 != addr->sa_len) &&
+          (sizeof(struct sockaddr_in6) > (size_t) addr->sa_len) )
+      {
+        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+                     "MHD_add_connection() has been called with " \
+                     "non-zero value of 'sa_len' member of " \
+                     "'struct sockaddr' which does not match 'sa_family'.");
+        return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
+      }
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+    }
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+    if ((0 != addr->sa_len) &&
+        (addrlen > (size_t) addr->sa_len))
+      addrlen = (size_t) addr->sa_len;   /* Use safest value */
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+#endif /* HAVE_INET6 */
+  }
+
+  if (! mhd_socket_nonblocking (client_socket))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
+                 "Failed to set nonblocking mode on the new client socket.");
+    sk_nonbl = false;
+  }
+  else
+    sk_nonbl = true;
+
+#ifndef MHD_WINSOCK_SOCKETS
+  sk_spipe_supprs = false;
+#else  /* MHD_WINSOCK_SOCKETS */
+  sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+#endif /* MHD_WINSOCK_SOCKETS */
+#if defined(MHD_socket_nosignal_)
+  if (! sk_spipe_supprs)
+    sk_spipe_supprs = MHD_socket_nosignal_ (client_socket);
+  if (! sk_spipe_supprs)
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, \
+                 "Failed to suppress SIGPIPE on the new client socket.");
+#ifndef MSG_NOSIGNAL
+    /* Application expects that SIGPIPE will be suppressed,
+     * but suppression failed and SIGPIPE cannot be suppressed with send(). */
+    if (! daemon->sigpipe_blocked)
+    {
+      int err = MHD_socket_get_error_ ();
+      MHD_socket_close_ (client_socket);
+      MHD_socket_fset_error_ (err);
+      return MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED;
+    }
+#endif /* MSG_NOSIGNAL */
+  }
+#endif /* MHD_socket_nosignal_ */
+
+  if (1) // TODO: implement turbo
+  {
+    if (! mhd_socket_noninheritable (client_socket))
+      MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
+                   "Failed to set noninheritable mode on new client socket.");
+  }
+
+  /* Copy to sockaddr_storage structure to avoid alignment problems */
+  if (0 < addrlen)
+    memcpy (&addrstorage, addr, addrlen);
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+  addrstorage.ss_len = addrlen; /* Force set the right length */
+#endif /* HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */
+
+#if defined(MHD_USE_THREADS)
+  if (mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type))
+  {
+    unsigned int i;
+    /* have a pool, try to find a pool with capacity; we use the
+       socket as the initial offset into the pool for load
+       balancing */
+    unsigned int offset;
+#ifdef MHD_WINSOCK_SOCKETS
+    uint_fast64_t osb = (uint_fast64_t) client_socket;
+    osb ^= (((uint_fast64_t) client_socket) >> 9);
+    osb ^= (((uint_fast64_t) client_socket) >> 18);
+    osb ^= (((uint_fast64_t) client_socket) >> 27);
+    osb ^= (((uint_fast64_t) client_socket) >> 36);
+    osb ^= (((uint_fast64_t) client_socket) >> 45);
+    osb ^= (((uint_fast64_t) client_socket) >> 54);
+    osb ^= (((uint_fast64_t) client_socket) >> 63);
+    offset = (unsigned int) osb;
+#else
+    offset = (unsigned int) client_socket;
+#endif
+
+    for (i = 0; i < daemon->threading.hier.pool.num; ++i)
+    {
+      struct MHD_Daemon *const volatile worker =
+        daemon->threading.hier.pool.workers
+        + (i + offset) % daemon->threading.hier.pool.num;
+      if (worker->conns.block_new)
+        continue;
+      return internal_add_connection (worker,
+                                      client_socket,
+                                      &addrstorage,
+                                      addrlen,
+                                      true,
+                                      sk_nonbl,
+                                      sk_spipe_supprs,
+                                      mhd_T_MAYBE);
+    }
+
+    /* all pools are at their connection limit, must refuse */
+    return MHD_SC_LIMIT_CONNECTIONS_REACHED;
+  }
+#endif /* MHD_USE_THREADS */
+
+  return internal_add_connection (daemon,
+                                  client_socket,
+                                  &addrstorage,
+                                  addrlen,
+                                  true,
+                                  sk_nonbl,
+                                  sk_spipe_supprs,
+                                  mhd_T_MAYBE);
+}
+
+
+MHD_INTERNAL enum mhd_DaemonAcceptResult
+mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
+{
+  struct sockaddr_storage addrstorage;
+  socklen_t addrlen;
+  MHD_Socket s;
+  MHD_Socket fd;
+  bool sk_nonbl;
+  bool sk_spipe_supprs;
+  bool sk_cloexec;
+  enum mhd_Tristate sk_non_ip;
+#if defined(_DEBUG) && defined (USE_ACCEPT4)
+  const bool use_accept4 = ! daemon->dbg.avoid_accept4;
+#elif defined (USE_ACCEPT4)
+  static const bool use_accept4 = true;
+#else  /* ! USE_ACCEPT4 && ! _DEBUG */
+  static const bool use_accept4 = false;
+#endif /* ! USE_ACCEPT4 && ! _DEBUG */
+
+#ifdef MHD_USE_THREADS
+  mhd_assert ((! mhd_D_HAS_THREADS (daemon)) || \
+              mhd_thread_handle_ID_is_current_thread (daemon->threading.tid));
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type));
+#endif /* MHD_USE_THREADS */
+
+  fd = daemon->net.listen.fd;
+  mhd_assert (MHD_INVALID_SOCKET != fd);
+
+  addrlen = (socklen_t) sizeof (addrstorage);
+  memset (&addrstorage,
+          0,
+          (size_t) addrlen);
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+  addrstorage.ss_len = addrlen;
+#endif /* HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */
+
+  /* Initialise with default values to avoid compiler warnings */
+  sk_nonbl = false;
+  sk_spipe_supprs = false;
+  sk_cloexec = false;
+  s = MHD_INVALID_SOCKET;
+
+#ifdef USE_ACCEPT4
+  if (use_accept4 &&
+      (MHD_INVALID_SOCKET !=
+       (s = accept4 (fd,
+                     (struct sockaddr *) &addrstorage,
+                     &addrlen,
+                     SOCK_CLOEXEC_OR_ZERO | SOCK_NONBLOCK_OR_ZERO
+                     | SOCK_NOSIGPIPE_OR_ZERO))))
+  {
+    sk_nonbl = (SOCK_NONBLOCK_OR_ZERO != 0);
+#ifndef MHD_WINSOCK_SOCKETS
+    sk_spipe_supprs = (SOCK_NOSIGPIPE_OR_ZERO != 0);
+#else  /* MHD_WINSOCK_SOCKETS */
+    sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+#endif /* MHD_WINSOCK_SOCKETS */
+    sk_cloexec = (SOCK_CLOEXEC_OR_ZERO != 0);
+  }
+#endif /* USE_ACCEPT4 */
+#if defined(_DEBUG) || ! defined(USE_ACCEPT4)
+  if (! use_accept4 &&
+      (MHD_INVALID_SOCKET !=
+       (s = accept (fd,
+                    (struct sockaddr *) &addrstorage,
+                    &addrlen))))
+  {
+#ifdef MHD_ACCEPT_INHERIT_NONBLOCK
+    sk_nonbl = daemon->listen_nonblk;
+#else  /* ! MHD_ACCEPT_INHERIT_NONBLOCK */
+    sk_nonbl = false;
+#endif /* ! MHD_ACCEPT_INHERIT_NONBLOCK */
+#ifndef MHD_WINSOCK_SOCKETS
+    sk_spipe_supprs = false;
+#else  /* MHD_WINSOCK_SOCKETS */
+    sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+#endif /* MHD_WINSOCK_SOCKETS */
+    sk_cloexec = false;
+  }
+#endif /* _DEBUG || !USE_ACCEPT4 */
+
+  if (MHD_INVALID_SOCKET == s)
+  { /* This could be a common occurrence with multiple worker threads */
+    const int err = mhd_SCKT_GET_LERR ();
+
+    if (mhd_SCKT_ERR_IS_EINVAL (err))
+      return mhd_DAEMON_ACCEPT_NO_MORE_PENDING; /* can happen during shutdown */   // FIXME: remove?
+    if (mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT (err))
+      return mhd_DAEMON_ACCEPT_NO_MORE_PENDING;   /* do not print error if client just disconnects early */
+    if (mhd_SCKT_ERR_IS_EINTR (err))
+      return mhd_DAEMON_ACCEPT_SKIPPED;
+    if (mhd_SCKT_ERR_IS_EAGAIN (err))
+      return mhd_DAEMON_ACCEPT_NO_MORE_PENDING;
+    if (mhd_SCKT_ERR_IS_LOW_RESOURCES (err) )
+    {
+      /* system/process out of resources */
+      if (0 == daemon->conns.count)
+      {
+        /* Not setting 'block_new' flag, as there is no way it
+           would ever be cleared.  Instead trying to produce
+           bit fat ugly warning. */
+        MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY, \
+                     "Hit process or system resource limit at FIRST " \
+                     "connection. This is really bad as there is no sane " \
+                     "way to proceed. Will try busy waiting for system " \
+                     "resources to become magically available.");
+      }
+      else
+      {
+        daemon->conns.block_new = true;
+        MHD_LOG_PRINT (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED, \
+                       MHD_LOG_FMT ("Hit process or system resource limit " \
+                                    "at %u connections, temporarily " \
+                                    "suspending accept(). Consider setting " \
+                                    "a lower MHD_OPTION_CONNECTION_LIMIT."), \
+                       daemon->conns.count);
+      }
+      return mhd_DAEMON_ACCEPT_FAILED;
+    }
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY,
+                 "Error accepting connection.");
+    return mhd_DAEMON_ACCEPT_FAILED;
+  }
+
+  if (mhd_FD_FITS_DAEMON (daemon, s))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_OUTSIDE_OF_SET_RANGE, \
+                 "The accepted socket has value outside of allowed range.");
+    (void) mhd_socket_close (s);
+    return mhd_DAEMON_ACCEPT_FAILED;
+  }
+  if (mhd_SOCKET_TYPE_IP == daemon->net.listen.type)
+    sk_non_ip = mhd_T_NO;
+  else if (mhd_SOCKET_TYPE_UNKNOWN == daemon->net.listen.type)
+    sk_non_ip = mhd_T_MAYBE;
+  else
+    sk_non_ip = mhd_T_YES;
+  if (0 >= addrlen)
+  {
+    if (mhd_SOCKET_TYPE_IP == daemon->net.listen.type)
+      MHD_LOG_MSG (daemon, MHD_SC_ACCEPTED_UNKNOWN_TYPE, \
+                   "Accepted socket has non-positive length of the address. " \
+                   "Processing the new socket as a socket with " \
+                   "unknown type.");
+    addrlen = 0;
+    sk_non_ip = mhd_T_MAYBE;
+  }
+  else if (((socklen_t) sizeof (addrstorage)) < addrlen)
+  {
+    /* Should not happen as 'sockaddr_storage' must be large enough to
+     * store any address supported by the system. */
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPTED_SOCKADDR_TOO_LARGE, \
+                 "Accepted socket address is larger than expected by " \
+                 "system headers. Processing the new socket as a socket with " \
+                 "unknown type.");
+    addrlen = 0;
+    sk_non_ip = mhd_T_MAYBE; /* IP-type addresses must fit */
+  }
+  else if (mhd_T_MAYBE == sk_non_ip)
+  {
+    if (AF_INET == ((struct sockaddr *) &addrstorage)->sa_family)
+      sk_non_ip = mhd_T_NO;
+#ifdef HAVE_INET6
+    else if (AF_INET6 == ((struct sockaddr *) &addrstorage)->sa_family)
+      sk_non_ip = mhd_T_NO;
+#endif /* HAVE_INET6 */
+  }
+
+  if (! sk_nonbl && ! mhd_socket_nonblocking (s))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
+                 "Failed to set nonblocking mode on incoming connection " \
+                 "socket.");
+  }
+  else
+    sk_nonbl = true;
+
+  if (! sk_cloexec && ! mhd_socket_noninheritable (s))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
+                 "Failed to set non-inheritable mode on incoming connection " \
+                 "socket.");
+  }
+
+#if defined(MHD_socket_nosignal_)
+  if (! sk_spipe_supprs && ! MHD_socket_nosignal_ (s))
+  {
+    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED,
+                 "Failed to suppress SIGPIPE on incoming connection " \
+                 "socket.");
+#ifndef MSG_NOSIGNAL
+    /* Application expects that SIGPIPE will be suppressed,
+     * but suppression failed and SIGPIPE cannot be suppressed with send(). */
+    if (! daemon->sigpipe_blocked)
+    {
+      (void) MHD_socket_close_ (s);
+      return MHD_NO;
+    }
+#endif /* MSG_NOSIGNAL */
+  }
+  else
+    sk_spipe_supprs = true;
+#endif /* MHD_socket_nosignal_ */
+  return (MHD_SC_OK == internal_add_connection (daemon,
+                                                s,
+                                                &addrstorage,
+                                                (size_t) addrlen,
+                                                false,
+                                                sk_nonbl,
+                                                sk_spipe_supprs,
+                                                sk_non_ip)) ?
+         mhd_DAEMON_ACCEPT_FAILED : mhd_DAEMON_ACCEPT_SUCCESS;
+}

+ 83 - 0
src/mhd2/daemon_add_conn.h

@@ -0,0 +1,83 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_add_conn.h
+ * @brief  The declaration of internal functions for adding new connections
+ * @author Karlson2k (Evgeny Grin)
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#ifndef MHD_DAEMON_ADD_CONN
+#define MHD_DAEMON_ADD_CONN 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Daemon; /* Forward declaration */
+
+/**
+ * The result of the accepting of the new connection
+ */
+enum mhd_DaemonAcceptResult
+{
+  /**
+   * New connection has been accepted successfully.
+   * Probably more connections are pending to be accepted.
+   */
+  mhd_DAEMON_ACCEPT_SUCCESS = 0
+  ,
+  /**
+   * New connection has been skipped for some reason.
+   * It is OK to try to accept more connections right now.
+   */
+  mhd_DAEMON_ACCEPT_SKIPPED = 1 << 0
+  ,
+  /**
+   * No more new connections are pending, the listen backlog is empty
+   */
+  mhd_DAEMON_ACCEPT_NO_MORE_PENDING = 1 << 1
+  ,
+  /**
+   * Connection accept failed, but the listen backlog could be not empty.
+   * Do not try to accept more connection right now.
+   */
+  mhd_DAEMON_ACCEPT_FAILED = 1 << 2
+};
+
+
+/**
+ * Accept an incoming connection and create the MHD_Connection object for
+ * it.  This function also enforces policy by way of checking with the
+ * accept policy callback.
+ * @remark To be called only from thread that process
+ * daemon's select()/poll()/etc.
+ *
+ * @param daemon handle with the listen socket
+ * @return a #mhd_DaemonAcceptResult value
+ */
+MHD_INTERNAL enum mhd_DaemonAcceptResult
+mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon);
+
+
+#endif /* ! MHD_DAEMON_ADD_CONN */

+ 112 - 0
src/mhd2/daemon_create.c

@@ -0,0 +1,112 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_create.c
+ * @brief  The implementation of the MHD_daemon_create()
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "sys_malloc.h"
+
+#include "mhd_public_api.h"
+
+#include "compat_calloc.h"
+
+#include "mhd_daemon.h"
+#include "daemon_options.h"
+
+#include "daemon_logger_default.h"
+
+#include "mhd_lib_init.h"
+
+
+MHD_FN_MUST_CHECK_RESULT_ MHD_EXTERN_ struct MHD_Daemon *
+MHD_daemon_create (MHD_RequestCallback req_cb,
+                   void *req_cb_cls)
+{
+  struct MHD_Daemon *d;
+  struct DaemonOptions *s;
+
+  MHD_GLOBAL_INIT_CHECK ();
+
+  if (NULL == req_cb)
+    return NULL;
+
+  d = (struct MHD_Daemon *) mhd_calloc (1, sizeof(struct MHD_Daemon));
+  if (NULL == d)
+    return NULL;
+
+  s = (struct DaemonOptions *) mhd_calloc (1, sizeof(struct DaemonOptions));
+  if (NULL == s)
+  {
+    free (d);
+    return NULL;
+  }
+  /* calloc() does not guarantee that floating point values and pointers
+     are initialised to zero and NULL (respectfully). */
+  /* Any floating point and pointer members must be initialised manually here */
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+  s->bind_sa.v_sa = NULL;
+  s->tls_key_cert.v_mem_key = NULL;
+  s->tls_key_cert.v_mem_cert = NULL;
+  s->tls_key_cert.v_mem_pass = NULL;
+  s->tls_client_ca = NULL;
+  s->tls_psk_callback.v_psk_cb = NULL;
+  s->tls_psk_callback.v_psk_cb_cls = NULL;
+  s->accept_policy.v_apc = NULL;
+  s->accept_policy.v_apc_cls = NULL;
+  s->early_uri_logger.v_cb = NULL;
+  s->early_uri_logger.v_cls = NULL;
+  s->daemon_ready_callback.v_cb = NULL;
+  s->daemon_ready_callback.v_cb_cls = NULL;
+  s->notify_connection.v_ncc = NULL;
+  s->notify_connection.v_cls = NULL;
+  s->notify_stream.v_nsc = NULL;
+  s->notify_stream.v_cls = NULL;
+  s->random_entropy.v_buf = NULL;
+
+  d->log_params.v_log_cb = NULL; /* optional */
+#endif /* !HAVE_NULL_PTR_ALL_ZEROS */
+
+  s->listen_socket = MHD_INVALID_SOCKET;
+  s->fd_number_limit = MHD_INVALID_SOCKET;
+
+  d->log_params.v_log_cb = mhd_logger_default;
+  d->req_cb = req_cb;
+  d->req_cb_cls = req_cb_cls;
+  d->settings = s;
+
+  return d;
+}
+
+
+/* This is a workaround for GCC/binutils bug.
+ * To make sure that initialisation functions are called when MHD is used as
+ * a static library, put initialisation functions to the file with function
+ * that always referred/used by application/user of the library.
+ * If application does not refer any function, the initialiser call could be
+ * missed for the static library.
+ */
+#define MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C 1
+#include "mhd_lib_init_impl.h"

+ 51 - 0
src/mhd2/daemon_funcs.c

@@ -0,0 +1,51 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_funcs.c
+ * @brief  The implementation of internal daemon-related functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#ifdef MHD_USE_THREADS
+
+#include "daemon_funcs.h"
+
+#include "mhd_itc.h"
+#include "mhd_daemon.h"
+#include "daemon_logger.h"
+
+MHD_INTERNAL bool
+mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+  if (! mhd_itc_activate (d->threading.itc))
+  {
+    MHD_LOG_MSG (d, MHD_SC_ITC_USE_FAILED, \
+                 "Failed to communicate by ITC with the daemon thread.");
+    return false;
+  }
+  return true;
+}
+
+
+#endif /* MHD_USE_THREADS */

+ 51 - 0
src/mhd2/daemon_funcs.h

@@ -0,0 +1,51 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_funcs.h
+ * @brief  The declarations of internal daemon-related functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DAEMON_FUNCS_H
+#define MHD_DAEMON_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef MHD_USE_THREADS
+
+#include "sys_bool_type.h"
+
+struct MHD_Daemon; /* forward declaration */
+
+
+/**
+ * Trigger daemon ITC.
+ * This should cause daemon's thread to stop waiting for the network events
+ * and process pending information
+ * @param d the daemon object, ITC should be initialised
+ * @return true if succeed, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d);
+
+#endif /* MHD_USE_THREADS */
+
+#endif /* ! MHD_DAEMON_FUNCS_H */

+ 53 - 0
src/mhd2/daemon_logger.c

@@ -0,0 +1,53 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_logger.c
+ * @brief  The implementation of the logger function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_LOG_FUNCTIONALITY
+#include <stdarg.h>
+#include "sys_null_macro.h"
+#include "mhd_daemon.h"
+#include "daemon_logger.h"
+
+
+MHD_INTERNAL void
+mhd_logger (struct MHD_Daemon *daemon,
+            enum MHD_StatusCode sc,
+            const char *fm,
+            ...)
+{
+  if (NULL != daemon->log_params.v_log_cb)
+  {
+    va_list vargs;
+    va_start (vargs, fm);
+    daemon->log_params.v_log_cb (daemon->log_params.v_log_cb_cls,
+                                 sc, fm, vargs);
+    va_end (vargs);
+  }
+}
+
+
+#endif /* ! HAVE_LOG_FUNCTIONALITY */

+ 133 - 0
src/mhd2/daemon_logger.h

@@ -0,0 +1,133 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_logger.h
+ * @brief  The declaration of the logger function and relevant macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DAEMON_LOGGER_H
+#define MHD_DAEMON_LOGGER_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_LOG_FUNCTIONALITY
+
+#include "mhd_public_api.h" /* For enum MHD_StatusCode */
+
+struct MHD_Daemon; /* Forward declaration */
+
+/* Do not use this function directly, use wrapper macros below */
+/**
+ * The daemon logger function.
+ * @param daemon the daemon handle
+ * @param sc the status code of the event
+ * @param fm the format string ('printf()'-style)
+ * @ingroup logging
+ */
+MHD_INTERNAL void
+mhd_logger (struct MHD_Daemon *daemon,
+            enum MHD_StatusCode sc,
+            const char *fm,
+            ...);
+
+/**
+ * Log a single message.
+ *
+ * The @a msg is a 'printf()' string, treated as format specifiers string.
+ * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format
+ * specifier symbol.
+ */
+#define MHD_LOG_MSG(daemon,sc,msg) mhd_logger (daemon,sc,msg)
+
+/**
+ * Format message and log it
+ *
+ * Always use with #MHD_LOG_FMT() for the format string.
+ */
+#define MHD_LOG_PRINT mhd_logger
+
+/**
+ * The wrapper macro for the format string to be used for format parameter for
+ * the #MHD_LOG_FMT() macro
+ */
+#define MHD_LOG_FMT(format_string) format_string
+
+#else  /* ! HAVE_LOG_FUNCTIONALITY */
+
+
+#ifdef HAVE_MACRO_VARIADIC
+
+/**
+ * Log a single message.
+ *
+ * The @a msg is a 'printf()' string, treated as format specifiers string.
+ * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format
+ * specifier symbol.
+ */
+#define MHD_LOG_MSG(daemon,sc,msg)  do { (void) daemon; } while (0)
+
+/**
+ * Format message and log it
+ *
+ * Always use with #MHD_LOG_FMT() for the format string.
+ */
+#define MHD_LOG_PRINT(daemon,sc,fm,...)  do { (void) daemon; } while (0)
+
+#else  /* ! HAVE_MACRO_VARIADIC */
+
+#include "sys_base_types.h" /* For NULL */
+
+/**
+ * Format message and log it
+ *
+ * Always use with #MHD_LOG_FMT() for the format string.
+ */
+MHD_static_inline_ void
+MHD_LOG_PRINT (struct MHD_Daemon *daemon,
+               enum MHD_StatusCode sc,
+               const char *fm,
+               ...)
+{
+  (void) daemon; (void) sc; (void) fm;
+}
+
+
+/**
+ * Log a single message.
+ *
+ * The @a msg is a 'printf()' string, treated as format specifiers string.
+ * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format
+ * specifier symbol.
+ */
+#define MHD_LOG_MSG(daemon,sc,msg) MHD_LOG_PRINT (daemon,sc,NULL)
+
+#endif /* ! HAVE_MACRO_VARIADIC */
+
+/**
+ * The wrapper macro for the format string to be used for format parameter for
+ * the #MHD_LOG_FMT() macro
+ */
+#define MHD_LOG_FMT(format_string) NULL
+
+#endif /* ! HAVE_LOG_FUNCTIONALITY */
+
+#endif /* ! MHD_DAEMON_LOGGER_H */

+ 57 - 0
src/mhd2/daemon_logger_default.c

@@ -0,0 +1,57 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_logger_default.h
+ * @brief  The declaration of the default logger function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_LOG_FUNCTIONALITY
+
+#include <stdio.h>
+
+#include "daemon_logger_default.h"
+#include "mhd_assert.h" /* For NDEBUG macro */
+
+MHD_INTERNAL void
+mhd_logger_default (void *cls,
+                    enum MHD_StatusCode sc,
+                    const char *fm,
+                    va_list ap)
+{
+  int res;
+  (void) cls; /* Not used by default logger */
+  (void) sc;  /* Not used by default logger */
+
+  res = vfprintf (stderr, fm, ap);
+  (void) res; /* The result of vfprintf() call is ignored */
+  res = fprintf (stderr, "\n");
+  (void) res; /* The result of vfprintf() call is ignored */
+#ifndef NDEBUG
+  res = fflush (stderr);
+  (void) res; /* The result of fflush() call is ignored */
+#endif /* ! NDEBUG */
+}
+
+
+#endif /* ! HAVE_LOG_FUNCTIONALITY */

+ 60 - 0
src/mhd2/daemon_logger_default.h

@@ -0,0 +1,60 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_logger_default.c
+ * @brief  The implementation of the default logger function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DAEMON_LOGGER_DEFAULT_H
+#define MHD_DAEMON_LOGGER_DEFAULT_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_LOG_FUNCTIONALITY
+
+#include "mhd_public_api.h" /* For enum MHD_StatusCode */
+
+#include <stdarg.h>
+
+/**
+ * Default logger function.
+ * @param cls the closure
+ * @param sc the status code of the event
+ * @param fm the format string (`printf()`-style)
+ * @param ap the arguments to @a fm
+ * @ingroup logging
+ */
+MHD_INTERNAL void
+mhd_logger_default (void *cls,
+                    enum MHD_StatusCode sc,
+                    const char *fm,
+                    va_list ap);
+
+#else /* ! HAVE_LOG_FUNCTIONALITY */
+
+#include "sys_null_macro.h"
+
+#define mhd_logger_default NULL
+
+#endif /* ! MHD_DAEMON_LOGGER_DEFAULT_H */
+
+#endif /* ! MHD_DAEMON_LOGGER_DEFAULT_H */

+ 2777 - 0
src/mhd2/daemon_start.c

@@ -0,0 +1,2777 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/daemon_start.c
+ * @brief  The implementation of the MHD_daemon_start()
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "sys_malloc.h"
+
+#include <string.h>
+#include "sys_sockets_headers.h"
+#include "sys_ip_headers.h"
+
+#ifdef MHD_POSIX_SOCKETS
+#  include <errno.h>
+#endif
+#ifdef MHD_USE_EPOLL
+#  include <sys/epoll.h>
+#endif
+
+#include "mhd_public_api.h"
+
+#include "mhd_daemon.h"
+#include "daemon_options.h"
+
+#include "daemon_logger.h"
+#include "mhd_assert.h"
+#include "mhd_sockets_funcs.h"
+#include "mhd_sockets_macros.h"
+#ifdef MHD_POSIX_SOCKETS
+#  ifdef MHD_USE_SELECT
+#    ifdef HAVE_SYS_SELECT_H
+#      include <sys/select.h> /* For FD_SETSIZE */
+#    else
+#      ifdef HAVE_SYS_TIME_H
+#        include <sys/time.h>
+#      endif
+#      ifdef HAVE_SYS_TYPES_H
+#        include <sys/types.h>
+#      endif
+#      ifdef HAVE_UNISTD_H
+#        include <unistd.h>
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef MHD_USE_THREADS
+#  include "mhd_itc.h"
+#  include "mhd_threads.h"
+#  include "events_process.h"
+#  include "daemon_funcs.h"
+#endif
+
+
+/**
+ * The default value for fastopen queue length (currently GNU/Linux only)
+ */
+#define MHD_TCP_FASTOPEN_DEF_QUEUE_LEN 64
+
+/**
+ * Release any internally allocated pointers, then deallocate the settings.
+ * @param s the pointer to the settings to release
+ */
+static void
+dsettings_release (struct DaemonOptions *s)
+{
+  /* Release starting from the last member */
+  if (NULL != s->random_entropy.v_buf)
+    free (s->random_entropy.v_buf);
+  if (MHD_INVALID_SOCKET != s->listen_socket)
+    mhd_socket_close (s->listen_socket);
+  if (NULL != s->bind_sa.v_sa)
+    free (s->bind_sa.v_sa);
+  free (s);
+}
+
+
+/**
+ * Set the daemon work mode and perform some related checks.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+daemon_set_work_mode (struct MHD_Daemon *restrict d,
+                      struct DaemonOptions *restrict s)
+{
+  switch (s->work_mode.mode)
+  {
+  case MHD_WM_EXTERNAL_PERIODIC:
+    d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS;
+    break;
+  case MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL:
+  case MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE:
+    if (MHD_SPS_AUTO != s->poll_syscall)
+    {
+      MHD_LOG_MSG ( \
+        d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \
+        "The requested work mode is not compatible with setting " \
+        "socket polling syscall.");
+      return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID;
+    }
+    if (MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL == s->work_mode.mode)
+      d->wmode_int = mhd_WM_INT_EXTERNAL_EVENTS_LEVEL;
+    else
+      d->wmode_int = mhd_WM_INT_EXTERNAL_EVENTS_EDGE;
+    break;
+  case MHD_WM_EXTERNAL_SINGLE_FD_WATCH:
+    if ((MHD_SPS_AUTO != s->poll_syscall) &&
+        (MHD_SPS_EPOLL != s->poll_syscall))
+    {
+      MHD_LOG_MSG ( \
+        d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \
+        "The requested work mode MHD_WM_EXTERNAL_SINGLE_FD_WATCH " \
+        "is not compatible with requested socket polling syscall.");
+      return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID;
+    }
+#ifndef MHD_USE_EPOLL
+    MHD_LOG_MSG ( \
+      d, MHD_SC_FEATURE_DISABLED, \
+      "The epoll is required for the requested work mode " \
+      "MHD_WM_EXTERNAL_SINGLE_FD_WATCH, but not available on this " \
+      "platform or MHD build.");
+    return MHD_SC_FEATURE_DISABLED;
+#else
+    d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS;
+#endif
+    break;
+  case MHD_WM_THREAD_PER_CONNECTION:
+    if (MHD_SPS_EPOLL == s->poll_syscall)
+    {
+      MHD_LOG_MSG ( \
+        d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \
+        "The requested work mode MHD_WM_THREAD_PER_CONNECTION " \
+        "is not compatible with 'epoll' sockets polling.");
+      return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID;
+    }
+  /* Intentional fallthrough */
+  case MHD_WM_WORKER_THREADS:
+#ifndef MHD_USE_THREADS
+    MHD_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \
+                 "The internal threads modes are not supported by this " \
+                 "build of MHD.");
+    return MHD_SC_FEATURE_DISABLED;
+#else  /* MHD_USE_THREADS */
+    if (MHD_WM_THREAD_PER_CONNECTION == s->work_mode.mode)
+      d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION;
+    else if (1 >= s->work_mode.params.num_worker_threads)   /* && (MHD_WM_WORKER_THREADS == s->work_mode.mode) */
+      d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD;
+    else
+      d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL;
+#endif /* MHD_USE_THREADS */
+    break;
+  default:
+    MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_WM, \
+                 "Wrong requested work mode.");
+    return MHD_SC_CONFIGURATION_UNEXPECTED_WM;
+  }
+
+  return MHD_SC_OK;
+}
+
+
+union mhd_SockaddrAny
+{
+  struct sockaddr sa;
+  struct sockaddr_in sa_i4;
+#ifdef HAVE_INET6
+  struct sockaddr_in6 sa_i6;
+#endif /* HAVE_INET6 */
+  struct sockaddr_storage sa_stor;
+};
+
+
+/**
+ * The type of the socket to create
+ */
+enum mhd_CreateSktType
+{
+  /**
+   * Unknown address family (could be IP or not IP)
+   */
+  mhd_SKT_UNKNOWN = -4
+  ,
+  /**
+   * The socket is not IP.
+   */
+  mhd_SKT_NON_IP = -2
+  ,
+  /**
+   * The socket is UNIX.
+   */
+  mhd_SKT_UNIX = -1
+  ,
+  /**
+   * No socket
+   */
+  mhd_SKT_NO_SOCKET = MHD_AF_NONE
+  ,
+  /**
+   * IPv4 only
+   */
+  mhd_SKT_IP_V4_ONLY = MHD_AF_INET4
+  ,
+  /**
+   * IPv6 only
+   */
+  mhd_SKT_IP_V6_ONLY = MHD_AF_INET6
+  ,
+  /**
+   * IPv6 with dual stack enabled
+   */
+  mhd_SKT_IP_DUAL_REQUIRED = MHD_AF_DUAL
+  ,
+  /**
+   * Try IPv6 with dual stack then IPv4
+   */
+  mhd_SKT_IP_V4_WITH_V6_OPT = MHD_AF_DUAL_v6_OPTIONAL
+  ,
+  /**
+   * IPv6 with optional dual stack
+   */
+  mhd_SKT_IP_V6_WITH_V4_OPT = MHD_AF_DUAL_v4_OPTIONAL
+  ,
+  /**
+   * Try IPv4 then IPv6 with optional dual stack
+   */
+  mhd_SKT_IP_V4_WITH_FALLBACK = 16
+};
+
+/**
+ * Create socket, bind to the address and start listening on the socket.
+ *
+ * The socket is assigned to the daemon as listening FD.
+ * @param d the daemon to use
+ * @param s the user settings
+ * @param v6_tried true if IPv6 has been tried already
+ * @param force_v6_any_dual true if IPv6 is forced with dual stack either
+ *                          enabled or not
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+static enum MHD_StatusCode
+create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
+                                  struct DaemonOptions *restrict s,
+                                  bool v6_tried,
+                                  bool force_v6_any_dual)
+{
+  MHD_Socket sk;
+  enum mhd_CreateSktType sk_type;
+  bool sk_already_listening;
+  union mhd_SockaddrAny sa_all;
+  const struct sockaddr *p_use_sa;
+  socklen_t use_sa_size;
+  uint_fast16_t sk_port;
+  bool is_non_block;
+  bool is_non_inhr;
+  enum MHD_StatusCode ret;
+
+  sk = MHD_INVALID_SOCKET;
+  sk_type = mhd_SKT_NO_SOCKET;
+  sk_already_listening = false;
+  p_use_sa = NULL;
+  use_sa_size = 0;
+  sk_port = 0;
+
+#ifndef HAVE_INET6
+  mhd_assert (! v6_tried);
+  mhd_assert (! force_v6_any_dual);
+#endif
+
+  if (MHD_INVALID_SOCKET != s->listen_socket)
+  {
+    mhd_assert (! v6_tried);
+    mhd_assert (! force_v6_any_dual);
+    /* Check for options conflicts */
+    if (0 != s->bind_sa.v_sa_len)
+    {
+      MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
+                   "MHD_D_O_BIND_SA cannot be used together " \
+                   "with MHD_D_O_LISTEN_SOCKET");
+      return MHD_SC_OPTIONS_CONFLICT;
+    }
+    else if (MHD_AF_NONE != s->bind_port.v_af)
+    {
+      MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
+                   "MHD_D_O_BIND_PORT cannot be used together " \
+                   "with MHD_D_O_LISTEN_SOCKET");
+      return MHD_SC_OPTIONS_CONFLICT;
+    }
+
+    /* No options conflicts */
+    sk = s->listen_socket;
+    s->listen_socket = MHD_INVALID_SOCKET; /* Prevent closing with settings cleanup */
+    sk_type = mhd_SKT_UNKNOWN;
+    sk_already_listening = true;
+  }
+  else if ((0 != s->bind_sa.v_sa_len) || (MHD_AF_NONE != s->bind_port.v_af))
+  {
+    if (0 != s->bind_sa.v_sa_len)
+    {
+      mhd_assert (! v6_tried);
+      mhd_assert (! force_v6_any_dual);
+
+      /* Check for options conflicts */
+      if (MHD_AF_NONE != s->bind_port.v_af)
+      {
+        MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
+                     "MHD_D_O_BIND_SA cannot be used together " \
+                     "with MHD_D_O_BIND_PORT");
+        return MHD_SC_OPTIONS_CONFLICT;
+      }
+
+      /* No options conflicts */
+      switch (s->bind_sa.v_sa->sa_family)
+      {
+      case AF_INET:
+        sk_type = mhd_SKT_IP_V4_ONLY;
+        if (sizeof(sa_all.sa_i4) > s->bind_sa.v_sa_len)
+        {
+          MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+                       "The size of the provided sockaddr does not match "
+                       "used address family");
+          return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
+        }
+        memcpy (&(sa_all.sa_i4), s->bind_sa.v_sa, sizeof(sa_all.sa_i4));
+        sk_port = (uint_fast16_t) ntohs (sa_all.sa_i4.sin_port);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+        sa_all.sa_i4.sin_len = (socklen_t) sizeof(sa_all.sa_i4);
+#endif
+        p_use_sa = (struct sockaddr *) &(sa_all.sa_i4);
+        use_sa_size = (socklen_t) sizeof(sa_all.sa_i4);
+        break;
+#ifdef HAVE_INET6
+      case AF_INET6:
+        sk_type = mhd_SKT_IP_V6_ONLY;
+        if (sizeof(sa_all.sa_i6) > s->bind_sa.v_sa_len)
+        {
+          MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+                       "The size of the provided sockaddr does not match "
+                       "used address family");
+          return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
+        }
+        memcpy (&(sa_all.sa_i6), s->bind_sa.v_sa, s->bind_sa.v_sa_len);
+        sk_port = (uint_fast16_t) ntohs (sa_all.sa_i6.sin6_port);
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
+        sa_all.sa_i6.sin6_len = (socklen_t) s->bind_sa.v_sa_len;
+#endif
+        p_use_sa = (struct sockaddr *) &(sa_all.sa_i6);
+        use_sa_size = (socklen_t) sizeof(sa_all.sa_i6);
+        break;
+#endif /* HAVE_INET6 */
+#ifdef MHD_AF_UNIX
+      case MHD_AF_UNIX:
+        sk_type = mhd_SKT_UNIX;
+        p_use_sa = NULL; /* To be set below */
+        break;
+#endif /* MHD_AF_UNIX */
+      default:
+        sk_type = mhd_SKT_UNKNOWN;
+        p_use_sa = NULL; /* To be set below */
+      }
+
+      if (s->bind_sa.v_dual)
+      {
+        if (mhd_SKT_IP_V6_ONLY != sk_type)
+        {
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_DUAL_STACK_NOT_SUITABLE, \
+                       "IP dual stack is not possible for provided sockaddr");
+        }
+#ifdef HAVE_INET6
+        else
+        {
+#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
+          sk_type = mhd_SKT_IP_DUAL_REQUIRED;
+#else  /* ! IPV6_V6ONLY */
+          MHD_LOG_MSG (d, \
+                       MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, \
+                       "IP dual stack is not supported by this platform or " \
+                       "by this MHD build");
+#endif /* ! IPV6_V6ONLY */
+        }
+#endif /* HAVE_INET6 */
+      }
+
+      if (NULL == p_use_sa)
+      {
+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) && \
+        defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN)
+        if ((((size_t) s->bind_sa.v_sa->sa_len) != s->bind_sa.v_sa_len) &&
+            (sizeof(sa_all) >= s->bind_sa.v_sa_len))
+        {
+          /* Fix embedded 'sa_len' member if possible */
+          memcpy (&sa_all, s->bind_sa.v_sa, s->bind_sa.v_sa_len);
+          sa_all.sa_stor.ss_len = (socklen_t) s->bind_sa.v_sa_len;
+          p_use_sa = (const struct sockaddr *) &(sa_all.sa_stor);
+        }
+        else
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN && HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */
+        p_use_sa = s->bind_sa.v_sa;
+        use_sa_size = (socklen_t) s->bind_sa.v_sa_len;
+      }
+    }
+    else /* if (MHD_AF_NONE != s->bind_port.v_af) */
+    {
+      /* No options conflicts */
+      switch (s->bind_port.v_af)
+      {
+      case MHD_AF_NONE:
+        mhd_assert (0);
+        MHD_UNREACHABLE_;
+        return MHD_SC_INTERNAL_ERROR;
+      case MHD_AF_AUTO:
+#ifdef HAVE_INET6
+#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
+        if (force_v6_any_dual)
+          sk_type = mhd_SKT_IP_V6_WITH_V4_OPT;
+        else if (v6_tried)
+          sk_type = mhd_SKT_IP_V4_WITH_FALLBACK;
+        else
+          sk_type = mhd_SKT_IP_V4_WITH_V6_OPT;
+#else  /* ! IPV6_V6ONLY */
+        mhd_assert (! v6_tried);
+        if (force_v6_any_dual)
+          sk_type = mhd_SKT_IP_V6_ONLY;
+        else
+          sk_type = mhd_SKT_IP_V4_WITH_FALLBACK;
+#endif /* ! IPV6_V6ONLY */
+#else  /* ! HAVE_INET6 */
+        sk_type = mhd_SKT_IP_V4_ONLY;
+#endif /* ! HAVE_INET6 */
+        break;
+      case MHD_AF_INET4:
+        mhd_assert (! v6_tried);
+        mhd_assert (! force_v6_any_dual);
+        sk_type = mhd_SKT_IP_V4_ONLY;
+        break;
+      case MHD_AF_INET6:
+        mhd_assert (! v6_tried);
+        mhd_assert (! force_v6_any_dual);
+#ifdef HAVE_INET6
+        sk_type = mhd_SKT_IP_V6_ONLY;
+#else  /* ! HAVE_INET6 */
+        MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
+                     "IPv6 is not supported by this MHD build or " \
+                     "by this platform");
+        return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
+#endif /* ! HAVE_INET6 */
+        break;
+      case MHD_AF_DUAL:
+        mhd_assert (! v6_tried);
+        mhd_assert (! force_v6_any_dual);
+#ifdef HAVE_INET6
+#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
+        sk_type = mhd_SKT_IP_DUAL_REQUIRED;
+#else  /* ! IPV6_V6ONLY */
+        MHD_LOG_MSG (d,
+                     MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, \
+                     "IP dual stack is not supported by this platform or " \
+                     "by this MHD build");
+        sk_type = mhd_SKT_IP_V6_ONLY;
+#endif /* ! IPV6_V6ONLY */
+#else  /* ! HAVE_INET6 */
+        MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
+                     "IPv6 is not supported by this MHD build or " \
+                     "by this platform");
+        return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
+#endif /* ! HAVE_INET6 */
+        break;
+      case MHD_AF_DUAL_v4_OPTIONAL:
+        mhd_assert (! v6_tried);
+        mhd_assert (! force_v6_any_dual);
+#ifdef HAVE_INET6
+#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
+        sk_type = mhd_SKT_IP_V6_WITH_V4_OPT;
+#else  /* ! IPV6_V6ONLY */
+        sk_type = mhd_SKT_IP_V6_ONLY;
+#endif /* ! IPV6_V6ONLY */
+#else  /* ! HAVE_INET6 */
+        MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
+                     "IPv6 is not supported by this MHD build or " \
+                     "by this platform");
+        return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
+#endif /* ! HAVE_INET6 */
+        break;
+      case MHD_AF_DUAL_v6_OPTIONAL:
+        mhd_assert (! force_v6_any_dual);
+#ifdef HAVE_INET6
+#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
+        sk_type = (! v6_tried) ?
+                  mhd_SKT_IP_V4_WITH_V6_OPT : mhd_SKT_IP_V4_ONLY;
+#else  /* ! IPV6_V6ONLY */
+        mhd_assert (! v6_tried);
+        sk_type = mhd_SKT_IP_V4_ONLY;
+#endif /* ! IPV6_V6ONLY */
+#else  /* ! HAVE_INET6 */
+        mhd_assert (! v6_tried);
+        sk_type = mhd_SKT_IP_V4_ONLY;
+#endif /* ! HAVE_INET6 */
+        break;
+      default:
+        MHD_LOG_MSG (d, MHD_SC_AF_NOT_SUPPORTED_BY_BUILD, \
+                     "Unknown address family specified");
+        return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD;
+      }
+
+      mhd_assert (mhd_SKT_NO_SOCKET < sk_type);
+
+      switch (sk_type)
+      {
+      case mhd_SKT_IP_V4_ONLY:
+      case mhd_SKT_IP_V4_WITH_FALLBACK:
+        /* Zeroing is not required, but may help on exotic platforms */
+        memset (&(sa_all.sa_i4), 0, sizeof(sa_all.sa_i4));
+        sa_all.sa_i4.sin_family = AF_INET;
+        sa_all.sa_i4.sin_port = htons (s->bind_port.v_port);
+        if (0 == INADDR_ANY) /* Optimised at compile time */
+          sa_all.sa_i4.sin_addr.s_addr = INADDR_ANY;
+        else
+          sa_all.sa_i4.sin_addr.s_addr = htonl (INADDR_ANY);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+        sa_all.sa_i4.sin_len = sizeof (sa_all.sa_i4);
+#endif
+        p_use_sa = (const struct sockaddr *) &(sa_all.sa_i4);
+        use_sa_size = (socklen_t) sizeof (sa_all.sa_i4);
+        break;
+      case mhd_SKT_IP_V6_ONLY:
+      case mhd_SKT_IP_DUAL_REQUIRED:
+      case mhd_SKT_IP_V4_WITH_V6_OPT:
+      case mhd_SKT_IP_V6_WITH_V4_OPT:
+#ifdef HAVE_INET6
+        if (1)
+        {
+#ifdef IN6ADDR_ANY_INIT
+          static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT;
+#endif
+          /* Zeroing is required by POSIX */
+          memset (&(sa_all.sa_i6), 0, sizeof(sa_all.sa_i6));
+          sa_all.sa_i6.sin6_family = AF_INET6;
+          sa_all.sa_i6.sin6_port = htons (s->bind_port.v_port);
+#ifdef IN6ADDR_ANY_INIT /* Optional assignment at the address is all zeros anyway */
+          sa_all.sa_i6.sin6_addr = static_in6any;
+#endif
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
+          sa_all.sa_i6.sin6_len = sizeof (sa_all.sa_i6);
+#endif
+          p_use_sa = (const struct sockaddr *) &(sa_all.sa_i6);
+          use_sa_size = (socklen_t) sizeof (sa_all.sa_i6);
+        }
+        break;
+#endif /* HAVE_INET6 */
+      case mhd_SKT_UNKNOWN:
+      case mhd_SKT_NON_IP:
+      case mhd_SKT_UNIX:
+      case mhd_SKT_NO_SOCKET:
+      default:
+        mhd_assert (0);
+        MHD_UNREACHABLE_;
+        return MHD_SC_INTERNAL_ERROR;
+      }
+
+      sk_port = s->bind_port.v_port;
+
+    }
+  }
+  else
+  {
+    /* No listen socket */
+    d->net.listen.fd = MHD_INVALID_SOCKET;
+    d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN;
+    d->net.listen.non_block = false;
+    d->net.listen.port = 0;
+
+    return MHD_SC_OK;
+  }
+
+  mhd_assert (mhd_SKT_NO_SOCKET != sk_type);
+  mhd_assert ((NULL != p_use_sa) || sk_already_listening);
+  mhd_assert ((MHD_INVALID_SOCKET == sk) || sk_already_listening);
+
+  if (MHD_INVALID_SOCKET == sk)
+  {
+    mhd_assert (NULL != p_use_sa);
+#if defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT)
+    /* May fail before Win7 SP1 */
+    sk = WSASocketW (p_use_sa->sa_family, SOCK_STREAM, 0,
+                     NULL, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
+
+    if (MHD_INVALID_SOCKET == sk)
+#endif /* MHD_WINSOCK_SOCKETS && WSA_FLAG_NO_HANDLE_INHERIT */
+    sk = socket (p_use_sa->sa_family,
+                 SOCK_STREAM | MHD_SOCK_NONBLOCK
+                 | MHD_SOCK_CLOEXEC | MHD_SOCK_NOSIGPIPE, 0);
+
+    if (MHD_INVALID_SOCKET == sk)
+    {
+      bool is_af_err = mhd_SCKT_LERR_IS_AF ();
+
+      if (is_af_err)
+        MHD_LOG_MSG (d, MHD_SC_AF_NOT_AVAILABLE, \
+                     "The requested socket address family is rejected " \
+                     "by the OS");
+
+#ifdef HAVE_INET6
+      if (mhd_SKT_IP_V4_WITH_FALLBACK == sk_type)
+        return create_bind_listen_stream_socket (d, s, v6_tried, true);
+      if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type)
+        return create_bind_listen_stream_socket (d, s, true, false);
+#endif /* HAVE_INET6 */
+
+      if (! is_af_err)
+        MHD_LOG_MSG (d, MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET, \
+                     "Failed to open listen socket");
+
+      return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
+    }
+    is_non_block = (0 != MHD_SOCK_NONBLOCK);
+    is_non_inhr = (0 == MHD_SOCK_CLOEXEC);
+  }
+  else
+  {
+    is_non_block = false; /* Try to set non-block */
+    is_non_inhr = false;  /* Try to set non-inheritable */
+  }
+
+  /* The listen socket must be closed if error code returned
+     beyond this point */
+
+  ret = MHD_SC_OK;
+
+  do
+  { /* The scope for automatic socket close for error returns */
+    if (! mhd_FD_FITS_DAEMON (d,sk))
+    {
+      MHD_LOG_MSG (d, MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE, \
+                   "The listen FD value is higher than allowed");
+      ret = MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE;
+      break;
+    }
+
+    if (! is_non_inhr)
+    {
+      if (! mhd_socket_noninheritable (sk))
+        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NOINHERIT_FAILED, \
+                     "OS refused to make the listen socket non-inheritable");
+    }
+
+    if (! sk_already_listening)
+    {
+#ifdef HAVE_INET6
+#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
+      if ((mhd_SKT_IP_V6_ONLY == sk_type) ||
+          (mhd_SKT_IP_DUAL_REQUIRED == sk_type) ||
+          (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) ||
+          (mhd_SKT_IP_V6_WITH_V4_OPT == sk_type) ||
+          (mhd_SKT_UNKNOWN == sk_type))
+      {
+        mhd_SCKT_OPT_BOOL no_dual_to_set;
+        bool use_dual;
+
+        use_dual = ((mhd_SKT_IP_DUAL_REQUIRED == sk_type) ||
+                    (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) ||
+                    (mhd_SKT_IP_V6_WITH_V4_OPT == sk_type));
+        no_dual_to_set = use_dual ? 0 : 1;
+
+        if (0 != setsockopt (sk, IPPROTO_IPV6, IPV6_V6ONLY,
+                             (void *) &no_dual_to_set, sizeof (no_dual_to_set)))
+        {
+          mhd_SCKT_OPT_BOOL no_dual_current;
+          socklen_t opt_size;
+          bool state_unknown;
+          bool state_match;
+
+          no_dual_current = 0;
+          opt_size = sizeof(no_dual_current);
+
+          /* Some platforms forbid setting this options, but allow
+             reading. */
+          if ((0 != getsockopt (sk, IPPROTO_IPV6, IPV6_V6ONLY,
+                                (void*) &no_dual_current, &opt_size))
+              || (((socklen_t) sizeof(no_dual_current)) < opt_size))
+          {
+            state_unknown = true;
+            state_match = false;
+          }
+          else
+          {
+            state_unknown = false;
+            state_match = ((! ! no_dual_current) == (! ! no_dual_to_set));
+          }
+
+          if (state_unknown || ! state_match)
+          {
+            if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type)
+            {
+              (void) mhd_socket_close (sk);
+              return create_bind_listen_stream_socket (d, s, true, false);
+            }
+            if (! state_unknown)
+            {
+              /* The dual-stack state is definitely wrong */
+              if (mhd_SKT_IP_V6_ONLY == sk_type)
+              {
+                MHD_LOG_MSG ( \
+                  d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED, \
+                  "Failed to disable IP dual-stack configuration " \
+                  "for the listen socket");
+                ret = MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED;
+                break;
+              }
+              else if (mhd_SKT_UNKNOWN != sk_type)
+              {
+                MHD_LOG_MSG ( \
+                  d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED, \
+                  "Cannot enable IP dual-stack configuration " \
+                  "for the listen socket");
+                if (mhd_SKT_IP_DUAL_REQUIRED == sk_type)
+                {
+                  ret = MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED;
+                  break;
+                }
+              }
+            }
+            else
+            {
+              /* The dual-stack state is unknown */
+              if (mhd_SKT_UNKNOWN != sk_type)
+                MHD_LOG_MSG (
+                  d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_UNKNOWN, \
+                  "Failed to set dual-stack (IPV6_ONLY) configuration " \
+                  "for the listen socket, using system defaults");
+            }
+          }
+        }
+      }
+#else  /* ! IPV6_V6ONLY */
+      mhd_assert (mhd_SKT_IP_DUAL_REQUIRED != sk_type);
+      mhd_assert (mhd_SKT_IP_V4_WITH_V6_OPT != sk_type);
+      mhd_assert (mhd_SKT_IP_V6_WITH_V4_OPT != sk_type);
+#endif /* ! IPV6_V6ONLY */
+#endif /* HAVE_INET6 */
+
+      if (MHD_FOM_AUTO <= d->settings->tcp_fastopen.v_option)
+      {
+#if defined(TCP_FASTOPEN)
+        int fo_param;
+#ifdef __linux__
+        /* The parameter is the queue length */
+        fo_param = (int) d->settings->tcp_fastopen.v_queue_length;
+        if (0 == fo_param)
+          fo_param = MHD_TCP_FASTOPEN_DEF_QUEUE_LEN;
+#else  /* ! __linux__ */
+        fo_param = 1; /* The parameter is on/off type of setting */
+#endif /* ! __linux__ */
+        if (0 != setsockopt (sk, IPPROTO_TCP, TCP_FASTOPEN,
+                             (const void *) &fo_param,
+                             sizeof (fo_param)))
+        {
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \
+                       "OS refused to enable TCP Fast Open on " \
+                       "the listen socket");
+          if (MHD_FOM_AUTO < d->settings->tcp_fastopen.v_option)
+          {
+            ret = MHD_SC_LISTEN_FAST_OPEN_FAILURE;
+            break;
+          }
+        }
+#else  /* ! TCP_FASTOPEN */
+        if (MHD_FOM_AUTO < d->settings->tcp_fastopen.v_option)
+        {
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \
+                       "The OS does not support TCP Fast Open");
+          ret = MHD_SC_LISTEN_FAST_OPEN_FAILURE;
+          break;
+        }
+#endif
+      }
+
+      if (MHD_D_OPTION_BIND_TYPE_NOT_SHARED >= d->settings->listen_addr_reuse)
+      {
+#ifndef MHD_WINSOCK_SOCKETS
+#ifdef SO_REUSEADDR
+        mhd_SCKT_OPT_BOOL on_val1 = 1;
+        if (0 != setsockopt (sk, IPPROTO_TCP, SO_REUSEADDR,
+                             (const void *) &on_val1, sizeof (on_val1)))
+        {
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_PORT_REUSE_ENABLE_FAILED, \
+                       "OS refused to enable address reuse on " \
+                       "the listen socket");
+        }
+#else  /* ! SO_REUSEADDR */
+        MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \
+                     "The OS does not support address reuse for sockets");
+#endif /* ! SO_REUSEADDR */
+#endif /* ! MHD_WINSOCK_SOCKETS */
+        if (MHD_D_OPTION_BIND_TYPE_NOT_SHARED > d->settings->listen_addr_reuse)
+        {
+#if defined(SO_REUSEPORT) || defined(MHD_WINSOCK_SOCKETS)
+          mhd_SCKT_OPT_BOOL on_val2 = 1;
+          if (0 != setsockopt (sk, IPPROTO_TCP,
+#ifndef MHD_WINSOCK_SOCKETS
+                               SO_REUSEPORT,
+#else  /* ! MHD_WINSOCK_SOCKETS */
+                               SO_REUSEADDR, /* On W32 it is the same as SO_REUSEPORT on other platforms */
+#endif /* ! MHD_WINSOCK_SOCKETS */
+                               (const void *) &on_val2, sizeof (on_val2)))
+          {
+            MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, \
+                         "OS refused to enable address sharing " \
+                         "on the listen socket");
+            ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
+            break;
+          }
+#else  /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \
+                       "The OS does not support address sharing for sockets");
+          ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED;
+          break;
+#endif /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */
+        }
+      }
+#if defined(SO_EXCLUSIVEADDRUSE) || defined(SO_EXCLBIND)
+      else if (MHD_D_OPTION_BIND_TYPE_EXCLUSIVE <=
+               d->settings->listen_addr_reuse)
+      {
+        mhd_SCKT_OPT_BOOL on_val = 1;
+        if (0 != setsockopt (sk, IPPROTO_TCP,
+#ifdef SO_EXCLUSIVEADDRUSE
+                             SO_EXCLUSIVEADDRUSE,
+#else
+                             SO_EXCLBIND,
+#endif
+                             (const void *) &on_val, sizeof (on_val)))
+        {
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED, \
+                       "OS refused to enable exclusive address use " \
+                       "on the listen socket");
+          ret = MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED;
+          break;
+        }
+      }
+#endif /* SO_EXCLUSIVEADDRUSE || SO_EXCLBIND */
+
+      mhd_assert (NULL != p_use_sa);
+      mhd_assert (0 != use_sa_size);
+      if (0 != bind (sk, p_use_sa, use_sa_size))
+      {
+#ifdef HAVE_INET6
+        if (mhd_SKT_IP_V4_WITH_FALLBACK == sk_type)
+        {
+          (void) mhd_socket_close (sk);
+          return create_bind_listen_stream_socket (d, s, v6_tried, true);
+        }
+        if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type)
+        {
+          (void) mhd_socket_close (sk);
+          return create_bind_listen_stream_socket (d, s, true, false);
+        }
+#endif /* HAVE_INET6 */
+        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_BIND_FAILED, \
+                     "Failed to bind the listen socket");
+        ret = MHD_SC_LISTEN_SOCKET_BIND_FAILED;
+        break;
+      }
+
+      if (1)
+      {
+        int accept_queue_len;
+        accept_queue_len = (int) s->listen_backlog;
+        if (0 > accept_queue_len)
+          accept_queue_len = 0;
+        if (0 == accept_queue_len)
+        {
+#ifdef SOMAXCONN
+          accept_queue_len = SOMAXCONN;
+#else  /* ! SOMAXCONN */
+          accept_queue_len = 127; /* Should be the safe value */
+#endif /* ! SOMAXCONN */
+        }
+        if (0 != listen (sk, accept_queue_len))
+        {
+#ifdef HAVE_INET6
+          if (mhd_SKT_IP_V4_WITH_FALLBACK == sk_type)
+          {
+            (void) mhd_socket_close (sk);
+            return create_bind_listen_stream_socket (d, s, v6_tried, true);
+          }
+          if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type)
+          {
+            (void) mhd_socket_close (sk);
+            return create_bind_listen_stream_socket (d, s, true, false);
+          }
+#endif /* HAVE_INET6 */
+          MHD_LOG_MSG (d, MHD_SC_LISTEN_FAILURE, \
+                       "Failed to start listening on the listen socket");
+          ret = MHD_SC_LISTEN_FAILURE;
+          break;
+        }
+      }
+    }
+    /* A valid listening socket is ready here */
+
+    if (! is_non_block)
+    {
+      is_non_block = mhd_socket_nonblocking (sk);
+      if (! is_non_block)
+        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
+                     "OS refused to make the listen socket non-blocking");
+    }
+
+    /* Set to the daemon only when the listening socket is fully ready */
+    d->net.listen.fd = sk;
+    switch (sk_type)
+    {
+    case mhd_SKT_UNKNOWN:
+      d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN;
+      break;
+    case mhd_SKT_NON_IP:
+      d->net.listen.type = mhd_SOCKET_TYPE_NON_IP;
+      break;
+    case mhd_SKT_UNIX:
+      d->net.listen.type = mhd_SOCKET_TYPE_UNIX;
+      break;
+    case mhd_SKT_IP_V4_ONLY:
+    case mhd_SKT_IP_V6_ONLY:
+    case mhd_SKT_IP_DUAL_REQUIRED:
+    case mhd_SKT_IP_V4_WITH_V6_OPT:
+    case mhd_SKT_IP_V6_WITH_V4_OPT:
+    case mhd_SKT_IP_V4_WITH_FALLBACK:
+      d->net.listen.type = mhd_SOCKET_TYPE_IP;
+      break;
+    case mhd_SKT_NO_SOCKET:
+    default:
+      mhd_assert (0 && "Impossible value");
+      MHD_UNREACHABLE_;
+      return MHD_SC_INTERNAL_ERROR;
+    }
+    d->net.listen.non_block = is_non_block;
+    d->net.listen.port = sk_port;
+
+    mhd_assert (ret == MHD_SC_OK);
+
+    return MHD_SC_OK;
+
+  } while (0);
+
+  mhd_assert (MHD_SC_OK != ret); /* This should be only error returns here */
+  mhd_assert (MHD_INVALID_SOCKET != sk);
+  (void) mhd_socket_close (sk);
+  return ret;
+}
+
+
+/**
+ * Detect and set the type and port of the listening socket
+ * @param d the daemon to use
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+detect_listen_type_and_port (struct MHD_Daemon *restrict d)
+{
+  union mhd_SockaddrAny sa_all;
+  socklen_t sa_size;
+  enum mhd_SocketType declared_type;
+
+  mhd_assert (MHD_INVALID_SOCKET != d->net.listen.fd);
+  mhd_assert (0 == d->net.listen.port);
+  memset (&sa_all, 0, sizeof(sa_all)); /* Actually not required */
+  sa_size = (socklen_t) sizeof(sa_all);
+
+  if (0 != getsockname (d->net.listen.fd, &(sa_all.sa), &sa_size))
+  {
+    if (mhd_SOCKET_TYPE_IP == d->net.listen.type)
+      MHD_LOG_MSG (d, MHD_SC_LISTEN_PORT_DETECT_FAILURE, \
+                   "Failed to detect the port number on the listening socket");
+    return;
+  }
+
+  declared_type = d->net.listen.type;
+  if (0 == sa_size)
+  {
+#ifndef __linux__
+    /* Used on some non-Linux platforms */
+    d->net.listen.type = mhd_SOCKET_TYPE_UNIX;
+    d->net.listen.port = 0;
+#else  /* ! __linux__ */
+    (void) 0;
+#endif /* ! __linux__ */
+  }
+  else
+  {
+    switch (sa_all.sa.sa_family)
+    {
+    case AF_INET:
+      d->net.listen.type = mhd_SOCKET_TYPE_IP;
+      d->net.listen.port = (uint_fast16_t) ntohs (sa_all.sa_i4.sin_port);
+      break;
+#ifdef HAVE_INET6
+    case AF_INET6:
+      d->net.listen.type = mhd_SOCKET_TYPE_IP;
+      d->net.listen.port = (uint_fast16_t) ntohs (sa_all.sa_i6.sin6_port);
+      break;
+#endif /* HAVE_INET6 */
+#ifdef MHD_AF_UNIX
+    case MHD_AF_UNIX:
+      d->net.listen.type = mhd_SOCKET_TYPE_UNIX;
+      d->net.listen.port = 0;
+      break;
+#endif /* MHD_AF_UNIX */
+    default:
+      d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN;
+      d->net.listen.port = 0;
+      break;
+    }
+  }
+
+  if ((declared_type != d->net.listen.type)
+      && (mhd_SOCKET_TYPE_IP == declared_type))
+    MHD_LOG_MSG (d, MHD_SC_UNEXPECTED_SOCKET_ERROR, \
+                 "The type of listen socket is detected as non-IP, while " \
+                 "the socket has been created as an IP socket");
+}
+
+
+#ifdef MHD_USE_EPOLL
+
+/**
+ * Initialise daemon's epoll FD
+ */
+MHD_FN_PAR_NONNULL_ (1) static enum MHD_StatusCode
+init_epoll (struct MHD_Daemon *restrict d)
+{
+  int e_fd;
+  mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int);
+  mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \
+              ((mhd_POLL_TYPE_EPOLL == d->events.poll_type) && \
+               (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)));
+  mhd_assert ((! d->dbg.net_inited) || \
+              (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int));
+  mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \
+              (NULL == d->events.data.epoll.events));
+  mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \
+              (MHD_INVALID_SOCKET == d->events.data.epoll.e_fd));
+#ifdef HAVE_EPOLL_CREATE1
+  e_fd = epoll_create1 (FD_CLOEXEC);
+#else  /* ! HAVE_EPOLL_CREATE1 */
+  e_fd = epoll_create (128); /* The number is usually ignored */
+  if (0 <= e_fd)
+  {
+    if (! mhd_socket_noninheritable (e_fd))
+      MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED, \
+                   "Failed to make epoll control FD non-inheritable");
+  }
+#endif /* ! HAVE_EPOLL_CREATE1 */
+  if (0 > e_fd)
+  {
+    MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_CREATE_FAILED, \
+                 "Failed to create epoll control FD");
+    return MHD_SC_EPOLL_CTL_CREATE_FAILED; /* Failure exit point */
+  }
+
+  if (! mhd_FD_FITS_DAEMON (d, e_fd))
+  {
+    MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE, \
+                 "The epoll control FD value is higher than allowed");
+    (void) close (e_fd);
+    return MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE; /* Failure exit point */
+  }
+
+  d->events.poll_type = mhd_POLL_TYPE_EPOLL;
+  d->events.data.epoll.e_fd = e_fd;
+  d->events.data.epoll.events = NULL; /* Memory allocated during event and threads init */
+  d->events.data.epoll.num_elements = 0;
+  return MHD_SC_OK; /* Success exit point */
+}
+
+
+/**
+ * Deinitialise daemon's epoll FD
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+deinit_epoll (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (mhd_POLL_TYPE_EPOLL == d->events.poll_type);
+  mhd_assert (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd);
+  /* With thread pool the epoll control FD could be migrated to the
+   * first worker daemon. */
+  mhd_assert ((MHD_INVALID_SOCKET != d->events.data.epoll.e_fd) || \
+              (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int));
+  mhd_assert ((MHD_INVALID_SOCKET != d->events.data.epoll.e_fd) || \
+              (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY == d->threading.d_type));
+  if (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd)
+    close (d->events.data.epoll.e_fd);
+}
+
+
+#endif /* MHD_USE_EPOLL */
+
+/**
+ * Choose sockets monitoring syscall and pre-initialise it
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
+                                  struct DaemonOptions *restrict s)
+{
+  enum mhd_IntPollType chosen_type;
+
+  mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \
+              (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) || \
+              (mhd_WM_INT_EXTERNAL_EVENTS_LEVEL == d->wmode_int) || \
+              (MHD_WM_EXTERNAL_SINGLE_FD_WATCH == s->work_mode.mode));
+  mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \
+              (d->events.poll_type == (enum mhd_IntPollType) s->poll_syscall) \
+              || ((MHD_SPS_AUTO == s->poll_syscall) && \
+                  ((mhd_POLL_TYPE_EXT == d->events.poll_type) || \
+                   (mhd_POLL_TYPE_EPOLL == d->events.poll_type))));
+
+  /* Check whether the provided parameter is in the range of expected values */
+  switch (s->poll_syscall)
+  {
+  case MHD_SPS_AUTO:
+    chosen_type = mhd_POLL_TYPE_NOT_SET_YET;
+    break;
+  case MHD_SPS_SELECT:
+    mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+#ifndef MHD_USE_SELECT
+    MHD_LOG_MSG (d, MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE, \
+                 "'select()' is not supported by the platform or " \
+                 "this MHD build");
+    return MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE;
+#else  /* MHD_USE_SELECT */
+    chosen_type = mhd_POLL_TYPE_SELECT;
+#endif /* MHD_USE_SELECT */
+    break;
+  case MHD_SPS_POLL:
+    mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+#ifndef MHD_USE_POLL
+    MHD_LOG_MSG (d, MHD_SC_POLL_SYSCALL_NOT_AVAILABLE, \
+                 "'poll()' is not supported by the platform or " \
+                 "this MHD build");
+    return MHD_SC_POLL_SYSCALL_NOT_AVAILABLE;
+#else  /* MHD_USE_POLL */
+    chosen_type = mhd_POLL_TYPE_POLL;
+#endif /* MHD_USE_POLL */
+    break;
+  case MHD_SPS_EPOLL:
+    mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+#ifndef MHD_USE_EPOLL
+    MHD_LOG_MSG (d, MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE, \
+                 "'epoll' is not supported by the platform or " \
+                 "this MHD build");
+    return MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE;
+#else  /* MHD_USE_EPOLL */
+    chosen_type = mhd_POLL_TYPE_EPOLL;
+#endif /* MHD_USE_EPOLL */
+    break;
+  default:
+    MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_SPS,
+                 "Wrong socket polling syscall specified");
+    return MHD_SC_CONFIGURATION_UNEXPECTED_SPS;
+  }
+
+  mhd_assert (mhd_POLL_TYPE_EXT != chosen_type);
+
+  if (mhd_POLL_TYPE_NOT_SET_YET == chosen_type)
+  {
+    if (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int))
+      chosen_type = mhd_POLL_TYPE_EXT;
+#ifdef MHD_USE_EPOLL
+    else if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int)
+      chosen_type = mhd_POLL_TYPE_EPOLL; /* with possible fallback */
+#endif
+    else
+    {
+#if defined(MHD_USE_POLL)
+      chosen_type = mhd_POLL_TYPE_POLL;
+#elif defined(MHD_USE_SELECT)
+      chosen_type = mhd_POLL_TYPE_SELECT;
+#else
+      (void) 0; /* Do nothing. Mute compiler warning */
+#endif
+    }
+  }
+
+  /* Try 'epoll' if possible */
+#ifdef MHD_USE_EPOLL
+  if (mhd_POLL_TYPE_EPOLL == chosen_type)
+  {
+    enum MHD_StatusCode epoll_res;
+
+    mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != \
+                d->wmode_int);
+    epoll_res = init_epoll (d);
+
+    if (MHD_SC_OK != epoll_res)
+    {
+      if ((MHD_SPS_EPOLL == s->poll_syscall) ||
+          (MHD_WM_EXTERNAL_SINGLE_FD_WATCH == s->work_mode.mode))
+        return epoll_res; /* Cannot init epoll, but epoll is required */
+      chosen_type = mhd_POLL_TYPE_NOT_SET_YET; /* Choose again */
+    }
+  }
+  mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \
+              (0 < d->events.data.epoll.e_fd));
+#endif /* MHD_USE_EPOLL */
+
+  if (mhd_POLL_TYPE_NOT_SET_YET == chosen_type)
+  {
+#if defined(MHD_USE_POLL)
+    chosen_type = mhd_POLL_TYPE_POLL;
+#elif defined(MHD_USE_SELECT)
+    chosen_type = mhd_POLL_TYPE_SELECT;
+#else
+    MHD_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \
+                 "All suitable internal sockets polling technologies are " \
+                 "disabled in this MHD build");
+    return MHD_SC_FEATURE_DISABLED;
+#endif
+  }
+
+  switch (chosen_type)
+  {
+  case mhd_POLL_TYPE_EXT:
+    mhd_assert ((MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL == s->work_mode.mode) || \
+                (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE != s->work_mode.mode));
+    mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+    mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode);
+    d->events.poll_type = mhd_POLL_TYPE_EXT;
+    d->events.data.ext.cb =
+      s->work_mode.params.v_external_event_loop_cb.reg_cb;
+    d->events.data.ext.cls =
+      s->work_mode.params.v_external_event_loop_cb.reg_cb_cls;
+    break;
+#ifdef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+    mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+    mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode);
+    d->events.poll_type = mhd_POLL_TYPE_SELECT;
+    d->events.data.select.rfds = NULL; /* Memory allocated during event and threads init */
+    d->events.data.select.wfds = NULL; /* Memory allocated during event and threads init */
+    d->events.data.select.efds = NULL; /* Memory allocated during event and threads init */
+    break;
+#endif /* MHD_USE_SELECT */
+#ifdef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+    mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+    mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode);
+    d->events.poll_type = mhd_POLL_TYPE_POLL;
+    d->events.data.poll.fds = NULL; /* Memory allocated during event and threads init */
+    d->events.data.poll.rel = NULL; /* Memory allocated during event and threads init */
+    break;
+#endif /* MHD_USE_POLL */
+#ifdef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+    mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+    /* Pre-initialised by init_epoll() */
+    mhd_assert (mhd_POLL_TYPE_EPOLL == d->events.poll_type);
+    mhd_assert (0 <= d->events.data.epoll.e_fd);
+    mhd_assert (NULL == d->events.data.epoll.events);
+    break;
+#endif /* MHD_USE_EPOLL */
+#ifndef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+#endif /* ! MHD_USE_SELECT */
+#ifndef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+#endif /* ! MHD_USE_POLL */
+#ifndef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+#endif /* ! MHD_USE_EPOLL */
+  case mhd_POLL_TYPE_NOT_SET_YET:
+  default:
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    return MHD_SC_INTERNAL_ERROR;
+    break;
+  }
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Initialise network/sockets for the daemon.
+ * Also choose events mode / sockets polling syscall.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+daemon_init_net (struct MHD_Daemon *restrict d,
+                 struct DaemonOptions *restrict s)
+{
+  enum MHD_StatusCode ret;
+
+  mhd_assert (! d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+#ifdef MHD_POSIX_SOCKETS
+  d->net.cfg.max_fd_num = s->fd_number_limit;
+#endif /* MHD_POSIX_SOCKETS */
+
+  ret = daemon_choose_and_preinit_events (d, s);
+  if (MHD_SC_OK != ret)
+    return ret;
+
+  mhd_assert (mhd_POLL_TYPE_NOT_SET_YET != d->events.poll_type);
+
+  /* No direct return of error codes is allowed beyond this point.
+     Deinit/cleanup must be performed before return of any error. */
+
+#if defined(MHD_POSIX_SOCKETS) && defined(MHD_USE_SELECT)
+  if (mhd_POLL_TYPE_SELECT == d->events.poll_type)
+  {
+    if ((MHD_INVALID_SOCKET == d->net.cfg.max_fd_num) ||
+        (FD_SETSIZE < d->net.cfg.max_fd_num))
+      d->net.cfg.max_fd_num = FD_SETSIZE;
+  }
+#endif /* MHD_POSIX_SOCKETS && MHD_USE_SELECT */
+
+  if (MHD_SC_OK == ret)
+  {
+    ret = create_bind_listen_stream_socket (d, s, false, false);
+
+    if (MHD_SC_OK == ret)
+    {
+      if ((MHD_INVALID_SOCKET != d->net.listen.fd)
+          && ! d->net.listen.non_block
+          && ((mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) ||
+              (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)))
+      {
+        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
+                     "The selected daemon work mode requires listening socket "
+                     "in non-blocking mode");
+        ret = MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE;
+      }
+
+      if (MHD_SC_OK == ret)
+      {
+        if ((MHD_INVALID_SOCKET != d->net.listen.fd) &&
+            ((0 == d->net.listen.port) ||
+             (mhd_SOCKET_TYPE_UNKNOWN == d->net.listen.type)))
+          detect_listen_type_and_port (d);
+
+#ifndef NDEBUG
+        d->dbg.net_inited = true;
+#endif
+        return MHD_SC_OK; /* Success exit point */
+      }
+
+      /* Below is a cleanup path */
+      if (MHD_INVALID_SOCKET != d->net.listen.fd)
+        mhd_socket_close (d->net.listen.fd);
+    }
+  }
+
+#ifdef MHD_USE_EPOLL
+  if ((mhd_POLL_TYPE_EPOLL == d->events.poll_type))
+    close (d->events.data.epoll.e_fd);
+#endif /* MHD_USE_EPOLL */
+
+  mhd_assert (MHD_SC_OK != ret);
+
+  return ret;
+}
+
+
+/**
+ * Deinitialise daemon's network data
+ * @param d the daemon object
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+daemon_deinit_net (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (mhd_POLL_TYPE_NOT_SET_YET != d->events.poll_type);
+#ifdef MHD_USE_EPOLL
+  if (mhd_POLL_TYPE_EPOLL == d->events.poll_type)
+    deinit_epoll (d);
+#endif /* MHD_USE_EPOLL */
+  if (MHD_INVALID_SOCKET != d->net.listen.fd)
+    mhd_socket_close (d->net.listen.fd);
+
+#ifndef NDEBUG
+  d->dbg.net_deinited = true;
+#endif
+}
+
+
+#if 0
+void
+dauth_init (struct MHD_Daemon *restrict d,
+            struct DaemonOptions *restrict s)
+{
+  mhd_assert ((NULL == s->random_entropy.v_buf) || \
+              (0 != s->random_entropy.v_buf_size));
+  mhd_assert ((0 == s->random_entropy.v_buf_size) || \
+              (NULL != s->random_entropy.v_buf));
+}
+
+
+#endif
+
+
+/**
+ * Finish initialisation of events processing
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+allocate_events (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (0 != d->conns.cfg.count_limit);
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+
+  mhd_DLINKEDL_INIT_LIST (&(d->events),proc_ready);
+
+  switch (d->events.poll_type)
+  {
+  case mhd_POLL_TYPE_EXT:
+    mhd_assert (NULL != d->events.data.ext.cb);
+#ifndef NDEBUG
+    d->dbg.events_allocated = true;
+#endif
+    return MHD_SC_OK; /* Success exit point */
+    break;
+#ifdef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+    /* The pointers have been set to NULL during pre-initialisations of the events */
+    mhd_assert (NULL == d->events.data.select.rfds);
+    mhd_assert (NULL == d->events.data.select.wfds);
+    mhd_assert (NULL == d->events.data.select.efds);
+    d->events.data.select.rfds = (fd_set *) malloc (sizeof(fd_set));
+    if (NULL != d->events.data.select.rfds)
+    {
+      d->events.data.select.wfds = (fd_set *) malloc (sizeof(fd_set));
+      if (NULL != d->events.data.select.wfds)
+      {
+        d->events.data.select.efds = (fd_set *) malloc (sizeof(fd_set));
+        if (NULL != d->events.data.select.efds)
+        {
+#ifndef NDEBUG
+          d->dbg.events_allocated = true;
+#endif
+          return MHD_SC_OK; /* Success exit point */
+        }
+
+        free (d->events.data.select.wfds);
+      }
+      free (d->events.data.select.rfds);
+    }
+    MHD_LOG_MSG (d, MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE, \
+                 "Failed to allocate memory for fd_sets for the daemon");
+    return MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE;
+    break;
+#endif /* MHD_USE_SELECT */
+#ifdef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+    /* The pointers have been set to NULL during pre-initialisations of the events */
+    mhd_assert (NULL == d->events.data.poll.fds);
+    mhd_assert (NULL == d->events.data.poll.rel);
+    if (1)
+    {
+      unsigned int num_slots;
+      num_slots = 2; /* For ITC and listening, even if they are unused */
+      if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int)
+        num_slots += d->conns.cfg.count_limit;
+      d->events.data.poll.fds =
+        (struct pollfd *) malloc (sizeof(struct pollfd) * num_slots);
+      if (NULL != d->events.data.poll.fds)
+      {
+        d->events.data.poll.rel =
+          (union mhd_SocketRelation *) malloc (sizeof(union mhd_SocketRelation)
+                                               * num_slots);
+        if (NULL != d->events.data.poll.rel)
+        {
+#ifndef NDEBUG
+          d->dbg.events_allocated = true;
+#endif
+          return MHD_SC_OK; /* Success exit point */
+        }
+        free (d->events.data.poll.fds);
+      }
+    }
+    MHD_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \
+                 "Failed to allocate memory for fd_sets for the daemon");
+    return MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE;
+    break;
+#endif /* MHD_USE_POLL */
+#ifdef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+    /* The event FD has been created during pre-initialisations of the events */
+    mhd_assert (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd);
+    /* The pointer has been set to NULL during pre-initialisations of the events */
+    mhd_assert (NULL == d->events.data.epoll.events);
+    mhd_assert (0 == d->events.data.epoll.num_elements);
+    if (1)
+    {
+      size_t num_elements;
+      const size_t upper_limit = (sizeof(void*) >= 8) ? 4096 : 1024;
+
+      num_elements = d->conns.cfg.count_limit;
+#ifdef MHD_USE_THREADS
+      ++num_elements;  /* For ITC */
+#endif
+      if (MHD_INVALID_SOCKET != d->net.listen)
+        ++num_elements;
+
+      /* Trade neglectable performance penalty for memory saving */
+      /* Very large amount of new events processed in batches */
+      if (num_elements > upper_limit)
+        num_elements = upper_limit;
+
+      d->events.data.epoll.events =
+        (struct epoll_event *) malloc (sizeof(struct epoll_event)
+                                       * num_elements);
+      if (NULL != d->events.data.epoll.events)
+      {
+        d->events.data.epoll.num_elements = num_elements;
+#ifndef NDEBUG
+        d->dbg.events_allocated = true;
+#endif
+        return MHD_SC_OK; /* Success exit point */
+      }
+    }
+    MHD_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \
+                 "Failed to allocate memory for fd_sets for the daemon");
+    return MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE;
+    break;
+#endif /* MHD_USE_EPOLL */
+#ifndef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+#endif /* ! MHD_USE_SELECT */
+#ifndef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+#endif /* ! MHD_USE_POLL */
+#ifndef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+#endif /* ! MHD_USE_EPOLL */
+  case mhd_POLL_TYPE_NOT_SET_YET:
+  default:
+    mhd_assert (0 && "Impossible value");
+  }
+  MHD_UNREACHABLE_;
+  return MHD_SC_INTERNAL_ERROR;
+}
+
+
+/**
+ * Deallocate events data
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+deallocate_events (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (0 != d->conns.cfg.count_limit);
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+  if (mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type)
+  {
+    mhd_assert (0 && "Wrong workflow");
+    MHD_UNREACHABLE_;
+    return;
+  }
+#ifdef MHD_USE_SELECT
+  else if (mhd_POLL_TYPE_SELECT == d->events.poll_type)
+  {
+    mhd_assert (NULL != d->events.data.select.efds);
+    mhd_assert (NULL != d->events.data.select.wfds);
+    mhd_assert (NULL != d->events.data.select.rfds);
+    free (d->events.data.select.efds);
+    free (d->events.data.select.wfds);
+    free (d->events.data.select.rfds);
+  }
+#endif /* MHD_USE_SELECT */
+#ifdef MHD_USE_POLL
+  else if (mhd_POLL_TYPE_POLL == d->events.poll_type)
+  {
+    mhd_assert (NULL != d->events.data.poll.rel);
+    mhd_assert (NULL != d->events.data.poll.fds);
+    free (d->events.data.poll.rel);
+    free (d->events.data.poll.fds);
+  }
+#endif /* MHD_USE_POLL */
+#ifdef MHD_USE_EPOLL
+  else if (mhd_POLL_TYPE_EPOLL == d->events.poll_type)
+  {
+    mhd_assert (0 != d->events.data.epoll.num_elements);
+    mhd_assert (NULL != d->events.data.epoll.events);
+    free (d->events.data.epoll.events);
+  }
+#endif /* MHD_USE_EPOLL */
+#ifndef NDEBUG
+  d->dbg.events_allocated = false;
+#endif
+  return;
+}
+
+
+/**
+ * Initialise daemon's ITC
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+init_itc (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+#ifdef MHD_USE_THREADS
+  // TODO: add and process "thread unsafe" daemon's option
+  if (! mhd_itc_init (&(d->threading.itc)))
+  {
+#if defined(MHD_ITC_EVENTFD_)
+    MHD_LOG_MSG ( \
+      d, MHD_SC_ITC_INITIALIZATION_FAILED, \
+      "Failed to initialise eventFD for inter-thread communication");
+#elif defined(MHD_ITC_PIPE_)
+    MHD_LOG_MSG ( \
+      d, MHD_SC_ITC_INITIALIZATION_FAILED, \
+      "Failed to create a pipe for inter-thread communication");
+#elif defined(MHD_ITC_SOCKETPAIR_)
+    MHD_LOG_MSG ( \
+      d, MHD_SC_ITC_INITIALIZATION_FAILED, \
+      "Failed to create a socketpair for inter-thread communication");
+#else
+#warning Missing expicit handling of the ITC type
+    MHD_LOG_MSG ( \
+      d, MHD_SC_ITC_INITIALIZATION_FAILED, \
+      "Failed to initialise inter-thread communication");
+#endif
+    return MHD_SC_ITC_INITIALIZATION_FAILED;
+  }
+#endif /* MHD_USE_THREADS */
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Deallocate events data
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+deinit_itc (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+#ifdef MHD_USE_THREADS
+  // TODO: add and process "thread unsafe" daemon's option
+  mhd_assert (! mhd_ITC_IS_INVALID (d->threading.itc));
+  mhd_itc_destroy (d->threading.itc);
+#endif /* MHD_USE_THREADS */
+}
+
+
+/**
+ * The final part of events initialisation: pre-add ITC and listening FD to
+ * the monitored items (if supported by monitoring syscall).
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (d->dbg.events_allocated);
+  mhd_assert (! d->dbg.events_fully_inited);
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+#ifdef MHD_USE_THREADS
+  mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+#endif
+
+  switch (d->events.poll_type)
+  {
+  case mhd_POLL_TYPE_EXT:
+    mhd_assert (NULL != d->events.data.ext.cb);
+    /* Nothing to do with the external events */
+    // FIXME: Register the ITC and the listening NOW?
+    return MHD_SC_OK;
+    break;
+#ifdef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+    mhd_assert (NULL != d->events.data.select.rfds);
+    mhd_assert (NULL != d->events.data.select.wfds);
+    mhd_assert (NULL != d->events.data.select.efds);
+    /* Nothing to do when using 'select()' */
+    return MHD_SC_OK;
+    break;
+#endif /* MHD_USE_SELECT */
+#ifdef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+    mhd_assert (NULL != d->events.data.poll.fds);
+    mhd_assert (NULL != d->events.data.poll.rel);
+    if (1)
+    {
+      unsigned int i;
+      i = 0;
+#ifdef MHD_USE_THREADS
+      d->events.data.poll.fds[i].fd = mhd_itc_r_fd (d->threading.itc);
+      d->events.data.poll.fds[i].events = MHD_POLL_IN;
+      d->events.data.poll.rel[i].fd_id = mhd_SOCKET_REL_MARKER_ITC;
+      ++i;
+#endif
+      if (MHD_INVALID_SOCKET != d->net.listen.fd)
+      {
+        d->events.data.poll.fds[i].fd = d->net.listen.fd;
+        d->events.data.poll.fds[i].events = MHD_POLL_IN;
+        d->events.data.poll.rel[i].fd_id = mhd_SOCKET_REL_MARKER_LISTEN;
+      }
+    }
+    return MHD_SC_OK;
+    break;
+#endif /* MHD_USE_POLL */
+#ifdef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+    mhd_assert (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd);
+    mhd_assert (NULL != d->events.data.epoll.events);
+    mhd_assert (0 < d->events.data.epoll.num_elements);
+    if (1)
+    {
+      struct epoll_event reg_event;
+#ifdef MHD_USE_THREADS
+      reg_event.events = EPOLLIN;
+      reg_event.data.u64 = (uint64_t) mhd_SOCKET_REL_MARKER_ITC; /* uint64_t is used in the epoll header */
+      if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD,
+                          mhd_itc_r_fd (d->threading.itc), reg_event))
+      {
+        MHD_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
+                     "Failed to add ITC fd to the epoll monitoring.");
+        return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE;
+      }
+#endif
+      if (MHD_INVALID_SOCKET != d->net.listen.fd)
+      {
+        reg_event.events = EPOLLIN;
+        reg_event.data.u64 = (uint64_t) mhd_SOCKET_REL_MARKER_LISTEN; /* uint64_t is used in the epoll header */
+        if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD,
+                            d->net.listen.fd, reg_event))
+        {
+          MHD_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
+                       "Failed to add listening fd to the epoll monitoring.");
+          return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE;
+        }
+      }
+    }
+    return MHD_SC_OK;
+    break;
+#endif /* MHD_USE_EPOLL */
+#ifndef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+#endif /* ! MHD_USE_SELECT */
+#ifndef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+#endif /* ! MHD_USE_POLL */
+#ifndef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+#endif /* ! MHD_USE_EPOLL */
+  case mhd_POLL_TYPE_NOT_SET_YET:
+  default:
+    mhd_assert (0 && "Impossible value");
+  }
+  MHD_UNREACHABLE_;
+  return MHD_SC_INTERNAL_ERROR;
+}
+
+
+/**
+ * Initialise daemon connections' data.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+init_individual_conns (struct MHD_Daemon *restrict d,
+                       struct DaemonOptions *restrict s)
+{
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (0 != d->conns.cfg.count_limit);
+
+  mhd_DLINKEDL_INIT_LIST (&(d->conns),all_conn);
+  mhd_DLINKEDL_INIT_LIST (&(d->conns),def_timeout);
+  mhd_DLINKEDL_INIT_LIST (&(d->conns),to_clean);
+  d->conns.count = 0;
+  d->conns.block_new = false;
+
+  d->conns.cfg.mem_pool_size = s->conn_memory_limit;
+  if (0 == d->conns.cfg.mem_pool_size)
+    d->conns.cfg.mem_pool_size = 32 * 1024;
+  else if (256 > d->conns.cfg.mem_pool_size)
+    d->conns.cfg.mem_pool_size = 256;
+
+#ifndef NDEBUG
+  d->dbg.connections_inited = true;
+#endif
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Prepare daemon-local (worker daemon for thread pool mode) threading data
+ * and finish events initialising.
+ * To be used only with non-master daemons.
+ * Do not start the thread even if configured for the internal threads.
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+init_individual_thread_data_events_conns (struct MHD_Daemon *restrict d,
+                                          struct DaemonOptions *restrict s)
+{
+  enum MHD_StatusCode res;
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (! d->dbg.connections_inited);
+
+  res = allocate_events (d);
+  if (MHD_SC_OK != res)
+    return res;
+
+  res = init_itc (d);
+  if (MHD_SC_OK == res)
+  {
+    res = add_itc_and_listen_to_monitoring (d);
+
+    if (MHD_SC_OK == res)
+    {
+#ifndef NDEBUG
+      d->dbg.events_fully_inited = true;
+#endif
+#ifdef MHD_USE_THREADS
+      mhd_thread_handle_ID_set_invalid (&(d->threading.tid));
+      d->threading.stop_requested = false;
+#endif /* MHD_USE_THREADS */
+#ifndef NDEBUG
+      d->dbg.threading_inited = true;
+#endif
+
+      res = init_individual_conns (d, s);
+      if (MHD_SC_OK == res)
+        return MHD_SC_OK;
+    }
+    deinit_itc (d);
+  }
+  deallocate_events (d);
+  mhd_assert (MHD_SC_OK != res);
+  return res;
+}
+
+
+/**
+ * Deinit daemon-local (worker daemon for thread pool mode) threading data
+ * and deallocate events.
+ * To be used only with non-master daemons.
+ * Do not start the thread even if configured for the internal threads.
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+deinit_individual_thread_data_events_conns (struct MHD_Daemon *restrict d)
+{
+  deinit_itc (d);
+  deallocate_events (d);
+  mhd_assert (NULL == mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn));
+  mhd_assert (NULL == mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready));
+#ifndef NDEBUG
+  d->dbg.events_fully_inited = false;
+#endif
+}
+
+
+/**
+ * Set the maximum number of handled connections for the daemon.
+ * Works only for global limit, does not work for the worker daemon.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+set_connections_total_limits (struct MHD_Daemon *restrict d,
+                              struct DaemonOptions *restrict s)
+{
+  unsigned int limit_by_conf;
+  unsigned int limit_by_num;
+  unsigned int limit_by_select;
+  unsigned int resulting_limit;
+  bool error_by_fd_setsize;
+  unsigned int num_worker_daemons;
+
+  mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+
+  if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)
+  {
+    mhd_assert (MHD_WM_WORKER_THREADS == s->work_mode.mode);
+    if ((0 != s->global_connection_limit) &&
+        (0 != s->work_mode.params.num_worker_threads) &&
+        (s->global_connection_limit < s->work_mode.params.num_worker_threads))
+    {
+      MHD_LOG_MSG ( \
+        d, MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL, \
+        "The limit specified by MHD_D_O_GLOBAL_CONNECTION_LIMIT is smaller " \
+        "then the number of worker threads.");
+      return MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL;
+    }
+  }
+  num_worker_daemons = 1;
+#ifdef MHD_USE_THREADS
+  if (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type))
+    num_worker_daemons = s->work_mode.params.num_worker_threads;
+#endif /* MHD_USE_THREADS */
+
+  limit_by_conf = s->global_connection_limit;
+  limit_by_num = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+  limit_by_select = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+
+  error_by_fd_setsize = false;
+#ifdef MHD_POSIX_SOCKETS
+  if (1)
+  {
+    limit_by_num = d->net.cfg.max_fd_num;
+    if (0 != limit_by_num)
+    {
+      /* Find the upper limit.
+         The real limit is lower, as any other process FDs will use the slots
+         in the allowed numbers range */
+      limit_by_num -= 3; /* The numbers zero, one and two are used typically */
+#ifdef MHD_USE_THREADS
+      limit_by_num -= mhd_ITC_NUM_FDS * num_worker_daemons;
+#endif /* MHD_USE_THREADS */
+      if (MHD_INVALID_SOCKET != d->net.listen.fd)
+        --limit_by_num; /* One FD is used for the listening socket */
+      if ((num_worker_daemons > limit_by_num) ||
+          (limit_by_num > d->net.cfg.max_fd_num) /* Underflow */)
+      {
+        if (d->net.cfg.max_fd_num == s->fd_number_limit)
+        {
+          MHD_LOG_MSG ( \
+            d, MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT, \
+            "The limit specified by MHD_D_O_FD_NUMBER_LIMIT is too strict " \
+            "for this daemon settings.");
+          return MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT;
+        }
+        else
+        {
+          mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type);
+          error_by_fd_setsize = true;
+        }
+      }
+    }
+    else
+      limit_by_num = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+  }
+#elif defined(MHD_WINSOCK_SOCKETS)
+  if (1)
+  {
+#ifdef MHD_USE_SELECT
+    if ((mhd_DAEMON_TYPE_SINGLE == d->threading.d_type) &&
+        (mhd_POLL_TYPE_SELECT == d->events.poll_type))
+    {
+      /* W32 limits the total number (count) of sockets used for select() */
+      unsigned int limit_per_worker;
+
+      limit_per_worker = FD_SETSIZE;
+      if (MHD_INVALID_SOCKET != d->net.listen.fd)
+        --limit_per_worker;  /* The slot for the listening socket */
+#ifdef MHD_USE_THREADS
+      --limit_per_worker;  /* The slot for the ITC */
+#endif /* MHD_USE_THREADS */
+      if ((0 == limit_per_worker) || (limit_per_worker > FD_SETSIZE))
+        error_by_fd_setsize = true;
+      else
+      {
+        limit_by_select = limit_per_worker * num_worker_daemons;
+        if (limit_by_select / limit_per_worker != num_worker_daemons)
+          limit_by_select = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+      }
+    }
+#endif /* MHD_USE_SELECT */
+    (void) 0; /* Mute compiler warning */
+  }
+#endif /* MHD_POSIX_SOCKETS */
+  if (error_by_fd_setsize)
+  {
+    MHD_LOG_MSG ( \
+      d, MHD_SC_SYS_FD_SETSIZE_TOO_STRICT, \
+      "The FD_SETSIZE is too strict to run daemon with the polling " \
+      "by select() and with the specified number of workers.");
+    return MHD_SC_SYS_FD_SETSIZE_TOO_STRICT;
+  }
+
+  if (0 != limit_by_conf)
+  {
+    /* The number has bet set explicitly */
+    resulting_limit = limit_by_conf;
+  }
+  else
+  {
+    /* No user configuration provided */
+    unsigned int suggested_limit;
+#ifndef MHD_WINSOCK_SOCKETS
+#define TYPICAL_NOFILES_LIMIT (1024)  /* The usual limit for the number of open FDs */
+    suggested_limit = TYPICAL_NOFILES_LIMIT;
+    suggested_limit -= 3; /* The numbers zero, one and two are used typically */
+#ifdef MHD_USE_THREADS
+    suggested_limit -= mhd_ITC_NUM_FDS * num_worker_daemons;
+#endif /* MHD_USE_THREADS */
+    if (MHD_INVALID_SOCKET != d->net.listen.fd)
+      --suggested_limit; /* One FD is used for the listening socket */
+    if (suggested_limit > TYPICAL_NOFILES_LIMIT)
+      suggested_limit = 0; /* Overflow */
+#else  /* MHD_WINSOCK_SOCKETS */
+#ifdef _WIN64
+    suggested_limit = 2048;
+#else
+    suggested_limit = 1024;
+#endif
+#endif /* MHD_WINSOCK_SOCKETS */
+    if (suggested_limit < num_worker_daemons)
+    {
+      /* Use at least one connection for every worker daemon and
+         let the system to restrict the new connections if they are above
+         the system limits. */
+      suggested_limit = num_worker_daemons;
+    }
+    resulting_limit = suggested_limit;
+  }
+  if (resulting_limit > limit_by_num)
+    resulting_limit = limit_by_num;
+
+  if (resulting_limit > limit_by_select)
+    resulting_limit = limit_by_select;
+
+  mhd_assert (resulting_limit >= num_worker_daemons);
+  d->conns.cfg.count_limit = resulting_limit;
+
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Set correct daemon threading type.
+ * Set the number of workers for thread pool type.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) \
+  MHD_FN_MUST_CHECK_RESULT_ static inline enum MHD_StatusCode
+set_d_threading_type (struct MHD_Daemon *restrict d)
+{
+  switch (d->wmode_int)
+  {
+  case mhd_WM_INT_EXTERNAL_EVENTS_EDGE:
+  case mhd_WM_INT_EXTERNAL_EVENTS_LEVEL:
+    mhd_assert (! mhd_WM_INT_HAS_THREADS (d->wmode_int));
+    mhd_assert (mhd_POLL_TYPE_EXT == d->events.poll_type);
+    mhd_assert (NULL != d->events.data.ext.cb);
+#ifdef MHD_USE_THREADS
+    d->threading.d_type = mhd_DAEMON_TYPE_SINGLE;
+#endif /* MHD_USE_THREADS */
+    return MHD_SC_OK;
+  case mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS:
+    mhd_assert (! mhd_WM_INT_HAS_THREADS (d->wmode_int));
+    mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type);
+#ifdef MHD_USE_THREADS
+    d->threading.d_type = mhd_DAEMON_TYPE_SINGLE;
+#endif /* MHD_USE_THREADS */
+    return MHD_SC_OK;
+#ifdef MHD_USE_THREADS
+  case mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD:
+    mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int));
+    mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type);
+    d->threading.d_type = mhd_DAEMON_TYPE_SINGLE;
+    return MHD_SC_OK;
+  case mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION:
+    mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int));
+    mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type);
+    d->threading.d_type = mhd_DAEMON_TYPE_LISTEN_ONLY;
+    return MHD_SC_OK;
+  case mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL:
+    mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int));
+    mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type);
+    mhd_assert (mhd_POLL_TYPE_EPOLL != d->events.poll_type);
+    d->threading.d_type = mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY;
+    return MHD_SC_OK;
+#else  /* ! MHD_USE_THREADS */
+  case mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD:
+  case mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION:
+  case mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL:
+#endif /* ! MHD_USE_THREADS */
+  default:
+    mhd_assert (0 && "Impossible value");
+  }
+  MHD_UNREACHABLE_;
+  return MHD_SC_INTERNAL_ERROR;
+}
+
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * De-initialise workers pool, including workers daemons.
+ * The threads must be not running.
+ * @param d the daemon object
+ * @param num_workers the number of workers to deinit
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+deinit_workers_pool (struct MHD_Daemon *restrict d,
+                     unsigned int num_workers)
+{
+  unsigned int i;
+  mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (NULL != d->threading.hier.pool.workers);
+  mhd_assert ((2 <= d->threading.hier.pool.num) || \
+              (mhd_DAEMON_STATE_STARTING == d->state));
+  mhd_assert ((num_workers == d->threading.hier.pool.num) || \
+              (mhd_DAEMON_STATE_STARTING == d->state));
+  mhd_assert ((mhd_DAEMON_STATE_STOPPING == d->state) || \
+              (mhd_DAEMON_STATE_STARTING == d->state));
+
+  /* Deinitialise in reverse order */
+  for (i = num_workers - 1; num_workers > i; --i)
+  { /* Note: loop exits after underflow of 'i' */
+    struct MHD_Daemon *const worker = d->threading.hier.pool.workers + i;
+    deinit_individual_thread_data_events_conns (worker);
+#ifdef MHD_USE_EPOLL
+    if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type)
+      deinit_epoll (worker);
+#endif /* MHD_USE_EPOLL */
+  }
+  free (d->threading.hier.pool.workers);
+#ifndef NDEBUG
+  d->dbg.thread_pool_inited = false;
+#endif
+}
+
+
+/**
+ * Initialise workers pool, including workers daemons.
+ * Do not start the threads.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+init_workers_pool (struct MHD_Daemon *restrict d,
+                   struct DaemonOptions *restrict s)
+{
+  enum MHD_StatusCode res;
+  size_t workers_pool_size;
+  unsigned int conn_per_daemon;
+  unsigned int num_workers;
+  unsigned int conn_remainder;
+  unsigned int i;
+
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int);
+  mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (mhd_POLL_TYPE_NOT_SET_YET < d->events.poll_type);
+  mhd_assert (1 < s->work_mode.params.num_worker_threads);
+  mhd_assert (0 != d->conns.cfg.count_limit);
+  mhd_assert (s->work_mode.params.num_worker_threads <= \
+              d->conns.cfg.count_limit);
+  mhd_assert (! d->dbg.thread_pool_inited);
+
+  num_workers = s->work_mode.params.num_worker_threads;
+  workers_pool_size =
+    (sizeof(struct MHD_Daemon) * num_workers);
+  if (workers_pool_size / num_workers != sizeof(struct MHD_Daemon))
+  { /* Overflow */
+    MHD_LOG_MSG ( \
+      d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \
+      "The size of the thread pool is too large.");
+    return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
+  }
+
+#ifndef NDEBUG
+  mhd_itc_set_invalid (&(d->threading.itc));
+  mhd_thread_handle_ID_set_invalid (&(d->threading.tid));
+#endif
+
+  d->threading.hier.pool.workers = malloc (workers_pool_size);
+  if (NULL == d->threading.hier.pool.workers)
+  {
+    MHD_LOG_MSG ( \
+      d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \
+      "Failed to allocate memory for the thread pool.");
+    return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
+  }
+
+  conn_per_daemon = d->conns.cfg.count_limit / num_workers;
+  conn_remainder = d->conns.cfg.count_limit % num_workers;
+  res = MHD_SC_OK;
+  for (i = 0; num_workers > i; ++i)
+  {
+    struct MHD_Daemon *restrict const worker =
+      d->threading.hier.pool.workers + i;
+    memcpy (worker, d, sizeof(struct MHD_Daemon));
+    worker->threading.d_type = mhd_DAEMON_TYPE_WORKER;
+    worker->threading.hier.master = d;
+    worker->conns.cfg.count_limit = conn_per_daemon;
+    if (conn_remainder > i)
+      worker->conns.cfg.count_limit++; /* Distribute the reminder */
+#ifdef MHD_USE_EPOLL
+    if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type)
+    {
+      if (0 == i)
+      {
+        mhd_assert (0 <= d->events.data.epoll.e_fd);
+        /* Move epoll control FD from the master daemon to the first worker */
+        /* The FD has been copied by memcpy(). Clean-up the master daemon. */
+        d->events.data.epoll.e_fd = MHD_INVALID_SOCKET;
+      }
+      else
+        res = init_epoll (worker);
+    }
+#endif /* MHD_USE_EPOLL */
+    if (MHD_SC_OK == res)
+    {
+      res = init_individual_thread_data_events_conns (worker, s);
+      if (MHD_SC_OK == res)
+        continue; /* Process the next worker */
+
+#ifdef MHD_USE_EPOLL
+      if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type)
+        deinit_epoll (worker);
+#endif /* MHD_USE_EPOLL */
+
+      /* Below is the clean-up of the current slot */
+    }
+    free (worker);
+    break;
+  }
+  if (num_workers == i)
+  {
+    mhd_assert (MHD_SC_OK == res);
+#ifndef NDEBUG
+    d->dbg.thread_pool_inited = true;
+    d->dbg.threading_inited = true;
+#endif
+    d->threading.hier.pool.num = num_workers;
+    return MHD_SC_OK;
+  }
+
+  /* Below is a clean-up */
+
+  mhd_assert (MHD_SC_OK != res);
+  deinit_workers_pool (d, i);
+  return res;
+}
+
+
+#endif /* MHD_USE_THREADS */
+
+/**
+ * Initialise threading and inter-thread communications.
+ * Also finish initialisation of events processing and initialise daemon's
+ * connection data.
+ * Do not start the thread even if configured for the internal threads.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+daemon_init_threading_and_conn (struct MHD_Daemon *restrict d,
+                                struct DaemonOptions *restrict s)
+{
+  enum MHD_StatusCode res;
+
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (mhd_POLL_TYPE_NOT_SET_YET != d->events.poll_type);
+
+  res = set_d_threading_type (d);
+  if (MHD_SC_OK != res)
+    return res;
+
+  res = set_connections_total_limits (d, s);
+  if (MHD_SC_OK != res)
+    return res;
+
+  d->threading.cfg.stack_size = s->stack_size;
+
+  if (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type))
+    res = init_individual_thread_data_events_conns (d, s);
+  else
+  {
+#ifdef MHD_USE_THREADS
+    res = init_workers_pool (d, s);
+#else  /* ! MHD_USE_THREADS */
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    return MHD_SC_INTERNAL_ERROR;
+#endif /* ! MHD_USE_THREADS */
+  }
+  mhd_assert (d->dbg.events_allocated || \
+              mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type) || \
+              ! d->dbg.events_allocated);
+  mhd_assert (! d->dbg.thread_pool_inited || \
+              mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type) || \
+              d->dbg.thread_pool_inited);
+  mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+  mhd_assert (! d->dbg.events_allocated || d->dbg.connections_inited);
+  mhd_assert (! d->dbg.connections_inited || d->dbg.events_allocated);
+  return res;
+}
+
+
+/**
+ * De-initialise threading and inter-thread communications.
+ * Also deallocate events and de-initialise daemon's connection data.
+ * No daemon-manged threads should be running.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+daemon_deinit_threading_and_conn (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (d->dbg.threading_inited);
+  mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+  if (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type))
+  {
+    mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL != d->wmode_int);
+    mhd_assert (d->dbg.connections_inited);
+    mhd_assert (d->dbg.events_allocated);
+    mhd_assert (! d->dbg.thread_pool_inited);
+    deinit_individual_thread_data_events_conns (d);
+  }
+  else
+  {
+#ifdef MHD_USE_THREADS
+    mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int);
+    mhd_assert (! d->dbg.connections_inited);
+    mhd_assert (! d->dbg.events_allocated);
+    mhd_assert (d->dbg.thread_pool_inited);
+    deinit_workers_pool (d, d->threading.hier.pool.num);
+#else  /* ! MHD_USE_THREADS */
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    (void) 0;
+#endif /* ! MHD_USE_THREADS */
+  }
+}
+
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * Start the daemon individual single thread.
+ * Works both for single thread daemons and for worker daemon for thread
+ * pool mode.
+ * Must be called only for daemons with internal threads.
+ * @param d the daemon object, must be completely initialised
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+start_individual_daemon_thread (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.threading_inited);
+  mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int));
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (! mhd_thread_handle_ID_is_valid_handle (d->threading.tid));
+
+  if (mhd_DAEMON_TYPE_SINGLE == d->threading.d_type)
+  {
+    if (! mhd_create_named_thread ( \
+          &(d->threading.tid), "MHD-single", \
+          d->threading.cfg.stack_size, \
+          &mhd_worker_all_events, \
+          (void*) d))
+    {
+      MHD_LOG_MSG (d, MHD_SC_THREAD_MAIN_LAUNCH_FAILURE, \
+                   "Failed to start daemon main thread.");
+      return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE;
+    }
+  }
+  else if (mhd_DAEMON_TYPE_WORKER == d->threading.d_type)
+  {
+    if (! mhd_create_named_thread ( \
+          &(d->threading.tid), "MHD-worker", \
+          d->threading.cfg.stack_size, \
+          &mhd_worker_all_events, \
+          (void*) d))
+    {
+      MHD_LOG_MSG (d, MHD_SC_THREAD_WORKER_LAUNCH_FAILURE, \
+                   "Failed to start daemon worker thread.");
+      return MHD_SC_THREAD_WORKER_LAUNCH_FAILURE;
+    }
+  }
+  else if (mhd_DAEMON_TYPE_LISTEN_ONLY == d->threading.d_type)
+  {
+    if (! mhd_create_named_thread ( \
+          &(d->threading.tid), "MHD-listen", \
+          d->threading.cfg.stack_size, \
+          &mhd_worker_listening_only, \
+          (void*) d))
+    {
+      MHD_LOG_MSG (d, MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE, \
+                   "Failed to start daemon listening thread.");
+      return MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE;
+    }
+  }
+  else
+  {
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    return MHD_SC_INTERNAL_ERROR;
+  }
+  mhd_assert (mhd_thread_handle_ID_is_valid_handle (d->threading.tid));
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Stop the daemon individual single thread.
+ * Works both for single thread daemons and for worker daemon for thread
+ * pool mode.
+ * Must be called only for daemons with internal threads.
+ * @param d the daemon object, must be completely initialised
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+stop_individual_daemon_thread (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.threading_inited);
+  mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int));
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert ((mhd_DAEMON_STATE_STOPPING == d->state) || \
+              (mhd_DAEMON_STATE_STARTING == d->state));
+  mhd_assert (mhd_thread_handle_ID_is_valid_handle (d->threading.tid));
+
+  mhd_daemon_trigger_itc (d);
+  if (! mhd_thread_handle_ID_join_thread (d->threading.tid))
+  {
+    MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \
+                 "Failed to stop daemon main thread.");
+  }
+}
+
+
+/**
+ * Stop all worker threads in the thread pool.
+ * Must be called only for master daemons with thread pool.
+ * @param d the daemon object, the workers threads must be running
+ * @param num_workers the number of threads to stop
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+stop_worker_pool_threads (struct MHD_Daemon *restrict d,
+                          unsigned int num_workers)
+{
+  unsigned int i;
+  mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (NULL != d->threading.hier.pool.workers);
+  mhd_assert (0 != d->threading.hier.pool.num);
+  mhd_assert (d->dbg.thread_pool_inited);
+  mhd_assert (2 <= d->threading.hier.pool.num);
+  mhd_assert ((num_workers == d->threading.hier.pool.num) || \
+              (mhd_DAEMON_STATE_STARTING == d->state));
+  mhd_assert ((mhd_DAEMON_STATE_STOPPING == d->state) || \
+              (mhd_DAEMON_STATE_STARTING == d->state));
+
+  /* Process all the threads in the reverse order */
+
+  /* Trigger all threads */
+  for (i = num_workers - 1; num_workers > i; --i)
+  { /* Note: loop exits after underflow of 'i' */
+    mhd_assert (mhd_ITC_IS_VALID ( \
+                  d->threading.hier.pool.workers[i].threading.itc));
+    mhd_daemon_trigger_itc (d->threading.hier.pool.workers + i);
+  }
+
+  /* Collect all threads */
+  for (i = num_workers - 1; num_workers > i; --i)
+  { /* Note: loop exits after underflow of 'i' */
+    struct MHD_Daemon *const restrict worker =
+      d->threading.hier.pool.workers + i;
+    mhd_assert (mhd_thread_handle_ID_is_valid_handle (worker->threading.tid));
+    if (! mhd_thread_handle_ID_join_thread (worker->threading.tid))
+    {
+      MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \
+                   "Failed to stop a worker thread.");
+    }
+  }
+}
+
+
+/**
+ * Start the workers pool threads.
+ * Must be called only for master daemons with thread pool.
+ * @param d the daemon object, must be completely initialised
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+start_worker_pool_threads (struct MHD_Daemon *restrict d)
+{
+  enum MHD_StatusCode res;
+  unsigned int i;
+
+  mhd_assert (d->dbg.threading_inited);
+  mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int));
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (d->dbg.thread_pool_inited);
+  mhd_assert (2 <= d->threading.hier.pool.num);
+
+  res = MHD_SC_OK;
+
+  for (i = 0; d->threading.hier.pool.num > i; ++i)
+  {
+    res = start_individual_daemon_thread (d->threading.hier.pool.workers + i);
+    if (MHD_SC_OK != res)
+      break;
+  }
+  if (d->threading.hier.pool.num == i)
+  {
+    mhd_assert (MHD_SC_OK == res);
+    return MHD_SC_OK;
+  }
+
+  stop_worker_pool_threads (d, i);
+  mhd_assert (MHD_SC_OK != res);
+  return res;
+}
+
+
+#endif /* MHD_USE_THREADS */
+
+/**
+ * Start the daemon internal threads, if the daemon configured to use them.
+ * @param d the daemon object, must be completely initialised
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+daemon_start_threads (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (d->dbg.threading_inited);
+  mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+  if (mhd_WM_INT_HAS_THREADS (d->wmode_int))
+  {
+#ifdef MHD_USE_THREADS
+    if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL != d->wmode_int)
+    {
+      mhd_assert (d->dbg.threading_inited);
+      mhd_assert (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY != d->threading.d_type);
+      return start_individual_daemon_thread (d);
+    }
+    else
+    {
+      mhd_assert (d->dbg.thread_pool_inited);
+      mhd_assert (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY == d->threading.d_type);
+      return start_worker_pool_threads (d);
+    }
+#else  /* ! MHD_USE_THREADS */
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    return MHD_SC_INTERNAL_ERROR;
+#endif /* ! MHD_USE_THREADS */
+  }
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Stop the daemon internal threads, if the daemon configured to use them.
+ * @param d the daemon object, the threads (if any) must be started
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+daemon_stop_threads (struct MHD_Daemon *restrict d)
+{
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (d->dbg.threading_inited);
+  if (mhd_WM_INT_HAS_THREADS (d->wmode_int))
+  {
+#ifdef MHD_USE_THREADS
+    if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL != d->wmode_int)
+    {
+      mhd_assert (d->dbg.threading_inited);
+      mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+      stop_individual_daemon_thread (d);
+      return;
+    }
+    else
+    {
+      mhd_assert (d->dbg.thread_pool_inited);
+      mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+      stop_worker_pool_threads (d, d->threading.hier.pool.num);
+      return;
+    }
+#else  /* ! MHD_USE_THREADS */
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    return MHD_SC_INTERNAL_ERROR;
+#endif /* ! MHD_USE_THREADS */
+  }
+}
+
+
+/**
+ * Internal daemon initialisation function.
+ * This function calls all required initialisation stages one-by-one.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode
+daemon_start_internal (struct MHD_Daemon *restrict d,
+                       struct DaemonOptions *restrict s)
+{
+  enum MHD_StatusCode res;
+
+  res = daemon_set_work_mode (d, s);
+  if (MHD_SC_OK != res)
+    return res;
+
+  res = daemon_init_net (d, s);
+  if (MHD_SC_OK != res)
+    return res;
+
+  // TODO: Other init
+
+  res = daemon_init_threading_and_conn (d, s);
+  if (MHD_SC_OK == res)
+  {
+    mhd_assert (d->dbg.net_inited);
+    mhd_assert (d->dbg.threading_inited);
+    mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+
+    res = daemon_start_threads (d);
+    if (MHD_SC_OK == res)
+    {
+      return MHD_SC_OK;
+    }
+
+    /* Below is a clean-up path */
+    daemon_deinit_threading_and_conn (d);
+  }
+
+  daemon_deinit_net (d);
+  mhd_assert (MHD_SC_OK != res);
+  return res;
+}
+
+
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_MUST_CHECK_RESULT_ \
+  MHD_EXTERN_ enum MHD_StatusCode
+MHD_daemon_start (struct MHD_Daemon *daemon)
+{
+  struct MHD_Daemon *const d = daemon; /* a short alias */
+  struct DaemonOptions *const s = daemon->settings; /* a short alias */
+  enum MHD_StatusCode res;
+
+  if (mhd_DAEMON_STATE_NOT_STARTED != daemon->state)
+    return MHD_SC_TOO_LATE;
+
+  mhd_assert (NULL != s);
+
+  d->state = mhd_DAEMON_STATE_STARTING;
+  res = daemon_start_internal (d, s);
+
+  d->settings = NULL;
+  dsettings_release (s);
+
+  d->state =
+    (MHD_SC_OK == res) ? mhd_DAEMON_STATE_STARTED : mhd_DAEMON_STATE_FAILED;
+
+  return res;
+}
+
+
+MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_ void
+MHD_daemon_destroy (struct MHD_Daemon *daemon)
+{
+  bool not_yet_started = (mhd_DAEMON_STATE_NOT_STARTED == daemon->state);
+  bool has_failed = (mhd_DAEMON_STATE_FAILED == daemon->state);
+  mhd_assert (mhd_DAEMON_STATE_STOPPING > daemon->state);
+  mhd_assert (mhd_DAEMON_STATE_STARTING != daemon->state);
+
+  daemon->state = mhd_DAEMON_STATE_STOPPING;
+  if (not_yet_started)
+  {
+    mhd_assert (NULL != daemon->settings);
+    dsettings_release (daemon->settings);
+    return;
+  }
+  else if (! has_failed)
+  {
+    mhd_assert (NULL == daemon->settings);
+    mhd_assert (daemon->dbg.threading_inited);
+
+    daemon_stop_threads (daemon);
+
+    daemon_deinit_threading_and_conn (daemon);
+
+    daemon_deinit_net (daemon);
+  }
+  daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */
+
+  free (daemon);
+}

+ 303 - 0
src/mhd2/data_process.c

@@ -0,0 +1,303 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/data_process.c
+ * @brief  The implementation of data receiving, sending and processing
+ *         functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
+ * contributors.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "data_process.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "mhd_recv.h"
+
+#include "daemon_logger.h"
+
+/**
+ * Perform data receiving for the connection and try to detect the socket error
+ * type.
+ *
+ * @param c the connection to use
+ * @param has_err if 'true' then just check for the network error
+ *        type is performed
+ */
+void
+conn_process_recv (struct MHD_Connection restrict *c,
+                   bool has_err)
+{
+  ssize_t bytes_read;
+  uint_fast64_t dummy_buf;
+  void *buf;
+  size_t buf_size;
+  size_t received;
+  enum mhd_SocketError res;
+
+  mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+  mhd_assert ((NULL != c->read_buffer) || has_err);
+  mhd_assert ((c->read_buffer_size > c->read_buffer_offset) || has_err);
+  mhd_assert (! has_err || \
+              (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)));
+  mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \
+              has_err);
+
+  // TODO: TLS support: handshake/transport layer
+
+  if (! has_err ||
+      (NULL != c->read_buffer) || (c->read_buffer_size > c->read_buffer_offset))
+  {
+    buf = c->read_buffer + c->read_buffer_offset;
+    buf_size = c->read_buffer_size - c->read_buffer_offset;
+  }
+  else
+  {
+    buf = &dummy_buf;
+    buf_size = sizeof(buf);
+  }
+
+  res = mhd_recv (c,buf_size, buf, &received);
+
+  if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
+  {
+    /* Handle errors */
+    if (! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
+    {
+      /* Re-try last time to detect the error */
+      res = mhd_recv (c, &dummy_buf, sizeof(dummy_buf), &received);
+    }
+    if (mhd_SOCKET_ERR_IS_HARD (res))
+    {
+      c->sk_discnt_err = res;
+      c->sk_ready =
+        (enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
+                                   | mhd_SOCKET_NET_STATE_ERROR_READY);
+    }
+    return;
+  }
+
+  if (0 == bytes_read)
+    c->sk_rmt_shut_wr = true;
+
+  c->read_buffer_offset += (size_t) bytes_read;
+  MHD_update_last_activity_ (c); // TODO: centralise activity update
+  return;
+}
+
+
+#if 0 // TODO: report disconnect
+if ((bytes_read < 0) || socket_error)
+{
+  if (MHD_ERR_CONNRESET_ == bytes_read)
+  {
+    if ( (MHD_CONNECTION_INIT < c->state) &&
+         (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (c->daemon,
+                _ ("Socket has been disconnected when reading request.\n"));
+#endif
+      c->discard_request = true;
+    }
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_READ_ERROR);
+    return;
+  }
+
+#ifdef HAVE_MESSAGES
+  if (MHD_CONNECTION_INIT != c->state)
+    MHD_DLOG (c->daemon,
+              _ ("Connection socket is closed when reading " \
+                 "request due to the error: %s\n"),
+              (bytes_read < 0) ? str_conn_error_ (bytes_read) :
+              "detected c closure");
+#endif
+  CONNECTION_CLOSE_ERROR (c,
+                          NULL);
+  return;
+}
+
+#if 0 // TODO: handle remote shut WR
+if (0 == bytes_read)
+{ /* Remote side closed c. */   // FIXME: Actually NOT!
+  c->sk_rmt_shut_wr = true;
+  if ( (MHD_CONNECTION_INIT < c->state) &&
+       (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (c->daemon,
+              _ ("Connection was closed by remote side with incomplete "
+                 "request.\n"));
+#endif
+    c->discard_request = true;
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_CLIENT_ABORT);
+  }
+  else if (MHD_CONNECTION_INIT == c->state)
+    /* This termination code cannot be reported to the application
+     * because application has not been informed yet about this request */
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_COMPLETED_OK);
+  else
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_WITH_ERROR);
+  return;
+}
+#endif
+#endif
+
+MHD_INTERNAL bool
+mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c)
+{
+  const bool send_ready_state_known =
+    ((0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)) ||
+     mhd_D_IS_USING_EDGE_TRIG (c->daemon));
+  const bool has_sock_err =
+    (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready));
+  bool data_processed;
+  bool need_send;
+
+  data_processed = false;
+
+  if ( ((0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready)) &&
+        ((0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
+         || has_sock_err))
+       || (has_sock_err && c->sk_nonblck))
+  {
+    conn_process_recv (c, has_sock_err);
+    if (! conn_process_data (c))
+      return false;
+    data_processed = true;
+  }
+  need_send = ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) &&
+               (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)));
+
+  if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))
+  {
+    /* Perform sending if:
+     * + connection is ready for sending or
+     * + just formed send data, connection send ready status is not known and
+     *   connection socket is non-blocking */
+    /* Assuming that after finishing receiving phase, connection send buffers
+       should have some space as sending was performed before receiving or has
+       not been performed yet. */
+    if ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) ||
+        ((data_processed && ! send_ready_state_known && c->sk_nonblck) ||
+         (has_sock_err && c->sk_nonblck)))
+    {
+      conn_process_send (c, has_sock_err);
+      if (! conn_process_data (c))
+        return false;
+      data_processed = true;
+    }
+  }
+  if (! data_processed)
+    return conn_process_data (c);
+  return true;
+
+#if 0 // TODO: re-implement fasttrack as a single unified buffer sending
+  if (! force_close)
+  {
+    /* No need to check value of 'ret' here as closed connection
+     * cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */
+    if ( (MHD_EVENT_LOOP_INFO_WRITE == c->event_loop_info) &&
+         write_ready)
+    {
+      MHD_connection_handle_write (c);
+      ret = MHD_connection_handle_idle (c);
+      states_info_processed = true;
+    }
+  }
+  else
+  {
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_WITH_ERROR);
+    return MHD_connection_handle_idle (c);
+  }
+
+  if (! states_info_processed)
+  {   /* Connection is not read or write ready, but external conditions
+       * may be changed and need to be processed. */
+    ret = MHD_connection_handle_idle (c);
+  }
+  /* Fast track for fast connections. */
+  /* If full request was read by single read_handler() invocation
+     and headers were completely prepared by single MHD_connection_handle_idle()
+     then try not to wait for next sockets polling and send response
+     immediately.
+     As writeability of socket was not checked and it may have
+     some data pending in system buffers, use this optimization
+     only for non-blocking sockets. */
+  /* No need to check 'ret' as connection is always in
+   * MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */
+  else if (on_fasttrack && c->sk_nonblck)
+  {
+    if (MHD_CONNECTION_HEADERS_SENDING == c->state)
+    {
+      MHD_connection_handle_write (c);
+      /* Always call 'MHD_connection_handle_idle()' after each read/write. */
+      ret = MHD_connection_handle_idle (c);
+    }
+    /* If all headers were sent by single write_handler() and
+     * response body is prepared by single MHD_connection_handle_idle()
+     * call - continue. */
+    if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
+        (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
+    {
+      MHD_connection_handle_write (c);
+      ret = MHD_connection_handle_idle (c);
+    }
+  }
+
+  /* All connection's data and states are processed for this turn.
+   * If connection already has more data to be processed - use
+   * zero timeout for next select()/poll(). */
+  /* Thread-per-connection do not need global zero timeout as
+   * connections are processed individually. */
+  /* Note: no need to check for read buffer availability for
+   * TLS read-ready connection in 'read info' state as connection
+   * without space in read buffer will be marked as 'info block'. */
+  if ( (! c->daemon->data_already_pending) &&
+       (! MHD_D_IS_USING_THREAD_PER_CONN_ (c->daemon)) )
+  {
+    if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info))
+      c->daemon->data_already_pending = true;
+#ifdef HTTPS_SUPPORT
+    else if ( (c->tls_read_ready) &&
+              (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info)) )
+      c->daemon->data_already_pending = true;
+#endif /* HTTPS_SUPPORT */
+  }
+  return ret;
+#endif
+}

+ 44 - 0
src/mhd2/data_process.h

@@ -0,0 +1,44 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/data_process.h
+ * @brief  The declarations of data processing functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DATA_PROCESS_H
+#define MHD_DATA_PROCESS_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Perform connection receiving, sending and processing data.
+ * @param c the connection to use
+ * @return true if data processed successfully,
+ *         false if connection needs to be closed
+ */
+MHD_INTERNAL bool
+mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c);
+
+#endif /* ! MHD_DATA_PROCESS_H */

+ 604 - 0
src/mhd2/events_process.c

@@ -0,0 +1,604 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/events_process.c
+ * @brief  The implementation of events processing functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "events_process.h"
+
+#include "sys_poll.h"
+
+#include "mhd_public_api.h"
+
+#include "mhd_itc.h"
+
+#include "mhd_panic.h"
+
+#include "mhd_sockets_macros.h"
+
+#include "mhd_daemon.h"
+#include "daemon_logger.h"
+#include "mhd_connection.h"
+#include "daemon_add_conn.h"
+
+
+MHD_FN_PAR_NONNULL_ (1) static void
+update_conn_net_status (struct MHD_Daemon *restrict d,
+                        struct MHD_Connection *restrict c,
+                        bool recv_ready,
+                        bool send_ready,
+                        bool err_state)
+{
+  enum mhd_SocketNetState sk_state;
+
+  mhd_assert (d == c->daemon);
+
+  sk_state = mhd_SOCKET_NET_STATE_NOTHING;
+  if (recv_ready)
+    sk_state = (enum mhd_SocketNetState)
+               (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_RECV_READY);
+  if (send_ready)
+    sk_state = (enum mhd_SocketNetState)
+               (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_SEND_READY);
+  if (err_state)
+    sk_state = (enum mhd_SocketNetState)
+               (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_ERROR_READY);
+  c->sk_ready = sk_state;
+
+  if (NULL != mhd_DLINKEDL_GET_PREV (c,proc_ready))
+    return; /* In the 'proc_ready' list already */
+
+  mhd_assert (0 != (((unsigned int) c->event_loop_info) \
+                    & MHD_EVENT_LOOP_INFO_PROCESS));
+
+  if ((0 !=
+       (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info)
+        & (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_WRITE))) ||
+      err_state)
+  {
+    mhd_DLINKEDL_INS_LAST (&(d->events),c,proc_ready);
+  }
+}
+
+
+/**
+ * Accept new connections on the daemon
+ * @param d the daemon to use
+ * @return true if all incoming connections has been accepted,
+ *         false if some connection may still wait to be accepted
+ */
+MHD_FN_PAR_NONNULL_ (1) static bool
+daemon_accept_new_conns (struct MHD_Daemon *restrict d)
+{
+  unsigned int num_to_accept;
+  mhd_assert (MHD_INVALID_SOCKET != d->net.listen.fd);
+  mhd_assert (! d->conns.block_new);
+  mhd_assert (d->conns.count < d->conns.cfg.count_limit);
+  mhd_assert (! mhd_D_HAS_WORKERS (d));
+
+  if (! d->net.listen.non_block)
+    num_to_accept = 1; /* listen socket is blocking, only one connection can be processed */
+  else
+  {
+    const unsigned int slots_left = d->conns.cfg.count_limit - d->conns.count;
+    if (! mhd_D_HAS_MASTER (d))
+    {
+      /* Fill up to one quarter of allowed limit in one turn */
+      num_to_accept = d->conns.cfg.count_limit / 4;
+      /* Limit to a reasonable number */
+      if (((sizeof(void *) > 4) ? 4096 : 1024) < num_to_accept)
+        num_to_accept = ((sizeof(void *) > 4) ? 4096 : 1024);
+    }
+#ifdef MHD_USE_THREADS
+    else
+    {
+      /* Has workers thread pool. Care must be taken to evenly distribute
+         new connections in the workers pool.
+         At the same time, the burst of new connections should be handled as
+         quick as possible. */
+      const unsigned int num_conn = d->conns.count;
+      const unsigned int limit = d->conns.cfg.count_limit;
+      const unsigned int num_workers =
+        d->threading.hier.master->threading.hier.pool.num;
+      if (num_conn < limit / 16)
+      {
+        num_to_accept = num_conn / num_workers;
+        if (8 > num_to_accept)
+        {
+          if (8 > slots_left / 16)
+            num_to_accept = slots_left / 16;
+          else
+            num_to_accept = 8;
+        }
+        if (64 < num_to_accept)
+          num_to_accept = 64;
+      }
+      else if (num_conn < limit / 8)
+      {
+        num_to_accept = num_conn * 2 / num_workers;
+        if (8 > num_to_accept)
+        {
+          if (8 > slots_left / 8)
+            num_to_accept = slots_left / 8;
+          else
+            num_to_accept = 8;
+        }
+        if (128 < num_to_accept)
+          num_to_accept = 128;
+      }
+      else if (num_conn < limit / 4)
+      {
+        num_to_accept = num_conn * 4 / num_workers;
+        if (8 > num_to_accept)
+          num_to_accept = 8;
+        if (slots_left / 4 < num_to_accept)
+          num_to_accept = slots_left / 4;
+        if (256 < num_to_accept)
+          num_to_accept = 256;
+      }
+      else if (num_conn < limit / 2)
+      {
+        num_to_accept = num_conn * 8 / num_workers;
+        if (16 > num_to_accept)
+          num_to_accept = 16;
+        if (slots_left / 4 < num_to_accept)
+          num_to_accept = slots_left / 4;
+        if (256 < num_to_accept)
+          num_to_accept = 256;
+      }
+      else if (slots_left > limit / 4)
+      {
+        num_to_accept = slots_left * 4 / num_workers;
+        if (slots_left / 8 < num_to_accept)
+          num_to_accept = slots_left / 8;
+        if (128 < num_to_accept)
+          num_to_accept = 128;
+      }
+      else if (slots_left > limit / 8)
+      {
+        num_to_accept = slots_left * 2 / num_workers;
+        if (slots_left / 16 < num_to_accept)
+          num_to_accept = slots_left / 16;
+        if (64 < num_to_accept)
+          num_to_accept = 64;
+      }
+      else /* (slots_left <= limit / 8) */
+        num_to_accept = slots_left / 16;
+
+      if (0 == num_to_accept)
+        num_to_accept = 1;
+      else if (slots_left > num_to_accept)
+        num_to_accept = slots_left;
+    }
+#endif /* MHD_USE_THREADS */
+  }
+
+  while (0 != --num_to_accept)
+  {
+    enum mhd_DaemonAcceptResult res;
+    res = mhd_daemon_accept_connection (d);
+    if (mhd_DAEMON_ACCEPT_NO_MORE_PENDING == res)
+      return true;
+    if (mhd_DAEMON_ACCEPT_FAILED == res)
+      break;
+  }
+  return false;
+}
+
+
+static bool
+daemon_process_all_act_coons (struct MHD_Daemon *restrict d)
+{
+  struct MHD_Connection *restrict c;
+  mhd_assert (! mhd_D_HAS_WORKERS (d));
+
+  for (c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready); c != NULL;
+       c = mhd_DLINKEDL_GET_NEXT (c, proc_ready))
+  {
+
+  }
+  return false;
+}
+
+
+#ifdef MHD_USE_POLL
+
+MHD_FN_PAR_NONNULL_ (1) static unsigned int
+poll_update_fds (struct MHD_Daemon *restrict d,
+                 bool listen_only)
+{
+  unsigned int i_s;
+  unsigned int i_c;
+  struct MHD_Connection *restrict c;
+  mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type);
+
+  i_s = 0;
+#ifdef MHD_USE_THREADS
+  mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+  mhd_assert (d->events.data.poll.fds[i_s].fd == \
+              mhd_itc_r_fd (d->threading.itc));
+  mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \
+              d->events.data.poll.rel[i_s].fd_id);
+  ++i_s;
+#endif
+  if (MHD_INVALID_SOCKET != d->net.listen.fd)
+  {
+    mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd);
+    mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \
+                d->events.data.poll.rel[i_s].fd_id);
+    ++i_s;
+  }
+  if (listen_only)
+    return i_s;
+
+  i_c = i_s;
+  for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn); NULL != c;
+       c = mhd_DLINKEDL_GET_NEXT (c,all_conn))
+  {
+    unsigned short events; /* 'unsigned' for correct bits manipulations */
+    mhd_assert ((i_c - i_s) < d->conns.cfg.count_limit);
+
+    d->events.data.poll.fds[i_c].fd = c->socket_fd;
+    d->events.data.poll.rel[i_c].connection = c;
+    events = POLLHUP; /* Actually, not needed and should be ignored in 'events' */
+    if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
+      events |= MHD_POLL_IN;
+    if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE))
+      events |= MHD_POLL_OUT;
+
+    d->events.data.poll.fds[i_c].events = (short) events;
+    ++i_c;
+  }
+  mhd_assert (d->conns.count == (i_c - i_s));
+  return i_c;
+}
+
+
+MHD_FN_PAR_NONNULL_ (1) static bool
+poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
+                               int num_events)
+{
+  unsigned int i_s;
+  unsigned int i_c;
+  mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type);
+  mhd_assert (0 <= num_events);
+
+  if (0 == num_events)
+    return true;
+
+  i_s = 0;
+#ifdef MHD_USE_THREADS
+  mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+  mhd_assert (d->events.data.poll.fds[i_s].fd == \
+              mhd_itc_r_fd (d->threading.itc));
+  mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \
+              d->events.data.poll.rel[i_s].fd_id);
+  if (0 != (d->events.data.poll.fds[i_s].revents & (POLLERR | POLLNVAL)))
+  {
+    MHD_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
+                 "System reported that ITC has an error status.");
+    /* ITC is broken, need to stop the daemon thread now as otherwise
+       application will not be able to stop the thread. */
+    return false;
+  }
+  if (0 != (d->events.data.poll.fds[i_s].revents & MHD_POLL_IN))
+  {
+    --num_events;
+    /* Clear ITC here, as before any other data processing.
+     * Any external events may activate ITC again if any data to process is
+     * added externally. Cleaning ITC early ensures guaranteed that new data
+     * will not be missed. */
+    mhd_itc_clear (d->threading.itc);
+  }
+  ++i_s;
+
+  if (0 == num_events)
+    return true;
+#endif /* MHD_USE_THREADS */
+
+  if (MHD_INVALID_SOCKET != d->net.listen.fd)
+  {
+    const short revents = d->events.data.poll.fds[i_s].revents;
+
+    mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd);
+    mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \
+                d->events.data.poll.rel[i_s].fd_id);
+    if (0 != (revents & (POLLERR | POLLNVAL | POLLHUP)))
+    {
+      --num_events;
+      MHD_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
+                   "System reported that the listening socket has an error " \
+                   "status. The daemon will not listen any more.");
+      /* Close the listening socket unless the master daemon should close it */
+      if (! mhd_D_TYPE_HAS_MASTER_DAEMON (d->threading.d_type))
+        mhd_socket_close (d->net.listen.fd);
+
+      /* Stop monitoring socket to avoid spinning with busy-waiting */
+      d->net.listen.fd = MHD_INVALID_SOCKET;
+    }
+    else if (0 != (d->events.data.poll.fds[i_s].revents & MHD_POLL_IN))
+    {
+      --num_events;
+      d->events.act_req.accept = true;
+    }
+    ++i_s;
+  }
+
+  mhd_assert ((0 == num_events) || \
+              (mhd_DAEMON_TYPE_LISTEN_ONLY != d->threading.d_type));
+
+  for (i_c = i_s; (i_c < i_s + d->conns.count) && (0 < num_events); ++i_c)
+  {
+    struct MHD_Connection *restrict c;
+    bool recv_ready;
+    bool send_ready;
+    bool err_state;
+    short revents;
+    mhd_assert (mhd_SOCKET_REL_MARKER_EMPTY != \
+                d->events.data.poll.rel[i_c].fd_id);
+    mhd_assert (mhd_SOCKET_REL_MARKER_ITC != \
+                d->events.data.poll.rel[i_c].fd_id);
+    mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN != \
+                d->events.data.poll.rel[i_c].fd_id);
+
+    c = d->events.data.poll.rel[i_c].connection;
+    mhd_assert (c->socket_fd == d->events.data.poll.fds[i_c].fd);
+    revents = d->events.data.poll.fds[i_c].revents;
+    recv_ready = (0 != (revents & MHD_POLL_IN));
+    send_ready = (0 != (revents & MHD_POLL_OUT));
+#ifndef MHD_POLLHUP_ON_REM_SHUT_WR
+    err_state = (0 != (revents & (POLLHUP | POLLERR | POLLNVAL)));
+#else
+    err_state = (0 != (revents & (POLLERR | POLLNVAL)));
+    if (0 != (revents & POLLHUP))
+    { /* This can be a disconnect OR remote side set SHUT_WR */
+      recv_ready = true; /* Check the socket by reading */
+      if (0 != (d->events.data.poll.fds[i_c].events | MHD_POLL_IN))
+        err_state = true; /* The socket will not be checked by reading, the only way to avoid spinning */
+    }
+#endif
+    if (0 != (revents & (MHD_POLLPRI | MHD_POLLRDBAND)))
+    { /* Statuses were not requested, but returned */
+      if (! recv_ready ||
+          (0 != (d->events.data.poll.fds[i_c].events | MHD_POLL_IN)))
+        err_state = true; /* The socket will not be read, the only way to avoid spinning */
+    }
+    if (0 != (revents & MHD_POLLWRBAND))
+    { /* Status was not requested, but returned */
+      if (! send_ready ||
+          (0 != (d->events.data.poll.fds[i_c].events | MHD_POLL_OUT)))
+        err_state = true; /* The socket will not be written, the only way to avoid spinning */
+    }
+
+    update_conn_net_status (d, c, recv_ready, send_ready, err_state);
+  }
+  mhd_assert (d->conns.count >= (i_c - i_s));
+  return true;
+}
+
+
+MHD_FN_PAR_NONNULL_ (1) static bool
+get_all_net_updates_by_poll (struct MHD_Daemon *restrict d,
+                             bool listen_only)
+{
+  unsigned int num_fds;
+  int num_events;
+  mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type);
+
+  num_fds = poll_update_fds (d, listen_only);
+
+  // TODO: handle empty list situation
+
+  num_events = mhd_poll (d->events.data.poll.fds, num_fds, -1);
+  if (0 > num_events)
+  {
+    int err;
+    bool is_hard_error;
+    bool is_ignored_error;
+    is_hard_error = false;
+    is_ignored_error = false;
+#if defined(MHD_POSIX_SOCKETS)
+    err = errno;
+    if (0 != err)
+    {
+      is_hard_error =
+        ((MHD_EFAULT_OR_ZERO == err) || (MHD_EINVAL_OR_ZERO == err));
+      is_ignored_error = (MHD_EINTR_OR_ZERO == err);
+    }
+#elif defined(MHD_WINSOCK_SOCKETS)
+    err = WSAGetLastError ();
+    is_hard_error =
+      ((WSAENETDOWN == err) || (WSAEFAULT == err) || (WSAEINVAL == err));
+#endif
+    if (! is_ignored_error)
+    {
+      if (is_hard_error)
+      {
+        MHD_LOG_MSG (d, MHD_SC_POLL_HARD_ERROR, \
+                     "The poll() encountered unrecoverable error.");
+        return false;
+      }
+      MHD_LOG_MSG (d, MHD_SC_POLL_SOFT_ERROR, \
+                   "The poll() encountered error.");
+    }
+  }
+
+  if (! poll_update_statuses_from_fds (d, num_events))
+    return false;
+
+  return true;
+}
+
+
+#endif /* MHD_USE_POLL */
+
+
+MHD_FN_PAR_NONNULL_ (1) static bool
+process_all_events_and_data (struct MHD_Daemon *restrict d)
+{
+  switch (d->events.poll_type)
+  {
+  case mhd_POLL_TYPE_EXT:
+    return false; // TODO: implement
+    break;
+#ifdef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+    return false; // TODO: implement
+    break;
+#endif /* MHD_USE_SELECT */
+#ifdef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+    if (! get_all_net_updates_by_poll (d, false))
+      return false;
+    break;
+#endif /* MHD_USE_POLL */
+#ifdef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+    return false; // TODO: implement
+    break;
+#endif /* MHD_USE_EPOLL */
+#ifndef MHD_USE_SELECT
+  case mhd_POLL_TYPE_SELECT:
+#endif /* ! MHD_USE_SELECT */
+#ifndef MHD_USE_POLL
+  case mhd_POLL_TYPE_POLL:
+#endif /* ! MHD_USE_POLL */
+#ifndef MHD_USE_EPOLL
+  case mhd_POLL_TYPE_EPOLL:
+#endif /* ! MHD_USE_EPOLL */
+  case mhd_POLL_TYPE_NOT_SET_YET:
+  default:
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    MHD_PANIC ("Daemon data integrity broken");
+  }
+  if (d->events.act_req.accept)
+  {
+    if (daemon_accept_new_conns (d))
+      d->events.act_req.accept = false;
+    else if (! d->net.listen.non_block)
+      d->events.act_req.accept = false;
+  }
+  daemon_process_all_act_coons (d);
+  return false;
+}
+
+
+/**
+ * The entry point for the daemon worker thread
+ * @param cls the closure
+ */
+mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
+mhd_worker_all_events (void *cls)
+{
+  struct MHD_Daemon *const restrict d = (struct MHD_Daemon *) cls;
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
+  mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type));
+  mhd_assert (mhd_DAEMON_TYPE_LISTEN_ONLY != d->threading.d_type);
+  mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type));
+  mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int);
+  mhd_assert (d->dbg.events_fully_inited);
+  mhd_assert (d->dbg.connections_inited);
+
+  while (! d->threading.stop_requested)
+  {
+    if (! process_all_events_and_data (d))
+      break;
+  }
+  if (! d->threading.stop_requested)
+  {
+    MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
+                 "The daemon thread is stopping, but termination has not " \
+                 "been requested by the daemon.");
+  }
+  return (mhd_THRD_RTRN_TYPE) 0;
+}
+
+
+MHD_FN_PAR_NONNULL_ (1) static bool
+process_listening_and_itc_only (struct MHD_Daemon *restrict d)
+{
+  if (false)
+    (void) 0;
+#ifdef MHD_USE_SELECT
+  else if (mhd_POLL_TYPE_SELECT == d->events.poll_type)
+  {
+    return false; // TODO: implement
+  }
+#endif /* MHD_USE_SELECT */
+#ifdef MHD_USE_POLL
+  else if (mhd_POLL_TYPE_POLL == d->events.poll_type)
+  {
+    if (! get_all_net_updates_by_poll (d, true))
+      return false;
+  }
+#endif /* MHD_USE_POLL */
+  else
+  {
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    MHD_PANIC ("Daemon data integrity broken");
+  }
+  // TODO: Accept connections
+  return false;
+}
+
+
+/**
+ * The entry point for the daemon listening thread
+ * @param cls the closure
+ */
+mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
+mhd_worker_listening_only (void *cls)
+{
+  struct MHD_Daemon *const restrict d = (struct MHD_Daemon *) cls;
+  mhd_assert (d->dbg.net_inited);
+  mhd_assert (! d->dbg.net_deinited);
+  mhd_assert (mhd_DAEMON_TYPE_LISTEN_ONLY == d->threading.d_type);
+  mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION == d->wmode_int);
+  mhd_assert (d->dbg.events_fully_inited);
+  mhd_assert (d->dbg.connections_inited);
+
+  while (! d->threading.stop_requested)
+  {
+    if (! process_listening_and_itc_only (d))
+      break;
+  }
+  if (! d->threading.stop_requested)
+  {
+    MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
+                 "The daemon thread is stopping, but termination has not been " \
+                 "requested by the daemon.");
+  }
+  return (mhd_THRD_RTRN_TYPE) 0;
+}
+
+
+mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
+mhd_worker_connection (void *cls)
+{
+  mhd_assert (! cls && "Not yet implemented");
+  return (mhd_THRD_RTRN_TYPE) 0;
+}

+ 55 - 0
src/mhd2/events_process.h

@@ -0,0 +1,55 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/events_process.h
+ * @brief  The declarations of events processing functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_EVENTS_PROCESS_H
+#define MHD_EVENTS_PROCESS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_thread_entry_type.h"
+
+/**
+ * The entry point for the daemon worker thread
+ * @param cls the closure
+ */
+mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
+mhd_worker_all_events (void *cls);
+
+/**
+ * The entry point for the daemon listening thread
+ * @param cls the closure
+ */
+mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
+mhd_worker_listening_only (void *cls);
+
+/**
+ * The entry point for the connection thread for thread-per-connection mode
+ * @param cls the closure
+ */
+mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
+mhd_worker_connection (void *cls);
+
+#endif /* ! MHD_EVENTS_PROCESS_H */

+ 179 - 0
src/mhd2/http_method.h

@@ -0,0 +1,179 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2021-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/http_method.h
+ * @brief  The definition of the enums for the HTTP methods
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_HTTP_METHOD_H
+#define MHD_HTTP_METHOD_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_HTTP_METHOD_DEFINED
+
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_Method
+{
+
+  /**
+   * Method did not match any of the methods given below.
+   */
+  MHD_HTTP_METHOD_OTHER = 255
+  ,
+  /* Main HTTP methods. */
+
+  /**
+   * "GET"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.1.
+   */
+  MHD_HTTP_METHOD_GET = 1
+  ,
+  /**
+   * "HEAD"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.2.
+   */
+  MHD_HTTP_METHOD_HEAD = 2
+  ,
+  /**
+   * "POST"
+   * Not safe. Not idempotent. RFC9110, Section 9.3.3.
+   */
+  MHD_HTTP_METHOD_POST = 3
+  ,
+  /**
+   * "PUT"
+   * Not safe. Idempotent.     RFC9110, Section 9.3.4.
+   */
+  MHD_HTTP_METHOD_PUT = 4
+  ,
+  /**
+   * "DELETE"
+   * Not safe. Idempotent.     RFC9110, Section 9.3.5.
+   */
+  MHD_HTTP_METHOD_DELETE = 5
+  ,
+  /**
+   * "CONNECT"
+   * Not safe. Not idempotent. RFC9110, Section 9.3.6.
+   */
+  MHD_HTTP_METHOD_CONNECT = 6
+  ,
+  /**
+   * "OPTIONS"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.7.
+   */
+  MHD_HTTP_METHOD_OPTIONS = 7
+  ,
+  /**
+   * "TRACE"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.8.
+   */
+  MHD_HTTP_METHOD_TRACE = 8
+  ,
+  /**
+   * "*"
+   * Not safe. Not idempotent. RFC9110, Section 18.2.
+   */
+  MHD_HTTP_METHOD_ASTERISK = 9
+};
+
+
+#define MHD_HTTP_METHOD_DEFINED 1
+#endif /* ! MHD_HTTP_METHOD_DEFINED */
+
+/**
+ * Internal version of MHD_HTTP_Method
+ * Extended with the #mhd_HTTP_METHOD_NO_METHOD value
+ */
+enum MHD_FIXED_ENUM_ mhd_HTTP_Method
+{
+
+  /**
+   * No method has been detected yet
+   */
+  mhd_HTTP_METHOD_NO_METHOD = 0
+  ,
+
+  /**
+   * Method did not match any of the methods given below.
+   */
+  mhd_HTTP_METHOD_OTHER = MHD_HTTP_METHOD_OTHER
+  ,
+  /* Main HTTP methods. */
+
+  /**
+   * "GET"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.1.
+   */
+  mhd_HTTP_METHOD_GET = MHD_HTTP_METHOD_GET
+  ,
+  /**
+   * "HEAD"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.2.
+   */
+  mhd_HTTP_METHOD_HEAD = MHD_HTTP_METHOD_HEAD
+  ,
+  /**
+   * "POST"
+   * Not safe. Not idempotent. RFC9110, Section 9.3.3.
+   */
+  mhd_HTTP_METHOD_POST = MHD_HTTP_METHOD_POST
+  ,
+  /**
+   * "PUT"
+   * Not safe. Idempotent.     RFC9110, Section 9.3.4.
+   */
+  mhd_HTTP_METHOD_PUT = MHD_HTTP_METHOD_PUT
+  ,
+  /**
+   * "DELETE"
+   * Not safe. Idempotent.     RFC9110, Section 9.3.5.
+   */
+  mhd_HTTP_METHOD_DELETE = MHD_HTTP_METHOD_DELETE
+  ,
+  /**
+   * "CONNECT"
+   * Not safe. Not idempotent. RFC9110, Section 9.3.6.
+   */
+  mhd_HTTP_METHOD_CONNECT = MHD_HTTP_METHOD_CONNECT
+  ,
+  /**
+   * "OPTIONS"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.7.
+   */
+  mhd_HTTP_METHOD_OPTIONS = MHD_HTTP_METHOD_OPTIONS
+  ,
+  /**
+   * "TRACE"
+   * Safe.     Idempotent.     RFC9110, Section 9.3.8.
+   */
+  mhd_HTTP_METHOD_TRACE = MHD_HTTP_METHOD_TRACE
+  ,
+  /**
+   * "*"
+   * Not safe. Not idempotent. RFC9110, Section 18.2.
+   */
+  mhd_HTTP_METHOD_ASTERISK = MHD_HTTP_METHOD_ASTERISK
+};
+
+#define MHD_HTTP_METHOD_DEFINED 1
+#endif /* ! MHD_HTTP_METHOD_H */

+ 58 - 0
src/mhd2/http_prot_ver.h

@@ -0,0 +1,58 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2021-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/http_prot_ver.h
+ * @brief  The definition of the HTTP versions enum
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_HTTP_PROT_VER_H
+#define MHD_HTTP_PROT_VER_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_HTTP_PROTOCOL_VER_DEFINED
+
+/**
+ * @brief HTTP protocol versions
+ * @defgroup versions HTTP versions
+ * @{
+ */
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_ProtocolVersion
+{
+  MHD_HTTP_VERSION_INVALID = 0
+  ,
+  MHD_HTTP_VERSION_1_0 = 1
+  ,
+  MHD_HTTP_VERSION_1_1 = 2
+  ,
+  MHD_HTTP_VERSION_2_0 = 3
+  ,
+  MHD_HTTP_VERSION_3_0 = 4
+  ,
+  MHD_HTTP_VERSION_FUTURE = 255
+};
+
+
+#define MHD_HTTP_PROTOCOL_VER_DEFINED 1
+#endif /* ! MHD_HTTP_PROTOCOL_VER_DEFINED */
+
+#endif /* ! MHD_HTTP_PROT_VER_H */

+ 207 - 0
src/mhd2/http_status_str.c

@@ -0,0 +1,207 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2017-2024 Evgeny Grin (Karlson2k)
+     Copyright (C) 2007, 2011, 2017, 2019 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 src/mhd2/http_status_str.c
+ * @brief  Tables of the string response phrases
+ * @author Elliot Glaysher
+ * @author Christian Grothoff (minor code clean up)
+ * @author Karlson2k (Evgeny Grin) (massively refactored and updated)
+ */
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "mhd_public_api.h"
+#include "mhd_str_macros.h"
+
+#define UNUSED_STATUS {0, NULL}
+
+static const struct MHD_String invalid_hundred[] = {
+  { 0, NULL }
+};
+
+static const struct MHD_String one_hundred[] = {
+  /* 100 */ mhd_MSTR_INIT ("Continue"),       /* RFC9110, Section 15.2.1 */
+  /* 101 */ mhd_MSTR_INIT ("Switching Protocols"), /* RFC9110, Section 15.2.2 */
+  /* 102 */ mhd_MSTR_INIT ("Processing"),     /* RFC2518 */
+  /* 103 */ mhd_MSTR_INIT ("Early Hints")     /* RFC8297 */
+};
+
+static const struct MHD_String two_hundred[] = {
+  /* 200 */ mhd_MSTR_INIT ("OK"),             /* RFC9110, Section 15.3.1 */
+  /* 201 */ mhd_MSTR_INIT ("Created"),        /* RFC9110, Section 15.3.2 */
+  /* 202 */ mhd_MSTR_INIT ("Accepted"),       /* RFC9110, Section 15.3.3 */
+  /* 203 */ mhd_MSTR_INIT ("Non-Authoritative Information"), /* RFC9110, Section 15.3.4 */
+  /* 204 */ mhd_MSTR_INIT ("No Content"),     /* RFC9110, Section 15.3.5 */
+  /* 205 */ mhd_MSTR_INIT ("Reset Content"),  /* RFC9110, Section 15.3.6 */
+  /* 206 */ mhd_MSTR_INIT ("Partial Content"), /* RFC9110, Section 15.3.7 */
+  /* 207 */ mhd_MSTR_INIT ("Multi-Status"),   /* RFC4918 */
+  /* 208 */ mhd_MSTR_INIT ("Already Reported"), /* RFC5842 */
+  /* 209 */ UNUSED_STATUS,                    /* Not used */
+  /* 210 */ UNUSED_STATUS,                    /* Not used */
+  /* 211 */ UNUSED_STATUS,                    /* Not used */
+  /* 212 */ UNUSED_STATUS,                    /* Not used */
+  /* 213 */ UNUSED_STATUS,                    /* Not used */
+  /* 214 */ UNUSED_STATUS,                    /* Not used */
+  /* 215 */ UNUSED_STATUS,                    /* Not used */
+  /* 216 */ UNUSED_STATUS,                    /* Not used */
+  /* 217 */ UNUSED_STATUS,                    /* Not used */
+  /* 218 */ UNUSED_STATUS,                    /* Not used */
+  /* 219 */ UNUSED_STATUS,                    /* Not used */
+  /* 220 */ UNUSED_STATUS,                    /* Not used */
+  /* 221 */ UNUSED_STATUS,                    /* Not used */
+  /* 222 */ UNUSED_STATUS,                    /* Not used */
+  /* 223 */ UNUSED_STATUS,                    /* Not used */
+  /* 224 */ UNUSED_STATUS,                    /* Not used */
+  /* 225 */ UNUSED_STATUS,                    /* Not used */
+  /* 226 */ mhd_MSTR_INIT ("IM Used")         /* RFC3229 */
+};
+
+static const struct MHD_String three_hundred[] = {
+  /* 300 */ mhd_MSTR_INIT ("Multiple Choices"), /* RFC9110, Section 15.4.1 */
+  /* 301 */ mhd_MSTR_INIT ("Moved Permanently"), /* RFC9110, Section 15.4.2 */
+  /* 302 */ mhd_MSTR_INIT ("Found"),          /* RFC9110, Section 15.4.3 */
+  /* 303 */ mhd_MSTR_INIT ("See Other"),      /* RFC9110, Section 15.4.4 */
+  /* 304 */ mhd_MSTR_INIT ("Not Modified"),   /* RFC9110, Section 15.4.5 */
+  /* 305 */ mhd_MSTR_INIT ("Use Proxy"),      /* RFC9110, Section 15.4.6 */
+  /* 306 */ mhd_MSTR_INIT ("Switch Proxy"),   /* Not used! RFC9110, Section 15.4.7 */
+  /* 307 */ mhd_MSTR_INIT ("Temporary Redirect"), /* RFC9110, Section 15.4.8 */
+  /* 308 */ mhd_MSTR_INIT ("Permanent Redirect") /* RFC9110, Section 15.4.9 */
+};
+
+static const struct MHD_String four_hundred[] = {
+  /* 400 */ mhd_MSTR_INIT ("Bad Request"),    /* RFC9110, Section 15.5.1 */
+  /* 401 */ mhd_MSTR_INIT ("Unauthorized"),   /* RFC9110, Section 15.5.2 */
+  /* 402 */ mhd_MSTR_INIT ("Payment Required"), /* RFC9110, Section 15.5.3 */
+  /* 403 */ mhd_MSTR_INIT ("Forbidden"),      /* RFC9110, Section 15.5.4 */
+  /* 404 */ mhd_MSTR_INIT ("Not Found"),      /* RFC9110, Section 15.5.5 */
+  /* 405 */ mhd_MSTR_INIT ("Method Not Allowed"), /* RFC9110, Section 15.5.6 */
+  /* 406 */ mhd_MSTR_INIT ("Not Acceptable"), /* RFC9110, Section 15.5.7 */
+  /* 407 */ mhd_MSTR_INIT ("Proxy Authentication Required"), /* RFC9110, Section 15.5.8 */
+  /* 408 */ mhd_MSTR_INIT ("Request Timeout"), /* RFC9110, Section 15.5.9 */
+  /* 409 */ mhd_MSTR_INIT ("Conflict"),       /* RFC9110, Section 15.5.10 */
+  /* 410 */ mhd_MSTR_INIT ("Gone"),           /* RFC9110, Section 15.5.11 */
+  /* 411 */ mhd_MSTR_INIT ("Length Required"), /* RFC9110, Section 15.5.12 */
+  /* 412 */ mhd_MSTR_INIT ("Precondition Failed"), /* RFC9110, Section 15.5.13 */
+  /* 413 */ mhd_MSTR_INIT ("Content Too Large"), /* RFC9110, Section 15.5.14 */
+  /* 414 */ mhd_MSTR_INIT ("URI Too Long"),   /* RFC9110, Section 15.5.15 */
+  /* 415 */ mhd_MSTR_INIT ("Unsupported Media Type"), /* RFC9110, Section 15.5.16 */
+  /* 416 */ mhd_MSTR_INIT ("Range Not Satisfiable"), /* RFC9110, Section 15.5.17 */
+  /* 417 */ mhd_MSTR_INIT ("Expectation Failed"), /* RFC9110, Section 15.5.18 */
+  /* 418 */ UNUSED_STATUS,                    /* Not used */
+  /* 419 */ UNUSED_STATUS,                    /* Not used */
+  /* 420 */ UNUSED_STATUS,                    /* Not used */
+  /* 421 */ mhd_MSTR_INIT ("Misdirected Request"), /* RFC9110, Section 15.5.20 */
+  /* 422 */ mhd_MSTR_INIT ("Unprocessable Content"), /* RFC9110, Section 15.5.21 */
+  /* 423 */ mhd_MSTR_INIT ("Locked"),         /* RFC4918 */
+  /* 424 */ mhd_MSTR_INIT ("Failed Dependency"), /* RFC4918 */
+  /* 425 */ mhd_MSTR_INIT ("Too Early"),      /* RFC8470 */
+  /* 426 */ mhd_MSTR_INIT ("Upgrade Required"), /* RFC9110, Section 15.5.22 */
+  /* 427 */ UNUSED_STATUS,                    /* Not used */
+  /* 428 */ mhd_MSTR_INIT ("Precondition Required"), /* RFC6585 */
+  /* 429 */ mhd_MSTR_INIT ("Too Many Requests"), /* RFC6585 */
+  /* 430 */ UNUSED_STATUS,                    /* Not used */
+  /* 431 */ mhd_MSTR_INIT ("Request Header Fields Too Large"), /* RFC6585 */
+  /* 432 */ UNUSED_STATUS,                    /* Not used */
+  /* 433 */ UNUSED_STATUS,                    /* Not used */
+  /* 434 */ UNUSED_STATUS,                    /* Not used */
+  /* 435 */ UNUSED_STATUS,                    /* Not used */
+  /* 436 */ UNUSED_STATUS,                    /* Not used */
+  /* 437 */ UNUSED_STATUS,                    /* Not used */
+  /* 438 */ UNUSED_STATUS,                    /* Not used */
+  /* 439 */ UNUSED_STATUS,                    /* Not used */
+  /* 440 */ UNUSED_STATUS,                    /* Not used */
+  /* 441 */ UNUSED_STATUS,                    /* Not used */
+  /* 442 */ UNUSED_STATUS,                    /* Not used */
+  /* 443 */ UNUSED_STATUS,                    /* Not used */
+  /* 444 */ UNUSED_STATUS,                    /* Not used */
+  /* 445 */ UNUSED_STATUS,                    /* Not used */
+  /* 446 */ UNUSED_STATUS,                    /* Not used */
+  /* 447 */ UNUSED_STATUS,                    /* Not used */
+  /* 448 */ UNUSED_STATUS,                    /* Not used */
+  /* 449 */ mhd_MSTR_INIT ("Reply With"),     /* MS IIS extension */
+  /* 450 */ mhd_MSTR_INIT ("Blocked by Windows Parental Controls"), /* MS extension */
+  /* 451 */ mhd_MSTR_INIT ("Unavailable For Legal Reasons") /* RFC7725 */
+};
+
+static const struct MHD_String five_hundred[] = {
+  /* 500 */ mhd_MSTR_INIT ("Internal Server Error"), /* RFC9110, Section 15.6.1 */
+  /* 501 */ mhd_MSTR_INIT ("Not Implemented"), /* RFC9110, Section 15.6.2 */
+  /* 502 */ mhd_MSTR_INIT ("Bad Gateway"),    /* RFC9110, Section 15.6.3 */
+  /* 503 */ mhd_MSTR_INIT ("Service Unavailable"), /* RFC9110, Section 15.6.4 */
+  /* 504 */ mhd_MSTR_INIT ("Gateway Timeout"), /* RFC9110, Section 15.6.5 */
+  /* 505 */ mhd_MSTR_INIT ("HTTP Version Not Supported"), /* RFC9110, Section 15.6.6 */
+  /* 506 */ mhd_MSTR_INIT ("Variant Also Negotiates"), /* RFC2295 */
+  /* 507 */ mhd_MSTR_INIT ("Insufficient Storage"), /* RFC4918 */
+  /* 508 */ mhd_MSTR_INIT ("Loop Detected"),  /* RFC5842 */
+  /* 509 */ mhd_MSTR_INIT ("Bandwidth Limit Exceeded"), /* Apache extension */
+  /* 510 */ mhd_MSTR_INIT ("Not Extended"),   /* (OBSOLETED) RFC2774; status-change-http-experiments-to-historic */
+  /* 511 */ mhd_MSTR_INIT ("Network Authentication Required") /* RFC6585 */
+};
+
+
+struct mhd_HttpStatusesBlock
+{
+  size_t max;
+  const struct MHD_String *const data;
+};
+
+#define STATUSES_BLOCK(m) { (sizeof(m) / sizeof(m[0])), m}
+
+static const struct mhd_HttpStatusesBlock statuses[] = {
+  STATUSES_BLOCK (invalid_hundred),
+  STATUSES_BLOCK (one_hundred),
+  STATUSES_BLOCK (two_hundred),
+  STATUSES_BLOCK (three_hundred),
+  STATUSES_BLOCK (four_hundred),
+  STATUSES_BLOCK (five_hundred)
+};
+
+MHD_EXTERN_ MHD_FN_CONST_ const struct MHD_String *
+MHD_HTTP_status_code_to_string (enum MHD_StatusCode code)
+{
+  struct MHD_String *res;
+  const unsigned int code_i = (unsigned int) code;
+  if (100 > code_i)
+    return NULL;
+  if (600 < code)
+    return NULL;
+  if (statuses[code_i / 100].max > (code_i % 100))
+    return NULL;
+  res = statuses[code_i / 100] + (code_i % 100);
+  if (NULL == res->cstr)
+    return NULL;
+  return res;
+}
+
+
+MHD_INTERNAL MHD_FN_CONST_ const struct MHD_String *
+mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code)
+{
+  static const struct MHD_String no_status =
+    mhd_MSTR_INIT ("Nonstandard Status");
+  const struct MHD_String *res;
+
+  res = MHD_status_code_to_string (code);
+  if (NULL != res)
+    return res;
+
+  return &no_status;
+}

+ 46 - 0
src/mhd2/http_status_str.h

@@ -0,0 +1,46 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/http_status_str.h
+ * @brief  The declaration for internal HTTP status string functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_HTTP_STATUS_STR_H
+#define MHD_HTTP_STATUS_STR_H 1
+
+#include "mhd_sys_options.h"
+
+#include "mhd_str_types.h"
+
+/**
+ * Get string for provided HTTP status code.
+ * Substitute a replacement string for unknown codes.
+ *
+ * @param code the HTTP status code
+ * @return pointer to MHD_String, never NULL.
+ */
+MHD_INTERNAL const struct MHD_String *
+mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code)
+MHD_FN_CONST_;
+
+
+#endif /* ! MHD_HTTP_STATUS_STR_H */

+ 293 - 0
src/mhd2/mhd_action.h

@@ -0,0 +1,293 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_action.h
+ * @brief  The definition of the MHD_Action and MHD_UploadAction structures
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_ACTION_H
+#define MHD_ACTION_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#include "mhd_str_types.h"
+
+/**
+ * The type of the action requested by application
+ */
+enum mhd_ActionType
+{
+  /**
+   * Action has not been set yet.
+   */
+  mhd_ACTION_NO_ACTION = 0
+  ,
+  /**
+   * Start replying with the response
+   */
+  mhd_ACTION_RESPONSE
+  ,
+  /**
+   * Suspend requests (connection)
+   */
+  mhd_ACTION_SUSPEND
+  ,
+  /**
+   * Hard close request with no response
+   */
+  mhd_ACTION_CLOSE
+  ,
+  /**
+   * Process clients upload by application callback
+   */
+  mhd_ACTION_UPLOAD
+  ,
+  /**
+   * Process clients upload by POST processor
+   */
+  mhd_ACTION_POST_PROCESS
+};
+
+
+struct MHD_Response; /* forward declaration */
+
+
+#ifndef MHD_UPLOADCALLBACK_DEFINED
+
+typedef const struct MHD_UploadAction *
+(MHD_FN_PAR_NONNULL_ (2)  MHD_FN_PAR_INOUT_SIZE_ (4,3)
+ *MHD_UploadCallback)(void *upload_cls,
+                      struct MHD_Request *request,
+                      size_t content_data_size,
+                      void *content_data);
+
+#define MHD_UPLOADCALLBACK_DEFINED 1
+#endif /* ! MHD_UPLOADCALLBACK_DEFINED */
+
+/**
+ * Upload callback data
+ */
+struct mhd_UploadCallbackData
+{
+  /**
+   * The callback
+   */
+  MHD_UploadCallback cb;
+
+  /**
+   * The closure for @a cb
+   */
+  void *cls;
+};
+
+/**
+ * The data for upload callbacks
+ */
+struct mhd_UploadCallbacks
+{
+  /**
+   * The size of the buffer for the @a full upload callback
+   */
+  size_t large_buffer_size;
+
+  /**
+   * The data for the callback that processes only complete upload
+   */
+  struct mhd_UploadCallbackData full;
+
+  /**
+   * The data for the callback that processes only incremental uploads
+   */
+  struct mhd_UploadCallbackData inc;
+};
+
+#ifndef MHD_POST_DATA_READER_DEFINED
+
+typedef const struct MHD_UploadAction *
+(*MHD_PostDataReader) (void *cls,
+                       const struct MHD_String *name,
+                       const struct MHD_String *filename,
+                       const struct MHD_String *content_type,
+                       const struct MHD_String *encoding,
+                       const void *data,
+                       uint_fast64_t off,
+                       size_t size);
+
+
+typedef const struct MHD_UploadAction *
+(*MHD_PostDataFinished) (struct MHD_Request *req,
+                         void *cls);
+
+#define MHD_POST_DATA_READER_DEFINED 1
+#endif /* ! MHD_POST_DATA_READER_DEFINED */
+
+#ifndef MHD_HTTP_POSTENCODING_DEFINED
+
+enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_HTTP_PostEncoding
+{
+  /**
+   * No post encoding / broken data / unknown encoding
+   */
+  MHD_HTTP_POST_ENCODING_OTHER = 0
+  ,
+  /**
+   * "application/x-www-form-urlencoded"
+   * See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#url-encoded-form-data
+   * See https://url.spec.whatwg.org/#application/x-www-form-urlencoded
+   * See https://datatracker.ietf.org/doc/html/rfc3986#section-2
+   */
+  MHD_HTTP_POST_ENCODING_FORM_URLENCODED = 1
+  ,
+  /**
+   * "multipart/form-data"
+   * See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data
+   * See https://www.rfc-editor.org/rfc/rfc7578.html
+   */
+  MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA = 2
+  ,
+  /**
+   * "text/plain"
+   * Introduced by HTML5
+   * See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
+   * @warning Format is ambiguous. Do not use unless there is a very strong reason.
+   */
+  MHD_HTTP_POST_ENCODING_TEXT_PLAIN = 3
+};
+
+
+/** @} */ /* end of group postenc */
+
+#define MHD_HTTP_POSTENCODING_DEFINED 1
+#endif /* ! MHD_HTTP_POSTENCODING_DEFINED */
+
+
+// TODO: correct and describe
+struct mhd_PostProcessorActionData
+{
+  size_t pp_buffer_size;
+  size_t pp_stream_limit; // FIXME: Remove? Duplicated with pp_buffer_size
+  enum MHD_HTTP_PostEncoding enc;
+  MHD_PostDataReader reader;
+  void *reader_cls;
+  MHD_PostDataFinished done_cb;
+  void *done_cb_cls;
+};
+
+/**
+ * The data for the application action
+ */
+union mhd_ActionData
+{
+  /**
+   * The data for the action #mhd_ACTION_RESPONSE
+   */
+  struct MHD_Response *response;
+
+  /**
+   * The data for the action #mhd_ACTION_UPLOAD
+   */
+  struct mhd_UploadCallbacks upload;
+
+  /**
+   * The data for the action #mhd_ACTION_POST_PROCESS
+   */
+  struct mhd_PostProcessorActionData post_process;
+};
+
+
+/**
+ * The action provided after reporting all headers to application
+ */
+struct MHD_Action
+{
+  /**
+   * The action
+   */
+  enum mhd_ActionType act;
+
+  /**
+   * The data for the @a act action
+   */
+  union mhd_ActionData data;
+};
+
+/**
+ * The type of the action requested by application
+ */
+enum mhd_UploadActionType
+{
+  /**
+   * Action has not been set yet.
+   */
+  mhd_UPLOAD_ACTION_NO_ACTION = 0
+  ,
+  /**
+   * Start replying with the response
+   */
+  mhd_UPLOAD_ACTION_RESPONSE
+  ,
+  /**
+   * Suspend requests (connection)
+   */
+  mhd_UPLOAD_ACTION_SUSPEND
+  ,
+  /**
+   * Hard close request with no response
+   */
+  mhd_UPLOAD_ACTION_CLOSE
+  ,
+  /**
+   * Continue processing the upload
+   */
+  mhd_UPLOAD_ACTION_CONTINUE
+};
+
+/**
+ * The data for the application action
+ */
+union mhd_UploadActionData
+{
+  /**
+   * The data for the action #mhd_ACTION_RESPONSE
+   */
+  struct MHD_Response *response;
+};
+
+/**
+ * The action provided when consuming client's upload
+ */
+struct MHD_UploadAction
+{
+  /**
+   * The action
+   */
+  enum mhd_UploadActionType act;
+
+  /**
+   * The data for the @a act action
+   */
+  union mhd_UploadActionData data;
+};
+
+#endif /* ! MHD_ACTION_H */

+ 81 - 0
src/mhd2/mhd_assert.h

@@ -0,0 +1,81 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2017-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_assert.h
+ * @brief  macros for mhd_assert()
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+/* Unlike POSIX version of 'assert.h', MHD version of 'assert' header
+ * does not allow multiple redefinition of 'mhd_assert' macro within single
+ * source file. */
+#ifndef MHD_ASSERT_H
+#define MHD_ASSERT_H 1
+
+#include "mhd_sys_options.h"
+
+#if ! defined(_DEBUG) && ! defined(NDEBUG)
+#  ifndef DEBUG /* Used by some toolchains */
+#    define NDEBUG 1 /* Use NDEBUG by default */
+#  else  /* DEBUG */
+#    define _DEBUG 1 /* Non-standart macro */
+#  endif /* DEBUG */
+#endif /* !_DEBUG && !NDEBUG */
+
+#if defined(_DEBUG) && defined(NDEBUG)
+#error Both _DEBUG and NDEBUG are defined
+#endif /* _DEBUG && NDEBUG */
+
+#ifdef NDEBUG
+#  define mhd_assert(ignore) ((void) 0)
+#else  /* ! NDEBUG */
+#  ifdef HAVE_ASSERT
+#    include <assert.h>
+#    define mhd_assert(CHK) assert (CHK)
+#  else  /* ! HAVE_ASSERT */
+#    include <stdio.h>
+#    ifdef HAVE_STDLIB_H
+#      include <stdlib.h>
+#    elif defined(HAVE_UNISTD_H)
+#      include <unistd.h>
+#    endif
+#    ifdef MHD_HAVE_MHD_FUNC_
+#      define mhd_assert(CHK) \
+        do { \
+          if (! (CHK)) { \
+            fprintf (stderr, \
+                     "%s:%s:%u Assertion failed: %s\nProgram aborted.\n", \
+                     __FILE__, MHD_FUNC_, (unsigned) __LINE__, #CHK); \
+            fflush (stderr); abort (); } \
+        } while (0)
+#    else
+#      define mhd_assert(CHK) \
+        do { \
+          if (! (CHK)) { \
+            fprintf (stderr, "%s:%u Assertion failed: %s\nProgram aborted.\n", \
+                     __FILE__, (unsigned) __LINE__, #CHK); \
+            fflush (stderr); abort (); } \
+        } while (0)
+#    endif
+#  endif /* ! HAVE_ASSERT */
+#endif /* NDEBUG */
+
+#endif /* ! MHD_ASSERT_H */

+ 537 - 0
src/mhd2/mhd_connection.h

@@ -0,0 +1,537 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_connection.h
+ * @brief  Definition of struct MHD_connection
+ * @author Karlson2k (Evgeny Grin)
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ *
+ * @warning Imported from MHD1 with minimal changes
+ * TODO: Rewrite
+ */
+
+#ifndef MHD_CONNECTION_H
+#define MHD_CONNECTION_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_threads.h"
+
+#include "mhd_tristate.h"
+#include "mhd_dlinked_list.h"
+
+#include "mhd_request.h"
+#include "mhd_reply.h"
+
+#include "mhd_socket_error.h"
+
+#include "mhd_public_api.h"
+
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * What is this connection waiting for?
+ */
+enum MHD_FIXED_FLAGS_ENUM_ MHD_ConnectionEventLoopInfo
+{
+  /**
+   * We are waiting to be able to read.
+   * The same value as #mhd_SOCKET_NET_STATE_RECV_READY
+   */
+  MHD_EVENT_LOOP_INFO_READ = 1 << 0
+  ,
+  /**
+   * We are waiting to be able to write.
+   * The same value as #mhd_SOCKET_NET_STATE_SEND_READY
+   */
+  MHD_EVENT_LOOP_INFO_WRITE = 1 << 1
+  ,
+  /**
+   * We are waiting for the application to provide data.
+   */
+  MHD_EVENT_LOOP_INFO_PROCESS = 1 << 4
+  ,
+  /**
+   * We are finished and are awaiting cleanup.
+   */
+  MHD_EVENT_LOOP_INFO_CLEANUP = 1 << 5
+};
+
+#define MHD_EVENT_LOOP_INFO_PROCESS_READ \
+        (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_PROCESS)
+
+
+/**
+ * The network states for connected sockets
+ * An internal version of #MHD_FdState. Keep in sync!
+ */
+enum MHD_FIXED_FLAGS_ENUM_ mhd_SocketNetState
+{
+  /**
+   * No active states of the socket
+   */
+  mhd_SOCKET_NET_STATE_NOTHING = 0
+  ,
+  /**
+   * The socket is ready for receiving
+   */
+  mhd_SOCKET_NET_STATE_RECV_READY = 1 << 0
+  ,
+  /**
+   * The socket is ready for sending
+   */
+  mhd_SOCKET_NET_STATE_SEND_READY = 1 << 1
+  ,
+  /**
+   * The socket has some unrecoverable error
+   */
+  mhd_SOCKET_NET_STATE_ERROR_READY = 1 << 2
+};
+
+
+/**
+ * States in a state machine for a connection.
+ *
+ * The main transitions are any-state to #MHD_CONNECTION_CLOSED, any
+ * state to state+1, #MHD_CONNECTION_FOOTERS_SENT to
+ * #MHD_CONNECTION_INIT.  #MHD_CONNECTION_CLOSED is the terminal state
+ * and #MHD_CONNECTION_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_CONNECTION_CLOSED or #MHD_CONNECTION_INIT requires the write
+ * to be complete.
+ */
+enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE
+{
+  /**
+   * Connection just started (no headers received).
+   * Waiting for the line with the request type, URL and version.
+   */
+  MHD_CONNECTION_INIT = 0,
+
+  /**
+   * Part of the request line was received.
+   * Wait for complete line.
+   */
+  MHD_CONNECTION_REQ_LINE_RECEIVING = MHD_CONNECTION_INIT + 1,
+
+  /**
+   * We got the URL (and request type and version).  Wait for a header line.
+   *
+   * A milestone state. No received data is processed in this state.
+   */
+  MHD_CONNECTION_REQ_LINE_RECEIVED = MHD_CONNECTION_REQ_LINE_RECEIVING + 1,
+
+  /**
+   * Receiving request headers.  Wait for the rest of the headers.
+   */
+  MHD_CONNECTION_REQ_HEADERS_RECEIVING = MHD_CONNECTION_REQ_LINE_RECEIVED + 1,
+
+  /**
+   * We got the request headers.  Process them.
+   */
+  MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_REQ_HEADERS_RECEIVING + 1,
+
+  /**
+   * We have processed the request headers.  Call application callback.
+   */
+  MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
+
+  /**
+   * We have processed the headers and need to send 100 CONTINUE.
+   */
+  MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
+
+  /**
+   * We have sent 100 CONTINUE (or do not need to).  Read the message body.
+   */
+  MHD_CONNECTION_BODY_RECEIVING = MHD_CONNECTION_CONTINUE_SENDING + 1,
+
+  /**
+   * We got the request body.
+   *
+   * A milestone state. No received data is processed in this state.
+   */
+  MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_BODY_RECEIVING + 1,
+
+  /**
+   * We are reading the request footers.
+   */
+  MHD_CONNECTION_FOOTERS_RECEIVING = MHD_CONNECTION_BODY_RECEIVED + 1,
+
+  /**
+   * We received the entire footer.
+   *
+   * A milestone state. No received data is processed in this state.
+   */
+  MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVING + 1,
+
+  /**
+   * We received the entire request.
+   * Wait for a response to be queued.
+   */
+  MHD_CONNECTION_FULL_REQ_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
+
+  /**
+   * Finished reading of the request and the response is ready.
+   * Switch internal logic from receiving to sending, prepare connection
+   * sending the reply and build the reply header.
+   */
+  MHD_CONNECTION_START_REPLY = MHD_CONNECTION_FULL_REQ_RECEIVED + 1,
+
+  /**
+   * We have prepared the response headers in the write buffer.
+   * Send the response headers.
+   */
+  MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_START_REPLY + 1,
+
+  /**
+   * We have sent the response headers.  Get ready to send the body.
+   */
+  MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
+
+  /**
+   * We are waiting for the client to provide more
+   * data of a non-chunked body.
+   */
+  MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_HEADERS_SENT + 1,
+
+  /**
+   * We are ready to send a part of a non-chunked body.  Send it.
+   */
+  MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
+
+  /**
+   * We are waiting for the client to provide a chunk of the body.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
+
+  /**
+   * We are ready to send a chunk.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
+
+  /**
+   * We have sent the chunked response body. Prepare the footers.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
+
+  /**
+   * We have prepared the response footer.  Send it.
+   */
+  MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_CHUNKED_BODY_SENT + 1,
+
+  /**
+   * We have sent the entire reply.
+   * Shutdown connection or restart processing to get a new request.
+   */
+  MHD_CONNECTION_FULL_REPLY_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
+
+  /**
+   * This connection is to be closed.
+   */
+  MHD_CONNECTION_CLOSED = MHD_CONNECTION_FULL_REPLY_SENT + 1
+
+};
+
+
+/**
+ * Ability to use same connection for next request
+ */
+enum MHD_FIXED_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,
+
+  /**
+   * Connection will be upgraded
+   */
+  MHD_CONN_MUST_UPGRADE = 2
+};
+
+/**
+ * The helper struct for the connections list
+ */
+mhd_DLINKEDL_LINKS_DEF (MHD_Connection);
+
+
+/**
+ * State kept for HTTP network connection.
+ */
+struct MHD_Connection
+{
+
+  /**
+   * The list with all daemon's connections
+   */
+  mhd_DLNKDL_LINKS (MHD_Connection,all_conn);
+
+  /**
+   * The state of the connected socket
+   */
+  enum mhd_SocketNetState sk_ready;
+
+  /**
+   * The type of the error when disconnected early
+   */
+  enum mhd_SocketError sk_discnt_err;
+
+  /**
+   * Set to 'true' when the client shut down write/send and
+   * the last byte from remote has been read.
+   */
+  bool sk_rmt_shut_wr;
+
+  /**
+   * The list with all daemon's connections that ready to processing
+   */
+  mhd_DLNKDL_LINKS (MHD_Connection,proc_ready);
+
+  /**
+   * The list of connections sorted by timeout
+   */
+  mhd_DLNKDL_LINKS (MHD_Connection,by_timeout);
+
+  /**
+   * Reference to the MHD_Daemon struct.
+   */
+  struct MHD_Daemon *daemon;
+
+  /**
+   * Request-specific data
+   */
+  struct MHD_Request rq;
+
+  /**
+   * Reply-specific data
+   */
+  struct MHD_Reply rp;
+
+  /**
+   * 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 connection-related data except for the
+   * response (which maybe shared between connections) and the IP
+   * address (which persists across individual requests).
+   */
+  struct MemoryPool *pool;
+
+  /**
+   * We allow the main application to associate some pointer with the
+   * TCP connection (which may span multiple HTTP requests).  Here is
+   * where we store it.  (MHD does not know or care what it is).
+   * The location is given to the #MHD_NotifyConnectionCallback and
+   * also accessible via #MHD_CONNECTION_INFO_SOCKET_CONTEXT.
+   */
+  void *socket_context;
+
+  /**
+   * 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;
+
+  /**
+   * Foreign address (of length @e addr_len).  MALLOCED (not
+   * in pool!).
+   */
+  struct sockaddr_storage *addr;
+
+#if defined(MHD_USE_THREADS)
+  /**
+   * Thread handle for this connection (if we are using
+   * one thread per connection).
+   */
+  mhd_thread_handle_ID tid;
+#endif
+
+  /**
+   * Size of @e read_buffer (in bytes).
+   * This value indicates how many bytes we're willing to read
+   * into the buffer.
+   */
+  size_t read_buffer_size;
+
+  /**
+   * Position where we currently append data in @e read_buffer (the
+   * next char after the 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;
+
+  /**
+   * Position in the 100 CONTINUE message that
+   * we need to send when receiving http 1.1 requests.
+   */
+  size_t continue_message_write_offset;
+
+  /**
+   * Length of the foreign address.
+   */
+  size_t addr_len;
+
+  /**
+   * Last time this connection had any activity
+   * (reading or writing).
+   */
+  uint_fast64_t last_activity;
+
+  /**
+   * After how many milliseconds of inactivity should
+   * this connection time out?
+   * Zero for no timeout.
+   */
+  uint_fast64_t connection_timeout_ms;
+
+  /**
+   * 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;
+
+  /**
+   * The type of the socket: TCP/IP or non TCP/IP (a UNIX domain socket, a pipe)
+   */
+  enum mhd_Tristate is_nonip;
+
+  /**
+   * true if #socket_fd is non-blocking, false otherwise.
+   */
+  bool sk_nonblck;
+
+  /**
+   * true if connection socket has set SIGPIPE suppression
+   */
+  bool sk_spipe_suppress;
+
+  /**
+   * Tracks TCP_CORK / TCP_NOPUSH of the connection socket.
+   */
+  enum mhd_Tristate sk_corked;
+
+  /**
+   * Tracks TCP_NODELAY state of the connection socket.
+   */
+  enum mhd_Tristate sk_nodelay;
+
+  /**
+   * 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;
+
+  /**
+   * Some error happens during processing the connection therefore this
+   * connection must be closed.
+   * The error may come from the client side (like wrong request format),
+   * from the application side (like data callback returned error), or from
+   * the OS side (like out-of-memory).
+   */
+  bool stop_with_error;
+
+  /**
+   * Response queued early, before the request is fully processed,
+   * the client upload is rejected.
+   * The connection cannot be reused for additional requests as the current
+   * request is incompletely read and it is unclear where is the initial
+   * byte of the next request.
+   */
+  bool discard_request;
+
+#if defined(MHD_USE_THREADS)
+  /**
+   * Set to `true` if the thread has been joined.
+   */
+  bool thread_joined;
+#endif
+
+  /**
+   * Connection is in the cleanup DL-linked list.
+   */
+  bool in_cleanup;
+
+  /**
+   * State in the FSM for this connection.
+   */
+  enum MHD_CONNECTION_STATE state;
+
+  /**
+   * What is this connection waiting for?
+   */
+  enum MHD_ConnectionEventLoopInfo event_loop_info;
+};
+
+
+#endif /* ! MHD_CONNECTION_H */

+ 900 - 0
src/mhd2/mhd_daemon.h

@@ -0,0 +1,900 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_daemon.h
+ * @brief  The header for declaration of struct MHD_Daemon
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DAEMON_H
+#define MHD_DAEMON_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+#include "mhd_socket_type.h"
+
+#include "mhd_public_api.h"
+
+#ifdef MHD_USE_THREADS
+#  include "mhd_threads.h"
+#  include "mhd_itc_types.h"
+#endif
+
+#if defined(MHD_USE_SELECT) && defined(MHD_POSIX_SOCKETS)
+#  ifdef HAVE_SYS_SELECT_H
+#    include <sys/select.h>
+#  else
+#    ifdef HAVE_SYS_TIME_H
+#      include <sys/time.h>
+#    endif
+#    ifdef HAVE_SYS_TYPES_H
+#      include <sys/types.h>
+#    endif
+#    ifdef HAVE_UNISTD_H
+#      include <unistd.h>
+#    else
+#      include <stdlib.h>
+#    endif
+#    ifdef HAVE_SELECTLIB_H
+#      include  <selectLib.h>
+#    endif
+#  endif
+#endif
+
+#include "sys_poll.h"
+
+#include "mhd_dlinked_list.h"
+
+struct DaemonOptions; /* Forward declaration */
+struct MHD_Connection; /* Forward declaration */
+
+/**
+ * The helper struct for the connections list
+ */
+mhd_DLINKEDL_LIST_DEF (MHD_Connection);
+
+// #define MHD_USE_EPOLL /////////////////////////////////////////
+// #undef MHD_WINSOCK_SOCKETS
+// #define MHD_POSIX_SOCKETS /////////////////////////////
+
+/**
+ * The current phase of the daemon life
+ */
+enum MHD_FIXED_ENUM_ mhd_DaemonState
+{
+  /**
+   * The daemon has been created, but not yet started.
+   * Setting configuration options is possible.
+   */
+  mhd_DAEMON_STATE_NOT_STARTED = 0
+  ,
+  /**
+   * The daemon is being started.
+   */
+  mhd_DAEMON_STATE_STARTING
+  ,
+  /**
+   * The daemon has been started.
+   * Normal operations.
+   */
+  mhd_DAEMON_STATE_STARTED
+  ,
+  /**
+   * The daemon has failed to start
+   */
+  mhd_DAEMON_STATE_FAILED
+  ,
+  /**
+   * The daemon is being stopped.
+   */
+  mhd_DAEMON_STATE_STOPPING
+  ,
+  /**
+   * The daemon is stopped.
+   * The state should rarely visible as daemon should be destroyed when stopped.
+   */
+  mhd_DAEMON_STATE_STOPPED
+};
+
+
+/**
+ * Internal version of the daemon work mode type
+ */
+enum MHD_FIXED_ENUM_ mhd_WorkModeIntType
+{
+  /**
+   * Network edge-triggered events are monitored and provided by application.
+   * Receiving, sending and processing of the network data if performed when
+   * special MHD function is called by application.
+   * No threads managed by the daemon.
+   */
+  mhd_WM_INT_EXTERNAL_EVENTS_EDGE
+  ,
+  /**
+   * Network level-triggered events are monitored and provided by application.
+   * Receiving, sending and processing of the network data if performed when
+   * special MHD function is called by application.
+   * No threads managed by the daemon.
+   */
+  mhd_WM_INT_EXTERNAL_EVENTS_LEVEL
+  ,
+  /**
+   * The daemon checks for the network events, receives, sends and process
+   * the network data when special MHD function is called by application.
+   * No threads managed by the daemon.
+   */
+  mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS
+  ,
+  /**
+   * The daemon runs its own single thread, where the daemon monitors
+   * all network events, receives, sends and process the network data.
+   */
+  mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD
+  ,
+  /**
+   * The daemon runs its own single thread, where the daemon monitors
+   * the new incoming connections, and runs individual thread for each
+   * established connection, where the daemon monitors connection, receives,
+   * sends and process the network data.
+   */
+  mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION
+  ,
+  /**
+   * The daemon runs its fixed number of threads, all threads monitors the
+   * new incoming connections and each thread handles own subset of the network
+   * connections (monitors connections network events, receives, sends and
+   * process the network data).
+   */
+  mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL
+};
+
+/**
+ * Check whether given mhd_WorkModeIntType value should have internal threads,
+ * either directly controlled or indirectly, via additional workers daemons.
+ */
+#define mhd_WM_INT_HAS_THREADS(wm_i) \
+        (mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD <= wm_i)
+
+/**
+ * Check whether given mhd_WorkModeIntType value has external events
+ */
+#define mhd_WM_INT_HAS_EXT_EVENTS(wm_i) \
+        (mhd_WM_INT_EXTERNAL_EVENTS_LEVEL >= wm_i)
+
+
+/**
+ * Sockets polling internal syscalls used by MHD.
+ *
+ * The same value used as by #MHD_SockPollSyscall, however instead of "auto"
+ * method this enum uses "not yet set" and enum is extended with an additional
+ * "external" value.
+ */
+enum MHD_FIXED_ENUM_ mhd_IntPollType
+{
+  /**
+   * External sockets polling is used.
+   */
+  mhd_POLL_TYPE_EXT = -1
+  ,
+  /**
+   * Internal sockets polling syscall has not been selected yet.
+   */
+  mhd_POLL_TYPE_NOT_SET_YET = MHD_SPS_AUTO
+  ,
+  /**
+   * Use select().
+   */
+  mhd_POLL_TYPE_SELECT = MHD_SPS_SELECT
+  ,
+  /**
+   * Use poll().
+   */
+  mhd_POLL_TYPE_POLL = MHD_SPS_POLL
+  ,
+  /**
+   * Use epoll.
+   */
+  mhd_POLL_TYPE_EPOLL = MHD_SPS_EPOLL
+};
+
+#if defined(HAVE_UINTPTR_T)
+typedef uintptr_t mhd_SockRelMarker;
+#else
+typedef unsigned char *mhd_SockRelMarker;
+#endif
+
+
+#define mhd_SOCKET_REL_MARKER_EMPTY    ((mhd_SockRelMarker) 0)
+
+#define mhd_SOCKET_REL_MARKER_ITC      ((mhd_SockRelMarker) - 1)
+
+#define mhd_SOCKET_REL_MARKER_LISTEN   (mhd_SOCKET_REL_MARKER_ITC - 1)
+/**
+ * Identifier of the FD related to event
+ */
+union mhd_SocketRelation
+{
+  /**
+   * Identifier of the FD.
+   * Only valid when it is equal to #mhd_SOCKET_REL_MARKER_EMPTY,
+   * #mhd_SOCKET_REL_MARKER_ITC or #mhd_SOCKET_REL_MARKER_LISTEN.
+   */
+  mhd_SockRelMarker fd_id;
+  /**
+   * This is a connection's FD.
+   * This is valid only when @a fd_id is not valid.
+   */
+  struct MHD_Connection *connection;
+};
+
+#ifdef MHD_USE_SELECT
+
+/**
+ * Daemon's pointers to the preallocated arrays for running sockets monitoring
+ * by poll().
+ */
+struct mhd_DaemonEventsSelectData
+{
+  /**
+   * Set of sockets monitored for read (receive) readiness.
+   */
+  fd_set *rfds;
+  /**
+   * Set of sockets monitored for write (send) readiness.
+   */
+  fd_set *wfds;
+  /**
+   * Set of sockets monitored for exception (error) readiness.
+   */
+  fd_set *efds;
+};
+
+#endif /* MHD_USE_SELECT */
+
+#ifdef MHD_USE_POLL
+
+/**
+ * Daemon's pointers to the preallocated arrays for running sockets monitoring
+ * by poll().
+ */
+struct mhd_DaemonEventsPollData
+{
+  /**
+   * Array of sockets monitored for read (receive) readiness.
+   * The size of the array is maximum number of connections per this daemon plus
+   * two (one for the listen socket and one for ITC).
+   * ITC FDs and the listening are always the first (and the second), if used.
+   * The number of elements is always two plus maximum number of connections
+   * allowed for the daemon.
+   */
+  struct pollfd *fds;
+  /**
+   * Array of the @a fds identifications.
+   * Each slot matches the slot with the same number in @a fds.
+   * Up to two first positions reserved for the ITC and the listening.
+   * The number of elements is always two plus maximum number of connections
+   * allowed for the daemon.
+   */
+  union mhd_SocketRelation *rel;
+};
+
+#endif /* MHD_USE_POLL */
+
+#ifdef MHD_USE_EPOLL
+/**
+ * Daemon's parameters and pointers to the preallocated memory for running
+ * sockets monitoring by epoll.
+ */
+struct mhd_DaemonEventsEPollData
+{
+  /**
+   * The epoll control FD.
+   */
+  int e_fd;
+  /**
+   * The array of events reported by epoll.
+   */
+  struct epoll_event *events;
+
+  /**
+   * The number of elements in the allocated @a events arrays.
+   */
+  size_t num_elements;
+};
+
+#endif
+
+/**
+ * Daemon's data for external events for sockets monitoring.
+ * Internal version of struct MHD_WorkModeExternalEventLoopCBParam.
+ */
+struct mhd_DaemonEventsExternal
+{
+  /**
+   * Socket registration callback
+   */
+  MHD_SocketRegistrationUpdateCallback cb;
+  /**
+   * Closure for the @a cb
+   */
+  void *cls;
+};
+
+/**
+ * Type-specific events monitoring data
+ */
+union mhd_DaemonEventMonitoringTypeSpecificData
+{
+#ifdef MHD_USE_SELECT
+  /**
+   * Daemon's pointers to the preallocated arrays for running sockets monitoring
+   * by poll().
+   */
+  struct mhd_DaemonEventsSelectData select;
+#endif /* MHD_USE_SELECT */
+
+#ifdef MHD_USE_POLL
+  /**
+   * Daemon's pointers to the preallocated arrays for running sockets monitoring
+   * by poll().
+   */
+  struct mhd_DaemonEventsPollData poll;
+#endif /* MHD_USE_POLL */
+
+#ifdef MHD_USE_EPOLL
+  /**
+   * Daemon's parameters and pointers to the preallocated memory for running
+   * sockets monitoring by epoll.
+   */
+  struct mhd_DaemonEventsEPollData epoll;
+#endif
+
+  /**
+   * Daemon's data for external events for sockets monitoring.
+   * Internal version of struct MHD_WorkModeExternalEventLoopCBParam.
+   */
+  struct mhd_DaemonEventsExternal ext;
+};
+
+
+/**
+ * The required actions for the daemon
+ */
+struct mhd_DaemonEventActionRequired
+{
+  /**
+   * If 'true' then connection is waiting to be accepted
+   */
+  bool accept;
+};
+
+
+/**
+ * The data for events monitoring
+ */
+struct mhd_DaemonEventMonitoringData
+{
+  /**
+   * Sockets polling type used by the daemon.
+   */
+  enum mhd_IntPollType poll_type;
+
+  /**
+   * Type-specific events monitoring data
+   */
+  union mhd_DaemonEventMonitoringTypeSpecificData data;
+
+  /**
+   * The required actions for the daemon.
+   * If daemon has internal thread, this should be changed only inside
+   * the daemon's thread.
+   */
+  struct mhd_DaemonEventActionRequired act_req;
+
+  /**
+   * The list of the daemon's connections that need processing
+   */
+  mhd_DLNKDL_LIST (MHD_Connection,proc_ready);
+
+};
+
+
+/**
+ * The type of the socket
+ */
+enum MHD_FIXED_ENUM_ mhd_SocketType
+{
+  /**
+   * The socket type is some non-IP type.
+   */
+  mhd_SOCKET_TYPE_NON_IP = -2
+  ,
+  /**
+   * The socket type is UNIX (LOCAL)
+   */
+  mhd_SOCKET_TYPE_UNIX = -1
+  ,
+  /**
+   * The socket is unknown yet. It can be IP or non-IP.
+   */
+  mhd_SOCKET_TYPE_UNKNOWN = 0
+  ,
+  /**
+   * The socket is definitely IP.
+   */
+  mhd_SOCKET_TYPE_IP = 1
+};
+
+/**
+ * Listen socket data
+ */
+struct mhd_ListenSocket
+{
+  /**
+   * The listening socket
+   */
+  MHD_Socket fd;
+  /**
+   * The type of the listening socket @a fd
+   */
+  enum mhd_SocketType type;
+  /**
+   * 'true' if @a fd is non-blocking
+   */
+  bool non_block;
+  /**
+   * The port number for @a fd
+   *
+   * Zero if unknown and for non-IP socket.
+   */
+  uint_fast16_t port;
+};
+
+/**
+ * Configured settings for the daemon's network data
+ */
+struct mhd_DaemonNetworkSettings
+{
+#ifdef MHD_POSIX_SOCKETS
+  /**
+   * The maximum number for the network FDs.
+   * The valid FD number must be less then @a max_fd_num.
+   */
+  MHD_Socket max_fd_num;
+#else
+  int dummy; /* mute compiler warning */
+#endif
+};
+
+/**
+ * The daemon network/sockets data
+ */
+struct mhd_DaemonNetwork
+{
+  /**
+   * The listening socket
+   */
+  struct mhd_ListenSocket listen;
+
+#ifdef HAVE_EPOLL
+  /**
+   * The epoll FD.
+   * Set to '-1' when epoll is not used.
+   */
+  int epoll_fd;
+#endif
+  /**
+   * Configured settings for the daemon's network data
+   */
+  struct mhd_DaemonNetworkSettings cfg;
+};
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * The type of the daemon
+ */
+enum MHD_FIXED_ENUM_ mhd_DaemonType
+{
+  /**
+   * A single daemon, performing all the work.
+   *
+   * This daemon may have a optional single thread, managed by MHD.
+   */
+  mhd_DAEMON_TYPE_SINGLE
+#ifndef NDEBUG
+    = 1
+#endif
+  ,
+  /**
+   * A master daemon, only controlling worker daemons.
+   *
+   * This daemon never handle any network activity directly.
+   */
+  mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY
+  ,
+  /**
+   * A daemon with single internal thread for listening and multiple threads
+   * handling connections with the clients, one thread per connection.
+   */
+  mhd_DAEMON_TYPE_LISTEN_ONLY
+  ,
+  /**
+   * A worker daemon, performing the same work as a single daemon, but
+   * controlled by master daemon.
+   *
+   * This type of daemon always have single internal tread and never exposed
+   * to application directly.
+   */
+  mhd_DAEMON_TYPE_WORKER
+};
+
+/**
+ * Check whether the daemon type is allowed to have internal thread with
+ * direct control
+ */
+#define mhd_D_TYPE_IS_VALID(t) \
+        ((mhd_DAEMON_TYPE_SINGLE <= (t)) && (mhd_DAEMON_TYPE_WORKER >= (t)))
+
+/**
+ * Check whether the daemon type must not be exposed to the application
+ */
+#define mhd_D_TYPE_IS_INTERNAL_ONLY(t) \
+        (mhd_DAEMON_TYPE_WORKER == (t))
+
+/**
+ * Check whether the daemon type is allowed to process the network data
+ */
+#define mhd_D_TYPE_HAS_EVENTS_PROCESSING(t) \
+        (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY != (t))
+
+/**
+ * Check whether the daemon type must not be exposed to the application
+ */
+#define mhd_D_TYPE_HAS_WORKERS(t) \
+        (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY == (t))
+
+/**
+ * Check whether the daemon type has master (controlling) daemon
+ */
+#define mhd_D_TYPE_HAS_MASTER_DAEMON(t) \
+        (mhd_DAEMON_TYPE_WORKER == (t))
+
+#else  /* ! MHD_USE_THREADS */
+
+/**
+ * Check whether the daemon type is allowed to have internal thread with
+ * direct control
+ */
+#define mhd_D_TYPE_IS_VALID(t) (! 0)
+
+/**
+ * Check whether the daemon type must not be exposed to the application
+ */
+#define mhd_D_TYPE_IS_INTERNAL_ONLY(t) (0)
+
+/**
+ * Check whether the daemon type is allowed to process the network data
+ */
+#define mhd_D_TYPE_HAS_EVENTS_PROCESSING(t) (! 0)
+
+/**
+ * Check whether the daemon type must not be exposed to the application
+ */
+#define mhd_D_TYPE_HAS_WORKERS(t) (0)
+
+/**
+ * Check whether the daemon type has master (controlling) daemon
+ */
+#define mhd_D_TYPE_HAS_MASTER_DAEMON(t)  (0)
+
+#endif /* ! MHD_USE_THREADS */
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * Workers pool data
+ */
+struct mhd_DaemonWorkerPoolData
+{
+  /**
+   * Array of worker daemons
+   */
+  struct MHD_Daemon *workers;
+
+  /**
+   * The number of workers in the @a workers array
+   */
+  unsigned int num;
+};
+
+/**
+ * Hierarchy data for the daemon
+ */
+union mhd_DeamonHierarchyData
+{
+  /**
+   * The pointer to the master daemon
+   * Only for #mhd_DAEMON_TYPE_WORKER daemons.
+   */
+  struct MHD_Daemon *master;
+
+  /**
+   * Workers pool data.
+   * Only for #mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY daemons.
+   */
+  struct mhd_DaemonWorkerPoolData pool;
+};
+
+/**
+ * Configured settings for threading
+ */
+struct mhd_DaemonThreadingDataSettings
+{
+  /**
+   * The size of the stack.
+   * Zero to use system's defaults.
+   */
+  size_t stack_size;
+};
+
+/**
+ * Threading and Inter-Thread Communication data
+ */
+struct mhd_DaemonThreadingData
+{
+  /**
+   * The type of this daemon
+   */
+  enum mhd_DaemonType d_type;
+
+  /**
+   * Inter-Thread Communication channel.
+   * Used to trigger processing of the command or the data provided or updated
+   * by the application.
+   */
+  struct mhd_itc itc;
+
+  /**
+   * 'True' if stop has been requested.
+   * The daemon thread should stop all connections and then close.
+   */
+  volatile bool stop_requested;
+
+  /**
+   * The handle of the daemon's thread (if managed by the daemon)
+   */
+  mhd_thread_handle_ID tid;
+
+  /**
+   * The hierarchy data for the daemon.
+   * Used only when @a d_type is #mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY or
+   * #mhd_DAEMON_TYPE_WORKER.
+   */
+  union mhd_DeamonHierarchyData hier;
+
+  /**
+   * Configured settings for threading
+   */
+  struct mhd_DaemonThreadingDataSettings cfg;
+};
+
+#endif /* MHD_USE_THREADS */
+
+/**
+ * Configured settings for the daemon's connections
+ */
+struct mhd_DaemonConnectionsSettings
+{
+  /**
+   * The maximum number of connections handled by the daemon
+   */
+  unsigned int count_limit;
+
+  /**
+   * Connection's default timeout value (in seconds)
+   */
+  unsigned int timeout;
+
+  /**
+   * Connection's memory pool size
+   */
+  size_t mem_pool_size;
+};
+
+/**
+ * Connections handling data
+ */
+struct mhd_DaemonConnections
+{
+
+  /**
+   * The list of all daemon's connections.
+   * All connection are listed here, expect connection in @a to_clean list.
+   */
+  mhd_DLNKDL_LIST (MHD_Connection,all_conn);
+
+  /**
+   * The list of all daemon's connections
+   */
+  mhd_DLNKDL_LIST (MHD_Connection,def_timeout);
+
+  /**
+   * The list of all daemon's connections
+   */
+  mhd_DLNKDL_LIST (MHD_Connection,to_clean);
+
+  /**
+   * The current number of connections handled by the daemon
+   */
+  unsigned int count;
+
+  /**
+   * If set to 'true' then no new connection is allowed.
+   * New connection may be blocked because of various system limits, when
+   * additional connection would fail anyway. This flag should be cleared
+   * when any already processing connection closed.
+   * Can be checked from other threads
+   */
+  volatile bool block_new;
+
+  /**
+   * Configured settings for the daemon's connections
+   */
+  struct mhd_DaemonConnectionsSettings cfg;
+};
+
+
+#ifndef NDEBUG
+/**
+ * Various debugging data
+ */
+struct mhd_daemon_debug
+{
+  bool net_inited;
+  bool net_deinited;
+  bool events_allocated;
+  bool events_fully_inited;
+  bool thread_pool_inited;
+  bool threading_inited;
+  bool connections_inited;
+  bool avoid_accept4;
+};
+#endif /* NDEBUG */
+
+
+struct MHD_Daemon
+{
+  /* General data */
+
+  /**
+   * The daemon state
+   */
+  enum mhd_DaemonState state;
+
+  /**
+   * The daemon work mode (private version)
+   */
+  enum mhd_WorkModeIntType wmode_int;
+
+  /* Events/sockets monitoring/polling data */
+
+  /**
+   * The data for events monitoring
+   */
+  struct mhd_DaemonEventMonitoringData events;
+
+  /* Network/sockets data */
+
+  /**
+   * The daemon network/sockets data
+   */
+  struct mhd_DaemonNetwork net;
+
+#ifdef MHD_USE_THREADS
+  /* Threading data */
+
+  /**
+   * The daemon threading and Inter-Thread Communication data
+   */
+  struct mhd_DaemonThreadingData threading;
+#endif
+
+  /* Connections handling */
+
+  /**
+   * The connections handling data
+   */
+  struct mhd_DaemonConnections conns;
+
+  /* Request processing data */
+
+  /**
+   * Request callback.
+   * The main request processing callback.
+   */
+  MHD_RequestCallback req_cb;
+
+  /**
+   * The closure for @a req_cb
+   */
+  void *req_cb_cls;
+
+  /* Other data */
+
+  /**
+   * Daemon logging parameters
+   */
+  struct MHD_DaemonOptionValueLog log_params;
+
+
+  /* Temporal data */
+
+  /**
+   * User settings, before applied to the daemon itself
+   */
+  struct DaemonOptions *settings;
+
+#ifndef NDEBUG
+  /* Debug data */
+
+  struct mhd_daemon_debug dbg;
+#endif
+};
+
+
+#ifdef MHD_POSIX_SOCKETS
+/**
+ * Checks whether @a fd socket number fits limitations for the @a d_ptr daemon
+ */
+#  define mhd_FD_FITS_DAEMON(d_ptr,fd) \
+        ((MHD_INVALID_SOCKET == d_ptr->net.cfg.max_fd_num) || \
+         (d_ptr->net.cfg.max_fd_num > fd))
+#else
+#  define mhd_FD_FITS_DAEMON(d_ptr,fd) (! 0)
+#endif
+#endif /* ! MHD_DAEMON_H */
+
+#ifdef MHD_USE_EPOLL
+#  define mhd_D_IS_USING_EPOLL(d) \
+        (mhd_POLL_TYPE_EPOLL == ((d)->events.poll_type))
+#else
+#  define mhd_D_IS_USING_EPOLL(d) (0)
+#endif
+
+#ifdef MHD_USE_THREADS
+#  define mhd_D_HAS_THREADS(d) mhd_WM_INT_HAS_THREADS ((d)->wmode_int)
+#else
+#  define mhd_D_HAS_THREADS(d) (0)
+#endif
+
+#define mhd_D_HAS_WORKERS(d) mhd_D_TYPE_HAS_WORKERS ((d)->threading.d_type)
+
+#define mhd_D_HAS_MASTER(d) mhd_D_TYPE_HAS_MASTER_DAEMON ((d)->threading.d_type)
+
+#define mhd_D_IS_INTERNAL_ONLY(d) \
+        mhd_D_TYPE_IS_INTERNAL_ONLY ((d)->threading.d_type)
+
+#define mhd_D_IS_USING_EDGE_TRIG(d) \
+        (mhd_D_IS_USING_EPOLL (d) || \
+         (mhd_WM_INT_EXTERNAL_EVENTS_EDGE ==((d)->wmode_int)))

+ 290 - 0
src/mhd2/mhd_dlinked_list.h

@@ -0,0 +1,290 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_dlinked_list.h
+ * @brief  Double-linked list macros and declarations
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Double-linked list macros help create and manage the chain of objects
+ * connected via inter-object pointers (named here @a links_name), while
+ * the list is held by the owner within the helper (named here @a list_name).
+ */
+
+#ifndef MHD_DLINKED_LIST_H
+#define MHD_DLINKED_LIST_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_null_macro.h"
+#include "mhd_assert.h"
+
+
+/* This header defines macros for handling double-linked lists of object.
+   The pointers to the first and the last objects in the list are held in
+   the "owner".
+   The list member objects links to each other via "next" and "prev" links.
+   Each member object can be part of several lists. For example, connections are
+   maintained in "all connections" and "need to process" lists simultaneously.
+   List member can be removed from the list or inserted to the list at any
+   moment.
+   Typically the name of the list (inside the "owner" object) is the same as
+   the name of inter-links. However, it is possible to use different names.
+   For example, connections can be removed from "all connections" list and
+   moved the "clean up" list using the same internal links "all connections".
+   As this is a double-linked list, it can be walked from begin to the end and
+   in the opposite direction.
+   The list is compatible only with "struct" types.
+ */
+
+/* Helpers */
+
+#define mhd_DLNKDL_LIST_TYPE_(base_name) struct base_name ## s_list
+
+#define mhd_DLNKDL_LINKS_TYPE_(base_name) struct base_name ## _link
+
+
+/* Names */
+
+/**
+ * The name of struct that hold the list in the owner object
+ */
+#define mhd_DLNKDL_LIST_TYPE(base_name) mhd_DLNKDL_LIST_TYPE_ (base_name)
+
+/**
+ * The name of struct that links between the list members
+ */
+#define mhd_DLNKDL_LINKS_TYPE(base_name) mhd_DLNKDL_LINKS_TYPE_ (base_name)
+
+/* Definitions of the structures */
+
+/**
+ * Template for declaration of the list helper struct
+ * @param l_type the name of the struct objects that list links
+ */
+#define mhd_DLINKEDL_LIST_DEF(l_type) \
+        mhd_DLNKDL_LIST_TYPE (l_type) { /* Holds the list in the owner */         \
+          struct l_type *first; /* The pointer to the first object in the list */ \
+          struct l_type *last; /* The pointer to the last object in the list */   \
+        }
+
+/**
+ * Template for declaration of links helper struct
+ * @param l_type the name of the struct objects that list links
+ */
+#define mhd_DLINKEDL_LINKS_DEF(l_type) \
+        mhd_DLNKDL_LINKS_TYPE (l_type) { /* Holds the links in the members */       \
+          struct l_type *prev; /* The pointer to the previous object in the list */ \
+          struct l_type *next; /* The pointer to the next object in the list */     \
+        }
+
+
+/**
+ * Template for declaration of list helper structs
+ * @param l_type the name of the struct objects that list links
+ */
+#define mhd_DLINKEDL_STRUCTS_DEFS(l_type) \
+        mhd_DLINKEDL_LIST_DEF (l_type); mhd_DLINKEDL_LINKS_DEF (l_type)
+
+/* Declarations for the list owners and the list members */
+
+/**
+ * Declare the owner's list member
+ */
+#define mhd_DLNKDL_LIST(l_type,list_name) \
+        mhd_DLNKDL_LIST_TYPE (l_type) list_name
+
+/**
+ * Declare the list object links member
+ */
+#define mhd_DLNKDL_LINKS(l_type,links_name) \
+        mhd_DLNKDL_LINKS_TYPE (l_type) links_name
+
+/* Direct work with the list */
+/* These macros directly use the pointer to the list and allow using
+ * names of the list object (within the owner object) different from the names
+ * of link object (in the list members). */
+
+/**
+ * Initialise the double linked list pointers in the list object using
+ * the directly pointer to the list
+ * @param p_list the pointer to the list
+ * @param l_name the name of the list
+ */
+#define mhd_DLINKEDL_INIT_LIST_D(p_list) \
+        do {(p_list)->first = NULL; (p_list)->last = NULL;} while (0)
+
+/**
+ * Insert object into the first position in the list using direct pointer
+ * to the list
+ * @param p_list the pointer to the list
+ * @param p_obj the pointer to the new list member object to insert to
+ *              the @a l_name list
+ * @param l_name the name of the list
+ */
+#define mhd_DLINKEDL_INS_FIRST_D(p_list,p_obj,links_name) do { \
+          mhd_assert (NULL == (p_obj)->links_name.prev); \
+          mhd_assert (NULL == (p_obj)->links_name.next); \
+          mhd_assert (((p_list)->first) || (! ((p_list)->last)));  \
+          mhd_assert ((! ((p_list)->first)) || ((p_list)->last));  \
+          if (NULL != (p_list)->first)                             \
+          { mhd_assert (NULL == (p_list)->first->links_name.prev); \
+            (p_obj)->links_name.next = (p_list)->first;            \
+            (p_obj)->links_name.next->links_name.prev = (p_obj); } else \
+          { (p_list)->last = (p_obj); } \
+          (p_list)->first = (p_obj);  } while (0)
+
+/**
+ * Insert object into the last position in the list using direct pointer
+ * to the list
+ * @param p_list the pointer to the list
+ * @param p_obj the pointer to the new list member object to insert to
+ *              the @a l_name list
+ * @param l_name the name of the list
+ */
+#define mhd_DLINKEDL_INS_LAST_D(p_list,p_obj,links_name) do { \
+          mhd_assert (NULL == (p_obj)->links_name.prev); \
+          mhd_assert (NULL == (p_obj)->links_name.next); \
+          mhd_assert (((p_list)->first) || (! ((p_list)->last))); \
+          mhd_assert ((! ((p_list)->first)) || ((p_list)->last)); \
+          if (NULL != (p_list)->last)                             \
+          { mhd_assert (NULL == (p_list)->last->links_name.next); \
+            (p_obj)->links_name.prev = (p_list)->last;            \
+            (p_obj)->links_name.prev->links_name.next = (p_obj); } else \
+          { (p_list)->first = (p_obj); } \
+          (p_list)->last = (p_obj);  } while (0)
+
+/**
+ * Remove object from the list using direct pointer to the list
+ * @param p_list the pointer to the list
+ * @param p_own the pointer to the existing @a l_name list member object
+ *              to remove from the list
+ * @param l_name the name of the list
+ */
+#define mhd_DLINKEDL_DEL_D(p_list,p_obj,links_name) do { \
+          mhd_assert (NULL != (p_list)->first); \
+          mhd_assert (NULL != (p_list)->last);  \
+          if (NULL != (p_obj)->links_name.next) \
+          { mhd_assert (NULL != (p_obj)->links_name.next->links_name.prev); \
+            (p_obj)->links_name.next->links_name.prev =  \
+              (p_obj)->links_name.prev; } else           \
+          { mhd_assert ((p_obj) == (p_list)->last);      \
+            (p_list)->last = (p_obj)->links_name.prev; } \
+          if (NULL != (p_obj)->links_name.prev)          \
+          { mhd_assert (NULL != (p_obj)->links_name.prev->links_name.next); \
+            (p_obj)->links_name.prev->links_name.next =   \
+              (p_obj)->links_name.next; } else            \
+          { mhd_assert ((p_obj) == (p_list)->first);      \
+            (p_list)->first = (p_obj)->links_name.next; } \
+          (p_obj)->links_name.prev = NULL;                \
+          (p_obj)->links_name.next = NULL;  } while (0)
+
+/**
+ * Get the fist object in the list using direct pointer to the list
+ */
+#define mhd_DLINKEDL_GET_FIRST_D(p_list) ((p_list)->first)
+
+/**
+ * Get the last object in the list using direct pointer to the list
+ */
+#define mhd_DLINKEDL_GET_LAST_D(p_list) ((p_list)->last)
+
+
+/* ** The main interface ** */
+/* These macros use identical names for the list object itself (within the
+ * owner object) and the links object (within the list members). */
+
+/* Initialisers */
+
+/**
+ * Initialise the double linked list pointers in the owner object
+ * @param p_own the pointer to the owner object with the @a l_name list
+ * @param l_name the name of the list
+ */
+#define mhd_DLINKEDL_INIT_LIST(p_own,list_name) \
+        mhd_DLINKEDL_INIT_LIST_D (&((p_own)->list_name))
+
+/**
+ * Initialise the double linked list pointers in the list member object
+ * @param p_obj the pointer to the future member object of the @a l_name list
+ * @param l_name the name of the list
+ */
+#define mhd_DLINKEDL_INIT_LINKS(p_obj,links_name) \
+        do {(p_obj)->links_name.prev = NULL;      \
+            (p_obj)->links_name.next = NULL;} while (0)
+
+/* List manipulations */
+
+/**
+ * Insert object into the first position in the list
+ * @param p_own the pointer to the owner object with the @a l_name list
+ * @param p_obj the pointer to the new list member object to insert to
+ *              the @a l_name list
+ * @param l_name the same name for the list and the links
+ */
+#define mhd_DLINKEDL_INS_FIRST(p_own,p_obj,l_name) \
+        mhd_DLINKEDL_INS_FIRST_D (&((p_own)->l_name),(p_obj),l_name)
+
+/**
+ * Insert object into the last position in the list
+ * @param p_own the pointer to the owner object with the @a l_name list
+ * @param p_obj the pointer to the new list member object to insert to
+ *              the @a l_name list
+ * @param l_name the same name for the list and the links
+ */
+#define mhd_DLINKEDL_INS_LAST(p_own,p_obj,l_name) \
+        mhd_DLINKEDL_INS_LAST_D (&((p_own)->l_name),(p_obj),l_name)
+
+/**
+ * Remove object from the list
+ * @param p_mem the pointer to the owner object with the @a l_name list
+ * @param p_own the pointer to the existing @a l_name list member object
+ *              to remove from the list
+ * @param l_name the same name for the list and the links
+ */
+#define mhd_DLINKEDL_DEL(p_own,p_obj,l_name) \
+        mhd_DLINKEDL_DEL_D (&((p_own)->l_name),(p_obj),l_name)
+
+/* List iterations */
+
+/**
+ * Get the fist object in the list
+ */
+#define mhd_DLINKEDL_GET_FIRST(p_own,list_name) \
+        mhd_DLINKEDL_GET_FIRST_D (&((p_own)->list_name))
+
+/**
+ * Get the last object in the list
+ */
+#define mhd_DLINKEDL_GET_LAST(p_own,list_name) \
+        mhd_DLINKEDL_GET_LAST_D (&((p_own)->list_name))
+
+/**
+ * Get the next object in the list
+ */
+#define mhd_DLINKEDL_GET_NEXT(p_obj,links_name) ((p_obj)->links_name.next)
+
+/**
+ * Get the previous object in the list
+ */
+#define mhd_DLINKEDL_GET_PREV(p_obj,links_name) ((p_obj)->links_name.prev)
+
+
+#endif /* ! MHD_DLINKED_LIST_H */

+ 105 - 0
src/mhd2/mhd_iovec.h

@@ -0,0 +1,105 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_iovec.h
+ * @brief  The definition of the tristate type and helper macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_IOVEC_H
+#define MHD_IOVEC_H 1
+
+#include "mhd_sys_options.h"
+#include "mhd_socket_type.h"
+
+#if defined(HAVE_WRITEV) || defined(HAVE_SENDMSG)
+#  ifdef HAVE_SYS_TYPES_H
+#    include <sys/types.h>
+#  endif
+#  ifdef HAVE_SYS_SOCKET_H
+#    include <sys/socket.h>
+#  elif defined(HAVE_UNISTD_H)
+#    include <unistd.h>
+#  endif
+#  ifdef HAVE_SOCKLIB_H
+#    include <sockLib.h>
+#  endif
+#  ifdef HAVE_SYS_UIO_H
+#    include <sys/uio.h>
+#  endif
+#endif
+
+#if defined(MHD_WINSOCK_SOCKETS)
+/**
+ * Internally used I/O vector type for use with winsock.
+ * Binary matches system "WSABUF".
+ */
+struct mhd_w32_iovec
+{
+  unsigned long iov_len;
+  char *iov_base;
+};
+typedef struct mhd_w32_iovec mhd_iovec;
+#define MHD_IOV_ELMN_MAX_SIZE    ULONG_MAX
+typedef unsigned long mhd_iov_size;
+#elif defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+/**
+ * Internally used I/O vector type for use when writev or sendmsg
+ * is available. Matches system "struct iovec".
+ */
+typedef struct iovec mhd_iovec;
+#define MHD_IOV_ELMN_MAX_SIZE    SIZE_MAX
+typedef size_t mhd_iov_size;
+#else
+/**
+ * Internally used I/O vector type for use when writev or sendmsg
+ * is not available.
+ */
+typedef struct MHD_IoVec mhd_iovec;
+#define MHD_IOV_ELMN_MAX_SIZE    SIZE_MAX
+typedef size_t mhd_iov_size;
+#endif
+
+
+struct mhd_iovec_track
+{
+  /**
+   * The copy of array of iovec elements.
+   * The copy of elements are updated during sending.
+   * The number of elements is not changed during lifetime.
+   */
+  mhd_iovec *iov;
+
+  /**
+   * The number of elements in @a iov.
+   * This value is not changed during lifetime.
+   */
+  size_t cnt;
+
+  /**
+   * The number of sent elements.
+   * At the same time, it is the index of the next (or current) element
+   * to send.
+   */
+  size_t sent;
+};
+
+#endif /* ! MHD_IOVEC_H */

+ 45 - 0
src/mhd2/mhd_itc.c

@@ -0,0 +1,45 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_itc.c
+ * @brief  Implementation of inter-thread communication functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_itc.h"
+#if defined(MHD_ITC_PIPE_)
+#  ifdef MHD_HAVE_MHD_ITC_NONBLOCKING
+#    include "mhd_sockets_funcs.h"
+#    ifndef MHD_POSIX_SOCKETS
+#error Pipe-based ITC can be used only with POSIX sockets
+#    endif
+
+MHD_INTERNAL bool
+mhd_itc_nonblocking (struct mhd_itc *pitc)
+{
+  return mhd_socket_nonblocking ((MHD_Socket) pitc->sk[0]) &&
+         mhd_socket_nonblocking ((MHD_Socket) pitc->sk[1]);
+}
+
+
+#  endif /* ! MHD_HAVE_MHD_ITC_NONBLOCKING */
+#endif /* MHD_ITC_PIPE_ */

+ 343 - 0
src/mhd2/mhd_itc.h

@@ -0,0 +1,343 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2016-2024 Evgeny Grin (Karlson2k), Christian Grothoff
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_itc.h
+ * @brief  Header for platform-independent inter-thread communication
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ *
+ * Provides basic abstraction for inter-thread communication.
+ * Any functions can be implemented as macro on some platforms
+ * unless explicitly marked otherwise.
+ * Any "function" argument can be unused in macro, so avoid
+ * variable modification in function parameters.
+ */
+#ifndef MHD_ITC_H
+#define MHD_ITC_H 1
+#include "mhd_itc_types.h"
+
+#include "mhd_panic.h"
+
+#if defined(MHD_ITC_EVENTFD_)
+
+/* **************** Optimised ITC implementation by eventfd ********** */
+#  include <sys/eventfd.h>
+#  include <stdint.h>      /* for uint_fast64_t */
+#  ifdef HAVE_UNISTD_H
+#    include <unistd.h>      /* for read(), write() */
+#  else
+#    include <stdlib.h>
+#  endif /* HAVE_UNISTD_H */
+#  include <errno.h>
+
+/**
+ * Number of FDs used by every ITC.
+ */
+#  define mhd_ITC_NUM_FDS (1)
+
+/**
+ * Set @a itc to the invalid value.
+ * @param pitc the pointer to the itc to set
+ */
+#define mhd_itc_set_invalid(pitc) ((pitc)->fd = -1)
+
+/**
+ * Check whether ITC has valid value.
+ *
+ * Macro check whether @a itc value is valid (allowed),
+ * macro does not check whether @a itc was really initialised.
+ * @param itc the itc to check
+ * @return boolean true if @a itc has valid value,
+ *         boolean false otherwise.
+ */
+#define mhd_ITC_IS_VALID(itc)  (0 <= ((itc).fd))
+
+/**
+ * Initialise ITC by generating eventFD
+ * @param pitc the pointer to the ITC to initialise
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  define mhd_itc_init(pitc) \
+        (-1 != ((pitc)->fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK)))
+
+/**
+ * Helper for mhd_itc_activate()
+ */
+#  ifdef HAVE_COMPOUND_LITERALS_LVALUES
+#    define mhd_ITC_WR_DATA ((uint_fast64_t){1})
+#  else
+/**
+ * Internal static const helper for mhd_itc_activate()
+ */
+static const uint_fast64_t mhd_ITC_WR_DATA = 1;
+#  endif
+
+/**
+ * Activate signal on the @a itc
+ * @param itc the itc to use
+ * @return non-zero if succeeded, zero otherwise
+ */
+#define mhd_itc_activate(itc)                                \
+        ((write ((itc).fd, (const void*) &mhd_ITC_WR_DATA, 8) > 0) \
+         || (EAGAIN == errno))
+
+/**
+ * Return read FD of @a itc which can be used for poll(), select() etc.
+ * @param itc the itc to get FD
+ * @return FD of read side
+ */
+#define mhd_itc_r_fd(itc) ((itc).fd)
+
+/**
+ * Clear signalled state on @a itc
+ * @param itc the itc to clear
+ */
+#define mhd_itc_clear(itc)                      \
+        do { uint_fast64_t __b;                       \
+             (void) read ((itc).fd, (void*) &__b, 8);  \
+        } while (0)
+
+/**
+ * Destroy previously initialised ITC.  Note that close()
+ * on some platforms returns odd errors, so we ONLY fail
+ * if the errno is EBADF.
+ * @param itc the itc to destroy
+ * @return non-zero if succeeded, zero otherwise
+ */
+#define mhd_itc_destroy(itc) \
+        ((0 == close ((itc).fd)) || (EBADF != errno))
+
+#elif defined(MHD_ITC_PIPE_)
+
+/* **************** Standard UNIX ITC implementation by pipe ********** */
+
+#  if defined(HAVE_PIPE2_FUNC)
+#    include <fcntl.h>     /* for O_CLOEXEC, O_NONBLOCK */
+#  endif /* HAVE_PIPE2_FUNC && HAVE_FCNTL_H */
+#  ifdef HAVE_UNISTD_H
+#    include <unistd.h>      /* for read(), write() */
+#  else
+#    include <stdlib.h>
+#  endif /* HAVE_UNISTD_H */
+#  include <errno.h>
+#  if defined(HAVE_PIPE2_FUNC) && defined(O_CLOEXEC) && defined(O_NONBLOCK)
+#    define MHD_USE_PIPE2 1
+#  else
+#    include "sys_bool_type.h"
+#  endif
+
+
+/**
+ * Number of FDs used by every ITC.
+ */
+#  define mhd_ITC_NUM_FDS (2)
+
+/**
+ * Set @a itc to the invalid value.
+ * @param pitc the pointer to the itc to set
+ */
+#  define mhd_itc_set_invalid(pitc) ((pitc)->fd[0] = (pitc)->fd[1] = -1)
+
+/**
+ * Check whether ITC has valid value.
+ *
+ * Macro check whether @a itc value is valid (allowed),
+ * macro does not check whether @a itc was really initialised.
+ * @param itc the itc to check
+ * @return boolean true if @a itc has valid value,
+ *         boolean false otherwise.
+ */
+#  define mhd_ITC_IS_VALID(itc)  (0 <= (itc).fd[0])
+
+/**
+ * Initialise ITC by generating pipe
+ * @param pitc the pointer to the ITC to initialise
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  if defined(MHD_USE_PIPE2)
+#    define mhd_itc_init(pitc) (! pipe2 ((pitc)->fd, O_CLOEXEC | O_NONBLOCK))
+#  else  /* ! MHD_USE_PIPE2 */
+#    define mhd_itc_init(pitc)         \
+        ( (! pipe ((pitc)->fd)) ?           \
+          (mhd_itc_nonblocking ((pitc)) ?   \
+           (! 0) :                          \
+           (mhd_itc_destroy (*(pitc)), 0) ) \
+    : (0) )
+
+#    define MHD_HAVE_MHD_ITC_NONBLOCKING 1
+/**
+ * Change itc FD options to be non-blocking.
+ *
+ * @param pitc the pointer to ITC to manipulate
+ * @return true if succeeded, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_itc_nonblocking (struct mhd_itc *pitc);
+
+#  endif /* ! MHD_USE_PIPE2 */
+
+/**
+ * Activate signal on @a itc
+ * @param itc the itc to use
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  define mhd_itc_activate(itc) \
+        ((write ((itc).fd[1], (const void*) "", 1) > 0) || (EAGAIN == errno))
+
+/**
+ * Return read FD of @a itc which can be used for poll(), select() etc.
+ * @param itc the itc to get FD
+ * @return FD of read side
+ */
+#  define mhd_itc_r_fd(itc) ((itc).fd[0])
+
+/**
+ * Clear signaled state on @a itc
+ * @param itc the itc to clear
+ */
+#  define mhd_itc_clear(itc) do                                \
+        { long __b;                                                  \
+          while (0 < read ((itc).fd[0], (void*) &__b, sizeof(__b)))  \
+          {(void) 0;} } while (0)
+
+/**
+ * Destroy previously initialised ITC
+ * @param itc the itc to destroy
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  define mhd_itc_destroy(itc)     \
+        (0 == (close ((itc).fd[0]) + close ((itc).fd[1])))
+
+#elif defined(MHD_ITC_SOCKETPAIR_)
+
+/* **************** ITC implementation by socket pair ********** */
+
+#  include "mhd_sockets_macros.h"
+#  if ! defined(mhd_socket_pair_nblk)
+#    include "mhd_sockets_funcs.h"
+#  endif
+
+/**
+ * Number of FDs used by every ITC.
+ */
+#  define mhd_ITC_NUM_FDS (2)
+
+/**
+ * Set @a itc to the invalid value.
+ * @param pitc the pointer to the itc to set
+ */
+#  define mhd_itc_set_invalid(pitc) \
+        ((pitc)->sk[0] = (pitc)->sk[1] = MHD_INVALID_SOCKET)
+
+/**
+ * Check whether ITC has valid value.
+ *
+ * Macro check whether @a itc value is valid (allowed),
+ * macro does not check whether @a itc was really initialised.
+ * @param itc the itc to check
+ * @return boolean true if @a itc has valid value,
+ *         boolean false otherwise.
+ */
+#  define mhd_ITC_IS_VALID(itc)  (MHD_INVALID_SOCKET != (itc).sk[0])
+
+/**
+ * Initialise ITC by generating socketpair
+ * @param itc the itc to initialise
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  ifdef mhd_socket_pair_nblk
+#    define mhd_itc_init(pitc) mhd_socket_pair_nblk ((pitc)->sk)
+#  else  /* ! mhd_socket_pair_nblk */
+#    define mhd_itc_init(pitc)                \
+        ( (! mhd_socket_pair ((pitc)->sk)) ?        \
+          (0) : ( (! mhd_itc_nonblocking ((pitc))) ? \
+                  (mhd_itc_destroy (*(pitc)), 0) : (! 0) ) )
+
+/**
+ * Change itc FD options to be non-blocking.
+ *
+ * @param pitc the pointer to ITC to manipulate
+ * @return true if succeeded, false otherwise
+ */
+#    define mhd_itc_nonblocking(pitc)         \
+        (mhd_socket_nonblocking ((pitc)->sk[0]) &&  \
+         mhd_socket_nonblocking ((pitc)->sk[1]))
+
+#  endif /* ! mhd_socket_pair_nblk */
+
+/**
+ * Activate signal on @a itc
+ * @param itc the itc to use
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  define mhd_itc_activate(itc) \
+        ((0 < mhd_send ((itc).sk[1], "", 1)) || mhd_SCKT_LERR_IS_EAGAIN ())
+
+/**
+ * Return read FD of @a itc which can be used for poll(), select() etc.
+ * @param itc the itc to get FD
+ * @return FD of read side
+ */
+#  define mhd_itc_r_fd(itc) ((itc).sk[0])
+
+/**
+ * Clear signaled state on @a itc
+ * @param itc the itc to clear
+ */
+#  define mhd_itc_clear(itc) do                                   \
+        { long __b;                                                     \
+          while (0 < recv ((itc).sk[0], (void*) &__b, sizeof(__b), 0))  \
+          {(void) 0;} } while (0)
+
+/**
+ * Destroy previously initialised ITC
+ * @param itc the itc to destroy
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  define mhd_itc_destroy(itc)       \
+        (mhd_socket_close ((itc).sk[1]) ?  \
+         mhd_socket_close ((itc).sk[0]) :  \
+         ((void) mhd_socket_close ((itc).sk[0]), ! ! 0) )
+
+#endif /* MHD_ITC_SOCKETPAIR_ */
+
+/**
+ * Destroy previously initialised ITC and abort execution
+ * if error is detected.
+ * @param itc the itc to destroy
+ */
+#define mhd_itc_destroy_chk(itc) do {          \
+          if (! mhd_itc_destroy (itc))               \
+          MHD_PANIC ("Failed to destroy ITC.\n");  \
+} while (0)
+
+/**
+ * Check whether ITC has invalid value.
+ *
+ * Macro check whether @a itc value is invalid,
+ * macro does not check whether @a itc was destroyed.
+ * @param itc the itc to check
+ * @return boolean true if @a itc has invalid value,
+ *         boolean false otherwise.
+ */
+#define mhd_ITC_IS_INVALID(itc)  (! mhd_ITC_IS_VALID (itc))
+
+#endif /* ! MHD_ITC_H */

+ 26 - 26
src/microhttpd/mhd_itc_types.h → src/mhd2/mhd_itc_types.h

@@ -1,13 +1,13 @@
 /*
-  This file is part of libmicrohttpd
-  Copyright (C) 2016-2020 Karlson2k (Evgeny Grin), Christian Grothoff
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2016-2024 Evgeny Grin (Karlson2k), Christian Grothoff
 
-  This library is free software; you can redistribute it and/or
+  GNU libmicrohttpd 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,
+  GNU libmicrohttpd 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.
@@ -19,7 +19,7 @@
 */
 
 /**
- * @file microhttpd/mhd_itc_types.h
+ * @file src/mhd2/mhd_itc_types.h
  * @brief  Types for platform-independent inter-thread communication
  * @author Karlson2k (Evgeny Grin)
  * @author Christian Grothoff
@@ -29,66 +29,66 @@
  */
 #ifndef MHD_ITC_TYPES_H
 #define MHD_ITC_TYPES_H 1
-#include "mhd_options.h"
+#include "mhd_sys_options.h"
 
 /* Force socketpair on native W32 */
-#if defined(_WIN32) && ! defined(__CYGWIN__) && ! defined(_MHD_ITC_SOCKETPAIR)
-#error _MHD_ITC_SOCKETPAIR is not defined on naitive W32 platform
-#endif /* _WIN32 && !__CYGWIN__ && !_MHD_ITC_SOCKETPAIR */
+#if defined(_WIN32) && ! defined(__CYGWIN__) && ! defined(MHD_ITC_SOCKETPAIR_)
+#error MHD_ITC_SOCKETPAIR_ is not defined on native W32 platform
+#endif /* _WIN32 && !__CYGWIN__ && !MHD_ITC_SOCKETPAIR_ */
 
-#if defined(_MHD_ITC_EVENTFD)
-/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */
+#if defined(MHD_ITC_EVENTFD_)
+/* **************** Optimised ITC implementation by eventfd ********** */
 
 /**
  * Data type for a MHD ITC.
  */
-struct MHD_itc_
+struct mhd_itc
 {
   int fd;
 };
 
 /**
- * Static initialiser for struct MHD_itc_
+ * Static initialiser for struct mhd_itc
  */
-#define MHD_ITC_STATIC_INIT_INVALID { -1 }
+#  define mhd_ITC_STATIC_INIT_INVALID { -1 }
 
 
-#elif defined(_MHD_ITC_PIPE)
+#elif defined(MHD_ITC_PIPE_)
 /* **************** Standard UNIX ITC implementation by pipe ********** */
 
 /**
  * Data type for a MHD ITC.
  */
-struct MHD_itc_
+struct mhd_itc
 {
   int fd[2];
 };
 
 /**
- * Static initialiser for struct MHD_itc_
+ * Static initialiser for struct mhd_itc
  */
-#define MHD_ITC_STATIC_INIT_INVALID { { -1, -1 } }
+#  define mhd_ITC_STATIC_INIT_INVALID { { -1, -1 } }
 
 
-#elif defined(_MHD_ITC_SOCKETPAIR)
+#elif defined(MHD_ITC_SOCKETPAIR_)
 /* **************** ITC implementation by socket pair ********** */
 
-#include "mhd_sockets.h"
+#  include "mhd_socket_type.h"
 
 /**
  * Data type for a MHD ITC.
  */
-struct MHD_itc_
+struct mhd_itc
 {
-  MHD_socket sk[2];
+  MHD_Socket sk[2];
 };
 
 /**
- * Static initialiser for struct MHD_itc_
+ * Static initialiser for struct mhd_itc
  */
-#define MHD_ITC_STATIC_INIT_INVALID \
-  { { MHD_INVALID_SOCKET, MHD_INVALID_SOCKET } }
+#  define mhd_ITC_STATIC_INIT_INVALID \
+        { { MHD_INVALID_SOCKET, MHD_INVALID_SOCKET } }
 
-#endif /* _MHD_ITC_SOCKETPAIR */
+#endif /* MHD_ITC_SOCKETPAIR_ */
 
 #endif /* ! MHD_ITC_TYPES_H */

+ 85 - 0
src/mhd2/mhd_lib_init.c

@@ -0,0 +1,85 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_lib_init_impl.c
+ * @brief  Library global initialisers and de-initialisers
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include "mhd_sys_options.h"
+#include "mhd_lib_init.h"
+#include "mhd_panic.h"
+#include "mhd_mono_clock.h"
+#include "mhd_socket_type.h"
+#ifdef MHD_WINSOCK_SOCKETS
+#  include <winsock2.h>
+#endif
+
+void
+mhd_lib_global_init (void)
+{
+  mhd_panic_init_default ();
+
+#if defined(MHD_WINSOCK_SOCKETS)
+  if (1)
+  {
+    WSADATA wsd;
+    if ((0 != WSAStartup (MAKEWORD (2, 2), &wsd)) || (MAKEWORD (2, 2) != wsd.
+                                                      wVersion))
+      MHD_PANIC ("Failed to initialise WinSock.");
+  }
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+void
+mhd_lib_global_deinit (void)
+{
+#if defined(MHD_WINSOCK_SOCKETS)
+  (void) WSACleanup ();
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+#ifndef _AUTOINIT_FUNCS_ARE_SUPPORTED
+static volatile int mhd_lib_global_inited = 0;
+static volatile int mhd_lib_global_not_inited = ! 0;
+
+MHD_EXTERN_ void
+MHD_lib_global_check_init (void)
+{
+  if ((! mhd_lib_global_inited) || (mhd_lib_global_not_inited))
+    mhd_lib_global_init ();
+  mhd_lib_global_inited = ! 0;
+  mhd_lib_global_not_inited = 0;
+}
+
+
+MHD_EXTERN_ void
+MHD_lib_global_check_deinit (void)
+{
+  if ((mhd_lib_global_inited) && (! mhd_lib_global_not_inited))
+    mhd_lib_global_deinit ();
+  mhd_lib_global_inited = 0;
+  mhd_lib_global_not_inited = ! 0;
+}
+
+
+#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */

+ 66 - 0
src/mhd2/mhd_lib_init.h

@@ -0,0 +1,66 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_lib_init.h
+ * @brief  Declarations for the library global initialiser
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_LIB_INIT_H
+#define MHD_LIB_INIT_H 1
+#include "mhd_sys_options.h"
+#include "autoinit_funcs.h"
+
+/**
+ * Initialise library global resources
+ */
+void
+mhd_lib_global_init (void);
+
+/**
+ * Deinitialise and free library global resources
+ */
+void
+mhd_lib_global_deinit (void);
+
+#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED
+#  define MHD_GLOBAL_INIT_CHECK() ((void) 0)
+#else /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */
+/* The functions are exported, but not declared in public header */
+
+/**
+ * Check whether the library was initialised and initialise if needed
+ */
+MHD_EXTERN_ void
+MHD_lib_global_check_init (void);
+
+/**
+ * Check whether the library has been de-initialised and de-initialise if needed
+ */
+MHD_EXTERN_ void
+MHD_lib_global_check_deinit (void)
+
+#  define MHD_GLOBAL_INIT_CHECK() MHD_lib_global_check_init ()
+
+#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */
+
+
+#endif /* ! MHD_LIB_INIT_H */

+ 76 - 0
src/mhd2/mhd_lib_init_impl.h

@@ -0,0 +1,76 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_lib_init_impl.h
+ * @brief  Library global initialisers and de-initialisers
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This file should be a .c file, but used as .h file to workaround
+ * a GCC/binutils bug.
+ */
+
+#ifdef MHD_LIB_INIT_IMPL_H
+#error This file must not be included more the one time only
+#endif
+#define MHD_LIB_INIT_IMPL_H 1
+
+/* Due to the bug in GCC/binutils, on some platforms (at least on W32)
+ * the automatic initialisation functions are not called when library is used
+ * as a static library and no function is used/referred from the same
+ * object/module/c-file.
+ */
+#ifndef MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C
+#error This file must in included only in 'daemon_create.c' file
+#else  /* MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C */
+
+#include "mhd_sys_options.h"
+#include "mhd_lib_init.h"
+
+/* Forward declarations */
+void
+mhd_lib_global_init_wrap (void);
+
+void
+mhd_lib_global_deinit_wrap (void);
+
+
+void
+mhd_lib_global_init_wrap (void)
+{
+  mhd_lib_global_init ();
+}
+
+
+void
+mhd_lib_global_deinit_wrap (void)
+{
+  mhd_lib_global_deinit ();
+}
+
+
+#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED
+
+_SET_INIT_AND_DEINIT_FUNCS (mhd_lib_global_init_wrap, \
+                            mhd_lib_global_deinit_wrap);
+
+#endif /* _AUTOINIT_FUNCS_ARE_SUPPORTED */
+
+#endif /* MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C */

+ 243 - 0
src/mhd2/mhd_locks.h

@@ -0,0 +1,243 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_locks.h
+ * @brief  Header for platform-independent locks abstraction
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ *
+ * Provides basic abstraction for locks/mutex.
+ * Any functions can be implemented as macro on some platforms
+ * unless explicitly marked otherwise.
+ * Any function argument can be skipped in macro, so avoid
+ * variable modification in function parameters.
+ *
+ * @warning Unlike pthread functions, most of functions return
+ *          nonzero on success.
+ */
+
+#ifndef MHD_LOCKS_H
+#define MHD_LOCKS_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef MHD_USE_THREADS
+
+#if defined(MHD_USE_W32_THREADS)
+#  define MHD_W32_MUTEX_ 1
+#  if _WIN32_WINNT >= 0x0602 /* Win8 or later */
+#    include <synchapi.h>
+#  else
+#    include <windows.h>
+#  endif
+#elif defined(HAVE_PTHREAD_H) && defined(MHD_USE_POSIX_THREADS)
+#  define MHD_PTHREAD_MUTEX_ 1
+#  include <pthread.h>
+#  ifdef HAVE_STDDEF_H
+#    include <stddef.h> /* for NULL */
+#  else
+#    include "sys_base_types.h"
+#  endif
+#else
+#error No base mutex API is available.
+#endif
+
+#include "mhd_panic.h"
+
+#if defined(MHD_PTHREAD_MUTEX_)
+typedef pthread_mutex_t mhd_mutex;
+#elif defined(MHD_W32_MUTEX_)
+typedef CRITICAL_SECTION mhd_mutex;
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Initialise a new mutex.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_mutex_init(pmutex) (! (pthread_mutex_init ((pmutex), NULL)))
+#elif defined(MHD_W32_MUTEX_)
+#  if _WIN32_WINNT < 0x0600
+/* Before Vista */
+/**
+ * Initialise a new mutex.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#    define mhd_mutex_init(pmutex) \
+        (InitializeCriticalSectionAndSpinCount ((pmutex), 0))
+#  else
+/* The function always succeed starting from Vista */
+/**
+ * Initialise a new mutex.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#    define mhd_mutex_init(pmutex) \
+        (InitializeCriticalSection (pmutex), ! 0)
+#  endif
+#endif
+
+#ifdef MHD_W32_MUTEX_
+#  if _WIN32_WINNT < 0x0600
+/* Before Vista */
+/**
+ * Initialise a new mutex for short locks.
+ *
+ * Initialised mutex is optimised for locks held only for very short period of
+ * time. It should be used when only a single or just a few variables are
+ * modified under the lock.
+ *
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#    define mhd_mutex_init_short(pmutex) \
+        (InitializeCriticalSectionAndSpinCount ((pmutex), 128))
+#  else
+/* The function always succeed starting from Vista */
+/**
+ * Initialise a new mutex for short locks.
+ *
+ * Initialised mutex is optimised for locks held only for very short period of
+ * time. It should be used when only a single or just a few variables are
+ * modified under the lock.
+ *
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#    define mhd_mutex_init_short(pmutex) \
+        ((void) InitializeCriticalSectionAndSpinCount ((pmutex), 128), ! 0)
+#  endif
+#endif
+
+#ifndef mhd_mutex_init_short
+#  define mhd_mutex_init_short(pmutex) mhd_mutex_init ((pmutex))
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+#  if defined(PTHREAD_MUTEX_INITIALIZER)
+/**
+ *  Define static mutex and statically initialise it.
+ */
+#    define MHD_MUTEX_STATIC_DEFN_INIT_(m) \
+        static mhd_mutex m = PTHREAD_MUTEX_INITIALIZER
+#  endif /* PTHREAD_MUTEX_INITIALIZER */
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Destroy previously initialised mutex.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_mutex_destroy(pmutex) (! (pthread_mutex_destroy ((pmutex))))
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Destroy previously initialised mutex.
+ * @param pmutex the pointer to the mutex
+ * @return Always nonzero
+ */
+#  define mhd_mutex_destroy(pmutex) (DeleteCriticalSection ((pmutex)), ! 0)
+#endif
+
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Acquire a lock on previously initialised mutex.
+ * If the mutex was already locked by other thread, function blocks until
+ * the mutex becomes available.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_mutex_lock(pmutex) (! (pthread_mutex_lock ((pmutex))))
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Acquire a lock on previously initialised mutex.
+ * If the mutex was already locked by other thread, function blocks until
+ * the mutex becomes available.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_mutex_lock(pmutex) (EnterCriticalSection ((pmutex)), ! 0)
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Unlock previously locked mutex.
+ * @param pmutex the pointer to the mutex
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_mutex_unlock(pmutex) (! (pthread_mutex_unlock ((pmutex))))
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Unlock previously initialised and locked mutex.
+ * @param pmutex pointer to mutex
+ * @return Always nonzero
+ */
+#  define mhd_mutex_unlock(pmutex) (LeaveCriticalSection ((pmutex)), ! 0)
+#endif
+
+/**
+ * Destroy previously initialised mutex and abort execution if error is
+ * detected.
+ * @param pmutex the pointer to the mutex
+ */
+#define mhd_mutex_destroy_chk(pmutex) do {      \
+          if (! mhd_mutex_destroy (pmutex))           \
+          MHD_PANIC ("Failed to destroy mutex.\n"); \
+} while (0)
+
+/**
+ * Acquire a lock on previously initialised mutex.
+ * If the mutex was already locked by other thread, function blocks until
+ * the mutex becomes available.
+ * If error is detected, execution is aborted.
+ * @param pmutex the pointer to the mutex
+ */
+#define mhd_mutex_lock_chk(pmutex) do {      \
+          if (! mhd_mutex_lock (pmutex))           \
+          MHD_PANIC ("Failed to lock mutex.\n"); \
+} while (0)
+
+/**
+ * Unlock previously locked mutex.
+ * If error is detected, execution is aborted.
+ * @param pmutex the pointer to the mutex
+ */
+#define mhd_mutex_unlock_chk(pmutex) do {      \
+          if (! mhd_mutex_unlock (pmutex))           \
+          MHD_PANIC ("Failed to unlock mutex.\n"); \
+} while (0)
+
+#else  /* ! MHD_USE_THREADS */
+
+#define mhd_mutex_init(ignore) (! 0)
+#define mhd_mutex_destroy(ignore) (! 0)
+#define mhd_mutex_destroy_chk(ignore) (void) 0
+#define mhd_mutex_lock(ignore) (! 0)
+#define mhd_mutex_lock_chk(ignore) (void) 0
+#define mhd_mutex_unlock(ignore) (! 0)
+#define mhd_mutex_unlock_chk(ignore) (void) 0
+
+#endif /* ! MHD_USE_THREADS */
+
+#endif /* ! MHD_LOCKS_H */

+ 41 - 30
src/microhttpd/memorypool.c → src/mhd2/mhd_mempool.c

@@ -19,43 +19,52 @@
 */
 
 /**
- * @file memorypool.c
+ * @file src/mhd2/mhd_mempool.h
  * @brief memory pool
  * @author Christian Grothoff
  * @author Karlson2k (Evgeny Grin)
+ * TODO:
+ * + Update code style
+ * + Detect mmap() in configure (it is purely optional!)
  */
-#include "memorypool.h"
+#include "mhd_mempool.h"
 #ifdef HAVE_STDLIB_H
-#include <stdlib.h>
+#  include <stdlib.h>
 #endif /* HAVE_STDLIB_H */
 #include <string.h>
-#include <stdint.h>
 #include "mhd_assert.h"
 #ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
+#  include <sys/mman.h>
 #endif
 #ifdef _WIN32
-#include <windows.h>
+#  include <windows.h>
 #endif
 #ifdef HAVE_SYSCONF
-#include <unistd.h>
-#if defined(_SC_PAGE_SIZE)
-#define MHD_SC_PAGESIZE _SC_PAGE_SIZE
-#elif defined(_SC_PAGESIZE)
-#define MHD_SC_PAGESIZE _SC_PAGESIZE
-#endif /* _SC_PAGESIZE */
+#  include <unistd.h>
+#  if defined(_SC_PAGE_SIZE)
+#    define MHD_SC_PAGESIZE _SC_PAGE_SIZE
+#  elif defined(_SC_PAGESIZE)
+#    define MHD_SC_PAGESIZE _SC_PAGESIZE
+#  endif /* _SC_PAGESIZE */
 #endif /* HAVE_SYSCONF */
-#include "mhd_limits.h" /* for SIZE_MAX, PAGESIZE / PAGE_SIZE */
 
 #if defined(MHD_USE_PAGESIZE_MACRO) || defined(MHD_USE_PAGE_SIZE_MACRO)
-#ifndef HAVE_SYSCONF /* Avoid duplicate include */
-#include <unistd.h>
-#endif /* HAVE_SYSCONF */
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif /* HAVE_SYS_PARAM_H */
+#  ifndef HAVE_SYSCONF /* Avoid duplicate include */
+#    include <unistd.h>
+#  endif /* HAVE_SYSCONF */
+#  ifdef HAVE_LIMITS_H
+#    include <limits.h>
+#  endif
+#  ifdef HAVE_SYS_PARAM_H
+#    include <sys/param.h>
+#  endif /* HAVE_SYS_PARAM_H */
 #endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */
 
+#ifndef SIZE_MAX
+/* This file does not use SIZE_MAX macro with preprocessor */
+#  define SIZE_MAX ((size_t) ~((size_t) 0))
+#endif
+
 /**
  * Fallback value of page size
  */
@@ -99,40 +108,42 @@
 #ifndef MHD_ASAN_POISON_ACTIVE
 #define _MHD_NOSANITIZE_PTRS /**/
 #define _MHD_RED_ZONE_SIZE (0)
-#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN(n)
-#define _MHD_POISON_MEMORY(pointer, size) (void)0
-#define _MHD_UNPOISON_MEMORY(pointer, size) (void)0
+#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN (n)
+#define _MHD_POISON_MEMORY(pointer, size) (void) 0
+#define _MHD_UNPOISON_MEMORY(pointer, size) (void) 0
 /**
  * Boolean 'true' if the first pointer is less or equal the second pointer
  */
 #define mp_ptr_le_(p1,p2) \
-  (((const uint8_t*)(p1)) <= ((const uint8_t*)(p2)))
+        (((const uint8_t*) (p1)) <= ((const uint8_t*) (p2)))
 /**
  * The difference in bytes between positions of the first and
  * the second pointers
  */
 #define mp_ptr_diff_(p1,p2) \
-  ((size_t)(((const uint8_t*)(p1)) - ((const uint8_t*)(p2))))
+        ((size_t) (((const uint8_t*) (p1)) - ((const uint8_t*) (p2))))
 #else  /* MHD_ASAN_POISON_ACTIVE */
 #define _MHD_RED_ZONE_SIZE (ALIGN_SIZE)
-#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE)
+#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) \
+        (ROUND_TO_ALIGN (n) + _MHD_RED_ZONE_SIZE)
 #define _MHD_POISON_MEMORY(pointer, size) \
-  ASAN_POISON_MEMORY_REGION ((pointer), (size))
+        ASAN_POISON_MEMORY_REGION ((pointer), (size))
 #define _MHD_UNPOISON_MEMORY(pointer, size) \
-  ASAN_UNPOISON_MEMORY_REGION ((pointer), (size))
+        ASAN_UNPOISON_MEMORY_REGION ((pointer), (size))
 #if defined(FUNC_PTRCOMPARE_CAST_WORKAROUND_WORKS)
 /**
  * Boolean 'true' if the first pointer is less or equal the second pointer
  */
 #define mp_ptr_le_(p1,p2) \
-  (((uintptr_t)((const void*)(p1))) <= ((uintptr_t)((const void*)(p2))))
+        (((uintptr_t) ((const void*) (p1))) <= \
+         ((uintptr_t) ((const void*) (p2))))
 /**
  * The difference in bytes between positions of the first and
  * the second pointers
  */
 #define mp_ptr_diff_(p1,p2) \
-  ((size_t)(((uintptr_t)((const uint8_t*)(p1))) - \
-            ((uintptr_t)((const uint8_t*)(p2)))))
+        ((size_t) (((uintptr_t) ((const uint8_t*) (p1))) - \
+                   ((uintptr_t) ((const uint8_t*) (p2)))))
 #elif defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \
   defined(FUNC_ATTR_PTRSUBTRACT_WORKS)
 #ifdef _DEBUG

+ 9 - 11
src/microhttpd/memorypool.h → src/mhd2/mhd_mempool.h

@@ -19,24 +19,22 @@
 */
 
 /**
- * @file memorypool.h
+ * @file src/mhd2/mhd_mempool.h
  * @brief memory pool; mostly used for efficient (de)allocation
  *        for each connection and bounding memory use for each
  *        request
  * @author Christian Grothoff
  * @author Karlson2k (Evgeny Grin)
+ *
+ * TODO: Update code style
  */
 
-#ifndef MEMORYPOOL_H
-#define MEMORYPOOL_H
+#ifndef MHD_MEMPOOL_H
+#define MHD_MEMPOOL_H 1
 
-#include "mhd_options.h"
-#ifdef HAVE_STDDEF_H
-#include <stddef.h>
-#endif /* HAVE_STDDEF_H */
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
 
 /**
  * Opaque handle for a memory pool.
@@ -198,4 +196,4 @@ MHD_pool_reset (struct MemoryPool *pool,
                 size_t copy_bytes,
                 size_t new_size);
 
-#endif
+#endif /* ! MHD_MEMPOOL_H */

+ 48 - 101
src/microhttpd/mhd_mono_clock.c → src/mhd2/mhd_mono_clock.c

@@ -18,60 +18,53 @@
 */
 
 /**
- * @file microhttpd/mhd_mono_clock.h
+ * @file src/mhd2/mhd_mono_clock.h
  * @brief  internal monotonic clock functions implementations
  * @author Karlson2k (Evgeny Grin)
+ *
+ * TODO: update code style
  */
 
 #include "mhd_mono_clock.h"
 
 #if defined(_WIN32) && ! defined(__CYGWIN__)
 /* Prefer native clock source over wrappers */
-#ifdef HAVE_CLOCK_GETTIME
-#undef HAVE_CLOCK_GETTIME
-#endif /* HAVE_CLOCK_GETTIME */
-#ifdef HAVE_GETTIMEOFDAY
-#undef HAVE_GETTIMEOFDAY
-#endif /* HAVE_GETTIMEOFDAY */
+#  ifdef HAVE_CLOCK_GETTIME
+#    undef HAVE_CLOCK_GETTIME
+#  endif /* HAVE_CLOCK_GETTIME */
+#  ifdef HAVE_GETTIMEOFDAY
+#    undef HAVE_GETTIMEOFDAY
+#  endif /* HAVE_GETTIMEOFDAY */
 #endif /* _WIN32 && ! __CYGWIN__ */
 
 #ifdef HAVE_TIME_H
-#include <time.h>
+#  include <time.h>
 #endif /* HAVE_TIME_H */
 #ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
+#  include <sys/time.h>
 #endif /* HAVE_SYS_TIME_H */
 
 #ifdef HAVE_CLOCK_GET_TIME
-#include <mach/mach.h>
+#  include <mach/mach.h>
 /* for host_get_clock_service(), mach_host_self(), mach_task_self() */
-#include <mach/clock.h>
+#  include <mach/clock.h>
 /* for clock_get_time() */
 
-#define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2)
+#  define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2)
 
 static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV;
 #endif /* HAVE_CLOCK_GET_TIME */
 
 #ifdef _WIN32
-#ifndef WIN32_LEAN_AND_MEAN
-/* Do not include unneeded parts of W32 headers. */
-#define WIN32_LEAN_AND_MEAN 1
-#endif /* !WIN32_LEAN_AND_MEAN */
-#include <windows.h>
-#include <stdint.h>
+#  include <windows.h>
 #endif /* _WIN32 */
 
-#ifndef NULL
-#define NULL ((void*)0)
-#endif /* ! NULL */
-
 #ifdef HAVE_CLOCK_GETTIME
-#ifdef CLOCK_REALTIME
-#define _MHD_UNWANTED_CLOCK CLOCK_REALTIME
-#else  /* !CLOCK_REALTIME */
-#define _MHD_UNWANTED_CLOCK ((clockid_t) -2)
-#endif /* !CLOCK_REALTIME */
+#  ifdef CLOCK_REALTIME
+#    define _MHD_UNWANTED_CLOCK CLOCK_REALTIME
+#  else  /* !CLOCK_REALTIME */
+#    define _MHD_UNWANTED_CLOCK ((clockid_t) -2)
+#  endif /* !CLOCK_REALTIME */
 
 static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK;
 #endif /* HAVE_CLOCK_GETTIME */
@@ -81,21 +74,24 @@ static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK;
   defined(HAVE_GETHRTIME)
 static time_t mono_clock_start;
 #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */
+
 #if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY)
 /* The start value shared for timespec_get() and gettimeofday () */
 static time_t gettime_start;
 #endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */
 static time_t sys_clock_start;
+
 #ifdef HAVE_GETHRTIME
 static hrtime_t hrtime_start;
 #endif /* HAVE_GETHRTIME */
+
 #ifdef _WIN32
-#if _WIN32_WINNT >= 0x0600
+#  if _WIN32_WINNT >= 0x0600
 static uint64_t tick_start;
-#else  /* _WIN32_WINNT < 0x0600 */
+#  else  /* _WIN32_WINNT < 0x0600 */
 static uint64_t perf_freq;
 static uint64_t perf_start;
-#endif /* _WIN32_WINNT < 0x0600 */
+#  endif /* _WIN32_WINNT < 0x0600 */
 #endif /* _WIN32 */
 
 
@@ -137,10 +133,10 @@ enum _MHD_mono_clock_source
 
 
 /**
- * Initialise monotonic seconds and milliseconds counters.
+ * Initialise milliseconds counters.
  */
 void
-MHD_monotonic_sec_counter_init (void)
+MHD_monotonic_msec_counter_init (void)
 {
 #ifdef HAVE_CLOCK_GET_TIME
   mach_timespec_t cur_time;
@@ -360,11 +356,10 @@ MHD_monotonic_sec_counter_init (void)
 
 
 /**
- * Deinitialise monotonic seconds  and milliseconds counters by freeing
- * any allocated resources
+ * Deinitialise milliseconds counters by freeing any allocated resources
  */
 void
-MHD_monotonic_sec_counter_finish (void)
+MHD_monotonic_msec_counter_finish (void)
 {
 #ifdef HAVE_CLOCK_GET_TIME
   if (_MHD_INVALID_CLOCK_SERV != mono_clock_service)
@@ -377,58 +372,6 @@ MHD_monotonic_sec_counter_finish (void)
 }
 
 
-/**
- * Monotonic seconds counter.
- * Tries to be not affected by manually setting the system real time
- * clock or adjustments by NTP synchronization.
- *
- * @return number of seconds from some fixed moment
- */
-time_t
-MHD_monotonic_sec_counter (void)
-{
-#ifdef HAVE_CLOCK_GETTIME
-  struct timespec ts;
-
-  if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) &&
-       (0 == clock_gettime (mono_clock_id,
-                            &ts)) )
-    return ts.tv_sec - mono_clock_start;
-#endif /* HAVE_CLOCK_GETTIME */
-#ifdef HAVE_CLOCK_GET_TIME
-  if (_MHD_INVALID_CLOCK_SERV != mono_clock_service)
-  {
-    mach_timespec_t cur_time;
-
-    if (KERN_SUCCESS == clock_get_time (mono_clock_service,
-                                        &cur_time))
-      return cur_time.tv_sec - mono_clock_start;
-  }
-#endif /* HAVE_CLOCK_GET_TIME */
-#if defined(_WIN32)
-#if _WIN32_WINNT >= 0x0600
-  if (1)
-    return (time_t) (((uint64_t) (GetTickCount64 () - tick_start)) / 1000);
-#else  /* _WIN32_WINNT < 0x0600 */
-  if (0 != perf_freq)
-  {
-    LARGE_INTEGER perf_counter;
-
-    QueryPerformanceCounter (&perf_counter);   /* never fail on XP and later */
-    return (time_t) (((uint64_t) perf_counter.QuadPart - perf_start)
-                     / perf_freq);
-  }
-#endif /* _WIN32_WINNT < 0x0600 */
-#endif /* _WIN32 */
-#ifdef HAVE_GETHRTIME
-  if (1)
-    return (time_t) (((uint64_t) (gethrtime () - hrtime_start)) / 1000000000);
-#endif /* HAVE_GETHRTIME */
-
-  return time (NULL) - sys_clock_start;
-}
-
-
 /**
  * Monotonic milliseconds counter, useful for timeout calculation.
  * Tries to be not affected by manually setting the system real time
@@ -436,7 +379,7 @@ MHD_monotonic_sec_counter (void)
  *
  * @return number of microseconds from some fixed moment
  */
-uint64_t
+uint_fast64_t
 MHD_monotonic_msec_counter (void)
 {
 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET)
@@ -447,8 +390,9 @@ MHD_monotonic_msec_counter (void)
   if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) &&
        (0 == clock_gettime (mono_clock_id,
                             &ts)) )
-    return (uint64_t) (((uint64_t) (ts.tv_sec - mono_clock_start)) * 1000
-                       + (uint64_t) (ts.tv_nsec / 1000000));
+    return (uint_fast64_t) (((uint_fast64_t) (ts.tv_sec - mono_clock_start))
+                            * 1000
+                            + (uint_fast64_t) (ts.tv_nsec / 1000000));
 #endif /* HAVE_CLOCK_GETTIME */
 #ifdef HAVE_CLOCK_GET_TIME
   if (_MHD_INVALID_CLOCK_SERV != mono_clock_service)
@@ -457,22 +401,24 @@ MHD_monotonic_msec_counter (void)
 
     if (KERN_SUCCESS == clock_get_time (mono_clock_service,
                                         &cur_time))
-      return (uint64_t) (((uint64_t) (cur_time.tv_sec - mono_clock_start))
-                         * 1000 + (uint64_t) (cur_time.tv_nsec / 1000000));
+      return (uint_fast64_t) (((uint_fast64_t) (cur_time.tv_sec
+                                                - mono_clock_start))
+                              * 1000 + (uint_fast64_t) (cur_time.tv_nsec
+                                                        / 1000000));
   }
 #endif /* HAVE_CLOCK_GET_TIME */
 #if defined(_WIN32)
 #if _WIN32_WINNT >= 0x0600
   if (1)
-    return (uint64_t) (GetTickCount64 () - tick_start);
+    return (uint_fast64_t) (GetTickCount64 () - tick_start);
 #else  /* _WIN32_WINNT < 0x0600 */
   if (0 != perf_freq)
   {
     LARGE_INTEGER perf_counter;
-    uint64_t num_ticks;
+    uint_fast64_t num_ticks;
 
     QueryPerformanceCounter (&perf_counter);   /* never fail on XP and later */
-    num_ticks = (uint64_t) (perf_counter.QuadPart - perf_start);
+    num_ticks = (uint_fast64_t) (perf_counter.QuadPart - perf_start);
     return ((num_ticks / perf_freq) * 1000)
            + ((num_ticks % perf_freq) / (perf_freq / 1000));
   }
@@ -480,24 +426,25 @@ MHD_monotonic_msec_counter (void)
 #endif /* _WIN32 */
 #ifdef HAVE_GETHRTIME
   if (1)
-    return ((uint64_t) (gethrtime () - hrtime_start)) / 1000000;
+    return ((uint_fast64_t) (gethrtime () - hrtime_start)) / 1000000;
 #endif /* HAVE_GETHRTIME */
 
   /* Fallbacks, affected by system time change */
 #ifdef HAVE_TIMESPEC_GET
   if (TIME_UTC == timespec_get (&ts, TIME_UTC))
-    return (uint64_t) (((uint64_t) (ts.tv_sec - gettime_start)) * 1000
-                       + (uint64_t) (ts.tv_nsec / 1000000));
+    return (uint_fast64_t) (((uint_fast64_t) (ts.tv_sec - gettime_start)) * 1000
+                            + (uint_fast64_t) (ts.tv_nsec / 1000000));
 #elif defined(HAVE_GETTIMEOFDAY)
   if (1)
   {
     struct timeval tv;
     if (0 == gettimeofday (&tv, NULL))
-      return (uint64_t) (((uint64_t) (tv.tv_sec - gettime_start)) * 1000
-                         + (uint64_t) (tv.tv_usec / 1000));
+      return (uint_fast64_t) (((uint_fast64_t) (tv.tv_sec - gettime_start))
+                              * 1000
+                              + (uint_fast64_t) (tv.tv_usec / 1000));
   }
 #endif /* HAVE_GETTIMEOFDAY */
 
   /* The last resort fallback with very low resolution */
-  return (uint64_t) (time (NULL) - sys_clock_start) * 1000;
+  return (uint_fast64_t) (time (NULL) - sys_clock_start) * 1000;
 }

+ 9 - 26
src/microhttpd/mhd_mono_clock.h → src/mhd2/mhd_mono_clock.h

@@ -1,6 +1,6 @@
 /*
   This file is part of libmicrohttpd
-  Copyright (C) 2015-2021 Karlson2k (Evgeny Grin)
+  Copyright (C) 2015-2024 Karlson2k (Evgeny Grin)
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -18,46 +18,29 @@
 */
 
 /**
- * @file microhttpd/mhd_mono_clock.h
+ * @file src/mhd2/mhd_mono_clock.h
  * @brief  internal monotonic clock functions declarations
  * @author Karlson2k (Evgeny Grin)
  */
 
 #ifndef MHD_MONO_CLOCK_H
 #define MHD_MONO_CLOCK_H 1
-#include "mhd_options.h"
+#include "mhd_sys_options.h"
 
-#if defined(HAVE_TIME_H)
-#include <time.h>
-#elif defined(HAVE_SYS_TYPES_H)
-#include <sys/types.h>
-#endif
-#include <stdint.h>
+#include "sys_base_types.h"
 
 /**
- * Initialise monotonic seconds and milliseconds counters.
+ * Initialise milliseconds counters.
  */
 void
-MHD_monotonic_sec_counter_init (void);
+MHD_monotonic_msec_counter_init (void);
 
 
 /**
- * Deinitialise monotonic seconds  and milliseconds counters by freeing
- * any allocated resources
+ * Deinitialise milliseconds counters by freeing any allocated resources
  */
 void
-MHD_monotonic_sec_counter_finish (void);
-
-
-/**
- * Monotonic seconds counter.
- * Tries to be not affected by manually setting the system real time
- * clock or adjustments by NTP synchronization.
- *
- * @return number of seconds from some fixed moment
- */
-time_t
-MHD_monotonic_sec_counter (void);
+MHD_monotonic_msec_counter_finish (void);
 
 
 /**
@@ -67,7 +50,7 @@ MHD_monotonic_sec_counter (void);
  *
  * @return number of microseconds from some fixed moment
  */
-uint64_t
+uint_fast64_t
 MHD_monotonic_msec_counter (void);
 
 #endif /* MHD_MONO_CLOCK_H */

+ 110 - 0
src/mhd2/mhd_panic.c

@@ -0,0 +1,110 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_panic.h
+ * @brief  mhd_panic() and MHD_lib_set_panic_func() implementations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#elif defined(HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+#include "sys_null_macro.h"
+#include "mhd_public_api.h"
+#include "mhd_panic.h"
+
+/**
+ * The user handler for panic
+ */
+static MHD_PanicCallback user_panic_handler = (MHD_PanicCallback) NULL;
+
+/**
+ * The closure argument for the #user_panic_handler
+ */
+static void *user_panic_handler_cls = NULL;
+
+MHD_INTERNAL void
+mhd_panic_init_default (void)
+{
+  user_panic_handler = (MHD_PanicCallback) NULL;
+}
+
+
+MHD_EXTERN_ void
+MHD_lib_set_panic_func (MHD_PanicCallback cb,
+                        void *cls)
+{
+  user_panic_handler = cb;
+  user_panic_handler_cls = cls;
+}
+
+
+MHD_NORETURN_ MHD_INTERNAL void
+mhd_panic (const char *file,
+           const char *func,
+           unsigned int line,
+           const char *message)
+{
+  static const char empty_str[1] = "";
+  if (NULL == file)
+    file = empty_str;
+  if (NULL == func)
+    func = empty_str;
+  if (NULL == message)
+    message = empty_str;
+  if (NULL != user_panic_handler)
+    user_panic_handler (user_panic_handler_cls,
+                        file, func, line, message);
+#ifdef HAVE_LOG_FUNCTIONALITY
+  if (0 == file[0])
+    fprintf (stderr,
+             "Unrecoverable error detected in GNU libmicrohttpd%s%s\n",
+             (0 == message[0]) ? "" : ": ",
+             message);
+  else
+  {
+    if (0 != func[0])
+    {
+      fprintf (stderr,
+               "Unrecoverable error detected in GNU libmicrohttpd, " \
+               "file '%s' at %s:%u%s%s\n",
+               file, func, line,
+               (0 == message[0]) ? "" : ": ",
+               message);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Unrecoverable error detected in GNU libmicrohttpd, " \
+               "file '%s' at line %u%s%s\n",
+               file, line,
+               (0 == message[0]) ? "" : ": ",
+               message);
+    }
+  }
+#endif /* HAVE_LOG_FUNCTIONALITY */
+  abort ();
+}

+ 105 - 0
src/mhd2/mhd_panic.h

@@ -0,0 +1,105 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_panic.h
+ * @brief  MHD_PANIC() macro and declarations of the related functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_PANIC_H
+#define MHD_PANIC_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef BUILDING_MHD_LIB
+/* Simplified implementation, utilised by unit tests that use some parts of
+   the library code directly. */
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#elif defined(HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#define MHD_PANIC(msg) \
+        do { fprintf (stderr,"Unrecoverable error: %s\n", msg); abort (); } \
+        while (0)
+
+#else  /* BUILDING_MHD_LIB */
+/* Fully functional implementation for the library */
+
+/**
+ * Internal panic handler
+ * @param file the name of the file where the panic was triggered
+ * @param func the name of the function where the panic was triggered
+ * @param line the number of the line where the panic was triggered
+ * @param message the message with the description of the panic
+ */
+MHD_NORETURN_ MHD_INTERNAL void
+mhd_panic (const char *file,
+           const char *func,
+           unsigned int line,
+           const char *message);
+
+
+#ifdef MHD_PANIC
+#error MHD_PANIC macro is already defined. Check other headers.
+#endif /* MHD_PANIC */
+
+#ifdef HAVE_LOG_FUNCTIONALITY
+#  ifdef MHD_HAVE_MHD_FUNC_
+/**
+ * Panic processing for unrecoverable errors.
+ *
+ * @param msg the error message string
+ */
+#    define MHD_PANIC(msg) \
+        mhd_panic (__FILE__, MHD_FUNC_, __LINE__, msg)
+#  else
+#    include "sys_null_macro.h"
+/**
+ * Panic processing for unrecoverable errors.
+ *
+ * @param msg the error message string
+ */
+#    define MHD_PANIC(msg) \
+        mhd_panic (__FILE__, NULL, __LINE__, msg)
+#  endif
+#else
+#  include "sys_null_macro.h"
+/**
+ * Panic processing for unrecoverable errors.
+ *
+ * @param msg the error message string
+ */
+#    define MHD_PANIC(msg) \
+        mhd_panic (NULL, NULL, __LINE__, NULL)
+#endif
+
+/**
+ * Initialise panic handler to default value
+ */
+MHD_INTERNAL void
+mhd_panic_init_default (void);
+
+#endif /* BUILDING_MHD_LIB */
+
+#endif /* ! MHD_PANIC_H */

+ 2 - 1
src/mhd2/mhd_public_api.h

@@ -33,7 +33,8 @@
 #include "mhd_sys_options.h"
 
 #include "sys_base_types.h"
-#include "sys_socket_types.h"
+#include "mhd_socket_type.h"
+#include "sys_sockets_types.h"
 
 #include "microhttpd2.h"
 

+ 106 - 0
src/mhd2/mhd_recv.c

@@ -0,0 +1,106 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_recv.c
+ * @brief  The implementation of the mhd_recv() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_recv.h"
+
+#include "mhd_connection.h"
+
+#include "mhd_socket_type.h"
+#include "sys_sockets_headers.h"
+#include "mhd_sockets_macros.h"
+
+#include <limits.h>
+
+#ifndef SSIZE_MAX
+#  define SSIZE_MAX ((ssize_t) ((~((size_t) 0)) >> 1))
+#endif
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_SIZE_ (3,2) \
+  MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_recv (struct MHD_Connection *restrict c,
+          size_t buf_size,
+          void *restrict buf,
+          size_t *restrict received)
+{
+  mhd_assert (MHD_INVALID_SOCKET != c->socket_fd);
+  mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+
+  // TODO: implement TLS support
+  if (1) /* Plain TCP connection */
+  {
+    ssize_t res;
+    int err;
+
+    if (MHD_SCKT_SEND_MAX_SIZE_ < buf_size)
+      buf_size = MHD_SCKT_SEND_MAX_SIZE_;
+
+    res = mhd_sys_recv (c->socket_fd, buf, buf_size);
+    if (0 <= res)
+    {
+      *received = (size_t) res;
+      if (buf_size > (size_t) res)
+        c->sk_ready =
+          (enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
+                                     & (~mhd_SOCKET_NET_STATE_RECV_READY));
+      return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */
+    }
+
+    err = mhd_SCKT_GET_LERR ();
+
+    if (mhd_SCKT_ERR_IS_EAGAIN (err))
+    {
+      c->sk_ready = /* Clear 'recv-ready' */
+                    (enum mhd_SocketNetState)
+                    (((unsigned int) c->sk_ready)
+                     & (~mhd_SOCKET_NET_STATE_RECV_READY));
+      return mhd_SOCKET_ERR_AGAIN;
+    }
+    else if (mhd_SCKT_ERR_IS_CONNRESET (err))
+      return mhd_SOCKET_ERR_CONNRESET;
+    else if (mhd_SCKT_ERR_IS_EINTR (err))
+      return mhd_SOCKET_ERR_INTR;
+    else if (mhd_SCKT_ERR_IS_CONN_BROKEN (err))
+      return mhd_SOCKET_ERR_CONN_BROKEN;
+    else if (mhd_SCKT_ERR_IS_NOTCONN (err))
+      return mhd_SOCKET_ERR_NOTCONN;
+    else if (mhd_SCKT_ERR_IS_LOW_MEM (err))
+      return mhd_SOCKET_ERR_NOMEM;
+    else if (mhd_SCKT_ERR_IS_BADF (err))
+      return mhd_SOCKET_ERR_BADF;
+    else if (mhd_SCKT_ERR_IS_EINVAL (err))
+      return mhd_SOCKET_ERR_INVAL;
+    else if (mhd_SCKT_ERR_IS_OPNOTSUPP (err))
+      return mhd_SOCKET_ERR_OPNOTSUPP;
+    else if (mhd_SCKT_ERR_IS_NOTSOCK (err))
+      return mhd_SOCKET_ERR_NOTSOCK;
+  }
+
+  return mhd_SOCKET_ERR_OTHER;
+}

+ 56 - 0
src/mhd2/mhd_recv.h

@@ -0,0 +1,56 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_recv.h
+ * @brief  The definition of the mhd_recv() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RECV_H
+#define MHD_RECV_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "mhd_socket_error.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Receive the data from the network socket.
+ * Clear #mhd_SOCKET_NET_STATE_RECV_READY in sk_ready if necessary.
+ *
+ * @param c the connection to use
+ * @param buf_size the size of the @a buf buffer
+ * @param[out] buf the buffer to fill with the received data
+ * @param[out] received the pointer to variable to get the size of the data
+ *                      actually put to the @a buffer
+ * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets
+ *         the received size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_recv (struct MHD_Connection *restrict c,
+          size_t buf_size,
+          void *restrict buf,
+          size_t *restrict received)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2) MHD_FN_PAR_OUT_ (4);
+
+#endif /* ! MHD_RECV_H */

+ 115 - 0
src/mhd2/mhd_reply.h

@@ -0,0 +1,115 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2021-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_reply.h
+ * @brief  The definition of the working reply data
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Data structures in this header are used when responding to client's request.
+ * Do not be confused with terms "response" and "reply" using in MHD code.
+ * The "MHD_Response" is an connection-independent object that have all
+ * data required to form a respond.
+ * The "MHD_Reply" is working connection-specific data used to format
+ * the respond based on provided data in "MHD_Response".
+ */
+
+#ifndef MHD_REPLY_H
+#define MHD_REPLY_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+#include "mhd_iovec.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Reply-specific properties.
+ */
+struct MHD_Reply_Properties
+{
+#ifdef _DEBUG
+  bool set; /**< Indicates that other members are set and valid */
+#endif /* _DEBUG */
+  bool use_reply_body_headers; /**< Use reply body-specific headers */
+  bool send_reply_body; /**< Send reply body (can be zero-sized) */
+  bool chunked; /**< Use chunked encoding for reply */
+};
+
+#if defined(MHD_USE_SENDFILE)
+enum MHD_resp_sender_
+{
+  MHD_resp_sender_std = 0,
+  MHD_resp_sender_sendfile
+};
+#endif /* MHD_USE_SENDFILE */
+
+/**
+ * Reply-specific values.
+ *
+ * Meaningful for the current reply only.
+ */
+struct MHD_Reply
+{
+  /**
+   * Response to transmit (initially NULL).
+   */
+  struct MHD_Response *response;
+
+  /**
+   * HTTP response code.  Only valid if response object
+   * is already set.
+   */
+  unsigned int responseCode;
+
+  /**
+   * The "ICY" response.
+   * Reply begins with the SHOUTcast "ICY" line instead of "HTTP".
+   */
+  bool responseIcy;
+
+  /**
+   * Current write position in the actual response
+   * (excluding headers, content only; should be 0
+   * while sending headers).
+   */
+  uint_fast64_t rsp_write_position;
+
+  /**
+   * The copy of iov response.
+   * Valid if iovec response is used.
+   * Updated during send.
+   * Members are allocated in the pool.
+   */
+  struct mhd_iovec_track resp_iov;
+
+#if defined(MHD_USE_SENDFILE)
+  enum MHD_resp_sender_ resp_sender;
+#endif /* MHD_USE_SENDFILE */
+
+  /**
+   * Reply-specific properties
+   */
+  struct MHD_Reply_Properties props;
+};
+
+#endif /* ! MHD_REPLY_H */

+ 376 - 0
src/mhd2/mhd_request.h

@@ -0,0 +1,376 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_request.h
+ * @brief  The definition of the request data structures
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Data structures in this header are used when parsing client's request
+ */
+
+#ifndef MHD_REQUEST_H
+#define MHD_REQUEST_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+#include "mhd_dlinked_list.h"
+
+#include "http_prot_ver.h"
+#include "http_method.h"
+#include "mhd_action.h"
+
+
+/**
+ * The action set by the application
+ */
+struct mhd_ApplicationAction
+{
+  /**
+   * The action after header reporting
+   */
+  struct MHD_Action head_act;
+  /**
+   * The action during upload processing
+   */
+  struct MHD_UploadAction upl_act;
+};
+
+/**
+ * The request line processing data
+ */
+struct MHD_RequestLineProcessing
+{
+  /**
+   * The position of the next character to be processed
+   */
+  size_t proc_pos;
+  /**
+   * The number of empty lines skipped
+   */
+  unsigned int skipped_empty_lines;
+  /**
+   * The position of the start of the current/last found whitespace block,
+   * zero if not found yet.
+   */
+  size_t last_ws_start;
+  /**
+   * The position of the next character after the last known whitespace
+   * character in the current/last found whitespace block,
+   * zero if not found yet.
+   */
+  size_t last_ws_end;
+  /**
+   * The pointer to the request target.
+   * The request URI will be formed based on it.
+   */
+  char *rq_tgt;
+  /**
+   * The pointer to the first question mark in the @a rq_tgt.
+   */
+  char *rq_tgt_qmark;
+  /**
+   * The number of whitespace characters in the request URI
+   */
+  size_t num_ws_in_uri;
+};
+
+/**
+ * The request header processing data
+ */
+struct MHD_HeaderProcessing
+{
+  /**
+   * The position of the last processed character
+   */
+  size_t proc_pos;
+
+  /**
+   * The position of the first whitespace character in current contiguous
+   * whitespace block.
+   * Zero when no whitespace found or found non-whitespace character after
+   * whitespace.
+   * Must be zero, if the current character is not whitespace.
+   */
+  size_t ws_start;
+
+  /**
+   * Indicates that end of the header (field) name found.
+   * Must be false until the first colon in line is found.
+   */
+  bool name_end_found;
+
+  /**
+   * The length of the header name.
+   * Must be zero until the first colon in line is found.
+   * Name always starts at zero position.
+   */
+  size_t name_len;
+
+  /**
+   * The position of the first character of the header value.
+   * Zero when the first character has not been found yet.
+   */
+  size_t value_start;
+
+  /**
+   * Line starts with whitespace.
+   * It's meaningful only for the first line, as other lines should be handled
+   * as "folded".
+   */
+  bool starts_with_ws;
+};
+
+/**
+ * The union of request line and header processing data
+ */
+union MHD_HeadersProcessing
+{
+  /**
+   * The request line processing data
+   */
+  struct MHD_RequestLineProcessing rq_line;
+
+  /**
+   * The request header processing data
+   */
+  struct MHD_HeaderProcessing hdr;
+};
+
+
+/**
+ * The union of text staring point and the size of the text
+ */
+union MHD_StartOrSize
+{
+  /**
+   * The starting point of the text.
+   * Valid when the text is being processed and the end of the text
+   * is not yet determined.
+   */
+  const char *start;
+  /**
+   * The size of the text.
+   * Valid when the text has been processed and the end of the text
+   * is known.
+   */
+  size_t size;
+};
+
+struct MHD_HTTP_Req_Header; /* forward declarations */
+
+mhd_DLINKEDL_LINKS_DEF (MHD_HTTP_Req_Header);
+
+/**
+ * Header, footer, or cookie for HTTP request.
+ */
+struct MHD_HTTP_Req_Header
+{
+  /**
+   * Headers are kept in a double-linked list.
+   */
+  mhd_DLNKDL_LINKS (MHD_HTTP_Req_Header,list);
+
+  /**
+   * The name of the header (key), without the colon.
+   */
+  const char *header;
+
+  /**
+   * The length of the @a header, not including the final zero termination.
+   */
+  size_t header_size;
+
+  /**
+   * The value of the header.
+   */
+  const char *value;
+
+  /**
+   * The length of the @a value, not including the final zero termination.
+   */
+  size_t value_size;
+
+  /**
+   * Type of the value.
+   */
+  enum MHD_ValueKind kind;
+
+};
+
+mhd_DLINKEDL_LIST_DEF (MHD_HTTP_Req_Header);
+
+/**
+ * Request-specific values.
+ *
+ * Meaningful for the current request only.
+ */
+struct MHD_Request
+{
+  /**
+   * Linked list of parsed headers.
+   */
+  mhd_DLNKDL_LIST (MHD_HTTP_Req_Header,list);
+
+  /**
+   * The action set by the application
+   */
+  struct mhd_ApplicationAction app_act;
+
+  /**
+   * HTTP version string (i.e. http/1.1).  Allocated
+   * in pool.
+   */
+  const char *version;
+
+  /**
+   * HTTP protocol version as enum.
+   */
+  enum MHD_HTTP_ProtocolVersion http_ver;
+
+  /**
+   * Request method.  Should be GET/POST/etc.  Allocated in pool.
+   */
+  const char *method;
+
+  /**
+   * The request method as enum.
+   */
+  enum mhd_HTTP_Method http_mthd;
+
+  /**
+   * Requested URL, the part before '?' (excluding parameters).  Allocated
+   * in pool.
+   */
+  const char *url;
+
+  /**
+   * The length of the @a url in characters, not including the terminating zero.
+   */
+  size_t url_len;
+
+  /**
+   * The original length of the request target.
+   */
+  size_t req_target_len;
+
+  /**
+   * Number of bytes we had in the HTTP header, set once we
+   * pass #MHD_CONNECTION_HEADERS_RECEIVED.
+   * This includes the request line, all request headers, the header section
+   * terminating empty line, with all CRLF (or LF) characters.
+   */
+  size_t header_size;
+
+  /**
+   * The union of the size of all request field lines (headers) and
+   * the starting point of the first request field line (the first header).
+   * Until #MHD_CONNECTION_HEADERS_RECEIVED the @a start member is valid,
+   * staring with #MHD_CONNECTION_HEADERS_RECEIVED the @a size member is valid.
+   * The size includes CRLF (or LR) characters, but does not include
+   * the terminating empty line.
+   */
+  union MHD_StartOrSize field_lines;
+
+  /**
+   * How many more bytes of the body do we expect
+   * to read? #MHD_SIZE_UNKNOWN for unknown.
+   */
+  uint_fast64_t remaining_upload_size;
+
+  /**
+   * 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).
+   * It is used only for requests, chunked encoding for response is
+   * indicated by @a rp_props.
+   */
+  bool have_chunked_upload;
+
+  /**
+   * 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.
+   */
+  uint_fast64_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)?
+   */
+  uint_fast64_t current_chunk_offset;
+
+  /**
+   * Indicate that some of the upload payload data (from the currently
+   * processed chunk for chunked uploads) have been processed by the
+   * last call of the connection handler.
+   * If any data have been processed, but some data left in the buffer
+   * for further processing, then MHD will use zero timeout before the
+   * next data processing round. This allow the application handler
+   * process the data by the fixed portions or other way suitable for
+   * application developer.
+   * If no data have been processed, than MHD will wait for more data
+   * to come (as it makes no sense to call the same connection handler
+   * under the same conditions). However this is dangerous as if buffer
+   * is completely used then connection is aborted. Connection
+   * suspension should be used in such case.
+   */
+  bool some_payload_processed;
+
+  /**
+   * 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;
+
+  /**
+   * Did we ever call the "default_handler" on this request?
+   * This flag determines if we have called the #MHD_OPTION_NOTIFY_COMPLETED
+   * handler when the request finishes.
+   */
+  bool client_aware;
+
+  /**
+   * Number of bare CR characters that were replaced with space characters
+   * in the request line or in the headers (field lines).
+   */
+  size_t num_cr_sp_replaced;
+
+  /**
+   * The number of header lines skipped because they have no colon
+   */
+  size_t skipped_broken_lines;
+
+  /**
+   * The data of the request line / request headers processing
+   */
+  union MHD_HeadersProcessing hdrs;
+};
+
+
+#endif /* ! MHD_REQUEST_H */

+ 324 - 0
src/mhd2/mhd_response.h

@@ -0,0 +1,324 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_response.h
+ * @brief  The definition of the MHD_Response type and related structures
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_H
+#define MHD_RESPONSE_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_public_api.h"
+
+#include "mhd_dlinked_list.h"
+#include "mhd_str_types.h"
+
+#include "mhd_iovec.h"
+
+#include "mhd_locks.h"
+
+
+struct ResponseOptions; /* forward declaration */
+
+struct mhd_ResponseHeader; /* forward declaration */
+
+mhd_DLINKEDL_LINKS_DEF (mhd_ResponseHeader);
+
+/**
+ * Response header / field
+ */
+struct mhd_ResponseHeader
+{
+  /**
+   * The name of the header / field
+   */
+  struct MHD_String name;
+
+  /**
+   * The value of the header / field
+   */
+  struct MHD_String value;
+
+  /**
+   * The links to other headers
+   */
+  mhd_DLNKDL_LINKS (mhd_ResponseHeader,headers);
+};
+
+/**
+ * The type of content
+ */
+enum mhd_ResponseContentType
+{
+  mhd_RESPONSE_CONTENT_INVALID = 0
+  ,
+  mhd_RESPONSE_CONTENT_BUFFER
+  ,
+  mhd_RESPONSE_CONTENT_IOVEC
+  ,
+  mhd_RESPONSE_CONTENT_FILE
+  ,
+  mhd_RESPONSE_CONTENT_CALLBACK
+};
+
+/**
+ * I/O vector response data
+ */
+struct mhd_ResponseIoVec
+{
+  /**
+   * The copy of array of iovec elements.
+   * Must be freed!
+   */
+  mhd_iovec *iov;
+
+  /**
+   * The number of elements in the @a iov array
+   */
+  size_t cnt;
+};
+
+/**
+ * The file data for the the response
+ */
+struct mhd_ResponseFD
+{
+  /**
+   * The file description of the response
+   */
+  int fd;
+
+  /**
+   * The offset in the file of the response content
+   */
+  uint_fast64_t offset;
+
+  /**
+   * Indicate that @a fd is a pipe
+   */
+  bool is_pipe;
+
+#ifdef MHD_USE_SENDFILE
+  /**
+   * Use 'sendfile()' function for the @a FD
+   * Initially 'true' (except for pipes) but can be flipped to 'false' if
+   * sendfile() cannot handle this file.
+   */
+  volatile bool use_sf;
+#endif
+};
+
+/**
+ * Dynamic response data
+ */
+struct mhd_ResponseDynamic
+{
+  /**
+   * The callback for the content data
+   */
+  MHD_DynamicContentCreator cb;
+  /**
+   * The closure for the @a cb
+   */
+  void *cls;
+};
+
+/**
+ * The response content data
+ */
+union mhd_ResponseContent
+{
+  /**
+   * The fixed unmodifiable data.
+   * 'unsigned char' pointer is used to simplify individual ranges addressing.
+   */
+  const unsigned char *restrict buf;
+
+  /**
+   * The
+   */
+  struct mhd_ResponseIoVec iovec;
+
+  /**
+   * The file data for the the response
+   */
+  struct mhd_ResponseFD file;
+
+  /**
+   * Dynamic response data
+   */
+  struct mhd_ResponseDynamic dyn;
+};
+
+/**
+ * The data of the free/cleanup callback
+ */
+struct mhd_FreeCbData
+{
+  /**
+   * The Free/Cleanup callback
+   */
+  MHD_FreeCallback cb;
+
+  /**
+   * The closure for the @a cb
+   */
+  void *cls;
+};
+
+
+struct mhd_ResponseReuseData
+{
+  /**
+   * Indicate that response could be used more than one time
+   */
+  bool reusable;
+
+  /**
+   * The number of active uses of the response.
+   * Used only when @a reusable is 'true'.
+   * When number reached zero, the response is destroyed.
+   * Must be checked only under the @a cnt_lock
+   */
+  volatile uint_fast64_t counter;
+
+#ifdef MHD_USE_THREADS
+  /**
+   * The mutex for @a counter access.
+   * Used only when @a reusable is 'true'.
+   */
+  mhd_mutex cnt_lock;
+
+  /**
+   * The mutex for @a settings access.
+   * Used only when @a reusable is 'true'.
+   */
+  mhd_mutex settings_lock;
+#endif /* MHD_USE_THREADS */
+};
+
+struct mhd_ResponseConfiguration
+{
+  /**
+   * Response have undefined content
+   * Must be used only when response content (even zero-size) is not allowed.
+   */
+  bool head_only;
+
+  /**
+   * If set to 'true' then the chunked encoding must be used (if allowed
+   * by HTTP version).
+   * If 'false' then chunked encoding must not be used.
+   */
+  bool chunked;
+
+  /**
+   * If 'true', "Connection: close" header must be always used
+   */
+  bool conn_close;
+
+  /**
+   * Use "HTTP/1.0" in the reply header
+   */
+  bool mode_1_0;
+
+  /**
+   * The (possible incorrect) content length is provided by application
+   */
+  bool cnt_len_by_app;
+};
+
+#ifndef NDEBUG
+struct mhd_ResponseDebug
+{
+  bool is_internal;
+};
+#endif
+
+mhd_DLINKEDL_LIST_DEF (mhd_ResponseHeader);
+
+struct MHD_Response
+{
+  /**
+   * The response status code
+   */
+  enum MHD_HTTP_StatusCode sc;
+
+  /**
+   * The size of the response.
+   * #MHD_SIZE_UNKNOWN if size is undefined
+   */
+  uint_fast64_t size;
+
+  /**
+   * The type of the content data
+   */
+  enum mhd_ResponseContentType cntn_type;
+
+  /**
+   * The data of the content of the response
+   */
+  union mhd_ResponseContent cntn;
+
+  /**
+   * The data of the free/cleanup callback
+   */
+  struct mhd_FreeCbData free;
+
+  /**
+   * Configuration data of the response
+   */
+  struct mhd_ResponseConfiguration cfg;
+
+  /**
+   * If response is "frozen" then response data cannot be changed.
+   * The use counter for re-usable responses is the exception and can be
+   * changed when "frozen".
+   */
+  volatile bool frozen;
+
+  /**
+   * The re-use parameters
+   */
+  struct mhd_ResponseReuseData reuse;
+
+  /**
+   * The settings, before the response is @a frozen
+   */
+  struct ResponseOptions *restrict settings;
+
+  /**
+   * The double linked list of the response headers
+   */
+  mhd_DLNKDL_LIST (mhd_ResponseHeader,headers);
+
+#ifndef NDEBUG
+  struct mhd_ResponseDebug dbg;
+#endif
+};
+
+#endif /* ! MHD_RESPONSE_H */

+ 119 - 0
src/mhd2/mhd_socket_error.h

@@ -0,0 +1,119 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_socket_error.h
+ * @brief  The definition of the mhd_SocketError enum
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SOCKET_ERROR_H
+#define MHD_SOCKET_ERROR_H 1
+
+#include "mhd_sys_options.h"
+
+/**
+ * Recognised socket errors
+ */
+enum MHD_FIXED_ENUM_ mhd_SocketError
+{
+  /**
+   * No error.
+   */
+  mhd_SOCKET_ERR_NO_ERROR = 0
+  ,
+  /**
+   * No more data to get / no more space to put the data.
+   */
+  mhd_SOCKET_ERR_AGAIN
+  ,
+  /**
+   * The process has been interrupted by external factors.
+   */
+  mhd_SOCKET_ERR_INTR
+  ,
+  /**
+   * "Not enough memory" / "not enough system resources"
+   */
+  mhd_SOCKET_ERR_NOMEM
+  ,
+  /**
+   * The connection has been hard-closed by remote peer.
+   */
+  mhd_SOCKET_ERR_CONNRESET
+  ,
+  /**
+   * Meta-error for any other errors indicating a broken connection.
+   * It can be keep-alive ping failure or timeout to get ACK for the
+   * transmitted data.
+   */
+  mhd_SOCKET_ERR_CONN_BROKEN
+  ,
+  /**
+   * Connection is not connected anymore due to network error or
+   * any other reason.
+   */
+  mhd_SOCKET_ERR_NOTCONN
+  ,
+  /**
+   * The socket FD is invalid
+   */
+  mhd_SOCKET_ERR_BADF
+  ,
+  /**
+   * Socket function parameters are invalid
+   */
+  mhd_SOCKET_ERR_INVAL
+  ,
+  /**
+   * Socket function parameters are not supported
+   */
+  mhd_SOCKET_ERR_OPNOTSUPP
+  ,
+  /**
+   * Used FD is not a socket
+   */
+  mhd_SOCKET_ERR_NOTSOCK
+  ,
+  /**
+   * The remote side shut down reading, the socket has been shut down
+   * for writing or no longer connected
+   * Only for 'send()'.
+   */
+  mhd_SOCKET_ERR_PIPE
+  ,
+  /**
+   * General TLS encryption or decryption error
+   */
+  mhd_SOCKET_ERR_TLS
+  ,
+  /**
+   * Other socket error
+   */
+  mhd_SOCKET_ERR_OTHER
+
+};
+
+/**
+ * Check whether the socket error is unrecoverable
+ */
+#define mhd_SOCKET_ERR_IS_HARD(err) (mhd_SOCKET_ERR_CONNRESET <= (err))
+
+#endif /* ! MHD_SOCKET_ERROR_H */

+ 252 - 0
src/mhd2/mhd_sockets_funcs.c

@@ -0,0 +1,252 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Karlson2k (Evgeny Grin)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_sockets_funcs.c
+ * @brief  Implementations of sockets manipulating functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include "mhd_sys_options.h"
+#include "mhd_sockets_funcs.h"
+#include "sys_sockets_headers.h"
+#include "sys_ip_headers.h"
+#ifdef MHD_POSIX_SOCKETS
+#  ifdef HAVE_SYS_TYPES_H
+#    include <sys/types.h>
+#  endif
+#  ifdef HAVE_UNISTD_H
+#    include <unistd.h>
+#  else
+#    include <stdlib.h>
+#  endif
+#  include <fcntl.h>
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  include <windows.h>
+#endif
+#ifndef INADDR_LOOPBACK
+#  include <string.h> /* For memcpy() */
+#endif
+
+#include "mhd_sockets_macros.h"
+
+
+MHD_INTERNAL bool
+mhd_socket_nonblocking (MHD_Socket sckt)
+{
+#if defined(MHD_POSIX_SOCKETS)
+  // TODO: detect constants in configure
+#if defined(F_GETFL) && defined(O_NONBLOCK) && defined(F_SETFL)
+  int get_flags;
+  int set_flags;
+
+  get_flags = fcntl (sckt, F_GETFL);
+  if (0 > get_flags)
+    return false;
+
+  set_flags = (flags | O_NONBLOCK);
+  if (get_flags == set_flags)
+    return true;
+
+  if (-1 != fcntl (sckt, F_SETFL, set_flags))
+    return true;
+#endif /* F_GETFL && O_NONBLOCK && F_SETFL */
+#elif defined(MHD_WINSOCK_SOCKETS)
+  unsigned long set_flag = 1;
+
+  if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag))
+    return true;
+#endif /* MHD_WINSOCK_SOCKETS */
+
+  return false;
+}
+
+
+MHD_INTERNAL bool
+mhd_socket_noninheritable (MHD_Socket sckt)
+{
+#if defined(MHD_POSIX_SOCKETS)
+  // TODO: detect constants in configure
+#if defined(F_GETFD) && defined(FD_CLOEXEC) && defined(F_SETFD)
+  int get_flags;
+  int set_flags;
+
+  get_flags = fcntl (sckt, F_GETFD);
+  if (0 > flags)
+    return false;
+
+  set_flags = (flags | FD_CLOEXEC);
+  if (get_flags == set_flags)
+    return true;
+
+  if (-1 != fcntl (sckt, F_SETFD, set_flags))
+    return true;
+#endif /* F_GETFD && FD_CLOEXEC && F_SETFD */
+#elif defined(MHD_WINSOCK_SOCKETS)
+  if (SetHandleInformation ((HANDLE) sckt, HANDLE_FLAG_INHERIT, 0))
+    return true;
+#endif /* MHD_WINSOCK_SOCKETS */
+  return false;
+}
+
+
+MHD_INTERNAL bool
+mhd_socket_set_nodelay (MHD_Socket sckt,
+                        bool on)
+{
+  // TODO: detect constants in configure
+#ifdef TCP_NODELAY
+  mhd_SCKT_OPT_BOOL value;
+
+  value = on ? 1 : 0;
+
+  return 0 == setsockopt (sckt, IPPROTO_TCP, TCP_NODELAY,
+                          (const void *) &value, sizeof (value));
+#else  /* ! TCP_NODELAY */
+  (void) sock; (void) on;
+  return false;
+#endif /* ! TCP_NODELAY */
+}
+
+
+#ifndef HAVE_SOCKETPAIR
+
+static bool
+mhd_socket_blocking (MHD_Socket sckt)
+{
+#if defined(MHD_POSIX_SOCKETS)
+  // TODO: detect constants in configure
+#if defined(F_GETFL) && defined(O_NONBLOCK) && defined(F_SETFL)
+  int get_flags;
+  int set_flags;
+
+  get_flags = fcntl (sckt, F_GETFL);
+  if (0 > get_flags)
+    return false;
+
+  set_flags = (flags & ~O_NONBLOCK);
+  if (get_flags == set_flags)
+    return true;
+
+  if (-1 != fcntl (sckt, F_SETFL, set_flags))
+    return true;
+#endif /* F_GETFL && O_NONBLOCK && F_SETFL */
+#elif defined(MHD_WINSOCK_SOCKETS)
+  unsigned long set_flag = 0;
+
+  if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag))
+    return true;
+#endif /* MHD_WINSOCK_SOCKETS */
+
+  return false;
+}
+
+
+MHD_INTERNAL bool
+mhd_socket_pair_func (MHD_Socket sckt[2], bool non_blk)
+{
+  int i;
+
+#define PAIR_MAX_TRIES 511
+  for (i = 0; i < PAIR_MAX_TRIES; i++)
+  {
+    struct sockaddr_in listen_addr;
+    MHD_Socket listen_s;
+    static const socklen_t c_addinlen = sizeof(struct sockaddr_in);   /* Try to help compiler to optimise */
+    socklen_t addr_len = c_addinlen;
+
+    listen_s = socket (AF_INET,
+                       SOCK_STREAM,
+                       IPPROTO_TCP);
+    if (INVALID_SOCKET == listen_s)
+      break;   /* can't create even single socket */
+
+    listen_addr.sin_family = AF_INET;
+    listen_addr.sin_port = 0;   /* same as htons(0) */
+#ifdef INADDR_LOOPBACK
+    listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+#else
+    memcpy (&(listen_addr.sin_addr.s_addr), "\x7F\x00\x00\x01", 4);
+#endif
+    if ( ((0 == bind (listen_s,
+                      (struct sockaddr *) &listen_addr,
+                      c_addinlen)) &&
+          (0 == listen (listen_s,
+                        1) ) &&
+          (0 == getsockname (listen_s,
+                             (struct sockaddr *) &listen_addr,
+                             &addr_len))) )
+    {
+      MHD_Socket client_s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+      struct sockaddr_in accepted_from_addr;
+      struct sockaddr_in client_addr;
+
+      if (INVALID_SOCKET != client_s)
+      {
+        if (mhd_socket_nonblocking (client_s) &&
+            ( (0 == connect (client_s,
+                             (struct sockaddr *) &listen_addr,
+                             c_addinlen)) ||
+              mhd_SCKT_LERR_IS_EAGAIN () ))
+        {
+          MHD_Socket server_s;
+
+          addr_len = c_addinlen;
+          server_s = accept (listen_s,
+                             (struct sockaddr *) &accepted_from_addr,
+                             &addr_len);
+          if (MHD_INVALID_SOCKET != server_s)
+          {
+            addr_len = c_addinlen;
+            if ( (0 == getsockname (client_s,
+                                    (struct sockaddr *) &client_addr,
+                                    &addr_len)) &&
+                 (accepted_from_addr.sin_port == client_addr.sin_port) &&
+                 (accepted_from_addr.sin_addr.s_addr ==
+                  client_addr.sin_addr.s_addr) )
+            {
+              (void) mhd_socket_set_nodelay (server_s, true);
+              (void) mhd_socket_set_nodelay (client_s, true);
+              if (non_blk ?
+                  mhd_socket_nonblocking (server_s) :
+                  mhd_socket_blocking (client_s))
+              {
+                mhd_socket_close (listen_s);
+                sckt[0] = server_s;
+                sckt[1] = client_s;
+                return true;
+              }
+            }
+            mhd_socket_close (server_s);
+          }
+        }
+        mhd_socket_close (client_s);
+      }
+    }
+    mhd_socket_close (listen_s);
+  }
+
+  sckt[0] = INVALID_SOCKET;
+  sckt[1] = INVALID_SOCKET;
+
+  return false;
+}
+
+
+#endif /* ! HAVE_SOCKETPAIR */

+ 87 - 0
src/mhd2/mhd_sockets_funcs.h

@@ -0,0 +1,87 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Karlson2k (Evgeny Grin)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_sockets_funcs.h
+ * @brief  Declarations for sockets manipulating functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SOCKETS_FUNCS_H
+#define MHD_SOCKETS_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "mhd_socket_type.h"
+
+
+/**
+ * Change socket options to be non-blocking.
+ *
+ * @param sckt the socket to manipulate
+ * @return true on success, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_socket_nonblocking (MHD_Socket sckt);
+
+/**
+ * Change socket options to be non-inheritable.
+ *
+ * @param sock the socket to manipulate
+ * @return true on success, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_socket_noninheritable (MHD_Socket sckt);
+
+
+/**
+ * Control Nagle's algorithm on @a sock.
+ *
+ * @param sckt the socket to manipulate
+ * @param on the value to use: true to set "no delay" (disable Nagle's
+ *           algorithm), false to clear "no delay" (enable Nagle's algorithm)
+ * @return true on success, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_socket_set_nodelay (MHD_Socket sckt,
+                        bool on);
+
+
+#ifndef HAVE_SOCKETPAIR
+
+#  define mhd_socket_pair(fdarr_ptr) mhd_socket_pair_func (fdarr_ptr, false)
+#  define mhd_socket_pair_nblk(fdarr_ptr) mhd_socket_pair_func (fdarr_ptr, true)
+
+
+/**
+ * Create pair of mutually connected sockets on loopback address
+ * @param sockets_pair the array to receive resulted sockets
+ * @param non_blk if set to true, sockets created in non-blocking mode
+ *                otherwise sockets will be in blocking mode
+ * @return true if succeeded, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_socket_pair_func (MHD_Socket sckt[2], bool non_blk);
+
+#endif /* ! HAVE_SOCKETPAIR */
+
+
+#endif /* ! MHD_SOCKETS_FUNCS_H */

+ 334 - 0
src/mhd2/mhd_sockets_macros.h

@@ -0,0 +1,334 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Karlson2k (Evgeny Grin)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_sockets_macros.h
+ * @brief  Various helper macros functions related to sockets
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SOCKETS_MACROS_H
+#define MHD_SOCKETS_MACROS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "mhd_socket_type.h"
+#include "sys_base_types.h"
+#include "sys_sockets_headers.h"
+
+#if defined(MHD_POSIX_SOCKETS)
+#  include "sys_errno.h"
+#  ifdef HAVE_UNISTD_H
+#    include <unistd.h>
+#  else
+#    include <stdlib.h>
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  include <winsock2.h>
+#endif
+
+/**
+ * Close the socket FD
+ * @param sckt the socket to close
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#  define mhd_socket_close(sckt) close (sckt)
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_socket_close(sckt) closesocket (sckt)
+#endif
+
+/**
+ * mhd_send4 is a wrapper for system's send()
+ * @param s the socket to use
+ * @param b the buffer with data to send
+ * @param l the length of data in @a b
+ * @param f the additional flags
+ * @return ssize_t type value
+ */
+#define mhd_sys_send4(s,b,l,f) \
+        ((ssize_t) send ((s),(const void*) (b),(mhd_SCKT_SEND_SIZE) (l), \
+                         ((MHD_MSG_NOSIGNAL) | (f))))
+
+
+/**
+ * mhd_send is a simple wrapper for system's send()
+ * @param s the socket to use
+ * @param b the buffer with data to send
+ * @param l the length of data in @a b
+ * @return ssize_t type value
+ */
+#define mhd_sys_send(s,b,l) mhd_sys_send4 ((s),(b),(l), 0)
+
+
+/**
+ * mhd_recv is wrapper for system's recv()
+ * @param s the socket to use
+ * @param b the buffer for data to receive
+ * @param l the length of @a b
+ * @return ssize_t type value
+ */
+#define mhd_sys_recv(s,b,l) \
+        ((ssize_t) recv ((s),(void*) (b),(mhd_SCKT_SEND_SIZE) (l), 0))
+
+#ifdef EMFILE
+#  define MHD_EMFILE_OR_ZERO EMFILE
+#else
+#  define MHD_EMFILE_OR_ZERO (0)
+#endif
+
+#ifdef ENFILE
+#  define MHD_ENFILE_OR_ZERO ENFILE
+#else
+#  define MHD_ENFILE_OR_ZERO (0)
+#endif
+
+#ifdef ENOMEM
+#  define MHD_ENOMEM_OR_ZERO ENOMEM
+#else
+#  define MHD_ENOMEM_OR_ZERO (0)
+#endif
+
+#ifdef ENOBUFS
+#  define MHD_ENOBUFS_OR_ZERO ENOBUFS
+#else
+#  define MHD_ENOBUFS_OR_ZERO (0)
+#endif
+
+#ifdef EHOSTUNREACH
+#  define MHD_EHOSTUNREACH_OR_ZERO EHOSTUNREACH
+#else
+#  define MHD_EHOSTUNREACH_OR_ZERO (0)
+#endif
+
+#ifdef ETIMEDOUT
+#  define MHD_ETIMEDOUT_OR_ZERO ETIMEDOUT
+#else
+#  define MHD_ETIMEDOUT_OR_ZERO (0)
+#endif
+
+#ifdef ENETUNREACH
+#  define MHD_ENETUNREACH_OR_ZERO ENETUNREACH
+#else
+#  define MHD_ENETUNREACH_OR_ZERO (0)
+#endif
+
+/**
+ * Last socket error
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#  define mhd_SCKT_GET_LERR() errno
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_GET_LERR() (WSAGetLastError ())
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  if defined(EAGAIN) && defined(EWOULDBLOCK) && \
+  ((EWOULDBLOCK + 0) != (EAGAIN + 0))
+#    define mhd_SCKT_ERR_IS_EAGAIN(err) \
+        ((EAGAIN == (err)) || (EWOULDBLOCK == (err)))
+#  elif defined(EAGAIN)
+#    define mhd_SCKT_ERR_IS_EAGAIN(err) (EAGAIN == (err))
+#  elif defined(EWOULDBLOCK)
+#    define mhd_SCKT_ERR_IS_EAGAIN(err) (EWOULDBLOCK == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_EAGAIN(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_EAGAIN(err) (WSAEWOULDBLOCK == (err))
+#endif
+
+#define mhd_SCKT_LERR_IS_EAGAIN() mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ())
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef EAFNOSUPPORT
+#    define mhd_SCKT_ERR_IS_AF(err) (EAFNOSUPPORT == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_AF(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_AF(err) (WSAEAFNOSUPPORT == (err))
+#endif
+
+#define mhd_SCKT_LERR_IS_AF() mhd_SCKT_ERR_IS_AF (mhd_SCKT_GET_LERR ())
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef EINVAL
+#    define mhd_SCKT_ERR_IS_EINVAL(err) (EINVAL == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_EINVAL(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_EINVAL(err) (WSAEINVAL == (err))
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef EINTR
+#    define mhd_SCKT_ERR_IS_EINTR(err) (EINTR == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_EINTR(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_EINTR(err) (WSAEINTR == (err))
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef ECONNRESET
+#    define mhd_SCKT_ERR_IS_CONNRESET(err) (ECONNRESET == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_CONNRESET(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_CONNRESET(err) (WSAECONNRESET == (err))
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef ENOTCONN
+#    define mhd_SCKT_ERR_IS_NOTCONN(err) (ENOTCONN == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_NOTCONNT(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_NOTCONN(err) (WSAENOTCONN == (err))
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef EOPNOTSUPP
+#    define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (EOPNOTSUPP == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_OPNOTSUPP(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (WSAEOPNOTSUPP == (err))
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef EBADF
+#    define mhd_SCKT_ERR_IS_BADF(err) (EBADF == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_BADF(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_BADF(err) ((void) (err), ! ! 0)
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef ENOTSOCK
+#    define mhd_SCKT_ERR_IS_NOTSOCK(err) (ENOTSOCK == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_NOTSOCK(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_NOTSOCK(err) (WSAENOTSOCK == (err))
+#endif
+
+/**
+ * Check whether is given socket error is type of "incoming connection
+ * was disconnected before 'accept()' is called".
+ * @return boolean true is @a err match described socket error code,
+ *         boolean false otherwise.
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef ECONNABORTED
+#    define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) (ECONNABORTED == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) (WSAECONNRESET == (err))
+#endif
+
+/**
+ * Error for any reason when the system detects connection broke, but not
+ * because of the peer.
+ * It can be keep-alive ping failure or timeout to get ACK for the
+ * transmitted data.
+ */
+#if defined(MHD_POSIX_SOCKETS)
+/* + EHOSTUNREACH: probably reported by intermediate
+    + ETIMEDOUT: probably keep-alive ping failure
+    + ENETUNREACH: probably cable physically disconnected or similar */
+#    define mhd_SCKT_ERR_IS_CONN_BROKEN(err) \
+        ((0 != (err)) && \
+         ((MHD_EHOSTUNREACH_OR_ZERO == (err)) || \
+          (MHD_ETIMEDOUT_OR_ZERO == (err)) || \
+          (MHD_ENETUNREACH_OR_ZERO == (err))))
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_CONN_BROKEN(err) \
+        ( (WSAENETRESET == (err)) || (WSAECONNABORTED == (err)) || \
+          (WSAETIMEDOUT == (err)) )
+#endif
+
+/**
+ * Check whether given socket error is any kind of "low resource" error.
+ * @return boolean true if @a err is any kind of "low resource" error,
+ *         boolean false otherwise.
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#    define mhd_SCKT_ERR_IS_LOW_RESOURCES(err) \
+        ((0 != (err)) && \
+         ((MHD_EMFILE_OR_ZERO == (err)) || (MHD_ENFILE_OR_ZERO == (err)) || \
+          (MHD_ENOMEM_OR_ZERO == (err)) || (MHD_ENOBUFS_OR_ZERO == (err))))
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_LOW_RESOURCES(err) \
+        ( (WSAEMFILE == (err)) || (WSAENOBUFS == (err)) )
+#endif
+
+/**
+ * Check whether given socket error is any kind of "low memory" error.
+ * This is subset of #mhd_SCKT_ERR_IS_LOW_RESOURCES()
+ * @return boolean true if @a err is any kind of "low memory" error,
+ *         boolean false otherwise.
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#    define mhd_SCKT_ERR_IS_LOW_MEM(err) \
+        ((0 != (err)) && \
+         ((MHD_ENOMEM_OR_ZERO == (err)) || (MHD_ENOBUFS_OR_ZERO == (err))))
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_LOW_MEM(err) (WSAENOBUFS == (err))
+#endif
+
+
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef HAVE_SOCKETPAIR
+#    ifdef MHD_AF_UNIX
+#      define mhd_socket_pair(fdarr_ptr) \
+        (0 != socketpair (MHD_AF_UNIX, SOCK_STREAM, 0, (fdarr_ptr)))
+#    else
+#      define mhd_socket_pair(fdarr_ptr) \
+        (0 != socketpair (AF_INET, SOCK_STREAM, 0, (fdarr_ptr))) /* Fallback, could be broken on many platforms */
+#    endif
+#    if defined(HAVE_SOCK_NONBLOCK)
+#      ifdef MHD_AF_UNIX
+#        define mhd_socket_pair_nblk(fdarr_ptr) \
+        (0 != socketpair (MHD_AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, ( \
+                            fdarr_ptr)))
+#      else
+#        define mhd_socket_pair_nblk(fdarr_ptr) \
+        (0 != socketpair (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0, (fdarr_ptr))) /* Fallback, could be broken on many platforms */
+#      endif
+#    endif /* HAVE_SOCK_NONBLOCK*/
+#  endif /* HAVE_SOCKETPAIR */
+#endif
+
+#ifndef mhd_socket_pair
+/* mhd_socket_pair() implemented in "mhd_sockets_funcs.h" based on local function */
+#endif
+
+#endif /* ! MHD_SOCKETS_MACROS_H */

+ 44 - 0
src/mhd2/mhd_str_macros.h

@@ -0,0 +1,44 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_str_macros.h
+ * @brief  The definition of the MHD_String helper macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STR_MACROS_H
+#define MHD_STR_MACROS_H 1
+
+#include "mhd_sys_options.h"
+
+/**
+ * The length of static string, not including terminating zero.
+ * Can be used with char[] arrays.
+ */
+#define mhd_SSTR_LEN(sstr) (sizeof(sstr) / sizeof(char) - 1)
+
+/**
+ * The initialiser for the struct MHD_String
+ */
+#define mhd_MSTR_INIT(sstr) { mhd_SSTR_LEN (sstr), (sstr)}
+
+
+#endif /* ! MHD_STR_MACROS_H */

+ 76 - 0
src/mhd2/mhd_str_types.h

@@ -0,0 +1,76 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_str_types.h
+ * @brief  The definition of the tristate type and helper macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STR_TYPES_H
+#define MHD_STR_TYPES_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_STRINGS_DEFINED
+#include "sys_base_types.h"
+
+
+/**
+ * String with length data.
+ * This type should always have valid @a cstr pointer.
+ */
+struct MHD_String
+{
+  /**
+   * Number of characters in @e str, not counting 0-termination.
+   */
+  size_t len;
+
+  /**
+   * 0-terminated C-string.
+   * Must not be NULL.
+   */
+  const char *cstr;
+};
+
+/**
+ * String with length data.
+ * This type of data may have NULL as the @a cstr pointer.
+ */
+struct MHD_StringNullable
+{
+  /**
+   * Number of characters in @e str, not counting 0-termination.
+   * If @a cstr is NULL, it must be zero.
+   */
+  size_t len;
+
+  /**
+   * 0-terminated C-string.
+   * In some cases it could be NULL.
+   */
+  const char *cstr;
+};
+
+#define MHD_STRINGS_DEFINED 1
+#endif /* ! MHD_STRINGS_DEFINED */
+
+#endif /* ! MHD_STR_TYPES_H */

+ 139 - 137
src/microhttpd/mhd_threads.c → src/mhd2/mhd_threads.c

@@ -1,13 +1,13 @@
 /*
-  This file is part of libmicrohttpd
-  Copyright (C) 2016-2023 Karlson2k (Evgeny Grin)
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
 
-  This library is free software; you can redistribute it and/or
+  GNU libmicrohttpd 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,
+  GNU libmicrohttpd 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.
@@ -25,37 +25,36 @@
  */
 
 #include "mhd_threads.h"
+#include "sys_null_macro.h"
 #ifdef MHD_USE_W32_THREADS
-#include "mhd_limits.h"
-#include <process.h>
+#  include <process.h>
 #endif
-#ifdef MHD_USE_THREAD_NAME_
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif /* HAVE_STDLIB_H */
-#ifdef HAVE_PTHREAD_NP_H
-#include <pthread_np.h>
-#endif /* HAVE_PTHREAD_NP_H */
+#if defined(MHD_USE_THREAD_NAME_)
+#  if ! defined(MHD_USE_THREAD_ATTR_SETNAME)
+#    include "sys_malloc.h"
+#  endif
+#  ifdef HAVE_PTHREAD_NP_H
+#    include <pthread_np.h>
+#  endif /* HAVE_PTHREAD_NP_H */
 #endif /* MHD_USE_THREAD_NAME_ */
 #include <errno.h>
 #include "mhd_assert.h"
 
-
 #ifndef MHD_USE_THREAD_NAME_
 
-#define MHD_set_thread_name_(t, n) (void)
-#define MHD_set_cur_thread_name_(n) (void)
+#  define mhd_set_thread_name(t, n) (void)
+#  define mhd_set_cur_thread_name(n) (void)
 
 #else  /* MHD_USE_THREAD_NAME_ */
 
-#if defined(MHD_USE_POSIX_THREADS)
-#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
+#  if defined(MHD_USE_POSIX_THREADS)
+#    if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
   defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
-#  define MHD_USE_THREAD_ATTR_SETNAME 1
-#endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \
+#      define MHD_USE_THREAD_ATTR_SETNAME 1
+#    endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \
           HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */
 
-#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
+#    if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
   defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \
   || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
 
@@ -64,67 +63,68 @@
  *
  * @param thread_id ID of thread
  * @param thread_name name to set
- * @return non-zero on success, zero otherwise
+ * @return true on success, false otherwise
  */
-static int
-MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id,
-                      const char *thread_name)
+static bool
+mhd_set_thread_name (const mhd_thread_ID_native thread_id,
+                     const char *thread_name)
 {
   if (NULL == thread_name)
-    return 0;
+    return false;
 
-#if defined(HAVE_PTHREAD_SETNAME_NP_GNU)
-  return ! pthread_setname_np (thread_id, thread_name);
-#elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD)
+#      if defined(HAVE_PTHREAD_SETNAME_NP_GNU)
+  return 0 == pthread_setname_np (thread_id, thread_name);
+#      elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD)
   /* FreeBSD and OpenBSD use different function name and void return type */
   pthread_set_name_np (thread_id, thread_name);
-  return ! 0;
-#elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
-  /* NetBSD use 3 arguments: second argument is string in printf-like format,
-   *                         third argument is a single argument for printf();
+  return true;
+#      elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
+  /* NetBSD uses 3 arguments: second argument is string in printf-like format,
+   *                          third argument is a single argument for printf();
    * OSF1 use 3 arguments too, but last one always must be zero (NULL).
    * MHD doesn't use '%' in thread names, so both form are used in same way.
    */
-  return ! pthread_setname_np (thread_id, thread_name, 0);
-#endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */
+  return 0 == pthread_setname_np (thread_id, thread_name, 0);
+#      endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */
 }
 
 
-#ifndef __QNXNTO__
+#      ifndef __QNXNTO__
 /**
  * Set current thread name
  * @param n name to set
  * @return non-zero on success, zero otherwise
  */
-#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (pthread_self (),(n))
-#else  /* __QNXNTO__ */
+#        define mhd_set_cur_thread_name(n) \
+        mhd_set_thread_name (pthread_self (),(n))
+#      else  /* __QNXNTO__ */
 /* Special case for QNX Neutrino - using zero for thread ID sets name faster. */
-#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (0,(n))
-#endif /* __QNXNTO__ */
-#elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN)
+#        define mhd_set_cur_thread_name(n) mhd_set_thread_name (0,(n))
+#      endif /* __QNXNTO__ */
+#    elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN)
 
 /**
  * Set current thread name
  * @param n name to set
  * @return non-zero on success, zero otherwise
  */
-#define MHD_set_cur_thread_name_(n) (! (pthread_setname_np ((n))))
-#endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */
+#      define mhd_set_cur_thread_name(n) (! (pthread_setname_np ((n))))
+#    endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */
 
-#elif defined(MHD_USE_W32_THREADS)
-#ifndef _MSC_FULL_VER
-/* Thread name available only for VC-compiler */
-#else  /* _MSC_FULL_VER */
+#  elif defined(MHD_USE_W32_THREADS)
+#    ifndef _MSC_FULL_VER
+#error Thread name available only for VC-compiler
+#    else  /* _MSC_FULL_VER */
 /**
  * Set thread name
  *
  * @param thread_id ID of thread, -1 for current thread
  * @param thread_name name to set
- * @return non-zero on success, zero otherwise
+ * @return true on success, false otherwise
  */
-static int
-MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id,
-                      const char *thread_name)
+static bool
+mhd_set_thread_name (const mhd_thread_ID_native thread_id,
+                     const char *thread_name)
 {
   static const DWORD VC_SETNAME_EXC = 0x406D1388;
 #pragma pack(push,8)
@@ -138,7 +138,7 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id,
 #pragma pack(pop)
 
   if (NULL == thread_name)
-    return 0;
+    return false;
 
   thread_info.type  = 0x1000;
   thread_info.name  = thread_name;
@@ -155,19 +155,19 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id,
   __except (EXCEPTION_EXECUTE_HANDLER)
   {}
 
-  return ! 0;
+  return true;
 }
 
 
 /**
  * Set current thread name
  * @param n name to set
- * @return non-zero on success, zero otherwise
+ * @return true on success, false otherwise
  */
-#define MHD_set_cur_thread_name_(n) \
-  MHD_set_thread_name_ ((MHD_thread_ID_native_) -1,(n))
-#endif /* _MSC_FULL_VER */
-#endif /* MHD_USE_W32_THREADS */
+#      define mhd_set_cur_thread_name(n) \
+        mhd_set_thread_name ((mhd_thread_ID_native) (-1),(n))
+#    endif /* _MSC_FULL_VER */
+#  endif /* MHD_USE_W32_THREADS */
 
 #endif /* MHD_USE_THREAD_NAME_ */
 
@@ -175,7 +175,7 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id,
 /**
  * Create a thread and set the attributes according to our options.
  *
- * If thread is created, thread handle must be freed by MHD_join_thread_().
+ * If thread is created, thread handle must be freed by mhd_join_thread().
  *
  * @param handle_id     handle to initialise
  * @param stack_size    size of stack for new thread, 0 for default
@@ -183,23 +183,23 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id,
  * @param arg argument  for start_routine
  * @return non-zero on success; zero otherwise (with errno set)
  */
-int
-MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id,
-                    size_t stack_size,
-                    MHD_THREAD_START_ROUTINE_ start_routine,
-                    void *arg)
+bool
+mhd_create_thread (mhd_thread_handle_ID *handle_id,
+                   size_t stack_size,
+                   mhd_THREAD_START_ROUTINE start_routine,
+                   void *arg)
 {
 #if defined(MHD_USE_POSIX_THREADS)
   int res;
-#if defined(MHD_thread_handle_ID_get_native_handle_ptr_)
+#  if defined(mhd_thread_handle_ID_get_native_handle_ptr)
   pthread_t *const new_tid_ptr =
-    MHD_thread_handle_ID_get_native_handle_ptr_ (handle_id);
-#else  /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */
+    mhd_thread_handle_ID_get_native_handle_ptr (handle_id);
+#  else  /* ! mhd_thread_handle_ID_get_native_handle_ptr */
   pthread_t new_tid;
   pthread_t *const new_tid_ptr = &new_tid;
-#endif /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */
+#  endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */
 
-  mhd_assert (! MHD_thread_handle_ID_is_valid_handle_ (*handle_id));
+  mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id));
 
   if (0 != stack_size)
   {
@@ -226,53 +226,55 @@ MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id,
   if (0 != res)
   {
     errno = res;
-    MHD_thread_handle_ID_set_invalid_ (handle_id);
+    mhd_thread_handle_ID_set_invalid (handle_id);
   }
-#if ! defined(MHD_thread_handle_ID_get_native_handle_ptr_)
+#  if ! defined(mhd_thread_handle_ID_get_native_handle_ptr)
   else
-    MHD_thread_handle_ID_set_native_handle_ (handle_id, new_tid);
-#endif /* ! MHD_thread_handle_ID_set_current_thread_ID_ */
+    mhd_thread_handle_ID_set_native_handle (handle_id, new_tid);
+#  endif /* ! mhd_thread_handle_ID_set_current_thread_ID */
 
-  return ! res;
+  return 0 == res;
 #elif defined(MHD_USE_W32_THREADS)
   uintptr_t thr_handle;
-#if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT
+  unsigned int stack_size_w32;
+#  if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT
 
-  mhd_assert (! MHD_thread_handle_ID_is_valid_handle_ (*handle_id));
+  mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id));
 
-  if (stack_size > UINT_MAX)
+  stack_size_w32 = (unsigned int) stack_size;
+  if (stack_size != stack_size_w32)
   {
     errno = EINVAL;
-    return 0;
+    return false;
   }
 #endif /* SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT */
-  thr_handle = (uintptr_t) _beginthreadex (NULL,
-                                           (unsigned int) stack_size,
-                                           start_routine,
-                                           arg,
-                                           0,
-                                           NULL);
-  if ((MHD_thread_handle_native_) 0 == (MHD_thread_handle_native_) thr_handle)
-    return 0;
-
-  MHD_thread_handle_ID_set_native_handle_ (handle_id, \
-                                           (MHD_thread_handle_native_) \
-                                           thr_handle);
-
-  return ! 0;
-#endif
+  thr_handle = _beginthreadex (NULL,
+                               stack_size_w32,
+                               start_routine,
+                               arg,
+                               0,
+                               NULL);
+  if ((mhd_thread_handle_native) 0 == (mhd_thread_handle_native) thr_handle)
+    return false;
+
+  mhd_thread_handle_ID_set_native_handle (handle_id, \
+                                          (mhd_thread_handle_native) \
+                                          thr_handle);
+
+  return true;
+#endif /* MHD_USE_W32_THREADS */
 }
 
 
 #ifdef MHD_USE_THREAD_NAME_
 
-#ifndef MHD_USE_THREAD_ATTR_SETNAME
-struct MHD_named_helper_param_
+#  ifndef MHD_USE_THREAD_ATTR_SETNAME
+struct mhd_named_helper_param
 {
   /**
    * Real thread start routine
    */
-  MHD_THREAD_START_ROUTINE_ start_routine;
+  mhd_THREAD_START_ROUTINE start_routine;
 
   /**
    * Argument for thread start routine
@@ -286,18 +288,18 @@ struct MHD_named_helper_param_
 };
 
 
-static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
+static mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
 named_thread_starter (void *data)
 {
-  struct MHD_named_helper_param_ *const param =
-    (struct MHD_named_helper_param_ *) data;
+  struct mhd_named_helper_param *const param =
+    (struct mhd_named_helper_param *) data;
   void *arg;
-  MHD_THREAD_START_ROUTINE_ thr_func;
+  mhd_THREAD_START_ROUTINE thr_func;
 
   if (NULL == data)
-    return (MHD_THRD_RTRN_TYPE_) 0;
+    return (mhd_THRD_RTRN_TYPE) 0;
 
-  MHD_set_cur_thread_name_ (param->name);
+  mhd_set_cur_thread_name (param->name);
 
   arg = param->arg;
   thr_func = param->start_routine;
@@ -307,7 +309,7 @@ named_thread_starter (void *data)
 }
 
 
-#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
+#  endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
 
 
 /**
@@ -320,28 +322,28 @@ named_thread_starter (void *data)
  * @param arg argument  for start_routine
  * @return non-zero on success; zero otherwise (with errno set)
  */
-int
-MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id,
-                          const char *thread_name,
-                          size_t stack_size,
-                          MHD_THREAD_START_ROUTINE_ start_routine,
-                          void *arg)
+bool
+mhd_create_named_thread (mhd_thread_handle_ID *handle_id,
+                         const char *thread_name,
+                         size_t stack_size,
+                         mhd_THREAD_START_ROUTINE start_routine,
+                         void *arg)
 {
-#if defined(MHD_USE_THREAD_ATTR_SETNAME)
+#  if defined(MHD_USE_THREAD_ATTR_SETNAME)
   int res;
   pthread_attr_t attr;
-#if defined(MHD_thread_handle_ID_get_native_handle_ptr_)
+#    if defined(mhd_thread_handle_ID_get_native_handle_ptr)
   pthread_t *const new_tid_ptr =
-    MHD_thread_handle_ID_get_native_handle_ptr_ (handle_id);
-#else  /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */
+    mhd_thread_handle_ID_get_native_handle_ptr (handle_id);
+#    else  /* ! mhd_thread_handle_ID_get_native_handle_ptr */
   pthread_t new_tid;
   pthread_t *const new_tid_ptr = &new_tid;
-#endif /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */
+#    endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */
 
   res = pthread_attr_init (&attr);
   if (0 == res)
   {
-#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD)
+#    if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD)
     /* NetBSD uses 3 arguments: second argument is string in printf-like format,
      *                          third argument is single argument for printf;
      * OSF1 uses 3 arguments too, but last one always must be zero (NULL).
@@ -350,12 +352,12 @@ MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id,
     res = pthread_attr_setname_np (&attr,
                                    thread_name,
                                    0);
-#elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
+#    elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
     res = pthread_attr_setname_np (&attr,
                                    thread_name);
-#else
+#    else
 #error No pthread_attr_setname_np() function.
-#endif
+#    endif
     if ((res == 0) && (0 != stack_size) )
       res = pthread_attr_setstacksize (&attr,
                                        stack_size);
@@ -369,49 +371,49 @@ MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id,
   if (0 != res)
   {
     errno = res;
-    MHD_thread_handle_ID_set_invalid_ (handle_id);
+    mhd_thread_handle_ID_set_invalid (handle_id);
   }
-#if ! defined(MHD_thread_handle_ID_get_native_handle_ptr_)
+#    if ! defined(mhd_thread_handle_ID_get_native_handle_ptr)
   else
-    MHD_thread_handle_ID_set_native_handle_ (handle_id, new_tid);
-#endif /* ! MHD_thread_handle_ID_set_current_thread_ID_ */
+    mhd_thread_handle_ID_set_native_handle (handle_id, new_tid);
+#    endif /* ! mhd_thread_handle_ID_set_current_thread_ID */
 
-  return ! res;
-#else  /* ! MHD_USE_THREAD_ATTR_SETNAME */
-  struct MHD_named_helper_param_ *param;
+  return 0 == res;
+#  else  /* ! MHD_USE_THREAD_ATTR_SETNAME */
+  struct mhd_named_helper_param *param;
 
   if (NULL == thread_name)
   {
     errno = EINVAL;
-    return 0;
+    return false;
   }
 
-  param = malloc (sizeof (struct MHD_named_helper_param_));
+  param = malloc (sizeof (struct mhd_named_helper_param));
   if (NULL == param)
-    return 0;
+    return false;
 
   param->start_routine = start_routine;
   param->arg = arg;
   param->name = thread_name;
 
-  /* Set thread name in thread itself to avoid problems with
-   * threads which terminated before name is set in other thread.
+  /* Set the thread name in the thread itself to avoid problems with
+   * threads which terminated before the name is set in other thread.
    */
-  if (! MHD_create_thread_ (handle_id,
-                            stack_size,
-                            &named_thread_starter,
-                            (void *) param))
+  if (! mhd_create_thread (handle_id,
+                           stack_size,
+                           &named_thread_starter,
+                           (void *) param))
   {
     int err_num;
 
     err_num = errno;
     free (param);
     errno = err_num;
-    return 0;
+    return false;
   }
 
-  return ! 0;
-#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
+  return true;
+#  endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
 }
 
 

+ 545 - 0
src/mhd2/mhd_threads.h

@@ -0,0 +1,545 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_threads.h
+ * @brief  Header for platform-independent threads abstraction
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Provides basic abstraction for threads.
+ * Any functions can be implemented as macro on some platforms
+ * unless explicitly marked otherwise.
+ * Any function argument can be skipped in macro, so avoid
+ * variable modification in function parameters.
+ *
+ * @warning Unlike pthread functions, most of functions return
+ *          nonzero on success.
+ */
+
+#ifndef MHD_THREADS_H
+#define MHD_THREADS_H 1
+
+#include "mhd_sys_options.h"
+
+#if defined(MHD_USE_POSIX_THREADS)
+#  include <pthread.h>
+#  ifndef MHD_USE_THREADS
+#    define MHD_USE_THREADS 1
+#  endif
+#elif defined(MHD_USE_W32_THREADS)
+#  include <windows.h>
+#  ifndef MHD_USE_THREADS
+#    define MHD_USE_THREADS 1
+#  endif
+#else
+#  error No threading API is available.
+#endif
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "sys_thread_entry_type.h"
+
+#if defined(MHD_USE_POSIX_THREADS) && defined(MHD_USE_W32_THREADS)
+#  error Both MHD_USE_POSIX_THREADS and MHD_USE_W32_THREADS are defined
+#endif /* MHD_USE_POSIX_THREADS && MHD_USE_W32_THREADS */
+
+#ifndef MHD_NO_THREAD_NAMES
+#  if defined(MHD_USE_POSIX_THREADS)
+#    if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
+  defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \
+  defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \
+  defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \
+  defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
+  defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
+#      define MHD_USE_THREAD_NAME_
+#    endif /* HAVE_PTHREAD_SETNAME_NP */
+#  elif defined(MHD_USE_W32_THREADS)
+#    ifdef _MSC_FULL_VER
+/* Thread names only available with VC compiler */
+#      define MHD_USE_THREAD_NAME_
+#    endif /* _MSC_FULL_VER */
+#  endif
+#endif
+
+/* ** Thread handle - used to control the thread ** */
+
+#if defined(MHD_USE_POSIX_THREADS)
+/**
+ * The native type to control the thread from other threads
+ */
+typedef pthread_t mhd_thread_handle_native;
+#elif defined(MHD_USE_W32_THREADS)
+/**
+ * The native type to control the thread from other threads
+ */
+typedef HANDLE mhd_thread_handle_native;
+#endif
+
+#if defined(MHD_USE_POSIX_THREADS)
+#  if defined(__gnu_linux__) || \
+  (defined(__linux__) && defined(__GLIBC__))
+/* The next part of code is disabled because it relies on undocumented
+   behaviour (while the thread ID cannot be zero with GNU C Library, it is
+   not specified anywhere officially).
+   It could be enabled for neglectable performance and size improvements. */
+#  if 0 /* Disabled code */
+/**
+ * The native invalid value for native thread handle
+ */
+#    define mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID \
+        ((mhd_thread_handle_native) 0)
+#  endif /* Disabled code */
+#  endif /* __gnu_linux__ || (__linux__ && __GLIBC__) */
+#elif defined(MHD_USE_W32_THREADS)
+/* On W32 the invalid value for thread handle is described directly in
+  the official documentation. */
+/**
+ * The native invalid value for native thread handle
+ */
+#  define mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID \
+        ((mhd_thread_handle_native) NULL)
+#endif /* MHD_USE_W32_THREADS */
+
+#if defined(MHD_USE_POSIX_THREADS)
+/**
+ * Wait until specified thread is ended and free the thread handle on success.
+ * @param native_handle the handle to watch
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_join_thread(native_handle) \
+        (! pthread_join ((native_handle), NULL))
+#elif defined(MHD_USE_W32_THREADS)
+/**
+ * Wait until specified thread is ended and the free thread handle on success.
+ * @param native_handle the handle to watch
+ * @return nonzero on success, zero otherwise
+ */
+#  define mhd_join_thread(native_handle) \
+        ( (WAIT_OBJECT_0 == WaitForSingleObject ( (native_handle), INFINITE)) ? \
+          (CloseHandle ( (native_handle)), ! 0) : 0)
+#endif
+
+#if ! defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID)
+/**
+ * Structure with thread handle and validity flag
+ */
+struct mhd_thread_handle_struct
+{
+  bool valid;                       /**< true if native handle is set */
+  mhd_thread_handle_native native; /**< the native thread handle */
+};
+/**
+ * Type with thread handle that can be set to invalid value
+ */
+typedef struct mhd_thread_handle_struct mhd_thread_handle;
+
+/**
+ * Set variable pointed by @a handle_ptr to invalid (unset) value
+ */
+#  define mhd_thread_handle_set_invalid(handle_ptr) \
+        ((handle_ptr)->valid = false)
+/**
+ * Set the native handle in the variable pointed by the @a handle_ptr
+ * to the @a native_val value
+ */
+#  define mhd_thread_handle_set_native(handle_ptr,native_val) \
+        ((handle_ptr)->valid = true, (handle_ptr)->native = native_val)
+/**
+ * Check whether the native handle value is set in the @a handle_var variable
+ */
+#  define mhd_thread_handle_is_valid(handle_var) \
+        ((handle_var).valid)
+/**
+ * Get the native handle value from the @a handle_var variable
+ */
+#  define mhd_thread_handle_get_native(handle_var) \
+        ((handle_var).native)
+#else  /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */
+/**
+ * Type with thread handle that can be set to invalid value
+ */
+typedef mhd_thread_handle_native mhd_thread_handle;
+
+/**
+ * Set the variable pointed by the @a handle_ptr to the invalid (unset) value
+ */
+#  define mhd_thread_handle_set_invalid(handle_ptr) \
+        ((*(handle_ptr)) = mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID)
+/**
+ * Set the native handle in the variable pointed by the @a handle_ptr
+ * to the @a native_val value
+ */
+#  define mhd_thread_handle_set_native(handle_ptr,native_val) \
+        ((*(handle_ptr)) = (native_val))
+/**
+ * Check whether the native handle value is set in the @a handle_var variable
+ */
+#  define mhd_thread_handle_is_valid(handle_var) \
+        (mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID != (handle_var))
+/**
+ * Get the native handle value from the @a handle_var variable
+ */
+#  define mhd_thread_handle_get_native(handle_var) \
+        (handle_var)
+/**
+ * Get the pointer to the native handle stored the variable pointed by
+ * the @a handle_ptr pointer
+ * @note This macro could be not available if direct manipulation of
+ *       the native handle is not possible
+ */
+#  define mhd_thread_handle_get_native_ptr(handle_ptr) \
+        (handle_ptr)
+#endif /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */
+
+
+/* ** Thread ID - used to check threads match ** */
+
+#if defined(MHD_USE_POSIX_THREADS)
+/**
+ * The native type used to check whether the current thread matches
+ * the expected thread
+ */
+typedef pthread_t mhd_thread_ID_native;
+
+/**
+ * Function to get the current thread native ID.
+ */
+#  define mhd_thread_ID_native_current pthread_self
+
+/**
+ * Check whether two native thread IDs are equal.
+ * @return non-zero if equal, zero if not equal
+ */
+#  define mhd_thread_ID_native_equal(id1,id2) \
+        (pthread_equal ((id1),(id2)))
+#elif defined(MHD_USE_W32_THREADS)
+/**
+ * The native type used to check whether the current thread matches
+ * the expected thread
+ */
+typedef DWORD mhd_thread_ID_native;
+
+/**
+ * Function to get the current thread native ID.
+ */
+#  define mhd_thread_ID_native_current GetCurrentThreadId
+
+/**
+ * Check whether two native thread IDs are equal.
+ * @return non-zero if equal, zero if not equal
+ */
+#  define mhd_thread_ID_native_equal(id1,id2) \
+        ((id1) == (id2))
+#endif
+
+/**
+ * Check whether the specified thread ID matches current thread.
+ * @param id the thread ID to match
+ * @return nonzero on match, zero otherwise
+ */
+#define mhd_thread_ID_native_is_current_thread(id) \
+        mhd_thread_ID_native_equal ((id), mhd_thread_ID_native_current ())
+
+
+#if defined(MHD_USE_POSIX_THREADS)
+#  if defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID)
+/**
+ * The native invalid value for native thread ID
+ */
+#    define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \
+        mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID
+#  endif /* mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID */
+#elif defined(MHD_USE_W32_THREADS)
+/**
+ * The native invalid value for native thread ID
+ */
+ #  define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \
+         ((mhd_thread_ID_native) 0)
+#endif /* MHD_USE_W32_THREADS */
+
+#if ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
+/**
+ * Structure with thread id and validity flag
+ */
+struct mhd_thread_ID_struct
+{
+  bool valid;                   /**< true if native ID is set */
+  mhd_thread_ID_native native; /**< the native thread ID */
+};
+/**
+ * Type with thread ID that can be set to the invalid value
+ */
+typedef struct mhd_thread_ID_struct mhd_thread_ID;
+
+/**
+ * Set variable pointed by the @a ID_ptr to the invalid (unset) value
+ */
+#  define mhd_thread_ID_set_invalid(ID_ptr) \
+        ((ID_ptr)->valid = false)
+/**
+ * Set the native ID in the variable pointed by the @a ID_ptr
+ * to the @a native_val value
+ */
+#  define mhd_thread_ID_set_native(ID_ptr,native_val) \
+        ((ID_ptr)->valid = true, (ID_ptr)->native = (native_val))
+/**
+ * Check whether the native ID value is set in the @a ID_var variable
+ */
+#  define mhd_thread_ID_is_valid(ID_var) \
+        ((ID_var).valid)
+/**
+ * Get the native ID value from the @a ID_var variable
+ */
+#  define mhd_thread_ID_get_native(ID_var) \
+        ((ID_var).native)
+/**
+ * Check whether the @a ID_var variable is equal current thread
+ */
+#  define mhd_thread_ID_is_current_thread(ID_var) \
+        (mhd_thread_ID_is_valid (ID_var) && \
+         mhd_thread_ID_native_is_current_thread ((ID_var).native))
+#else  /* MHD_THREAD_ID_NATIVE_INVALID_ */
+/**
+ * Type with thread ID that can be set to the invalid value
+ */
+typedef mhd_thread_ID_native mhd_thread_ID;
+
+/**
+ * Set variable pointed by the @a ID_ptr to the invalid (unset) value
+ */
+#  define mhd_thread_ID_set_invalid(ID_ptr) \
+        ((*(ID_ptr)) = MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
+/**
+ * Set the native ID in the variable pointed by the @a ID_ptr
+ * to the @a native_val value
+ */
+#  define mhd_thread_ID_set_native(ID_ptr,native_val) \
+        ((*(ID_ptr)) = (native_val))
+/**
+ * Check whether the native ID value is set in the @a ID_var variable
+ */
+#  define mhd_thread_ID_is_valid(ID_var) \
+        (MHD_THREAD_ID_NATIVE_VALUE_INVALID_ != (ID_var))
+/**
+ * Get the native ID value from the @a ID_var variable
+ */
+#  define mhd_thread_ID_get_native(ID_var) \
+        (ID_var)
+/**
+ * Check whether the @a ID_var variable is equal current thread
+ */
+#  define mhd_thread_ID_is_current_thread(ID_var) \
+        mhd_thread_ID_native_is_current_thread (ID_var)
+#endif /* MHD_THREAD_ID_NATIVE_INVALID_ */
+
+/**
+ * Set current thread ID in the variable pointed by the @a ID_ptr
+ */
+#  define mhd_thread_ID_set_current_thread(ID_ptr) \
+        mhd_thread_ID_set_native ((ID_ptr),mhd_thread_ID_native_current ())
+
+
+#if defined(MHD_USE_POSIX_THREADS)
+#  if defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \
+  ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
+#    error \
+  MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined
+#  elif ! defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \
+  defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
+#    error \
+  MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined
+#  endif
+#endif /* MHD_USE_POSIX_THREADS */
+
+/* When staring a new thread, the kernel (and thread implementation) may
+ * pause the calling (initial) thread and start the new thread.
+ * If thread identifier is assigned to variable in the initial thread then
+ * the value of the identifier variable will be undefined in the new thread
+ * until the initial thread continue processing.
+ * However, it is also possible that the new thread created, but not executed
+ * for some time while the initial thread continue execution. In this case any
+ * variable assigned in the new thread will be undefined for some time until
+ * they really processed by the new thread.
+ * To avoid data races, a special structure mhd_thread_handle_ID is used.
+ * The "handle" is assigned by calling (initial) thread and should be always
+ * defined when checked in the initial thread.
+ * The "ID" is assigned by the new thread and should be always defined when
+ * checked inside the new thread.
+ */
+/* Depending on implementation, pthread_create() MAY set thread ID into
+ * provided pointer and after it start thread OR start thread and after
+ * it set thread ID. In the latter case, to avoid data races, additional
+ * pthread_self() call is required in thread routine. If some platform
+ * is known for setting thread ID BEFORE starting thread macro
+ * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined
+ * to save some resources. */
+
+/* #define MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 1 */
+
+/* * handle - must be valid when other thread knows that particular thread
+     is started.
+   * ID     - must be valid when code is executed inside thread */
+#if defined(MHD_USE_POSIX_THREADS) && \
+  defined(MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD) && \
+  defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \
+  defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) && \
+  defined(mhd_thread_handle_get_native_ptr)
+union mhd_thread_handle_ID_
+{
+  mhd_thread_handle handle;    /**< To be used in other threads */
+  mhd_thread_ID ID;            /**< To be used in the thread itself */
+};
+typedef union mhd_thread_handle_ID_ mhd_thread_handle_ID;
+#  define MHD_THREAD_HANDLE_ID_IS_UNION 1
+#else  /* !MHD_USE_POSIX_THREADS
+          || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD
+          || !mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID
+          || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_
+          || !mhd_thread_handle_get_native_ptr */
+struct mhd_thread_handle_ID_
+{
+  mhd_thread_handle handle;    /**< To be used in other threads */
+  mhd_thread_ID ID;            /**< To be used in the thread itself */
+};
+typedef struct mhd_thread_handle_ID_ mhd_thread_handle_ID;
+#endif /* !MHD_USE_POSIX_THREADS
+          || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD
+          || !mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID
+          || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_
+          || !mhd_thread_handle_get_native_ptr */
+
+/**
+ * Set the mhd_thread_handle_ID to the invalid value
+ */
+#define mhd_thread_handle_ID_set_invalid(hndl_id_ptr) \
+        (mhd_thread_handle_set_invalid (&((hndl_id_ptr)->handle)), \
+         mhd_thread_ID_set_invalid (&((hndl_id_ptr)->ID)))
+
+/**
+ * Check whether the thread handle is valid.
+ * To be used in threads other then the thread specified by the @a hndl_id.
+ */
+#define mhd_thread_handle_ID_is_valid_handle(hndl_id) \
+        mhd_thread_handle_is_valid ((hndl_id).handle)
+
+/**
+ * Set the native handle in the variable pointed by the @a hndl_id_ptr
+ * to the @a native_val value
+ */
+#define mhd_thread_handle_ID_set_native_handle(hndl_id_ptr,native_val) \
+        mhd_thread_handle_set_native (&((hndl_id_ptr)->handle),native_val)
+
+#if defined(mhd_thread_handle_get_native_ptr)
+/**
+ * Get the pointer to the native handle stored the variable pointed by
+ * the @a hndl_id_ptr
+ * @note This macro could not available if direct manipulation of
+ *       the native handle is not possible
+ */
+#  define mhd_thread_handle_ID_get_native_handle_ptr(hndl_id_ptr) \
+        mhd_thread_handle_get_native_ptr (&((hndl_id_ptr)->handle))
+#endif /* mhd_thread_handle_get_native_ptr */
+
+/**
+ * Get the native thread handle from the mhd_thread_handle_ID variable.
+ */
+#define mhd_thread_handle_ID_get_native_handle(hndl_id) \
+        mhd_thread_handle_get_native ((hndl_id).handle)
+
+/**
+ * Check whether the thread ID is valid.
+ * To be used in the thread itself.
+ */
+#define mhd_thread_handle_ID_is_valid_ID(hndl_id) \
+        mhd_thread_ID_is_valid ((hndl_id).ID)
+
+#if defined(MHD_THREAD_HANDLE_ID_IS_UNION)
+#  if defined(MHD_USE_W32_THREADS)
+#    error mhd_thread_handle_ID cannot be a union with W32 threads
+#  endif /* MHD_USE_W32_THREADS */
+/**
+ * Set current thread ID in the variable pointed by the @a hndl_id_ptr
+ */
+#  define mhd_thread_handle_ID_set_current_thread_ID(hndl_id_ptr) (void) 0
+#else  /* ! MHD_THREAD_HANDLE_ID_IS_UNION */
+/**
+ * Set current thread ID in the variable pointed by the @a hndl_id_ptr
+ */
+#  define mhd_thread_handle_ID_set_current_thread_ID(hndl_id_ptr) \
+        mhd_thread_ID_set_current_thread (&((hndl_id_ptr)->ID))
+#endif /* ! MHD_THREAD_HANDLE_ID_IS_UNION */
+
+/**
+ * Check whether provided thread ID matches current thread.
+ * @param ID thread ID to match
+ * @return nonzero on match, zero otherwise
+ */
+#define mhd_thread_handle_ID_is_current_thread(hndl_id) \
+        mhd_thread_ID_is_current_thread ((hndl_id).ID)
+
+/**
+ * Wait until specified thread is ended and free thread handle on success.
+ * @param hndl_id_ handle with ID to watch
+ * @return nonzero on success, zero otherwise
+ */
+#define mhd_thread_handle_ID_join_thread(hndl_id) \
+        mhd_join_thread (mhd_thread_handle_ID_get_native_handle (hndl_id))
+
+
+/**
+ * Create a thread and set the attributes according to our options.
+ *
+ * If thread is created, thread handle must be freed by mhd_join_thread().
+ *
+ * @param handle_id     handle to initialise
+ * @param stack_size    size of stack for new thread, 0 for default
+ * @param start_routine main function of thread
+ * @param arg argument  for start_routine
+ * @return non-zero on success; zero otherwise (with errno set)
+ */
+bool
+mhd_create_thread (mhd_thread_handle_ID *handle_id,
+                   size_t stack_size,
+                   mhd_THREAD_START_ROUTINE start_routine,
+                   void *arg);
+
+#ifndef MHD_USE_THREAD_NAME_
+#define mhd_create_named_thread(t,n,s,r,a) mhd_create_thread ((t),(s),(r),(a))
+#else  /* MHD_USE_THREAD_NAME_ */
+/**
+ * Create a named thread and set the attributes according to our options.
+ *
+ * @param handle_id     handle to initialise
+ * @param thread_name   name for new thread
+ * @param stack_size    size of stack for new thread, 0 for default
+ * @param start_routine main function of thread
+ * @param arg argument  for start_routine
+ * @return non-zero on success; zero otherwise
+ */
+int
+mhd_create_named_thread (mhd_thread_handle_ID *handle_id,
+                         const char *thread_name,
+                         size_t stack_size,
+                         mhd_THREAD_START_ROUTINE start_routine,
+                         void *arg);
+
+#endif /* MHD_USE_THREAD_NAME_ */
+
+#endif /* ! MHD_THREADS_H */

+ 83 - 0
src/mhd2/mhd_tristate.h

@@ -0,0 +1,83 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_tristate.h
+ * @brief  The definition of the tristate type and helper macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_TRISTATE_H
+#define MHD_TRISTATE_H 1
+
+#include "mhd_sys_options.h"
+
+/**
+ * Enum with three values / states
+ */
+enum MHD_FIXED_ENUM_ mhd_Tristate
+{
+  /**
+   * Definitely no / negative / false
+   */
+  mhd_T_NO = 0
+  ,
+  /**
+   * Definitely yes / positive / true
+   */
+  mhd_T_YES = 1
+  ,
+  /**
+   * Undetermined / not known / maybe yes-maybe no
+   */
+  mhd_T_MAYBE = -1
+};
+
+/**
+ * Check whether tristate value is mhd_T_YES
+ */
+#define mhd_T_IS_YES(v) (mhd_T_NO < (v))
+
+/**
+ * Check whether tristate value is mhd_T_NO
+ */
+#define mhd_T_IS_NO(v) (mhd_T_NO == (v))
+
+/**
+ * Check whether tristate value is mhd_T_MAYBE
+ */
+#define mhd_T_IS_MAYBE(v) (mhd_T_NO > (v))
+
+/**
+ * Check whether tristate value is NOT mhd_T_YES
+ */
+#define mhd_T_IS_NOT_YES(v) (mhd_T_NO >= (v))
+
+/**
+ * Check whether tristate value is NOT mhd_T_NO
+ */
+#define mhd_T_IS_NOT_NO(v) (mhd_T_NO != (v))
+
+/**
+ * Check whether tristate value is NOT mhd_T_MAYBE
+ */
+#define mhd_T_IS_NOT_MAYBE(v) (mhd_T_NO <= (v))
+
+#endif /* ! MHD_TRISTATE_H */

+ 124 - 0
src/mhd2/response_add_header.c

@@ -0,0 +1,124 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2021-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/response_add_header.c
+ * @brief  The definitions of MHD_response_add_*header() functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_response.h"
+#include "mhd_locks.h"
+
+#include <string.h>
+
+#include "mhd_public_api.h"
+
+
+static enum MHD_StatusCode
+response_add_header_int (struct MHD_Response *response,
+                         const char *name,
+                         const char *value)
+{
+  const size_t name_len = strlen (name);
+  const size_t value_len = strlen (name);
+  char *buf;
+  struct mhd_ResponseHeader *new_hdr;
+
+  if (response->frozen) /* Re-check with the lock held */
+    return MHD_SC_TOO_LATE;
+
+  if ((NULL != memchr (name, ' ', name_len)) ||
+      (NULL != memchr (name, '\t', name_len)) ||
+      (NULL != memchr (name, ':', name_len)) ||
+      (NULL != memchr (name, '\n', name_len)) ||
+      (NULL != memchr (name, '\r', name_len)))
+    return MHD_SC_RESP_HEADER_NAME_INVALID;
+  if ((NULL != memchr (value, '\n', value_len)) ||
+      (NULL != memchr (value, '\r', value_len)))
+    return MHD_SC_RESP_HEADER_VALUE_INVALID;
+
+  buf = malloc (sizeof(struct mhd_ResponseHeader) + name_len + value_len + 2);
+  if (NULL == buf)
+    return MHD_SC_RESPONSE_HEADER_MALLOC_FAILED;
+
+  new_hdr = (struct mhd_ResponseHeader *) buf;
+  buf += sizeof(struct mhd_ResponseHeader);
+  memcpy (buf, name, name_len + 1);
+  new_hdr->name.cstr = buf;
+  new_hdr->name.len = name_len;
+  buf += name_len + 1;
+  memcpy (buf, value, value_len + 1);
+  new_hdr->value.cstr = buf;
+  new_hdr->value.len = value_len;
+
+  mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
+
+  return MHD_SC_OK;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode
+MHD_response_add_header (struct MHD_Response *response,
+                         const char *name,
+                         const char *value)
+{
+  bool need_lock;
+  enum MHD_StatusCode res;
+
+  if (response->frozen)
+    return MHD_SC_TOO_LATE;
+
+  if (response->reuse.reusable)
+  {
+    need_lock = true;
+    if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
+      return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
+    mhd_assert (1 == response->reuse.counter);
+  }
+  else
+    need_lock = false;
+
+  // TODO: add special processing for "Date", "Connection", "Content-Length"
+
+  res = response_add_header_int (response, name, value);
+
+  if (need_lock)
+    mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
+
+  return res;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode
+MHD_response_add_predef_header (struct MHD_Response *response,
+                                enum MHD_PredefinedHeader stk,
+                                const char *content)
+{
+  (void) response; (void) stk; (void) content;
+  return MHD_SC_FEATURE_DISABLED;
+}

+ 368 - 0
src/mhd2/response_from.c

@@ -0,0 +1,368 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2021-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/response_from.c
+ * @brief  The definitions of MHD_response_from_X() functions and related
+ *         internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "response_from.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "compat_calloc.h"
+#include "sys_malloc.h"
+
+#include "mhd_public_api.h"
+
+#include "mhd_locks.h"
+#include "mhd_response.h"
+#include "response_options.h"
+
+#include "mhd_assert.h"
+
+#include <limits.h>
+
+#ifndef ULONG_MAX
+#  define ULONG_MAX ((unsigned long) ~((unsigned long) 0))
+#endif
+
+#ifndef SIZE_MAX
+#  define ULONG_MAX ((unsigned long) ~((unsigned long) 0))
+#endif
+
+static struct MHD_Response *
+response_create_basic (enum MHD_HTTP_StatusCode sc,
+                       uint_fast64_t cntn_size,
+                       MHD_FreeCallback free_cb,
+                       void *free_cb_cls)
+{
+  struct MHD_Response *restrict r;
+  struct ResponseOptions *restrict s;
+
+  r = mhd_calloc (1, sizeof(struct MHD_Response));
+  if (NULL != r)
+  {
+    s = mhd_calloc (1, sizeof(struct ResponseOptions));
+    if (NULL != s)
+    {
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+      mhd_DLINKEDL_INIT_LIST (r, headers);
+
+      s->termination_callback.v_term_cb = NULL;
+      s->termination_callback.v_term_cb_cls = NULL;
+#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
+
+      r->sc = sc;
+      r->size = cntn_size;
+      r->free.cb = free_cb;
+      r->free.cls = free_cb_cls;
+      r->settings = s;
+
+      return r; /* Success exit point */
+    }
+    free (r);
+  }
+  return NULL; /* Failure exit point */
+}
+
+
+static void
+response_destroy_unfrozen (struct MHD_Response *restrict r)
+{
+  mhd_assert (NULL != r->settings);
+  mhd_assert (! r->frozen);
+
+  // TODO: remove headers
+
+  if (mhd_RESPONSE_CONTENT_INVALID != r->cntn_type)
+  { /* Free callback has been set correctly already */
+    if (mhd_RESPONSE_CONTENT_IOVEC == r->cntn_type)
+      free (r->cntn.iovec.iov);
+    else if (mhd_RESPONSE_CONTENT_FILE == r->cntn_type)
+      close (r->cntn.file.fd);
+    /* For #mhd_RESPONSE_CONTENT_IOVEC clean-up performed by callback
+       for both modes: internal copy and external cleanup */
+    if (NULL != r->free.cb)
+      r->free.cb (r->free.cls);
+  }
+  free (r->settings);
+  free (r);
+}
+
+
+MHD_INTERNAL void
+mhd_response_dec_use_count (struct MHD_Response *restrict response)
+{
+  (void) response;
+  // TODO: implement
+  return;
+}
+
+
+MHD_EXTERN_ struct MHD_Response *
+MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
+                            uint_fast64_t size,
+                            MHD_DynamicContentCreator dyn_cont,
+                            void *dyn_cont_cls,
+                            MHD_FreeCallback dyn_cont_fc)
+{
+  struct MHD_Response *restrict res;
+  res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls);
+  if (NULL != res)
+  {
+    res->cntn_type = mhd_RESPONSE_CONTENT_CALLBACK;
+    res->cntn.dyn.cb = dyn_cont;
+    res->cntn.dyn.cls = dyn_cont_cls;
+  }
+  return res;
+}
+
+
+static const unsigned char empty_buf[1] = { 0 };
+
+MHD_EXTERN_
+MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response *
+MHD_response_from_buffer (
+  enum MHD_HTTP_StatusCode sc,
+  size_t buffer_size,
+  const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)],
+  MHD_FreeCallback free_cb,
+  void *free_cb_cls)
+{
+  struct MHD_Response *restrict res;
+
+  if (MHD_SIZE_UNKNOWN == buffer_size)
+    return NULL;
+
+  res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls);
+  if (NULL != res)
+  {
+    res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER;
+    res->cntn.buf = (0 != buffer_size) ?
+                    (const unsigned char *) buffer : empty_buf;
+  }
+  return res;
+}
+
+
+static void
+response_cntn_free_buf (void *ptr)
+{
+  free (ptr);
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response *
+MHD_response_from_buffer_copy (
+  enum MHD_HTTP_StatusCode sc,
+  size_t buffer_size,
+  const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)])
+{
+  struct MHD_Response *restrict res;
+  const unsigned char *buf_copy;
+
+  if (MHD_SIZE_UNKNOWN == buffer_size)
+    return NULL;
+
+  if (0 != buffer_size)
+  {
+    unsigned char *new_buf;
+    new_buf = (unsigned char *) malloc (buffer_size);
+    if (NULL == new_buf)
+      return NULL;
+    memcpy (new_buf, buffer, buffer_size);
+    res = response_create_basic (sc, buffer_size,
+                                 response_cntn_free_buf, new_buf);
+    buf_copy = new_buf;
+  }
+  else
+  {
+    buf_copy = empty_buf;
+    res = response_create_basic (sc, 0, NULL, NULL);
+  }
+
+  if (NULL != res)
+  {
+    res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER;
+    res->cntn.buf = buf_copy;
+  }
+  return res;
+}
+
+
+MHD_EXTERN_ struct MHD_Response *
+MHD_response_from_iovec (
+  enum MHD_HTTP_StatusCode sc,
+  unsigned int iov_count,
+  const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)],
+  MHD_FreeCallback free_cb,
+  void *free_cb_cls)
+{
+  unsigned int i;
+  size_t i_cp = 0;   /**< Index in the copy of iov */
+  uint_fast64_t total_size = 0;
+
+#if 0
+  response = mhd_calloc (1, sizeof (struct MHD_Response));
+  if (NULL == response)
+    return NULL;
+  if (! MHD_mutex_init_ (&response->mutex))
+  {
+    free (response);
+    return NULL;
+  }
+#endif
+  /* Calculate final size, number of valid elements, and check 'iov' */
+  for (i = 0; i < iov_count; ++i)
+  {
+    if (0 == iov[i].iov_len)
+      continue;     /* skip zero-sized elements */
+    if (NULL == iov[i].iov_base)
+      return NULL;  /* NULL pointer with non-zero size */
+
+    total_size += iov[i].iov_len;
+    if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size)
+        || (((size_t) total_size) != total_size))
+      return NULL; /* Larger than send function may report as success */
+#if defined(MHD_POSIX_SOCKETS) || ! defined(_WIN64)
+    i_cp++;
+#else  /* ! MHD_POSIX_SOCKETS && _WIN64 */
+    if (1)
+    {
+      size_t i_add;
+
+      i_add = (size_t) (iov[i].iov_len / MHD_IOV_ELMN_MAX_SIZE);
+      if (0 != iov[i].iov_len % MHD_IOV_ELMN_MAX_SIZE)
+        i_add++;
+      i_cp += i_add;
+      if (i_cp < i_add)
+        return NULL; /* Counter overflow */
+    }
+#endif /* ! MHD_POSIX_SOCKETS && _WIN64 */
+  }
+  if (0 == total_size)
+  {
+    struct MHD_Response *restrict res;
+
+    res = response_create_basic (sc, 0, free_cb, free_cb_cls);
+    if (NULL != res)
+    {
+      res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER;
+      res->cntn.buf = empty_buf;
+    }
+    return res;
+  }
+  if (MHD_SIZE_UNKNOWN == total_size)
+    return NULL;
+
+  mhd_assert (0 < i_cp);
+  if (1)
+  { /* for local variables local scope only */
+    struct MHD_Response *restrict res;
+    mhd_iovec *iov_copy;
+    size_t num_copy_elements = i_cp;
+
+    iov_copy = mhd_calloc (num_copy_elements, sizeof(mhd_iovec));
+    if (NULL == iov_copy)
+      return NULL;
+
+    i_cp = 0;
+    for (i = 0; i < iov_count; ++i)
+    {
+      size_t element_size = iov[i].iov_len;
+      const unsigned char *buf = (const unsigned char *) iov[i].iov_base;
+
+      if (0 == element_size)
+        continue;         /* skip zero-sized elements */
+#if defined(MHD_WINSOCK_SOCKETS) && defined(_WIN64)
+      while (MHD_IOV_ELMN_MAX_SIZE < element_size)
+      {
+        iov_copy[i_cp].iov_base = (char *) mhd_DROP_CONST (buf);
+        iov_copy[i_cp].iov_len = MHD_IOV_ELMN_MAX_SIZE;
+        buf += MHD_IOV_ELMN_MAX_SIZE;
+        element_size -= MHD_IOV_ELMN_MAX_SIZE;
+        i_cp++;
+      }
+#endif /* MHD_WINSOCK_SOCKETS && _WIN64 */
+      iov_copy[i_cp].iov_base = mhd_DROP_CONST (buf);
+      iov_copy[i_cp].iov_len = (mhd_iov_size) element_size;
+      i_cp++;
+    }
+    mhd_assert (num_copy_elements == i_cp);
+    mhd_assert (0 < i_cp);
+
+    res = response_create_basic (sc, total_size, free_cb, free_cb_cls);
+    if (NULL != res)
+    {
+      res->cntn_type = mhd_RESPONSE_CONTENT_IOVEC;
+      res->cntn.iovec.iov = iov_copy;
+      res->cntn.iovec.cnt = i_cp;
+      return res;
+    }
+    free (iov_copy);
+  }
+  return NULL;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
+MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
+                      int fd,
+                      uint_fast64_t offset,
+                      uint_fast64_t size)
+{
+  struct MHD_Response *restrict res;
+  res = response_create_basic (sc, size, NULL, NULL);
+  if (NULL != res)
+  {
+    res->cntn_type = mhd_RESPONSE_CONTENT_FILE;
+    res->cntn.file.fd = fd;
+    res->cntn.file.offset = offset;
+    res->cntn.file.is_pipe = false; /* Not necessary */
+  }
+  return res;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
+MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
+                        int fd)
+{
+  struct MHD_Response *restrict res;
+  res = response_create_basic (sc, MHD_SIZE_UNKNOWN, NULL, NULL);
+  if (NULL != res)
+  {
+    res->cntn_type = mhd_RESPONSE_CONTENT_FILE;
+    res->cntn.file.fd = fd;
+    res->cntn.file.offset = 0; /* Not necessary */
+    res->cntn.file.is_pipe = true;
+  }
+  return res;
+
+}

+ 36 - 0
src/mhd2/response_from.h

@@ -0,0 +1,36 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/response_from.h
+ * @brief  The declarations of internal functions for response creation and
+ *         deletion
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_FROM_H
+#define MHD_RESPONSE_FROM_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+
+#endif /* ! MHD_RESPONSE_FROM_H */

+ 85 - 0
src/mhd2/response_funcs.c

@@ -0,0 +1,85 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/response_funcs.с
+ * @brief  The definition of the internal response helper functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_null_macro.h"
+#include "mhd_response.h"
+#include "response_funcs.h"
+#include "mhd_locks.h"
+
+static void
+response_set_properties (struct MHD_Response *restrict r)
+{
+  struct ResponseOptions *restrict const s = r->settings;
+  mhd_assert (NULL != s);
+  r->frozen = true;
+
+  r->cfg.head_only = s->head_only_response;
+  if (s->http_1_0_compatible_strict)
+  {
+    r->cfg.conn_close = true;
+    r->cfg.chunked = false;
+  }
+  else
+  {
+    r->cfg.conn_close = s->conn_close;
+    r->cfg.chunked = s->chunked_enc || (MHD_SIZE_UNKNOWN == r->size);
+  }
+  r->cfg.mode_1_0 = s->http_1_0_server;
+  r->cfg.cnt_len_by_app = s->insanity_header_content_length; // TODO: set only if "content-lengh" header is used
+
+  r->settings = NULL;
+}
+
+
+/**
+ * Check whether response is "frozen" (modifications blocked) and "freeze"
+ * it if it was not frozen before
+ * @param response the response to manipulate
+ */
+MHD_INTERNAL void
+mhd_response_check_frozen_freeze (struct MHD_Response *restrict response)
+{
+  bool need_lock;
+  if (response->frozen)
+    return;
+
+  if (response->reuse.reusable)
+  {
+    need_lock = true;
+    mhd_mutex_lock_chk (&(response->reuse.settings_lock));
+    mhd_assert (1 == response->reuse.counter);
+  }
+  else
+    need_lock = false;
+
+  if (! response->frozen)/* Re-check under the lock */
+    response_set_properties (response);
+
+  if (need_lock)
+    mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
+}

+ 51 - 0
src/mhd2/response_funcs.h

@@ -0,0 +1,51 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/response_funcs.h
+ * @brief  The declarations of the internal response helper functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_FUNCS_H
+#define MHD_RESPONSE_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Check whether response is "frozen" (modifications blocked) and "freeze"
+ * it if not frozen before
+ * @param response the response to manipulate
+ */
+MHD_INTERNAL void
+mhd_response_check_frozen_freeze (struct MHD_Response *restrict response);
+
+/**
+ * Free/destroy non-reusable response, decrement use count for reusable
+ * response and free/destroy if it is not used any more.
+ * @param response the response to manipulate
+ */
+MHD_INTERNAL void
+mhd_response_dec_use_count (struct MHD_Response *restrict response);
+
+
+#endif /* ! MHD_RESPONSE_FUNCS_H */

+ 3 - 2
src/mhd2/response_options.h

@@ -16,11 +16,12 @@
 #include "mhd_public_api.h"
 
 
-struct ResponseOptions {
+struct ResponseOptions
+{
   /**
    * Value for #MHD_R_O_REUSABLE.
    */
-  enum MHD_Bool reusable;
+  enum MHD_Bool reusable; /* Not used in this struct. FIXME: remove? */
 
 
   /**

+ 82 - 18
src/mhd2/response_set_options.c

@@ -11,51 +11,86 @@
 
 #include "mhd_sys_options.h"
 #include "sys_base_types.h"
-#include "mhd_response.h"
 #include "response_options.h"
+#include "mhd_response.h"
 #include "mhd_public_api.h"
+#include "sys_bool_type.h"
+#include "mhd_locks.h"
 
 
-MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_ enum MHD_StatusCode
-MHD_response_set_options (struct MHD_Response *response,
-                          const struct MHD_ResponseOptionAndValue *options,
-                          size_t options_max_num)
+/**
+ * Internal version of the #MHD_response_set_options()
+ * Assuming that settings lock is held by the MHD_response_set_options()
+ * @param response the response to change
+ * @param options the options to use
+ * @param options_max_num the maximum number of @a options to use
+ * @return #MHD_SC_OK on success, error code otherwise
+ */
+static enum MHD_StatusCode
+response_set_options_int (
+  struct MHD_Response *volatile response,
+  const struct MHD_ResponseOptionAndValue *volatile options,
+  size_t options_max_num)
 {
+  struct ResponseOptions *volatile settings;
   size_t i;
 
   if (response->frozen)
-    return MHD_SC_TOO_LATE;
+    return MHD_SC_TOO_LATE; /* Re-check under the lock (if any) */
 
-  for (i=0;i<options_max_num;i++)
+  settings = response->settings;
+
+  for (i = 0; i < options_max_num; i++)
   {
-    const struct MHD_ResponseOptionAndValue *const option = options + i;
-    switch (option->opt) {
+    const struct MHD_ResponseOptionAndValue *const volatile option =
+      options + i;
+
+    switch (option->opt)
+    {
     case MHD_R_O_END:
       return MHD_SC_OK;
     case MHD_R_O_REUSABLE:
-      response->settings.reusable = option->val.reusable;
+      if (response->reuse.reusable && !option->val.reusable)
+        return MHD_SC_RESPONSE_CANNOT_CLEAR_REUSE;
+      else if (response->reuse.reusable)
+        continue; /* This flag has been set before */
+      else
+      {
+        if (mhd_mutex_init(&(response->reuse.settings_lock)))
+        {
+          if (mhd_mutex_init_short(&(response->reuse.cnt_lock)))
+          {
+            response->reuse.counter = 1;
+            response->reuse.reusable = true;
+            continue;
+          }
+          mhd_mutex_destroy_chk(&(response->reuse.settings_lock));
+        }
+        return MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
+      }
+      settings->reusable = option->val.reusable;
       continue;
     case MHD_R_O_HEAD_ONLY_RESPONSE:
-      response->settings.head_only_response = option->val.head_only_response;
+      settings->head_only_response = option->val.head_only_response;
       continue;
     case MHD_R_O_CHUNKED_ENC:
-      response->settings.chunked_enc = option->val.chunked_enc;
+      settings->chunked_enc = option->val.chunked_enc;
       continue;
     case MHD_R_O_CONN_CLOSE:
-      response->settings.conn_close = option->val.conn_close;
+      settings->conn_close = option->val.conn_close;
       continue;
     case MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT:
-      response->settings.http_1_0_compatible_strict = option->val.http_1_0_compatible_strict;
+      settings->http_1_0_compatible_strict = option->val.http_1_0_compatible_strict;
       continue;
     case MHD_R_O_HTTP_1_0_SERVER:
-      response->settings.http_1_0_server = option->val.http_1_0_server;
+      settings->http_1_0_server = option->val.http_1_0_server;
       continue;
     case MHD_R_O_INSANITY_HEADER_CONTENT_LENGTH:
-      response->settings.insanity_header_content_length = option->val.insanity_header_content_length;
+      settings->insanity_header_content_length = option->val.insanity_header_content_length;
       continue;
     case MHD_R_O_TERMINATION_CALLBACK:
-      response->settings.termination_callback.v_term_cb = option->val.termination_callback.v_term_cb;
-      response->settings.termination_callback.v_term_cb_cls = option->val.termination_callback.v_term_cb_cls;
+      settings->termination_callback.v_term_cb = option->val.termination_callback.v_term_cb;
+      settings->termination_callback.v_term_cb_cls = option->val.termination_callback.v_term_cb_cls;
       continue;
     case MHD_R_O_SENTINEL:
     default:
@@ -65,3 +100,32 @@ MHD_response_set_options (struct MHD_Response *response,
   }
   return MHD_SC_OK;
 }
+
+MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_ enum MHD_StatusCode
+MHD_response_set_options (struct MHD_Response *response,
+                          const struct MHD_ResponseOptionAndValue *options,
+                          size_t options_max_num)
+{
+  bool need_lock;
+  enum MHD_StatusCode res;
+
+  if (response->frozen)
+    return MHD_SC_TOO_LATE;
+
+  if (response->reuse.reusable)
+  {
+    need_lock = true;
+    if (! mhd_mutex_lock(&(response->reuse.settings_lock)))
+      return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
+    mhd_assert (1 == response->reuse.counter);
+  }
+  else
+    need_lock = false;
+
+  res = response_set_options_int(response, options, options_max_num);
+
+  if (need_lock)
+    mhd_mutex_unlock_chk(&(response->reuse.settings_lock));
+
+  return res;
+}

+ 5 - 5
src/mhd2/sys_base_types.h

@@ -32,6 +32,11 @@
 
 #include "mhd_sys_options.h"
 
+#if defined(HAVE_SYS_TYPES_H)
+#  include <sys/types.h> /* ssize_t */
+#elif defined(HAVE_UNISTD_H)
+#  include <unistd.h> /* should provide ssize_t */
+#endif
 #include <stdint.h> /* uint_fast_XXt, int_fast_XXt */
 #if defined(HAVE_STDDEF_H)
 #  include <stddef.h> /* size_t, NULL */
@@ -40,11 +45,6 @@
 #else
 #  include <stdio.h> /* should provide size_t, NULL */
 #endif
-#if defined(HAVE_SYS_TYPES_H)
-#  include <sys/types.h> /* ssize_t */
-#elif defined(HAVE_UNISTD_H)
-#  include <unistd.h> /* should provide ssize_t */
-#endif
 #ifdef HAVE_CRTDEFS_H
 #  include <crtdefs.h> /* W32-specific header */
 #endif

+ 52 - 0
src/mhd2/sys_errno.h

@@ -0,0 +1,52 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_errno.h
+ * @brief  The wrapper for system <errno.h>. Includes MHD helper macros.
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SYS_ERRNO_H
+#define MHD_SYS_ERRNO_H 1
+
+#include "mhd_sys_options.h"
+#include <errno.h>
+
+#ifdef EFAULT
+#  define MHD_EFAULT_OR_ZERO EFAULT
+#else
+#  define MHD_EFAULT_OR_ZERO (0)
+#endif
+
+#ifdef EINVAL
+#  define MHD_EINVAL_OR_ZERO EINVAL
+#else
+#  define MHD_EINVAL_OR_ZERO (0)
+#endif
+
+#ifdef EINTR
+#  define MHD_EINTR_OR_ZERO EINTR
+#else
+#  define MHD_EINTR_OR_ZERO (0)
+#endif
+
+
+#endif /* ! MHD_SYS_ERRNO_H */

+ 45 - 0
src/mhd2/sys_file_fd.h

@@ -0,0 +1,45 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_file_fd.h
+ * @brief  The system headers for file FD close, read, write functions.
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef SYS_FILE_FD_H
+#define SYS_FILE_FD_H 1
+
+#include "mhd_sys_options.h"
+
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+#  include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#else
+#  ifdef HAVE_STDLIB_H
+#    include <stdlib.h>
+#  endif
+#  include <stdio.h>
+#endif
+
+
+#endif /* ! SYS_FILE_FD_H */

+ 56 - 0
src/mhd2/sys_ip_headers.h

@@ -0,0 +1,56 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_ip_headers.h
+ * @brief  The header for system headers related to TCP/IP
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SYS_IP_HEADERS_H
+#define MHD_SYS_IP_HEADERS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "mhd_socket_type.h"
+#include "sys_sockets_headers.h"
+
+#ifdef MHD_POSIX_SOCKETS
+#  ifdef HAVE_INETLIB_H
+#    include <inetLib.h>
+#  endif /* HAVE_INETLIB_H */
+#  ifdef HAVE_NETINET_IN_H
+#    include <netinet/in.h>
+#  endif /* HAVE_NETINET_IN_H */
+#  ifdef HAVE_ARPA_INET_H
+#    include <arpa/inet.h>
+#  endif
+#  if ! defined(HAVE_NETINET_IN_H) && ! defined(HAVE_ARPA_INET_H) \
+  && defined(HAVE_NETDB_H)
+#    include <netdb.h>
+#  endif
+#  ifdef HAVE_NETINET_TCP_H
+#    include <netinet/tcp.h>
+#  endif
+#else
+#  include <ws2tcpip.h>
+#endif
+
+#endif /* ! MHD_SYS_IP_HEADERS_H */

+ 40 - 0
src/mhd2/sys_null_macro.h

@@ -0,0 +1,40 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_null_macro.h
+ * @brief  The header for the system NULL constant
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header tries to include the minimal header that defines NULL.
+ */
+
+#ifndef MHD_SYS_NULL_MACRO_H
+#define MHD_SYS_NULL_MACRO_H 1
+
+#include "mhd_sys_options.h"
+
+#if defined(HAVE_STDDEF_H)
+#  include <stddef.h> /* NULL */
+#else
+#  include <string.h> /* should provide NULL */
+#endif
+
+#endif /* ! MHD_SYS_NULL_MACRO_H */

+ 91 - 0
src/mhd2/sys_poll.h

@@ -0,0 +1,91 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_poll.h
+ * @brief  The header for the system 'poll()' function and related data types
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header includes system macros for 'poll()' and also has related
+ * MHD macros.
+ */
+
+#ifndef MHD_SYS_POLL_H
+#define MHD_SYS_POLL_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef MHD_USE_POLL
+#  include "mhd_socket_type.h"
+#  if defined(MHD_POSIX_SOCKETS)
+#    include <poll.h>
+#    define mhd_poll poll
+#  elif defined(MHD_WINSOCK_SOCKETS)
+#    include <winsock2.h>
+#    define mhd_poll WSAPoll
+#  else
+#error Uknown sockets type
+#  endif
+
+#  if (defined(HAVE_DECL_POLLRDNORM) && (0 != HAVE_DECL_POLLRDNORM + 0)) || \
+  defined(POLLRDNORM)
+#    define MHD_POLL_IN POLLRDNORM
+#  else
+#    define MHD_POLL_IN POLLIN
+#  endif
+
+#  if (defined(HAVE_DECL_POLLWRNORM) && (0 != HAVE_DECL_POLLWRNORM + 0)) || \
+  defined(POLLWRNORM)
+#    define MHD_POLL_OUT POLLWRNORM
+#  else
+#    define MHD_POLL_OUT POLLOUT
+#  endif
+
+#  if (defined(HAVE_DECL_POLLRDBAND) && (0 != HAVE_DECL_POLLRDBAND + 0)) || \
+  defined(POLLRDBAND)
+#    define MHD_POLLRDBAND POLLRDBAND
+#  else
+#    define MHD_POLLRDBAND (0)
+#  endif
+
+#  if (defined(HAVE_DECL_POLLWRBAND) && (0 != HAVE_DECL_POLLWRBAND + 0)) || \
+  defined(POLLWRBAND)
+#    define MHD_POLLWRBAND POLLWRBAND
+#  else
+#    define MHD_POLLWRBAND (0)
+#  endif
+
+#  if (defined(HAVE_DECL_POLLPRI) && (0 != HAVE_DECL_POLLPRI + 0)) || \
+  defined(POLLWRBAND)
+#    define MHD_POLLPRI POLLPRI
+#  else
+#    define MHD_POLLPRI (0)
+#  endif
+
+
+#  if (defined(__APPLE__) && defined(__MACH__)) || defined(__CYGWIN__)
+/* The platform incorrectly sets POLLHUP when remote use SHUT_WR.
+   The correct behaviour must be POLLHUP only on remote close/disconnect */
+#    define MHD_POLLHUP_ON_REM_SHUT_WR 1
+#  endif
+
+#endif /* MHD_USE_POLL */
+
+#endif /* ! MHD_SYS_POLL_H */

+ 49 - 0
src/mhd2/sys_sendfile.h

@@ -0,0 +1,49 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_sendfile.h
+ * @brief  The system headers for sendfile() function (if any)
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SYS_SENDFILE_H
+#define MHD_SYS_SENDFILE_H 1
+
+#include "mhd_sys_options.h"
+
+#if defined(HAVE_LINUX_SENDFILE)
+#  include <sys/sendfile.h>
+#elif defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
+#  ifdef HAVE_SYS_TYPES_H
+#    include <sys/types.h>
+#  endif
+#  ifdef HAVE_UNISTD_H
+#    include <unistd.h>
+#  endif
+#  ifdef HAVE_SYS_SOCKET_H
+#    include <sys/socket.h>
+#  endif
+#  include <sys/uio.h>
+#elif defined(MHD_USE_SENDFILE)
+#error MHD_USE_SENDFILE is defined, while no HAVE_xxx_SENDFILE defined
+#endif
+
+#endif /* ! MHD_SYS_SENDFILE_H */

+ 131 - 0
src/mhd2/sys_sockets_headers.h

@@ -0,0 +1,131 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_sockets_headers.h
+ * @brief  The header for system headers for the sockets and some basic macros
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * The macros are limited to simple local constants definitions
+ */
+
+#ifndef MHD_SYS_SOCKETS_HEADERS_H
+#define MHD_SYS_SOCKETS_HEADERS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "mhd_socket_type.h"
+
+#ifdef MHD_POSIX_SOCKETS
+#  include "sys_base_types.h" /* required on old platforms */
+#  ifdef HAVE_SYS_SOCKET_H
+#    include <sys/socket.h>
+#  endif
+#  ifdef HAVE_SOCKLIB_H
+#    include <sockLib.h>
+#  endif /* HAVE_SOCKLIB_H */
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  include <winsock2.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#  include <sys/uh.h>
+#endif
+
+#if defined(HAVE_SOCK_NONBLOCK) && ! defined(MHD_WINSOCK_SOCKETS)
+#  define MHD_SOCK_NONBLOCK SOCK_NONBLOCK
+#else
+#  define MHD_SOCK_NONBLOCK (0)
+#endif
+
+#if defined(SOCK_CLOEXEC) && ! defined(MHD_WINSOCK_SOCKETS)
+#  define MHD_SOCK_CLOEXEC SOCK_CLOEXEC
+#else
+#  define MHD_SOCK_CLOEXEC (0)
+#endif
+
+#if defined(SOCK_NOSIGPIPE) && ! defined(MHD_WINSOCK_SOCKETS)
+#  define MHD_SOCK_NOSIGPIPE SOCK_NOSIGPIPE
+#else
+#  define MHD_SOCK_NOSIGPIPE (0)
+#endif
+
+#if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS)
+#  define MHD_MSG_NOSIGNAL MSG_NOSIGNAL
+#else
+#  define MHD_MSG_NOSIGNAL (0)
+#endif
+
+#if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS)
+#  define MHD_MSG_NOSIGNAL MSG_MORE
+#else
+#  define MHD_MSG_NOSIGNAL (0)
+#endif
+
+
+/**
+ * mhd_SCKT_OPT_BOOL is the type for bool parameters
+ * for setsockopt()/getsockopt() functions
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#  define mhd_SCKT_OPT_BOOL int
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_OPT_BOOL BOOL
+#endif /* MHD_WINSOCK_SOCKETS */
+
+/**
+ * mhd_SCKT_SEND_SIZE is type used to specify size for send() and recv()
+ * functions
+ */
+#if defined(MHD_POSIX_SOCKETS)
+typedef size_t mhd_SCKT_SEND_SIZE;
+#elif defined(MHD_WINSOCK_SOCKETS)
+typedef int mhd_SCKT_SEND_SIZE;
+#endif
+
+/**
+ * MHD_SCKT_SEND_MAX_SIZE_ is maximum send()/recv() size value.
+ */
+#if defined(MHD_POSIX_SOCKETS)
+#  define MHD_SCKT_SEND_MAX_SIZE_ SSIZE_MAX
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define MHD_SCKT_SEND_MAX_SIZE_ (0x7FFFFFFF) /* INT_MAX */
+#endif
+
+
+#if defined(AF_UNIX) || \
+  (defined(HAVE_DECL_AF_UNIX) && (HAVE_DECL_AF_UNIX + 0 != 0))
+#  define MHD_AF_UNIX AF_UNIX
+#elif defined(AF_LOCAL) || \
+  (defined(HAVE_DECL_AF_LOCAL) && (HAVE_DECL_AF_LOCAL + 0 != 0))
+#  define MHD_AF_UNIX AF_LOCAL
+#endif /* AF_UNIX */
+
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
+  defined(__OpenBSD__) || defined(__NetBSD__) || \
+  defined(MHD_WINSOCK_SOCKETS) || defined(__MACH__) || defined(__sun) || \
+  defined(SOMEBSD)
+/* Most of the OSes inherit nonblocking setting from the listen socket */
+#  define MHD_ACCEPTED_INHERITS_NONBLOCK 1
+#elif defined(__gnu_linux__) || defined(__linux__)
+#  define MHD_ACCEPTED_DOES_NOT_INHERIT_NONBLOCK 1
+#endif
+
+#endif /* ! MHD_SYS_SOCKETS_HEADERS_H */

+ 2 - 1
src/mhd2/sys_sockets_types.h

@@ -23,7 +23,8 @@
  * @brief  The header for system types related to sockets
  * @author Karlson2k (Evgeny Grin)
  *
- * This header should provide declaration of 'struct sockaddr'.
+ * This header should provide declaration of 'struct sockaddr' and
+ * socklen_t (if it is present in headers).
  */
 
 #ifndef MHD_SYS_SOCKETS_TYPES_H

+ 49 - 0
src/mhd2/sys_thread_entry_type.h

@@ -0,0 +1,49 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/sys_thread_entry_type.h
+ * @brief  The type of the thread start routine
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SYS_THREAD_ENTRY_TYPE_H
+#define MHD_SYS_THREAD_ENTRY_TYPE_H 1
+
+#include "mhd_sys_options.h"
+
+#if defined(MHD_USE_POSIX_THREADS)
+#  define mhd_THRD_RTRN_TYPE void*
+#  define mhd_THRD_CALL_SPEC
+#elif defined(MHD_USE_W32_THREADS)
+#  define mhd_THRD_RTRN_TYPE unsigned
+#  define mhd_THRD_CALL_SPEC __stdcall
+#endif
+
+/**
+ * Signature of the entrance function for a thread.
+ *
+ * @param cls the closure argument for the function
+ * @return the termination code/result from the thread
+ */
+typedef mhd_THRD_RTRN_TYPE
+(mhd_THRD_CALL_SPEC *mhd_THREAD_START_ROUTINE)(void *cls);
+
+#endif /* ! MHD_SYS_THREAD_ENTRY_TYPE_H */

+ 51 - 0
src/mhd2/w32_lib_res.rc

@@ -0,0 +1,51 @@
+/* W32 resources for .dll */
+
+#include <winresrc.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+VS_VERSION_INFO VERSIONINFO
+  FILEVERSION 1,99,0,0
+  PRODUCTVERSION 1,99,0,0
+  FILEFLAGSMASK  VS_FFI_FILEFLAGSMASK
+#if defined(_DEBUG)
+  FILEFLAGS      VS_FF_DEBUG
+#else
+  FILEFLAGS      0
+#endif
+  FILEOS         VOS_NT_WINDOWS32
+#ifdef DLL_EXPORT
+  FILETYPE       VFT_DLL
+#else
+  FILETYPE       VFT_STATIC_LIB
+#endif
+  FILESUBTYPE    VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "04090000"  /* Lang = US English, Charset = ASCII */
+        BEGIN
+            VALUE "ProductName", "GNU libmicrohttpd\0"
+            VALUE "ProductVersion", "1.99.0\0"
+            VALUE "FileVersion", "1.99.0\0"
+#ifdef DLL_EXPORT
+            VALUE "FileDescription", "GNU libmicrohttpd2 DLL for Windows (MinGW build, MSVCRT run-time lib)\0"
+#else
+            VALUE "FileDescription", "GNU libmicrohttpd2 static library for Windows (MinGW build, MSVCRT run-time lib)\0"
+#endif
+            VALUE "InternalName", "libmicrohttpd2\0"
+#ifdef DLL_EXPORT
+            VALUE "OriginalFilename", "libmicrohttpd2-12.dll\0"
+#else
+            VALUE "OriginalFilename", "libmicrohttpd2.lib\0"
+#endif
+            VALUE "CompanyName", "Free Software Foundation\0"
+            VALUE "LegalCopyright",  "Copyright (C) 2007-2024 Christian Grothoff, Evgeny Grin, and project contributors\0"
+            VALUE "Comments", "http://www.gnu.org/software/libmicrohttpd/\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 0  /* US English, ASCII */
+    END
+END
+

+ 51 - 0
src/mhd2/w32_lib_res.rc.in

@@ -0,0 +1,51 @@
+/* W32 resources for .dll */
+
+#include <winresrc.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+VS_VERSION_INFO VERSIONINFO
+  FILEVERSION @PACKAGE_VERSION_MAJOR@,@PACKAGE_VERSION_MINOR@,@PACKAGE_VERSION_SUBMINOR@,0
+  PRODUCTVERSION @PACKAGE_VERSION_MAJOR@,@PACKAGE_VERSION_MINOR@,@PACKAGE_VERSION_SUBMINOR@,0
+  FILEFLAGSMASK  VS_FFI_FILEFLAGSMASK
+#if defined(_DEBUG)
+  FILEFLAGS      VS_FF_DEBUG
+#else
+  FILEFLAGS      0
+#endif
+  FILEOS         VOS_NT_WINDOWS32
+#ifdef DLL_EXPORT
+  FILETYPE       VFT_DLL
+#else
+  FILETYPE       VFT_STATIC_LIB
+#endif
+  FILESUBTYPE    VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "04090000"  /* Lang = US English, Charset = ASCII */
+        BEGIN
+            VALUE "ProductName", "GNU libmicrohttpd\0"
+            VALUE "ProductVersion", "@PACKAGE_VERSION@\0"
+            VALUE "FileVersion", "@PACKAGE_VERSION@\0"
+#ifdef DLL_EXPORT
+            VALUE "FileDescription", "GNU libmicrohttpd2 DLL for Windows (MinGW build, @W32CRT@ run-time lib)\0"
+#else
+            VALUE "FileDescription", "GNU libmicrohttpd2 static library for Windows (MinGW build, MSVCRT run-time lib)\0"
+#endif
+            VALUE "InternalName", "libmicrohttpd2\0"
+#ifdef DLL_EXPORT
+            VALUE "OriginalFilename", "libmicrohttpd2-@[email protected]\0"
+#else
+            VALUE "OriginalFilename", "libmicrohttpd2.lib\0"
+#endif
+            VALUE "CompanyName", "Free Software Foundation\0"
+            VALUE "LegalCopyright",  "Copyright (C) 2007-2024 Christian Grothoff, Evgeny Grin, and project contributors\0"
+            VALUE "Comments", "http://www.gnu.org/software/libmicrohttpd/\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 0  /* US English, ASCII */
+    END
+END
+

+ 0 - 72
src/microhttpd/mhd_itc.c

@@ -1,72 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2016 Karlson2k (Evgeny Grin)
-
-  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 microhttpd/mhd_itc.c
- * @brief  Implementation of inter-thread communication functions
- * @author Karlson2k (Evgeny Grin)
- * @author Christian Grothoff
- */
-
-#include "mhd_itc.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#include <fcntl.h>
-#include "internal.h"
-
-
-#if defined(_MHD_ITC_PIPE)
-#if ! defined(_WIN32) || defined(__CYGWIN__)
-
-#ifndef HAVE_PIPE2_FUNC
-/**
- * Change itc FD options to be non-blocking.
- *
- * @param itc the inter-thread communication primitive to manipulate
- * @return non-zero if succeeded, zero otherwise
- */
-int
-MHD_itc_nonblocking_ (struct MHD_itc_ itc)
-{
-  unsigned int i;
-
-  for (i = 0; i<2; i++)
-  {
-    int flags;
-
-    flags = fcntl (itc.fd[i],
-                   F_GETFL);
-    if (-1 == flags)
-      return 0;
-
-    if ( ((flags | O_NONBLOCK) != flags) &&
-         (0 != fcntl (itc.fd[i],
-                      F_SETFL,
-                      flags | O_NONBLOCK)) )
-      return 0;
-  }
-  return ! 0;
-}
-
-
-#endif /* ! HAVE_PIPE2_FUNC */
-#endif /* !_WIN32 || __CYGWIN__ */
-#endif /* _MHD_ITC_EVENTFD ||  _MHD_ITC_PIPE */

+ 0 - 387
src/microhttpd/mhd_itc.h

@@ -1,387 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2016-2023 Karlson2k (Evgeny Grin)
-
-  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 microhttpd/mhd_itc.h
- * @brief  Header for platform-independent inter-thread communication
- * @author Karlson2k (Evgeny Grin)
- * @author Christian Grothoff
- *
- * Provides basic abstraction for inter-thread communication.
- * Any functions can be implemented as macro on some platforms
- * unless explicitly marked otherwise.
- * Any function argument can be skipped in macro, so avoid
- * variable modification in function parameters.
- */
-#ifndef MHD_ITC_H
-#define MHD_ITC_H 1
-#include "mhd_itc_types.h"
-
-#include <fcntl.h>
-
-#ifndef MHD_PANIC
-#  include <stdio.h>
-#  ifdef HAVE_STDLIB_H
-#    include <stdlib.h>
-#  endif /* HAVE_STDLIB_H */
-/* Simple implementation of MHD_PANIC, to be used outside lib */
-#  define MHD_PANIC(msg)                                             \
-  do { fprintf (stderr,                                              \
-                "Abnormal termination at %d line in file %s: %s\n",  \
-                (int) __LINE__, __FILE__, msg); abort ();            \
-     } while (0)
-#endif /* ! MHD_PANIC */
-
-#if defined(_MHD_ITC_EVENTFD)
-
-/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */
-#include <sys/eventfd.h>
-#include <stdint.h>      /* for uint64_t */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>      /* for read(), write() */
-#endif /* HAVE_UNISTD_H */
-#ifdef HAVE_STRING_H
-#include <string.h> /* for strerror() */
-#include <errno.h>
-#endif
-
-/**
- * Number of FDs used by every ITC.
- */
-#define MHD_ITC_NUM_FDS_ (1)
-
-/**
- * Initialise ITC by generating eventFD
- * @param itc the itc to initialise
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_init_(itc) \
-  (-1 != ((itc).fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK)))
-
-/**
- * Get description string of last errno for itc operations.
- */
-#define MHD_itc_last_strerror_() strerror (errno)
-
-/**
- * Internal static const helper for MHD_itc_activate_()
- */
-static const uint64_t _MHD_itc_wr_data = 1;
-
-/**
- * Activate signal on @a itc
- * @param itc the itc to use
- * @param str ignored
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_activate_(itc, str)                    \
-  ((write ((itc).fd, (const void*) &_MHD_itc_wr_data,  \
-           sizeof(_MHD_itc_wr_data)) > 0) || (EAGAIN == errno))
-
-/**
- * Return read FD of @a itc which can be used for poll(), select() etc.
- * @param itc the itc to get FD
- * @return FD of read side
- */
-#define MHD_itc_r_fd_(itc) ((itc).fd)
-
-/**
- * Return write FD of @a itc
- * @param itc the itc to get FD
- * @return FD of write side
- */
-#define MHD_itc_w_fd_(itc) ((itc).fd)
-
-/**
- * Clear signaled state on @a itc
- * @param itc the itc to clear
- */
-#define MHD_itc_clear_(itc)                               \
-  do { uint64_t __b;                                      \
-       (void) read ((itc).fd, (void*)&__b, sizeof(__b));  \
-     } while (0)
-
-/**
- * Destroy previously initialised ITC.  Note that close()
- * on some platforms returns odd errors, so we ONLY fail
- * if the errno is EBADF.
- * @param itc the itc to destroy
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_destroy_(itc) \
-  ((0 == close ((itc).fd)) || (EBADF != errno))
-
-/**
- * Check whether ITC has valid value.
- *
- * Macro check whether @a itc value is valid (allowed),
- * macro does not check whether @a itc was really initialised.
- * @param itc the itc to check
- * @return boolean true if @a itc has valid value,
- *         boolean false otherwise.
- */
-#define MHD_ITC_IS_VALID_(itc)  (-1 != ((itc).fd))
-
-/**
- * Set @a itc to invalid value.
- * @param itc the itc to set
- */
-#define MHD_itc_set_invalid_(itc) ((itc).fd = -1)
-
-
-#elif defined(_MHD_ITC_PIPE)
-
-/* **************** Standard UNIX ITC implementation by pipe ********** */
-
-#if defined(HAVE_PIPE2_FUNC) && defined(HAVE_FCNTL_H)
-#  include <fcntl.h>     /* for O_CLOEXEC, O_NONBLOCK */
-#endif /* HAVE_PIPE2_FUNC && HAVE_FCNTL_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>      /* for read(), write(), errno */
-#endif /* HAVE_UNISTD_H */
-#ifdef HAVE_STRING_H
-#include <string.h> /* for strerror() */
-#endif
-
-
-/**
- * Number of FDs used by every ITC.
- */
-#define MHD_ITC_NUM_FDS_ (2)
-
-/**
- * Initialise ITC by generating pipe
- * @param itc the itc to initialise
- * @return non-zero if succeeded, zero otherwise
- */
-#ifdef HAVE_PIPE2_FUNC
-#  define MHD_itc_init_(itc) (! pipe2 ((itc).fd, O_CLOEXEC | O_NONBLOCK))
-#else  /* ! HAVE_PIPE2_FUNC */
-#  define MHD_itc_init_(itc)          \
-  ( (! pipe ((itc).fd)) ?             \
-    (MHD_itc_nonblocking_ ((itc)) ?   \
-     (! 0) :                          \
-     (MHD_itc_destroy_ ((itc)), 0) )  \
-    : (0) )
-#endif /* ! HAVE_PIPE2_FUNC */
-
-/**
- * Get description string of last errno for itc operations.
- */
-#define MHD_itc_last_strerror_() strerror (errno)
-
-/**
- * Activate signal on @a itc
- * @param itc the itc to use
- * @param str one-symbol string, useful only for strace debug
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_activate_(itc, str) \
-  ((write ((itc).fd[1], (const void*) (str), 1) > 0) || (EAGAIN == errno))
-
-
-/**
- * Return read FD of @a itc which can be used for poll(), select() etc.
- * @param itc the itc to get FD
- * @return FD of read side
- */
-#define MHD_itc_r_fd_(itc) ((itc).fd[0])
-
-/**
- * Return write FD of @a itc
- * @param itc the itc to get FD
- * @return FD of write side
- */
-#define MHD_itc_w_fd_(itc) ((itc).fd[1])
-
-/**
- * Clear signaled state on @a itc
- * @param itc the itc to clear
- */
-#define MHD_itc_clear_(itc) do                                 \
-  { long __b;                                                  \
-    while (0 < read ((itc).fd[0], (void*) &__b, sizeof(__b)))  \
-    {(void)0;} } while (0)
-
-/**
- * Destroy previously initialised ITC
- * @param itc the itc to destroy
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_destroy_(itc)     \
-  ( (0 == close ((itc).fd[0])) ?  \
-    (0 == close ((itc).fd[1])) :  \
-    ((close ((itc).fd[1])), 0) )
-
-/**
- * Check whether ITC has valid value.
- *
- * Macro check whether @a itc value is valid (allowed),
- * macro does not check whether @a itc was really initialised.
- * @param itc the itc to check
- * @return boolean true if @a itc has valid value,
- *         boolean false otherwise.
- */
-#define MHD_ITC_IS_VALID_(itc)  (-1 != (itc).fd[0])
-
-/**
- * Set @a itc to invalid value.
- * @param itc the itc to set
- */
-#define MHD_itc_set_invalid_(itc) ((itc).fd[0] = (itc).fd[1] = -1)
-
-#ifndef HAVE_PIPE2_FUNC
-/**
- * Change itc FD options to be non-blocking.
- *
- * @param fd the FD to manipulate
- * @return non-zero if succeeded, zero otherwise
- */
-int
-MHD_itc_nonblocking_ (struct MHD_itc_ itc);
-
-#endif /* ! HAVE_PIPE2_FUNC */
-
-
-#elif defined(_MHD_ITC_SOCKETPAIR)
-
-/* **************** ITC implementation by socket pair ********** */
-
-#include "mhd_sockets.h"
-
-
-/**
- * Number of FDs used by every ITC.
- */
-#define MHD_ITC_NUM_FDS_ (2)
-
-/**
- * Initialise ITC by generating socketpair
- * @param itc the itc to initialise
- * @return non-zero if succeeded, zero otherwise
- */
-#ifdef MHD_socket_pair_nblk_
-#  define MHD_itc_init_(itc) MHD_socket_pair_nblk_ ((itc).sk)
-#else  /* ! MHD_socket_pair_nblk_ */
-#  define MHD_itc_init_(itc)         \
-  (MHD_socket_pair_ ((itc).sk) ?     \
-   (MHD_itc_nonblocking_ ((itc)) ?   \
-    (! 0) :                          \
-    (MHD_itc_destroy_ ((itc)), 0) )  \
-   : (0))
-#endif /* ! MHD_socket_pair_nblk_ */
-
-/**
- * Get description string of last error for itc operations.
- */
-#define MHD_itc_last_strerror_() MHD_socket_last_strerr_ ()
-
-/**
- * Activate signal on @a itc
- * @param itc the itc to use
- * @param str one-symbol string, useful only for strace debug
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_activate_(itc, str)            \
-  ((MHD_send_ ((itc).sk[1], (str), 1) > 0) ||  \
-   (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())))
-
-/**
- * Return read FD of @a itc which can be used for poll(), select() etc.
- * @param itc the itc to get FD
- * @return FD of read side
- */
-#define MHD_itc_r_fd_(itc) ((itc).sk[0])
-
-/**
- * Return write FD of @a itc
- * @param itc the itc to get FD
- * @return FD of write side
- */
-#define MHD_itc_w_fd_(itc) ((itc).sk[1])
-
-/**
- * Clear signaled state on @a itc
- * @param itc the itc to clear
- */
-#define MHD_itc_clear_(itc) do                                    \
-  { long __b;                                                     \
-    while (0 < recv ((itc).sk[0], (void*) &__b, sizeof(__b), 0))  \
-    {(void)0;} } while (0)
-
-/**
- * Destroy previously initialised ITC
- * @param itc the itc to destroy
- * @return non-zero if succeeded, zero otherwise
- */
-#define MHD_itc_destroy_(itc)         \
-  (MHD_socket_close_ ((itc).sk[0]) ?  \
-   MHD_socket_close_ ((itc).sk[1]) :  \
-   ((void) MHD_socket_close_ ((itc).sk[1]), 0) )
-
-
-/**
- * Check whether ITC has valid value.
- *
- * Macro check whether @a itc value is valid (allowed),
- * macro does not check whether @a itc was really initialised.
- * @param itc the itc to check
- * @return boolean true if @a itc has valid value,
- *         boolean false otherwise.
- */
-#define MHD_ITC_IS_VALID_(itc)  (MHD_INVALID_SOCKET != (itc).sk[0])
-
-/**
- * Set @a itc to invalid value.
- * @param itc the itc to set
- */
-#define MHD_itc_set_invalid_(itc) \
-  ((itc).sk[0] = (itc).sk[1] = MHD_INVALID_SOCKET)
-
-#ifndef MHD_socket_pair_nblk_
-#  define MHD_itc_nonblocking_(pip)          \
-  (MHD_socket_nonblocking_ ((pip).sk[0]) &&  \
-   MHD_socket_nonblocking_ ((pip).sk[1]))
-#endif /* ! MHD_socket_pair_nblk_ */
-
-#endif /* _MHD_ITC_SOCKETPAIR */
-
-/**
- * Destroy previously initialised ITC and abort execution
- * if error is detected.
- * @param itc the itc to destroy
- */
-#define MHD_itc_destroy_chk_(itc) do {             \
-    if (! MHD_itc_destroy_ (itc))                  \
-      MHD_PANIC (_ ("Failed to destroy ITC.\n"));  \
-} while (0)
-
-/**
- * Check whether ITC has invalid value.
- *
- * Macro check whether @a itc value is invalid,
- * macro does not check whether @a itc was destroyed.
- * @param itc the itc to check
- * @return boolean true if @a itc has invalid value,
- *         boolean false otherwise.
- */
-#define MHD_ITC_IS_INVALID_(itc)  (! MHD_ITC_IS_VALID_ (itc))
-
-#endif /* MHD_ITC_H */

+ 0 - 566
src/microhttpd/mhd_threads.h

@@ -1,566 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2016-2023 Karlson2k (Evgeny Grin)
-
-  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 microhttpd/mhd_threads.h
- * @brief  Header for platform-independent threads abstraction
- * @author Karlson2k (Evgeny Grin)
- *
- * Provides basic abstraction for threads.
- * Any functions can be implemented as macro on some platforms
- * unless explicitly marked otherwise.
- * Any function argument can be skipped in macro, so avoid
- * variable modification in function parameters.
- *
- * @warning Unlike pthread functions, most of functions return
- *          nonzero on success.
- */
-
-#ifndef MHD_THREADS_H
-#define MHD_THREADS_H 1
-
-#include "mhd_options.h"
-#ifdef HAVE_STDDEF_H
-#  include <stddef.h> /* for size_t */
-#elif defined(HAVE_STDLIB_H)
-#  include <stdlib.h> /* for size_t */
-#else /* ! HAVE_STDLIB_H */
-#  include <stdio.h>  /* for size_t */
-#endif /* ! HAVE_STDLIB_H */
-
-#if defined(MHD_USE_POSIX_THREADS)
-#  undef HAVE_CONFIG_H
-#  include <pthread.h>
-#  define HAVE_CONFIG_H 1
-#  ifndef MHD_USE_THREADS
-#    define MHD_USE_THREADS 1
-#  endif
-#elif defined(MHD_USE_W32_THREADS)
-#  ifndef WIN32_LEAN_AND_MEAN
-#    define WIN32_LEAN_AND_MEAN 1
-#  endif /* !WIN32_LEAN_AND_MEAN */
-#  include <windows.h>
-#  ifndef MHD_USE_THREADS
-#    define MHD_USE_THREADS 1
-#  endif
-#else
-#  error No threading API is available.
-#endif
-
-#ifdef HAVE_STDBOOL_H
-#  include <stdbool.h>
-#endif /* HAVE_STDBOOL_H */
-
-#if defined(MHD_USE_POSIX_THREADS) && defined(MHD_USE_W32_THREADS)
-#  error Both MHD_USE_POSIX_THREADS and MHD_USE_W32_THREADS are defined
-#endif /* MHD_USE_POSIX_THREADS && MHD_USE_W32_THREADS */
-
-#ifndef MHD_NO_THREAD_NAMES
-#  if defined(MHD_USE_POSIX_THREADS)
-#    if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
-  defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \
-  defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \
-  defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \
-  defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
-  defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
-#      define MHD_USE_THREAD_NAME_
-#    endif /* HAVE_PTHREAD_SETNAME_NP */
-#  elif defined(MHD_USE_W32_THREADS)
-#    ifdef _MSC_FULL_VER
-/* Thread names only available with VC compiler */
-#      define MHD_USE_THREAD_NAME_
-#    endif /* _MSC_FULL_VER */
-#  endif
-#endif
-
-/* ** Thread handle - used to control the thread ** */
-
-#if defined(MHD_USE_POSIX_THREADS)
-/**
- * Wait until specified thread is ended and free thread handle on success.
- * @param thread handle to watch
- * @return nonzero on success, zero otherwise
- */
-#  define MHD_join_thread_(native_handle) \
-  (! pthread_join ((native_handle), NULL))
-#elif defined(MHD_USE_W32_THREADS)
-/**
- * Wait until specified thread is ended and free thread handle on success.
- * @param thread handle to watch
- * @return nonzero on success, zero otherwise
- */
-#  define MHD_join_thread_(native_handle) \
-  ( (WAIT_OBJECT_0 == WaitForSingleObject ( (native_handle), INFINITE)) ? \
-    (CloseHandle ( (native_handle)), ! 0) : 0 )
-#endif
-
-#if defined(MHD_USE_POSIX_THREADS)
-/**
- * The native type to control the thread from other threads
- */
-typedef pthread_t MHD_thread_handle_native_;
-#elif defined(MHD_USE_W32_THREADS)
-/**
- * The native type to control the thread from other threads
- */
-typedef HANDLE MHD_thread_handle_native_;
-#endif
-
-#if defined(MHD_USE_POSIX_THREADS)
-#  if defined(__gnu_linux__) || \
-  (defined(__linux__) && defined(__GLIBC__))
-/* The next part of code is disabled because it relies on undocumented
-   behaviour.
-   It could be enabled for neglectable performance and size improvements. */
-#  if 0 /* Disabled code */
-/**
- * The native invalid value for native thread handle
- */
-#    define MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ \
-     ((MHD_thread_handle_native_) 0)
-#  endif /* Disabled code */
-#  endif /* __gnu_linux__ || (__linux__ && __GLIBC__) */
-#elif defined(MHD_USE_W32_THREADS)
-/**
- * The native invalid value for native thread handle
- */
-#  define MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ \
-  ((MHD_thread_handle_native_) NULL)
-#endif /* MHD_USE_W32_THREADS */
-
-#if ! defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_)
-/**
- * Structure with thread handle and validity flag
- */
-struct MHD_thread_handle_struct_
-{
-  bool valid;                       /**< true if native handle is set */
-  MHD_thread_handle_native_ native; /**< the native thread handle */
-};
-/**
- * Type with thread handle that can be set to invalid value
- */
-typedef struct MHD_thread_handle_struct_ MHD_thread_handle_;
-
-/**
- * Set variable pointed by @a handle_ptr to invalid (unset) value
- */
-#  define MHD_thread_handle_set_invalid_(handle_ptr) \
-   ((handle_ptr)->valid = false)
-/**
- * Set native handle in variable pointed by @a handle_ptr
- * to @a native_val value
- */
-#  define MHD_thread_handle_set_native_(handle_ptr,native_val) \
-   ((handle_ptr)->valid = true, (handle_ptr)->native = native_val)
-/**
- * Check whether native handle value is set in @a handle_var variable
- */
-#  define MHD_thread_handle_is_valid_(handle_var) \
-   ((handle_var).valid)
-/**
- * Get native handle value from @a handle_var variable
- */
-#  define MHD_thread_handle_get_native_(handle_var) \
-   ((handle_var).native)
-#else  /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */
-/**
- * Type with thread handle that can be set to invalid value
- */
-typedef MHD_thread_handle_native_ MHD_thread_handle_;
-
-/**
- * Set variable pointed by @a handle_ptr to invalid (unset) value
- */
-#  define MHD_thread_handle_set_invalid_(handle_ptr) \
-    ((*(handle_ptr)) = MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_)
-/**
- * Set native handle in the variable pointed by @a handle_ptr
- * to @a native_val value
- */
-#  define MHD_thread_handle_set_native_(handle_ptr,native_val) \
-    ((*(handle_ptr)) = native_val)
-/**
- * Check whether native handle value is set in @a handle_var variable
- */
-#  define MHD_thread_handle_is_valid_(handle_var) \
-    (MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ != handle_var)
-/**
- * Get native handle value from @a handle_var variable
- */
-#  define MHD_thread_handle_get_native_(handle_var) \
-    (handle_var)
-/**
- * Get pointer to native handle stored the variable pointed by @a handle_ptr
- * @note This macro could not available if direct manipulation of
- *       the native handle is not possible
- */
-#  define MHD_thread_handle_get_native_ptr_(handle_ptr) \
-    (handle_ptr)
-#endif /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */
-
-
-/* ** Thread ID - used to check threads match ** */
-
-#if defined(MHD_USE_POSIX_THREADS)
-/**
- * The native type used to check whether current thread matches expected thread
- */
-typedef pthread_t MHD_thread_ID_native_;
-
-/**
- * Function to get the current thread native ID.
- */
-#  define MHD_thread_ID_native_current_ pthread_self
-
-/**
- * Check whether two native thread IDs are equal.
- * @return non-zero if equal, zero if not equal
- */
-#  define MHD_thread_ID_native_equal_(id1,id2) \
-  (pthread_equal(id1,id2))
-#elif defined(MHD_USE_W32_THREADS)
-/**
- * The native type used to check whether current thread matches expected thread
- */
-typedef DWORD MHD_thread_ID_native_;
-
-/**
- * Function to get the current thread native ID.
- */
-#  define MHD_thread_ID_native_current_ GetCurrentThreadId
-
-/**
- * Check whether two native thread IDs are equal.
- * @return non-zero if equal, zero if not equal
- */
-#  define MHD_thread_ID_native_equal_(id1,id2) \
-  ((id1) == (id2))
-#endif
-
-/**
- * Check whether specified thread ID matches current thread.
- * @param id the thread ID to match
- * @return nonzero on match, zero otherwise
- */
-#define MHD_thread_ID_native_is_current_thread_(id) \
-    MHD_thread_ID_native_equal_(id, MHD_thread_ID_native_current_())
-
-
-#if defined(MHD_USE_POSIX_THREADS)
-#  if defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_)
-/**
- * The native invalid value for native thread ID
- */
-#    define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \
-            MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_
-#  endif /* MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ */
-#elif defined(MHD_USE_W32_THREADS)
-/**
- * The native invalid value for native thread ID
- */
- #  define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \
-   ((MHD_thread_ID_native_) 0)
-#endif /* MHD_USE_W32_THREADS */
-
-#if ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
-/**
- * Structure with thread id and validity flag
- */
-struct MHD_thread_ID_struct_
-{
-  bool valid;                   /**< true if native ID is set */
-  MHD_thread_ID_native_ native; /**< the native thread ID */
-};
-/**
- * Type with thread ID that can be set to invalid value
- */
-typedef struct MHD_thread_ID_struct_ MHD_thread_ID_;
-
-/**
- * Set variable pointed by @a ID_ptr to invalid (unset) value
- */
-#  define MHD_thread_ID_set_invalid_(ID_ptr) \
-   ((ID_ptr)->valid = false)
-/**
- * Set native ID in variable pointed by @a ID_ptr
- * to @a native_val value
- */
-#  define MHD_thread_ID_set_native_(ID_ptr,native_val) \
-   ((ID_ptr)->valid = true, (ID_ptr)->native = native_val)
-/**
- * Check whether native ID value is set in @a ID_var variable
- */
-#  define MHD_thread_ID_is_valid_(ID_var) \
-   ((ID_var).valid)
-/**
- * Get native ID value from @a ID_var variable
- */
-#  define MHD_thread_ID_get_native_(ID_var) \
-    ((ID_var).native)
-/**
- * Check whether @a ID_var variable is equal current thread
- */
-#  define MHD_thread_ID_is_current_thread_(ID_var) \
-    (MHD_thread_ID_is_valid_(ID_var) && \
-     MHD_thread_ID_native_is_current_thread_((ID_var).native))
-#else  /* MHD_THREAD_ID_NATIVE_INVALID_ */
-/**
- * Type with thread ID that can be set to invalid value
- */
-typedef MHD_thread_ID_native_ MHD_thread_ID_;
-
-/**
- * Set variable pointed by @a ID_ptr to invalid (unset) value
- */
-#  define MHD_thread_ID_set_invalid_(ID_ptr) \
-    ((*(ID_ptr)) = MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
-/**
- * Set native ID in variable pointed by @a ID_ptr
- * to @a native_val value
- */
-#  define MHD_thread_ID_set_native_(ID_ptr,native_val) \
-    ((*(ID_ptr)) = native_val)
-/**
- * Check whether native ID value is set in @a ID_var variable
- */
-#  define MHD_thread_ID_is_valid_(ID_var) \
-    (MHD_THREAD_ID_NATIVE_VALUE_INVALID_ != ID_var)
-/**
- * Get native ID value from @a ID_var variable
- */
-#  define MHD_thread_ID_get_native_(ID_var) \
-    (ID_var)
-/**
- * Check whether @a ID_var variable is equal current thread
- */
-#  define MHD_thread_ID_is_current_thread_(ID_var) \
-    MHD_thread_ID_native_is_current_thread_(ID_var)
-#endif /* MHD_THREAD_ID_NATIVE_INVALID_ */
-
-/**
- * Set current thread ID in variable pointed by @a ID_ptr
- */
-#  define MHD_thread_ID_set_current_thread_(ID_ptr) \
-    MHD_thread_ID_set_native_(ID_ptr,MHD_thread_ID_native_current_())
-
-
-#if defined(MHD_USE_POSIX_THREADS)
-#  if defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \
-  ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
-#    error \
-  MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined
-#  elif ! defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \
-  defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_)
-#    error \
-  MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined
-#  endif
-#endif /* MHD_USE_POSIX_THREADS */
-
-/* When staring a new thread, the kernel (and thread implementation) may
- * pause the calling (initial) thread and start the new thread.
- * If thread identifier is assigned to variable in the initial thread then
- * the value of the identifier variable will be undefined in the new thread
- * until the initial thread continue processing.
- * However, it is also possible that the new thread created, but not executed
- * for some time while the initial thread continue execution. In this case any
- * variable assigned in the new thread will be undefined for some time until
- * they really processed by the new thread.
- * To avoid data races, a special structure MHD_thread_handle_ID_ is used.
- * The "handle" is assigned by calling (initial) thread and should be always
- * defined when checked in the initial thread.
- * The "ID" is assigned by the new thread and should be always defined when
- * checked inside the new thread.
- */
-/* Depending on implementation, pthread_create() MAY set thread ID into
- * provided pointer and after it start thread OR start thread and after
- * it set thread ID. In the latter case, to avoid data races, additional
- * pthread_self() call is required in thread routine. If some platform
- * is known for setting thread ID BEFORE starting thread macro
- * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined
- * to save some resources. */
-/* #define MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 1 */
-
-/* * handle - must be valid when other thread knows that particular thread
-     is started.
-   * ID     - must be valid when code is executed inside thread */
-#if defined(MHD_USE_POSIX_THREADS) && \
-  defined(MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD) && \
-  defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \
-  defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) && \
-  defined(MHD_thread_handle_get_native_ptr_)
-union _MHD_thread_handle_ID_
-{
-  MHD_thread_handle_ handle;    /**< To be used in other threads */
-  MHD_thread_ID_ ID;            /**< To be used in the thread itself */
-};
-typedef union _MHD_thread_handle_ID_ MHD_thread_handle_ID_;
-#  define MHD_THREAD_HANDLE_ID_IS_UNION 1
-#else  /* !MHD_USE_POSIX_THREADS
-          || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD
-          || !MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_
-          || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_
-          || !MHD_thread_handle_get_native_ptr_ */
-struct _MHD_thread_handle_ID_
-{
-  MHD_thread_handle_ handle;    /**< To be used in other threads */
-  MHD_thread_ID_ ID;            /**< To be used in the thread itself */
-};
-typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_;
-#endif /* !MHD_USE_POSIX_THREADS
-          || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD
-          || !MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_
-          || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_
-          || !MHD_thread_handle_get_native_ptr_ */
-
-/**
- * Set MHD_thread_handle_ID_ to invalid value
- */
-#define MHD_thread_handle_ID_set_invalid_(hndl_id_ptr) \
-  (MHD_thread_handle_set_invalid_(&((hndl_id_ptr)->handle)), \
-   MHD_thread_ID_set_invalid_(&((hndl_id_ptr)->ID)))
-
-/**
- * Check whether thread handle is valid.
- * To be used in threads other then the thread specified by @a hndl_id.
- */
-#define MHD_thread_handle_ID_is_valid_handle_(hndl_id) \
-    MHD_thread_handle_is_valid_((hndl_id).handle)
-
-/**
- * Set native handle in variable pointed by @a hndl_id_ptr
- * to @a native_val value
- */
-#define MHD_thread_handle_ID_set_native_handle_(hndl_id_ptr,native_val) \
-    MHD_thread_handle_set_native_(&((hndl_id_ptr)->handle),native_val)
-
-#if defined(MHD_thread_handle_get_native_ptr_)
-/**
- * Get pointer to native handle stored the variable pointed by @a hndl_id_ptr
- * @note This macro could not available if direct manipulation of
- *       the native handle is not possible
- */
-#  define MHD_thread_handle_ID_get_native_handle_ptr_(hndl_id_ptr) \
-    MHD_thread_handle_get_native_ptr_(&((hndl_id_ptr)->handle))
-#endif /* MHD_thread_handle_get_native_ptr_ */
-
-/**
- * Get native thread handle from MHD_thread_handle_ID_ variable.
- */
-#define MHD_thread_handle_ID_get_native_handle_(hndl_id) \
-    MHD_thread_handle_get_native_((hndl_id).handle)
-
-/**
- * Check whether thread ID is valid.
- * To be used in the thread itself.
- */
-#define MHD_thread_handle_ID_is_valid_ID_(hndl_id) \
-    MHD_thread_ID_is_valid_((hndl_id).ID)
-
-#if defined(MHD_THREAD_HANDLE_ID_IS_UNION)
-#  if defined(MHD_USE_W32_THREADS)
-#    error MHD_thread_handle_ID_ cannot be a union with W32 threads
-#  endif /* MHD_USE_W32_THREADS */
-/**
- * Set current thread ID in the variable pointed by @a hndl_id_ptr
- */
-#  define MHD_thread_handle_ID_set_current_thread_ID_(hndl_id_ptr) (void) 0
-#else  /* ! MHD_THREAD_HANDLE_ID_IS_UNION */
-/**
- * Set current thread ID in the variable pointed by @a hndl_id_ptr
- */
-#  define MHD_thread_handle_ID_set_current_thread_ID_(hndl_id_ptr) \
-    MHD_thread_ID_set_current_thread_(&((hndl_id_ptr)->ID))
-#endif /* ! MHD_THREAD_HANDLE_ID_IS_UNION */
-
-/**
- * Check whether provided thread ID matches current thread.
- * @param ID thread ID to match
- * @return nonzero on match, zero otherwise
- */
-#define MHD_thread_handle_ID_is_current_thread_(hndl_id) \
-     MHD_thread_ID_is_current_thread_((hndl_id).ID)
-
-/**
- * Wait until specified thread is ended and free thread handle on success.
- * @param hndl_id_ handle with ID to watch
- * @return nonzero on success, zero otherwise
- */
-#define MHD_thread_handle_ID_join_thread_(hndl_id) \
-  MHD_join_thread_(MHD_thread_handle_ID_get_native_handle_(hndl_id))
-
-#if defined(MHD_USE_POSIX_THREADS)
-#  define MHD_THRD_RTRN_TYPE_ void*
-#  define MHD_THRD_CALL_SPEC_
-#elif defined(MHD_USE_W32_THREADS)
-#  define MHD_THRD_RTRN_TYPE_ unsigned
-#  define MHD_THRD_CALL_SPEC_ __stdcall
-#endif
-
-/**
- * Signature of main function for a thread.
- *
- * @param cls closure argument for the function
- * @return termination code from the thread
- */
-typedef MHD_THRD_RTRN_TYPE_
-(MHD_THRD_CALL_SPEC_ *MHD_THREAD_START_ROUTINE_)(void *cls);
-
-
-/**
- * Create a thread and set the attributes according to our options.
- *
- * If thread is created, thread handle must be freed by MHD_join_thread_().
- *
- * @param handle_id     handle to initialise
- * @param stack_size    size of stack for new thread, 0 for default
- * @param start_routine main function of thread
- * @param arg argument  for start_routine
- * @return non-zero on success; zero otherwise (with errno set)
- */
-int
-MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id,
-                    size_t stack_size,
-                    MHD_THREAD_START_ROUTINE_ start_routine,
-                    void *arg);
-
-#ifndef MHD_USE_THREAD_NAME_
-#define MHD_create_named_thread_(t,n,s,r,a) MHD_create_thread_ ((t),(s),(r),(a))
-#else  /* MHD_USE_THREAD_NAME_ */
-/**
- * Create a named thread and set the attributes according to our options.
- *
- * @param handle_id     handle to initialise
- * @param thread_name   name for new thread
- * @param stack_size    size of stack for new thread, 0 for default
- * @param start_routine main function of thread
- * @param arg argument  for start_routine
- * @return non-zero on success; zero otherwise
- */
-int
-MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id,
-                          const char *thread_name,
-                          size_t stack_size,
-                          MHD_THREAD_START_ROUTINE_ start_routine,
-                          void *arg);
-
-#endif /* MHD_USE_THREAD_NAME_ */
-
-#endif /* ! MHD_THREADS_H */

+ 1 - 1
src/testcurl/https/Makefile.am

@@ -57,7 +57,7 @@ THREAD_ONLY_TESTS = \
 check_PROGRAMS = \
   test_https_get_select
 
-if USE_THREADS
+if MHD_USE_THREADS
 check_PROGRAMS += \
   $(THREAD_ONLY_TESTS)
 endif

+ 6 - 0
src/tests/Makefile.am

@@ -0,0 +1,6 @@
+# This Makefile.am is in the public domain
+
+SUBDIRS = basic
+
+
+.NOTPARALLEL:

+ 180 - 0
src/tests/basic/Makefile.am

@@ -0,0 +1,180 @@
+# This Makefile.am is in the public domain
+EMPTY_ITEM =
+
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -DMHD_CPU_COUNT=$(CPU_COUNT) \
+  $(CPPFLAGS_ac)
+
+AM_CFLAGS = $(CFLAGS_ac)
+
+AM_LDFLAGS = $(LDFLAGS_ac)
+
+AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
+
+if USE_COVERAGE
+  AM_CFLAGS += -fprofile-arcs -ftest-coverage
+endif
+
+LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la
+
+$(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile
+	@echo ' cd $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la'; \
+	$(am__cd) $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la
+
+check_PROGRAMS = \
+  test_create_destroy \
+  test_create_start_destroy \
+  test_create_destroy_ipv4 \
+  test_create_start_destroy_ipv4 \
+  test_create_destroy_ipv6 \
+  test_create_start_destroy_ipv6_ipv4 \
+  test_create_destroy_ipbest \
+  test_create_start_destroy_ipbest \
+  $(EMPTY_ITEM)
+
+if MHD_USE_SELECT
+check_PROGRAMS += \
+  test_create_destroy_select \
+  test_create_start_destroy_select \
+  test_create_destroy_select_ipv4 \
+  test_create_start_destroy_select_ipv4 \
+  $(EMPTY_ITEM)
+endif
+
+if MHD_USE_POLL
+check_PROGRAMS += \
+  test_create_destroy_poll \
+  test_create_start_destroy_poll \
+  test_create_destroy_poll_ipv4 \
+  test_create_start_destroy_poll_ipv4 \
+  $(EMPTY_ITEM)
+endif
+
+if MHD_USE_EPOLL
+check_PROGRAMS += \
+  test_create_destroy_epoll \
+  test_create_start_destroy_epoll \
+  test_create_destroy_epoll_ipv4 \
+  test_create_start_destroy_epoll_ipv4 \
+  $(EMPTY_ITEM)
+endif
+
+if MHD_USE_THREADS
+check_PROGRAMS += \
+  test_create_destroy_int_thread \
+  test_create_start_destroy_int_thread \
+  test_create_start_destroy_int_thread_ipv4 \
+  test_create_destroy_thread_per_conn \
+  test_create_start_destroy_thread_per_conn \
+  test_create_start_destroy_thread_per_conn_ipv4 \
+  test_create_destroy_thread_pool \
+  test_create_start_destroy_thread_pool \
+  test_create_start_destroy_thread_pool_ipv4 \
+  $(EMPTY_ITEM)
+  
+if MHD_USE_SELECT
+check_PROGRAMS += \
+  test_create_start_destroy_select_int_thread_ipv4 \
+  test_create_start_destroy_select_thread_per_conn_ipv4 \
+  $(EMPTY_ITEM)
+endif
+
+if MHD_USE_POLL
+check_PROGRAMS += \
+  test_create_start_destroy_poll_int_thread_ipv4 \
+  test_create_start_destroy_poll_thread_per_conn_ipv4 \
+  $(EMPTY_ITEM)
+endif
+
+if MHD_USE_EPOLL
+check_PROGRAMS += \
+  test_create_start_destroy_epoll_int_thread_ipv4 \
+  test_create_start_destroy_epoll_thread_per_conn_ipv4 \
+  $(EMPTY_ITEM)
+endif
+
+endif
+
+if USE_IPV6_TESTING
+check_PROGRAMS += \
+  test_create_start_destroy_ipv6
+endif
+
+TESTS = $(check_PROGRAMS)
+
+# The universal sources used in all tests
+basic_test_sources = test_basic_checks.c
+
+test_create_destroy_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_SOURCES = $(basic_test_sources)
+
+test_create_destroy_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_ipv6_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_ipv6_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_ipv6_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_ipbest_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_ipbest_SOURCES = $(basic_test_sources)
+
+test_create_destroy_select_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_select_SOURCES = $(basic_test_sources)
+
+test_create_destroy_select_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_select_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_poll_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_poll_SOURCES = $(basic_test_sources)
+
+test_create_destroy_poll_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_poll_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_epoll_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_epoll_SOURCES = $(basic_test_sources)
+
+test_create_destroy_epoll_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_epoll_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_int_thread_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_int_thread_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_int_thread_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_thread_per_conn_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_thread_per_conn_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_thread_per_conn_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_destroy_thread_pool_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_thread_pool_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_thread_pool_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_select_int_thread_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_select_thread_per_conn_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_poll_int_thread_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_poll_thread_per_conn_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_epoll_int_thread_ipv4_SOURCES = $(basic_test_sources)
+
+test_create_start_destroy_epoll_thread_per_conn_ipv4_SOURCES = $(basic_test_sources)
+

+ 363 - 0
src/tests/basic/test_basic_checks.c

@@ -0,0 +1,363 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2016, 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 test_start_destroy.c
+ * @brief  test for create, start and destroy
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <microhttpd2.h>
+
+/* Helper macros */
+
+#define ERR_PRINT_LINE() \
+        ((void) fprintf (stderr, "At the line number %u: ", \
+                         (unsigned) __LINE__))
+
+/**
+ * Check whether SC code is OK, print error if not.
+ * @warning Do not use function call in the argument
+ * @param sc the status code to check
+ */
+#define tst_EXPECT_OK(sc) \
+        ( (MHD_SC_OK == (sc)) ? (! 0) :                                    \
+          (ERR_PRINT_LINE (),                                              \
+           ((void) fprintf (stderr, "MHD function failed, returned: %u\n", \
+                            (unsigned int) (sc))), (0)) )
+#if 0
+#define tst_EXPECT_OK(sc) \
+        ( (MHD_SC_OK == (sc)) ? (! 0) :                            \
+          (ERR_PRINT_LINE (),                                      \
+           ((void) fprintf (stderr,                                \
+                            "MHD function failed, returned: %s\n", \
+                            MHD_status_code_to_string_lazy (sc))), (0)) )
+#endif
+
+/**
+ * Check whether SC code is OK, print error if not.
+ * @warning Do not use function call in the argument
+ * @param sc the status code to check
+ */
+#define tst_EXPECT_FAIL(sc) \
+        ( (MHD_SC_OK != (sc)) ? (! 0) : \
+          (ERR_PRINT_LINE (),           \
+           ((void) fprintf (stderr, "MHD function unexpectedly succeed.\n")), \
+           (0)) )
+
+/**
+ * Check whether SC code is success/failure as expected, print error if not.
+ * @warning Do not use function call in the argument
+ * @param sc the status code to check
+ * @param expect_ok non-zero if SC should be OK, zero is SC should NOT be OK
+ */
+#define tst_EXPECT_CHECK(sc,expect_ok) \
+        ((expect_ok) ? tst_EXPECT_OK ((sc)) : tst_EXPECT_FAIL ((sc)))
+
+/* Helper functions */
+
+/**
+ * Check whether program name contains specific @a marker string.
+ * Only last component in pathname is checked for marker presence,
+ * all leading directories names (if any) are ignored. Directories
+ * separators are handled correctly on both non-W32 and W32
+ * platforms.
+ * @param prog_name the program name, may include path
+ * @param marker    the marker to look for
+ * @return zero if any parameter is NULL or empty string or
+ *         @a prog_name ends with slash or @a marker is not found in
+ *         program name, non-zero if @a maker is found in program
+ *         name.
+ */
+static int
+has_in_name (const char *prog_name, const char *marker)
+{
+  const char *s;
+  const char *basename;
+
+  if (! prog_name || ! marker || ! prog_name[0] || ! marker[0])
+    return 0;
+
+  basename = prog_name;
+  for (s = prog_name; *s; ++s)
+  {
+    if ('/' == *s)
+      basename = s + 1;
+#if defined(_WIN32) || defined(__CYGWIN__)
+    else if ('\\' == *s)
+      basename = s + 1;
+#endif /* _WIN32 || __CYGWIN__ */
+  }
+
+  return strstr (basename, marker) != NULL;
+}
+
+
+/* The test */
+
+static int use_start = 0;
+
+static int use_ipv4 = 0;
+
+static int use_ipv6 = 0;
+
+static int use_ip_best = 0;
+
+static int use_select = 0;
+
+static int use_poll = 0;
+
+static int use_epoll = 0;
+
+static int use_int_thread = 0;
+
+static int use_thread_per_conn = 0;
+
+static int use_thread_pool = 0;
+
+/* Dynamic run-time variables */
+
+static int err_flag = 0;
+
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) static const struct MHD_Action *
+my_req_process (void *cls,
+                struct MHD_Request *request,
+                const struct MHD_String *path,
+                enum MHD_HTTP_Method method,
+                uint_fast64_t upload_size)
+{
+  (void) cls; (void) request; (void) path; (void) method; (void) upload_size;
+  // MHD_lib_global_fake();
+  fprintf (stderr, "Unexpected call of the request callback.\n");
+  err_flag = ! 0;
+  return NULL;
+}
+
+
+static struct MHD_Daemon *
+test_daemon_create (void)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_daemon_create (my_req_process, NULL);
+  if (NULL == d)
+  {
+    err_flag = ! 0;
+    ERR_PRINT_LINE ();
+    fprintf (stderr, "MHD_daemon_create() failed, NULL returned.\n");
+    return NULL;
+  }
+  return d;
+}
+
+
+static int
+test_daemon_setup (struct MHD_Daemon *d,
+                   int should_succeed)
+{
+  enum MHD_StatusCode sc;
+  int ret = ! 0;
+
+  if (use_ipv6)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL_v4_OPTIONAL, 0));
+    if (! tst_EXPECT_CHECK (sc,should_succeed))
+      ret = 0;
+  }
+
+  if (use_ipv4)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL_v6_OPTIONAL, 0));
+    if (! tst_EXPECT_CHECK (sc,should_succeed))
+      ret = 0;
+  }
+
+  if (use_ip_best)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, 0));
+    if (! tst_EXPECT_CHECK (sc,should_succeed))
+      ret = 0;
+  }
+
+  if (use_select)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_SELECT));
+    if (! tst_EXPECT_CHECK (sc,should_succeed))
+      ret = 0;
+  }
+
+  if (use_poll)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_POLL));
+    if (! tst_EXPECT_CHECK (sc, should_succeed))
+      ret = 0;
+  }
+
+  if (use_epoll)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_EPOLL));
+    if (! tst_EXPECT_CHECK (sc,should_succeed))
+      ret = 0;
+  }
+
+  if (use_int_thread)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_WORKER_THREADS (1)));
+    if (! tst_EXPECT_CHECK (sc, should_succeed))
+      ret = 0;
+  }
+
+  if (use_thread_per_conn)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_THREAD_PER_CONNECTION ()));
+    if (! tst_EXPECT_CHECK (sc, should_succeed))
+      ret = 0;
+  }
+
+  if (use_thread_pool)
+  {
+    sc = MHD_DAEMON_SET_OPTIONS ( \
+      d, MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_WORKER_THREADS (4)));
+    if (! tst_EXPECT_CHECK (sc, should_succeed))
+      ret = 0;
+  }
+
+  if (! ret)
+    err_flag = ! 0;
+
+  return ret;
+}
+
+
+static int
+test_daemon_start (struct MHD_Daemon *d,
+                   int should_succeed)
+{
+  enum MHD_StatusCode sc;
+
+  sc = MHD_daemon_start (d);
+  if (! tst_EXPECT_CHECK (sc,should_succeed))
+  {
+    err_flag = ! 0;
+    return 0;
+  }
+
+  return ! 0;
+}
+
+
+static int
+test_simple (void)
+{
+  struct MHD_Daemon *d;
+  int ret = ! 0;
+
+  err_flag = 0;
+
+  d = test_daemon_create ();
+  if (NULL == d)
+    return (ret && ! err_flag);
+
+  test_daemon_setup (d, ! 0);
+  if (use_start)
+    test_daemon_start (d, ! 0);
+
+  test_daemon_setup (d, ! use_start);
+
+  if (use_start)
+    test_daemon_start (d, 0); /* Second "start" should fail */
+
+  MHD_daemon_destroy (d);
+
+  return (ret && ! err_flag);
+}
+
+
+/**
+ * Initialise the test data
+ * @param prog_name the name of the this program
+ * @return non-zero if succeed,
+ *         zero if failed
+ */
+static int
+init_test (const char *prog_name)
+{
+  if (has_in_name (prog_name, "_start"))
+    use_start = ! 0;
+
+  if (has_in_name (prog_name, "_ipv4"))
+    use_ipv4 = ! 0;
+
+  if (has_in_name (prog_name, "_ipv6"))
+    use_ipv6 = ! 0;
+
+  if (has_in_name (prog_name, "_ipbest"))
+    use_ip_best = ! 0;
+
+  use_select = has_in_name (prog_name, "_select");
+
+  use_poll = has_in_name (prog_name, "_poll");
+
+  use_epoll = has_in_name (prog_name, "_epoll");
+
+  use_int_thread = has_in_name (prog_name, "_int_thread");
+
+  use_thread_per_conn = has_in_name (prog_name, "_thread_per_conn");
+
+  use_thread_pool = has_in_name (prog_name, "_thread_pool");
+
+  return ! 0;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  unsigned int num_err = 0;
+  (void) argc; /* Unused. Silence compiler warning. */
+
+  if (! init_test (argv[0]))
+  {
+    fprintf (stderr, "Failed to initialise the test!\n");
+    return 77;
+  }
+
+  if (! test_simple ())
+    ++num_err;
+
+  if (0 != num_err)
+  {
+    fprintf (stderr, "Number of failed checks: %u\n", num_err);
+    return 2;
+  }
+
+  printf ("All checks succeed.\n");
+  return 0;
+}

+ 1 - 1
src/tools/Makefile.am

@@ -29,7 +29,7 @@ $(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/
 # Tools
 noinst_PROGRAMS = 
 
-if USE_THREADS
+if MHD_USE_THREADS
 noinst_PROGRAMS += \
     perf_replies
 endif

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff