commit c744d067ebcec0677cf7fb6c769b5bd72a679143
parent 6775213ecb06de5e7064dcca8019b8bccc76ff96
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Sat, 27 Jul 2024 12:06:21 +0200
Implemented MHD2 core functionality + basic tests
Diffstat:
134 files changed, 36577 insertions(+), 951 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -4,10 +4,11 @@ Makefile
.libs/
/test-driver
/INSTALL
-/libmicrohttpd.pc
/configure.lineno
/config.status.lineno
/config.log
+/conftest*
+/confdef*
/debug
build-aux/
/exclude
diff --git a/Makefile.am b/Makefile.am
@@ -3,7 +3,7 @@ ACLOCAL_AMFLAGS = -I m4
SUBDIRS = contrib m4 src .
if BUILD_DOC
-SUBDIRS += doc
+# SUBDIRS += doc
endif
@@ -40,13 +40,9 @@ W32VSAV = w32/VS-Any-Version/libmicrohttpd.vcxproj w32/VS-Any-Version/libmicroht
w32/VS-Any-Version/libmicrohttpd.sln
EXTRA_DIST = \
- libmicrohttpd.pc.in \
$(W32COMMON) $(W32VS2013) $(W32VS2015) $(W32VS2017) \
$(W32VS2019) $(W32VS2022) $(W32VSAV)
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libmicrohttpd.pc
-
EXTRA_DIST += pre-dist-hook-dummy
MOSTLYCLEANFILES = pre-dist-hook-dummy
DISTCLEANFILES =
diff --git a/configure.ac b/configure.ac
@@ -30,22 +30,23 @@ AC_CONFIG_HEADERS([src/incl_priv/mhd_config.h])
AC_CONFIG_MACRO_DIR([m4])
m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl
-LIB_VERSION_CURRENT=80
-LIB_VERSION_REVISION=1
-LIB_VERSION_AGE=68
-AC_SUBST([LIB_VERSION_CURRENT])
-AC_SUBST([LIB_VERSION_REVISION])
-AC_SUBST([LIB_VERSION_AGE])
+LIB_VER_CURRENT=0
+LIB_VER_REVISION=0
+LIB_VER_AGE=0
+AC_SUBST([LIB_VER_CURRENT])
+AC_SUBST([LIB_VER_REVISION])
+AC_SUBST([LIB_VER_AGE])
PACKAGE_VERSION_MAJOR='m4_car(m4_unquote(m4_split(AC_PACKAGE_VERSION, [\.])))'
PACKAGE_VERSION_MINOR='m4_argn(2, m4_unquote(m4_split(AC_PACKAGE_VERSION, [\.])))'
PACKAGE_VERSION_SUBMINOR='m4_argn(3, m4_unquote(m4_split(AC_PACKAGE_VERSION, [\.])))'
-AS_VAR_ARITH([MHD_W32_DLL_SUFF],[[$LIB_VERSION_CURRENT - $LIB_VERSION_AGE]])
+AS_VAR_ARITH([MHD_W32_DLL_SUFF],[[$LIB_VER_CURRENT - $LIB_VER_AGE]])
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/mhd2/w32_lib_res.rc])
MHD_LIB_CPPFLAGS=""
MHD_LIB_CFLAGS=""
@@ -479,12 +480,12 @@ CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
# Additional flags are checked and added at the end of 'configure'
# Check for headers that are ALWAYS required
-AC_CHECK_HEADERS_ONCE([stdio.h string.h stdint.h errno.h limits.h fcntl.h], [],
- [AC_MSG_ERROR([Compiling libmicrohttpd requires standard POSIX headers files])], [AC_INCLUDES_DEFAULT])
+AC_CHECK_HEADERS_ONCE([stdio.h string.h stdint.h stdarg.h errno.h limits.h fcntl.h], [],
+ [AC_MSG_ERROR([Compiling libmicrohttpd requires standard C and POSIX headers files])], [AC_INCLUDES_DEFAULT])
# 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], [], [], [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])
@@ -496,8 +497,8 @@ AC_CHECK_HEADERS([endian.h machine/endian.h sys/endian.h sys/byteorder.h \
# Check for network and sockets optional headers
AC_CHECK_HEADERS([sys/socket.h sys/select.h netinet/in_systm.h netinet/in.h \
- arpa/inet.h netinet/ip.h netinet/tcp.h net/if.h \
- netdb.h sockLib.h inetLib.h], [], [],
+ sys/un.h arpa/inet.h netinet/ip.h netinet/tcp.h net/if.h \
+ netdb.h sockLib.h inetLib.h selectLib.h afunix.h], [], [],
[AC_INCLUDES_DEFAULT
[
#ifdef HAVE_SYS_TYPES_H
@@ -508,7 +509,9 @@ AC_CHECK_HEADERS([sys/socket.h sys/select.h netinet/in_systm.h netinet/in.h \
#endif /* HAVE_INTTYPES_H */
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
-#endif /* HAVE_SYS_SOCKET_H */
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif /* HAVE_NETINET_IN_SYSTM_H */
@@ -527,6 +530,72 @@ AC_CHECK_HEADERS([sys/socket.h sys/select.h netinet/in_systm.h netinet/in.h \
# Check for other optional headers
AC_CHECK_HEADERS([sys/msg.h sys/mman.h signal.h], [], [], [AC_INCLUDES_DEFAULT])
+AC_CHECK_TYPES([size_t,ssize_t,ptrdiff_t,intptr_t,uintptr_t,uint8_t],[],[],
+ [[
+/* Keep in sync with src/mhd2/sys_base_types.h */
+#include <stdint.h> /* uint_fast_XXt, int_fast_XXt */
+#if defined(HAVE_STDDEF_H)
+# include <stddef.h> /* size_t, NULL */
+#elif defined(HAVE_STDLIB_H)
+# include <stdlib.h> /* should provide size_t, NULL */
+#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
+ ]]
+)
+AS_IF([test "x$ac_cv_type_size_t" != "xyes"],
+ [AC_MSG_FAILURE(['size_t' type is not provided by system headers])]
+)
+AS_IF([test "x$ac_cv_type_uint8_t" != "xyes"],
+ [AC_MSG_FAILURE(['uint8_t' type is not provided by system headers])]
+)
+AC_CHECK_SIZEOF([char])
+AS_IF([test "x$ac_cv_sizeof_char" != "x1"],
+ [AC_MSG_FAILURE(['char' type with size different from '1' is not supported])]
+)
+
+
+AC_CHECK_TYPE([socklen_t],
+ [],
+ [AC_DEFINE([socklen_t],[int],[Define to suitable 'socklen_t' replacement if 'socklen_t' is not defined by system headers])],
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#include <stdint.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif /* HAVE_NETINET_IN_SYSTM_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#ifdef HAVE_SOCKLIB_H
+#include <sockLib.h>
+#endif /* HAVE_SOCKLIB_H */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif /* _WIN32 && ! __CYGWIN__ */
+ ]]
+)
+
AC_CHECK_HEADER([[search.h]],
[
MHD_CHECK_LINK_RUN([[for proper tsearch(), tfind() and tdelete()]],[[mhd_cv_sys_tsearch_usable]],
@@ -659,7 +728,9 @@ AC_CHECK_HEADERS([sys/sysctl.h netinet/ip_icmp.h netinet/icmp_var.h], [], [],
#endif /* HAVE_SYS_SYSCTL_H */
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
-#endif /* HAVE_SYS_SOCKET_H */
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif /* HAVE_NETINET_IN_SYSTM_H */
@@ -845,434 +916,818 @@ AS_VAR_IF([[mhd_cv_macro_true_false_valid]], [["yes"]], [[:]],
[AC_MSG_ERROR([[Value of "true" or value of "false" is not valid. Check config.log for details.]])])
-AX_CHECK_COMPILE_FLAG([[-Werror=attributes]],
+AC_CACHE_CHECK([whether the NULL pointer has all zero bits],
+ [mhd_cv_ptr_null_all_zeros],
[
- AC_MSG_CHECKING([[whether -Werror=attributes actually works]])
- CFLAGS="${CFLAGS_ac} ${user_CFLAGS} -Werror=attributes"
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
- [[__attribute__((non_existing_attrb_dummy)) static int SimpleFunc(void) {return 3;}]],
- [[int r = SimpleFunc(); if (r) return r;]])],
- [
- AC_MSG_RESULT([[no]])
- errattr_CFLAGS=""
- ], [
- AC_MSG_RESULT([[yes]])
- errattr_CFLAGS="-Werror=attributes"
- ])
- CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
- ],
- [[errattr_CFLAGS=""]], [], [])
-
-AC_MSG_CHECKING([[for function inline keywords supported by $CC]])
-CFLAGS="${CFLAGS_ac} ${user_CFLAGS} $errattr_CFLAGS"
-inln_prfx="none"
-# Prefer always inline functions
-for inln_prfx_chk in InlineWithAttr __forceinline inline __inline__ __inline _inline _Inline
-do
- # Try to link to avoid "symbol undefined" problems at build time
- AS_IF([[test "x$inln_prfx_chk" = "xInlineWithAttr"]],
- [
- AS_IF([[test "x$errattr_CFLAGS" = "x"]],
- [[ # Skip test with attribute as negative result can't be detected
- inln_prfx_chk="__forceinline" # use next value
- ]],[[inln_prfx_chk="inline __attribute__((always_inline))"]])
- ])
- AC_LINK_IFELSE(
- [
- AC_LANG_PROGRAM(
- [[
-#ifdef __cplusplus
-#error This test is only for C.
-choke me
-#endif
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif
- static $inln_prfx_chk bool cmpfn(int x, int y)
- { return x > y; }
- static $inln_prfx_chk int sumfn(int x, int y)
- { return x + y; }
- ]],[[
- int a = 1, b = 100, c;
- if (cmpfn(a, b))
- c = sumfn(a, b);
- else
- c = 0 - sumfn(a, b);
- if (c)
- return 0;
- ]])
- ],
- [[ inln_prfx="$inln_prfx_chk" ]])
- test "x$inln_prfx" != "xnone" && break
-done
-AS_IF([[test "x$inln_prfx" != "xnone"]],
+ AC_RUN_IFELSE(
[
- AC_DEFINE([INLINE_FUNC],[1],[Define to 1 if your C compiler supports inline functions.])
- AC_DEFINE_UNQUOTED([_MHD_static_inline],[static $inln_prfx],[Define to prefix which will be used with MHD static inline functions.])
- ], [
- AC_DEFINE([_MHD_static_inline],[static],[Define to prefix which will be used with MHD static inline functions.])
- ])
-AC_MSG_RESULT([[$inln_prfx]])
-CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
-
-AC_CHECK_HEADERS([stdalign.h], [], [], [AC_INCLUDES_DEFAULT])
-AC_CACHE_CHECK([[for C11 'alignof()' support]], [[mhd_cv_c_alignof]],
- [AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [[
-#ifdef HAVE_STDALIGN_H
-#include <stdalign.h>
+ AC_LANG_SOURCE([[
+#include <string.h>
+#if defined(HAVE_STDDEF_H)
+#include <stddef.h>
+#elif defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#else
+#include <stdio.h>
#endif
- ]], [[
- int var1[(alignof(int) >= 2) ? 1 : -1];
- int var2[alignof(unsigned int) - 1];
- int var3[(alignof(char) > 0) ? 1 : -1];
- int var4[(alignof(long) >= 4) ? 1 : -1];
- /* Mute compiler warnings */
- var1[0] = var2[0] = var3[0] = 0;
- var4[0] = 1;
- if (var1[0] + var2[0] + var3[0] == var4[0])
- return 1;
- ]])
- ], [
- AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [[
-#ifdef HAVE_STDALIGN_H
-#include <stdalign.h>
-#endif
- ]], [[
- /* Should fail if 'alignof()' works */
- int var1[alignof(nonexisting_type) - 1];
+int main(void)
+{
+ void *ptr1;
+ void *ptr2;
- /* Mute compiler warnings */
- var1[0] = 1;
- if (var1[0] + 1 == 1)
- return 1;
- ]])
- ], [[mhd_cv_c_alignof='no']], [[mhd_cv_c_alignof='yes']])
- ], [[mhd_cv_c_alignof='no']])
- ])
-AS_VAR_IF([mhd_cv_c_alignof], ["yes"],
- [AC_DEFINE([[HAVE_C_ALIGNOF]], [1], [Define to 1 if your compiler supports 'alignof()'])])
+ ptr1 = &ptr2;
+ ptr2 = NULL;
+ memset(&ptr1, 0, sizeof(ptr1));
+ if (ptr2 != ptr1)
+ return 2;
+ ptr2 = &ptr1;
+ ptr1 = NULL;
+ memset(&ptr2, 0, sizeof(ptr2));
+ if (0 != memcmp (&ptr1, &ptr2, sizeof(ptr1)))
+ return 3;
-# Check system type
-AC_MSG_CHECKING([[for target host OS]])
-os_is_windows="no"
-os_is_native_w32="no"
-AS_CASE(["$host_os"],
- [*darwin* | *rhapsody* | *macosx*],
- [AC_DEFINE([OSX],[1],[This is an OS X system])
- mhd_host_os='Darwin'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [kfreebsd*-gnu],
- [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
- AC_DEFINE([FREEBSD],[1],[This is a FreeBSD system])
- mhd_host_os='FreeBSD kernel with GNU userland'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [freebsd*],
- [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
- AC_DEFINE([FREEBSD],[1],[This is a FreeBSD system])
- mhd_host_os='FreeBSD'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [openbsd*],
- [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
- AC_DEFINE([OPENBSD],[1],[This is an OpenBSD system])
- mhd_host_os='OpenBSD'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [netbsd*],
- [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
- AC_DEFINE([NETBSD],[1],[This is a NetBSD system])
- mhd_host_os='NetBSD'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [*solaris*],
- [AC_DEFINE([SOLARIS],[1],[This is a Solaris system])
- AC_DEFINE([_REENTRANT],[1],[Need with solaris or errno does not work])
- mhd_host_os='Solaris'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [*linux*],
- [AC_DEFINE([LINUX],[1],[This is a Linux kernel])
- mhd_host_os='Linux'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [*cygwin*],
- [AC_DEFINE([CYGWIN],[1],[This is a Cygwin system])
- mhd_host_os='Windows/Cygwin'
- AC_MSG_RESULT([[$mhd_host_os]])
- os_is_windows="yes"],
- [*mingw*],
- [
- AC_DEFINE([MINGW],[1],[This is a MinGW system])
- AC_DEFINE([WINDOWS],[1],[This is a Windows system])
- mhd_host_os='Windows/MinGW'
- AC_MSG_RESULT([[$mhd_host_os]])
- AC_CHECK_HEADERS([winsock2.h ws2tcpip.h], [], [AC_MSG_ERROR([[Winsock2 headers are required for W32]])], [AC_INCLUDES_DEFAULT])
- AC_CACHE_CHECK([for MS lib utility], [ac_cv_use_ms_lib_tool],
- [mslibcheck=`lib 2>&1`
- AS_IF([echo "$mslibcheck" | $GREP -e '^Microsoft (R) Library Manager' - >/dev/null],
- [ac_cv_use_ms_lib_tool=yes],
- [ac_cv_use_ms_lib_tool=no])
- ])
- AS_IF([test "x$ac_cv_use_ms_lib_tool" = "xyes"],
- [AC_SUBST([MS_LIB_TOOL], [[lib]])])
- AC_SUBST([lt_cv_objdir])
- os_is_windows="yes"
- os_is_native_w32="yes"
- ],
- [*openedition*],
- [AC_DEFINE([OS390],[1],[This is a OS/390 system])
- mhd_host_os='OS/390'
- AC_MSG_RESULT([[$mhd_host_os]])],
- [gnu*],
- [AC_DEFINE([[GNU_HURD]], [[1]], [Define to `1' if host machine runs on GNU Hurd.])
- mhd_host_os='GNU Hurd'
- AC_MSG_RESULT([[$mhd_host_os]])
- ],
- [
- AC_MSG_RESULT([unrecognised OS])
- mhd_host_os="${host_os}"
- AC_MSG_WARN([Unrecognised OS $host_os])
- AC_DEFINE([OTHEROS],1,[Some strange OS])
- ])
+ ptr1 = (void*)&ptr1;
+ ptr2 = (void*)&ptr2;
+ memset(&ptr1, 0, sizeof(ptr1));
+ memset(&ptr2, 0, sizeof(ptr2));
+ if (NULL != ptr1)
+ return 4;
+ if (NULL != ptr2)
+ return 5;
-AM_CONDITIONAL([CYGWIN_TARGET], [[test "x$os_is_windows" = "xyes" && \
- test "x${os_is_native_w32}" != "xyes"]])
+ return 0;
+}
+ ]]
+ )
+ ],
+ [mhd_cv_ptr_null_all_zeros="yes"],
+ [mhd_cv_ptr_null_all_zeros="no"],
+ [
+ AS_CASE([$host_cpu],dnl
+ [[i[234567]86|x86_64|amd64|arm|armeb|armv[0123456789]|armv[0123456789]eb|aarch64|aarch64_be|arm64|mips|mipsel|mips64|mips64el|powerpc|powerpcle|powerpc64|powerpc64le|riscv32|riscv32be|riscv64|riscv64be]],
+ [
+ AS_CASE([$host_os],dnl
+ [[linux*|freebsd|cygwin|mingw*|msys|gnu|netbsd*|openbsd*|darwin*|solaris2*|haiku]],
+ [mhd_cv_ptr_null_all_zeros="assuming yes"],
+ [mhd_cv_ptr_null_all_zeros="assuming no"]
+ )
+ ],
+ [mhd_cv_ptr_null_all_zeros="assuming no"]
+ )
+ ]
+ )
+ ]
+)
+AS_IF([test "x${mhd_cv_ptr_null_all_zeros}" = "xyes" || test "x${mhd_cv_ptr_null_all_zeros}" = "xassuming yes"],
+ [AC_DEFINE([HAVE_NULL_PTR_ALL_ZEROS],[1],[Define to '1' if NULL pointers binary representation is all zero bits])]
+)
-AS_VAR_IF([os_is_windows], ["yes"],
+AC_CACHE_CHECK([whether $CC supports variadic macros],[mhd_cv_cc_macro_variadic],
[
- AC_MSG_CHECKING([[whether target W32 version is specified by precompiler defines]])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-/* Note: check logic is reversed for easy log reading */
-#ifdef WINVER
-#error WINVER is defined
-choke me now;
-#endif
-#ifdef _WIN32_WINNT
-#error _WIN32_WINNT is defined
-choke me now;
-#endif
-#ifdef NTDDI
-#error NTDDI is defined
-choke me now;
-#endif
- ]],[[(void)0]])
- ], [[mhd_w32_ver_preselect=no]], [[mhd_w32_ver_preselect=yes]]
+ AS_CASE([$ac_prog_cc_stdc],
+ [c89],[:],
+ [c??],[mhd_cv_cc_macro_variadic="yes"]
)
- AC_MSG_RESULT([[${mhd_w32_ver_preselect}]])
- AC_CHECK_HEADERS([windows.h sdkddkver.h], [], [], [AC_INCLUDES_DEFAULT])
- AS_VAR_IF([mhd_w32_ver_preselect],["yes"],
- [
- AC_MSG_CHECKING([[for specified target W32 version]])
- AS_UNSET([[mhd_w32_ver]])
- AS_UNSET([[mhd_w32_ver_msg]])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#if _WIN32_WINNT+0 < 0x0501
-/* Check before headers inclusion */
-#error _WIN32_WINNT is less than 0x0501
-choke me now;
-#endif
+ AS_VAR_SET_IF([mhd_cv_cc_macro_variadic],[:],
+ [
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ AC_COMPILE_IFELSE(
+ [
+ AC_LANG_SOURCE([[
+#define GET_THIRD(arg1,arg2,arg3,arg4) (arg3)
+#define GET_THIRD_VARIADIC(...) GET_THIRD(__VA_ARGS__)
-#ifdef HAVE_SDKDDKVER_H
-#include <sdkddkver.h>
-#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-#if _WIN32_WINNT+0 < 0x0501
-#error _WIN32_WINNT is less than 0x0501
-choke me now;
-#endif
- ]],[[(void)0]])
- ], [], [
- AC_MSG_RESULT([[pre-WinXP]])
- AC_MSG_ERROR([[libmicrohttpd cannot be compiled for Windows version before Windows XP]])
- ]
- )
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#ifdef HAVE_SDKDDKVER_H
-#include <sdkddkver.h>
-#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-#if _WIN32_WINNT+0 == 0x0501
-#error _WIN32_WINNT is 0x0501
-choke me now;
-#endif
-#if _WIN32_WINNT+0 == 0x0502
-#error _WIN32_WINNT is 0x0502
-choke me now;
-#endif
- ]],[[(void)0]])
- ], [], [
- mhd_w32_ver="WinXP"
- mhd_w32_ver_msg="WinXP (selected by precompiler flags)"
- ]
- )
- AS_VAR_SET_IF([mhd_w32_ver], [],
- [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#ifdef HAVE_SDKDDKVER_H
-#include <sdkddkver.h>
-#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
+int main(void)
+{
+ int arr[GET_THIRD_VARIADIC(-1,-2,5,-3) - 4] = {0};
+ if (0 != arr[GET_THIRD_VARIADIC(100,1000,0,10000)])
+ return 2;
+ return 0;
+}
+ ]]
+ )
+ ],
+ [mhd_cv_cc_macro_variadic="yes"],
+ [mhd_cv_cc_macro_variadic="no"]
+ )
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
+ ]
+ )
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_macro_variadic],["yes"],
+ [AC_DEFINE([HAVE_MACRO_VARIADIC],[1],[Define to '1' if your compiler supports variadic macros])]
+)
+AC_CACHE_CHECK([whether $CC supports compound literals],[mhd_cv_cc_compound_literals],
+ [
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ AC_COMPILE_IFELSE(
+ [
+ AC_LANG_SOURCE([[
+struct test_strct
+{
+ char c;
+ int i;
+};
-#if _WIN32_WINNT+0 < 0x0600
-#error _WIN32_WINNT is less than 0x0600 but greater than 0x0502
-choke me now;
-#endif
- ]],[[(void)0]])
- ], [], [
- AC_MSG_ERROR([[_WIN32_WINNT value is wrong (less than 0x0600 but greater than 0x0502)]])
- ]
- )
+int main(void)
+{
+ struct test_strct strct_var;
+ int i;
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#ifdef HAVE_SDKDDKVER_H
-#include <sdkddkver.h>
-#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
+ strct_var = (struct test_strct) { 'a', 0 };
-#if _WIN32_WINNT+0 == 0x0600
-#error _WIN32_WINNT is 0x0600
-choke me now;
-#endif
- ]],[[(void)0]])
- ], [], [
- mhd_w32_ver="Vista"
- mhd_w32_ver_msg="Vista (selected by precompiler flags)"
- ]
- )
- ]
- )
+ i = (int){0};
- AS_VAR_SET_IF([mhd_w32_ver], [],
- [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#ifdef HAVE_SDKDDKVER_H
-#include <sdkddkver.h>
-#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
+ if (strct_var.i != i) /* Process all variables to avoid warnings */
+ return 2;
-#if _WIN32_WINNT+0 > 0x0600
-#error _WIN32_WINNT is greater than 0x0600
-choke me now;
-#endif
- ]],[[(void)0]])
- ], [
- mhd_w32_ver="unknown"
- mhd_w32_ver_msg="unknown (cannot be detected)"
- ], [
- mhd_w32_ver="newer than Vista"
- mhd_w32_ver_msg="newer than Vista (selected by precompiler flags)"
- ]
- )
- ]
- )
- AC_MSG_RESULT([[${mhd_w32_ver}]])
- ], [
- mhd_w32_ver="Vista"
- mhd_w32_ver_msg="Vista (default, override by CPPFLAGS=-D_WIN32_WINNT=0xNNNN)"
- CPPFLAGS_ac="${CPPFLAGS_ac} -D_WIN32_WINNT=0x0600"
- CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}"
- AC_MSG_CHECKING([[whether headers accept _WIN32_WINNT=0x0600]])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#ifdef HAVE_SDKDDKVER_H
-#include <sdkddkver.h>
-#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-#include <stdio.h>
- ]],[[(void)0]])
- ], [
- AC_MSG_RESULT([[yes]])
- ], [
- AC_MSG_RESULT([[no]])
- AC_MSG_ERROR([Headers do not accept _WIN32_WINNT=0x0600. Consider override target W32 version by CPPFLAGS=-D_WIN32_WINNT=0xNNNN])
- ]
- )
- ]
+ return 0;
+}
+ ]]
+ )
+ ],
+ [mhd_cv_cc_compound_literals="yes"],
+ [mhd_cv_cc_compound_literals="no"]
)
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
]
)
-
-AS_IF([test "x${os_is_windows}" = "xyes" && test "x${os_is_native_w32}" = "xyes"],
+AS_VAR_IF([mhd_cv_cc_compound_literals],["yes"],
[
- AC_CACHE_CHECK([W32 run-time library type], [mhd_cv_wctr_type],
+ AC_DEFINE([HAVE_COMPOUND_LITERALS],[1],[Define to '1' if your compiler supports compound literals])
+
+ AC_CACHE_CHECK([whether $CC supports compound literals as arrays of the scope],[mhd_cv_cc_compound_literals_arr_scope],
[
- AC_EGREP_CPP([MHDMARKER: UCRT run-time library in use!], [
-#include <stdio.h>
-#if defined(_UCRT)
-#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
-#endif
-#if defined(__MSVCRT_VERSION__)
-#if (__MSVCRT_VERSION__ >= 0xE00) && (__MSVCRT_VERSION__ < 0x1000)
-#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
-#endif
-#if (__MSVCRT_VERSION__ > 0x1400)
-#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
-#endif
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ AC_COMPILE_IFELSE(
+ [
+ AC_LANG_SOURCE([[
+#ifdef __cplusplus
+choke me now; /* Clang++ actually may handle this check properly, but it is too tricky to test it corretly */
+#error This is a C++ compiler
#endif
-#ifndef CRT_STR
-#define CRT_STR "MHDMARKER: MSVCRT run-time library in use!"
-#endif
+struct test_strct
+{
+ char c;
+ int i;
+};
+
+static int test_strct_func(struct test_strct *strc)
+{
+ return 3 == strc->i;
+}
int main(void)
{
- printf ("%\n", CRT_STR);
- return 0;
+ struct test_strct *strct_ptr1;
+ struct test_strct *strct_ptr2;
+ int *int_ptr;
+ void *ptr;
+
+ ptr = (struct test_strct[]) { { 'p', 11 }, { 'q', 22 }, { 'r', 33 } };
+ strct_ptr1 = (struct test_strct[]) { { 'a', 1 }, { 'b', 2 }, { 'c', 3 } };
+ int_ptr = (int[]){0xFF,0xFE,0xFD,0xFC,0xFB,0xFA};
+
+ if (2 != (strct_ptr1 + 1)->i)
+ return 3;
+
+ strct_ptr2 = (struct test_strct[]) { { 'a', 8 }, { 'b', 9 }, { 'c', 7 } };
+
+ if (0xFE != int_ptr[1])
+ return 4;
+
+ if (*(int_ptr + 4) != 0xFB)
+ return 5;
+
+ if (!test_strct_func(strct_ptr1 + 2))
+ return 6;
+
+ if (int_ptr[2] != 0xFD)
+ return 7;
+
+ if (((struct test_strct *)ptr)[0].c == 'p')
+ ptr = (struct test_strct[]) { { 'x', 55 }, { 'y', 66 }, { 'z', 77 } };
+
+ if (*((char *)ptr) != 'x')
+ return 8;
+
+ if (*(int_ptr + 5) != 0xFA)
+ return 9;
+
+ if (strct_ptr2[1].c != 'b')
+ return 10;
+
+ return strct_ptr1[1].i == 2 ? 0 : 11;
}
+ ]]
+ )
],
- [mhd_cv_wctr_type="ucrt"], [mhd_cv_wctr_type="msvcrt"])
+ [mhd_cv_cc_compound_literals_arr_scope="yes"],
+ [mhd_cv_cc_compound_literals_arr_scope="no"]
+ )
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
]
)
- mhd_host_os="${mhd_host_os}-${mhd_cv_wctr_type}"
- AS_VAR_IF([mhd_cv_wctr_type], ["msvcrt"],
- [
- # Use CFLAGS here to override user-supplied wrong CPPFLAGS. Durty trick, but choice is limited.
- AX_APPEND_COMPILE_FLAGS([-U__USE_MINGW_ANSI_STDIO -D__USE_MINGW_ANSI_STDIO=0], [CFLAGS_ac])
- AC_SUBST([W32CRT], [MSVCRT])
- ], [AC_SUBST([W32CRT], [UCRT])]
+ AS_VAR_IF([mhd_cv_cc_compound_literals_arr_scope],["yes"],
+ [AC_DEFINE([HAVE_COMPOUND_LITERALS_ARRAYS_SCOPE],[1],[Define to '1' if your compiler supports compound literals as arrays of the scope])]
)
- CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
- LDFLAGS="${user_LDFLAGS}"
- AS_CASE([$mhd_w32_ver],
- [WinXP],
- [MHD_CHECK_ADD_CC_LDFLAG([-Wl,--major-subsystem-version,5,--minor-subsystem-version,1],[LDFLAGS_ac])],
- [Vista],
- [MHD_CHECK_ADD_CC_LDFLAG([-Wl,--major-subsystem-version,6,--minor-subsystem-version,0],[LDFLAGS_ac])]
+ AC_CACHE_CHECK([whether $CC supports compound literals as local arrays],[mhd_cv_cc_compound_literals_arr_local],
+ [
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ AC_COMPILE_IFELSE(
+ [
+ AC_LANG_SOURCE([[
+struct test_strct
+{
+ char c;
+ int i;
+};
+
+static int test_strct_func(struct test_strct *strc)
+{
+ return 0 == strc->i;
+}
+
+int main(void)
+{
+ if (test_strct_func((struct test_strct[]) { { 'a', 0 }, { 'b', 1 }, { 'c', 2 } }))
+ return 4;
+
+ return ((struct test_strct[]) { { 'a', 0 }, { 'b', 1 }, { 'c', 2 } })->i;
+}
+ ]]
+ )
+ ],
+ [mhd_cv_cc_compound_literals_arr_local="yes"],
+ [mhd_cv_cc_compound_literals_arr_local="no"]
+ )
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_compound_literals_arr_local],["yes"],
+ [AC_DEFINE([HAVE_COMPOUND_LITERALS_ARRAYS_LOCAL],[1],[Define to '1' if your compiler supports compound literals as local arrays])]
)
- LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}"
- ]
-)
-CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+ AC_CACHE_CHECK([whether $CC supports compound literals as lvalue],[mhd_cv_cc_compound_literals_lvalues],
+ [
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ AC_COMPILE_IFELSE(
+ [
+ AC_LANG_SOURCE([[
+struct test_strct
+{
+ char c;
+ int i;
+};
-AC_ARG_WITH([threads],
- [AS_HELP_STRING([--with-threads=LIB],[choose threading library (posix, w32, auto, none) [auto]])],
- [], [with_threads='auto'])
-AS_CASE([[$with_threads]],
- [[win32]], [[with_threads='w32']],
- [[pthreads]], [[with_threads='posix']],
- [[posix]], [[:]],
- [[w32]], [[:]],
- [[none]], [[with_threads='none']],
- [[no]], [[with_threads='none']],
- [[auto]], [[:]],
- [AC_MSG_ERROR([[incorrect parameter "$with_threads" specified for --with-threads]])]
-)
+static int test_strct_func(struct test_strct *strc)
+{
+ return 0 != strc->i;
+}
+
+int main(void)
+{
+ struct test_strct *strct_ptr;
+ int int_var;
+ int *int_ptr;
+
+ int_var = ++(int) {0};
+ int_ptr = &((int) {1});
+
+ if (int_var != *int_ptr) /* Process all variables to avoid warnings */
+ return 2;
+
+ if (test_strct_func(&( (struct test_strct) { 'a', 0 } )))
+ return 4;
+
+ return 0;
+}
+ ]]
+ )
+ ],
+ [mhd_cv_cc_compound_literals_lvalues="yes"],
+ [mhd_cv_cc_compound_literals_lvalues="no"]
+ )
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_compound_literals_lvalues],["yes"],
+ [AC_DEFINE([HAVE_COMPOUND_LITERALS_LVALUES],[1],[Define to '1' if your compiler supports compound literals as lvalues])]
+ )
+ ],
+ [
+ AS_UNSET([mhd_cv_cc_compound_literals_arr_local])
+ AS_UNSET([mhd_cv_cc_compound_literals_lvalue])
+ AS_UNSET([mhd_cv_cc_compound_literals_arr_scope])
+ ]
+)
+
+AS_VAR_SET_IF([ac_cv_c_restrict],[],
+ [
+ # "cache" result in advance if it is known from the compiler test
+ AS_CASE([$ac_prog_cc_stdc],
+ [c89],[:],
+ [c??],[ac_cv_c_restrict="restrict"]
+ )
+ ]
+)
+AC_C_RESTRICT
+AS_VAR_IF([ac_cv_c_restrict],["no"],[],
+ [AC_DEFINE([HAVE_RESTRICT],[1],[Define to '1' if your compiler supports 'restrict' keyword])]
+)
+AC_C_INLINE
+AS_UNSET([errattr_CFLAGS])
+CFLAGS="${user_CFLAGS}"
+MHD_CHECK_CC_CFLAG([-Werror=attributes],[CFLAGS_ac],
+ [
+ AC_CACHE_CHECK([whether -Werror=attributes actually works],[mhd_cv_cflag_werror_attr_works],
+ [
+ CFLAGS="${CFLAGS_ac} ${user_CFLAGS} -Werror=attributes"
+ AC_COMPILE_IFELSE([
+ AC_LANG_PROGRAM(
+ [[__attribute__((non_existing_attrb_dummy)) static int SimpleFunc(void) {return 3;}]],
+ [[int r = SimpleFunc(); if (r) return r;]]
+ )
+ ],
+ [mhd_cv_cflag_werror_attr_works="no"],
+ [mhd_cv_cflag_werror_attr_works="yes"]
+ )
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cflag_werror_attr_works],["yes"],
+ [errattr_CFLAGS="-Werror=attributes"]
+ )
+ ]
+)
+CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+AS_IF([test "x$ac_cv_c_inline" != "xno"],
+ [
+ AC_DEFINE([HAVE_INLINE_FUNCS],[1],[Define to 1 if your C compiler supports inline functions.])
+ AC_CACHE_CHECK([for function force inline keywords supported by $CC],[mhd_cv_cc_kwd_forceinline],
+ [
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ mhd_cv_cc_kwd_forceinline="none"
+ CFLAGS="${CFLAGS_ac} ${user_CFLAGS} $errattr_CFLAGS"
+ for keyword_chk in "inline __attribute__((always_inline))" __forceinline
+ do
+ AS_CASE([${keyword_chk}],
+ [*attribute*],
+ [AS_IF([test "x$errattr_CFLAGS" = "x"],[continue])]
+ )
+ AC_LINK_IFELSE([
+ AC_LANG_SOURCE([[
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#endif
+static ${keyword_chk} bool cmpfn(int x, int y)
+{ return x > y; }
+static ${keyword_chk} int sumfn(int x, int y)
+{ return x + y; }
+
+int main(void)
+{
+ int a = 1, b = 100, c;
+ if (cmpfn(a, b))
+ c = sumfn(a, b);
+ else
+ c = 0 - sumfn(a, b);
+ return (cmpfn(0, c) ? 0 : 5);
+}
+ ]]
+ )
+ ],
+ [mhd_cv_cc_kwd_forceinline="${keyword_chk}"]
+ )
+ test "x${mhd_cv_cc_kwd_forceinline}" != "xnone" && break
+ done
+ CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
+ ]
+ )
+ ]
+)
+AS_IF([test "x$ac_cv_c_inline" != "xno" && test "x${mhd_cv_cc_kwd_forceinline}" != "xnone"],
+ [AC_DEFINE_UNQUOTED([MHD_static_inline_],[static $mhd_cv_cc_kwd_forceinline],[Define to prefix which will be used with MHD static inline functions.])]
+ ,
+ [AC_DEFINE([MHD_static_inline_],[static inline],[Define to prefix which will be used with MHD static inline functions.])]
+)
+
+AC_CACHE_CHECK([for 'unreachable' keywords supported by $CC],[mhd_cv_cc_kwd_unreachable],
+ [
+ mhd_cv_cc_kwd_unreachable="none"
+ for keyword_chk in '__builtin_unreachable()' '__assume(0)' 'unreachable()'
+ do
+ AC_LINK_IFELSE([
+ AC_LANG_SOURCE([[
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+int zero_if_positive(int val)
+{
+ if (val > 0)
+ return 0;
+ ${keyword_chk};
+ return 1;
+}
+
+int main(void)
+{
+ return zero_if_positive(1);
+}
+ ]]
+ )
+ ],
+ [mhd_cv_cc_kwd_unreachable="$keyword_chk"]
+ )
+ test "x${mhd_cv_cc_kwd_unreachable}" != "xnone" && break
+ done
+ ]
+)
+AS_IF([test "x${mhd_cv_cc_kwd_unreachable}" != "xnone"],
+ [
+ AC_DEFINE_UNQUOTED([MHD_UNREACHABLE_],[$mhd_cv_cc_kwd_unreachable],[Define to keyword used to indicate unreachable code paths])
+ AS_IF([test "x${mhd_cv_cc_kwd_unreachable}" = 'xunreachable()'],
+ AC_DEFINE([MHD_UNREACHABLE_NEEDS_STDDEF_H],[$mhd_cv_cc_kwd_unreachable],[Define to '1' if MHD_UNREACHABLE_() requires include of <stddef.h> header])
+ )
+ ]
+)
+
+AC_CHECK_HEADERS([stdalign.h], [], [], [AC_INCLUDES_DEFAULT])
+AC_CACHE_CHECK([[for C11 'alignof()' support]], [[mhd_cv_c_alignof]],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_STDALIGN_H
+#include <stdalign.h>
+#endif
+ ]], [[
+ int var1[(alignof(int) >= 2) ? 1 : -1];
+ int var2[alignof(unsigned int) - 1];
+ int var3[(alignof(char) > 0) ? 1 : -1];
+ int var4[(alignof(long) >= 4) ? 1 : -1];
+
+ /* Mute compiler warnings */
+ var1[0] = var2[0] = var3[0] = 0;
+ var4[0] = 1;
+ if (var1[0] + var2[0] + var3[0] == var4[0])
+ return 1;
+ ]])
+ ], [
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_STDALIGN_H
+#include <stdalign.h>
+#endif
+ ]], [[
+ /* Should fail if 'alignof()' works */
+ int var1[alignof(nonexisting_type) - 1];
+
+ /* Mute compiler warnings */
+ var1[0] = 1;
+ if (var1[0] + 1 == 1)
+ return 1;
+ ]])
+ ], [[mhd_cv_c_alignof='no']], [[mhd_cv_c_alignof='yes']])
+ ], [[mhd_cv_c_alignof='no']])
+ ])
+AS_VAR_IF([mhd_cv_c_alignof], ["yes"],
+ [AC_DEFINE([[HAVE_C_ALIGNOF]], [1], [Define to 1 if your compiler supports 'alignof()'])])
+
+
+# Check system type
+AC_MSG_CHECKING([[for target host OS]])
+os_is_windows="no"
+os_is_native_w32="no"
+AS_CASE(["$host_os"],
+ [*darwin* | *rhapsody* | *macosx*],
+ [AC_DEFINE([OSX],[1],[This is an OS X system])
+ mhd_host_os='Darwin'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [kfreebsd*-gnu],
+ [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
+ AC_DEFINE([FREEBSD],[1],[This is a FreeBSD system])
+ mhd_host_os='FreeBSD kernel with GNU userland'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [freebsd*],
+ [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
+ AC_DEFINE([FREEBSD],[1],[This is a FreeBSD system])
+ mhd_host_os='FreeBSD'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [openbsd*],
+ [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
+ AC_DEFINE([OPENBSD],[1],[This is an OpenBSD system])
+ mhd_host_os='OpenBSD'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [netbsd*],
+ [AC_DEFINE([SOMEBSD],[1],[This is a BSD system])
+ AC_DEFINE([NETBSD],[1],[This is a NetBSD system])
+ mhd_host_os='NetBSD'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [*solaris*],
+ [AC_DEFINE([SOLARIS],[1],[This is a Solaris system])
+ AC_DEFINE([_REENTRANT],[1],[Need with solaris or errno does not work])
+ mhd_host_os='Solaris'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [*linux*],
+ [AC_DEFINE([LINUX],[1],[This is a Linux kernel])
+ mhd_host_os='Linux'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [*cygwin*],
+ [AC_DEFINE([CYGWIN],[1],[This is a Cygwin system])
+ mhd_host_os='Windows/Cygwin'
+ AC_MSG_RESULT([[$mhd_host_os]])
+ os_is_windows="yes"],
+ [*mingw*],
+ [
+ AC_DEFINE([MINGW],[1],[This is a MinGW system])
+ AC_DEFINE([WINDOWS],[1],[This is a Windows system])
+ mhd_host_os='Windows/MinGW'
+ AC_MSG_RESULT([[$mhd_host_os]])
+ AC_CHECK_HEADERS([winsock2.h ws2tcpip.h], [], [AC_MSG_ERROR([[Winsock2 headers are required for W32]])], [AC_INCLUDES_DEFAULT])
+ AC_CACHE_CHECK([for MS lib utility], [ac_cv_use_ms_lib_tool],
+ [mslibcheck=`lib 2>&1`
+ AS_IF([echo "$mslibcheck" | $GREP -e '^Microsoft (R) Library Manager' - >/dev/null],
+ [ac_cv_use_ms_lib_tool=yes],
+ [ac_cv_use_ms_lib_tool=no])
+ ])
+ AS_IF([test "x$ac_cv_use_ms_lib_tool" = "xyes"],
+ [AC_SUBST([MS_LIB_TOOL], [[lib]])])
+ AC_SUBST([lt_cv_objdir])
+ os_is_windows="yes"
+ os_is_native_w32="yes"
+ ],
+ [*openedition*],
+ [AC_DEFINE([OS390],[1],[This is a OS/390 system])
+ mhd_host_os='OS/390'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [gnu*],
+ [AC_DEFINE([[GNU_HURD]], [[1]], [Define to `1' if host machine runs on GNU Hurd.])
+ mhd_host_os='GNU Hurd'
+ AC_MSG_RESULT([[$mhd_host_os]])
+ ],
+ [
+ AC_MSG_RESULT([unrecognised OS])
+ mhd_host_os="${host_os}"
+ AC_MSG_WARN([Unrecognised OS $host_os])
+ AC_DEFINE([OTHEROS],1,[Some strange OS])
+ ])
+
+AM_CONDITIONAL([CYGWIN_TARGET], [[test "x$os_is_windows" = "xyes" && \
+ test "x${os_is_native_w32}" != "xyes"]])
+
+AS_VAR_IF([os_is_windows], ["yes"],
+ [
+ AC_MSG_CHECKING([[whether target W32 version is specified by precompiler defines]])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+/* Note: check logic is reversed for easy log reading */
+#ifdef WINVER
+#error WINVER is defined
+choke me now;
+#endif
+#ifdef _WIN32_WINNT
+#error _WIN32_WINNT is defined
+choke me now;
+#endif
+#ifdef NTDDI
+#error NTDDI is defined
+choke me now;
+#endif
+ ]],[[(void)0]])
+ ], [[mhd_w32_ver_preselect=no]], [[mhd_w32_ver_preselect=yes]]
+ )
+ AC_MSG_RESULT([[${mhd_w32_ver_preselect}]])
+ AC_CHECK_HEADERS([windows.h sdkddkver.h], [], [], [AC_INCLUDES_DEFAULT])
+ AS_VAR_IF([mhd_w32_ver_preselect],["yes"],
+ [
+ AC_MSG_CHECKING([[for specified target W32 version]])
+ AS_UNSET([[mhd_w32_ver]])
+ AS_UNSET([[mhd_w32_ver_msg]])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#if _WIN32_WINNT+0 < 0x0501
+/* Check before headers inclusion */
+#error _WIN32_WINNT is less than 0x0501
+choke me now;
+#endif
+
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#if _WIN32_WINNT+0 < 0x0501
+#error _WIN32_WINNT is less than 0x0501
+choke me now;
+#endif
+ ]],[[(void)0]])
+ ], [], [
+ AC_MSG_RESULT([[pre-WinXP]])
+ AC_MSG_ERROR([[libmicrohttpd cannot be compiled for Windows version before Windows XP]])
+ ]
+ )
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#if _WIN32_WINNT+0 == 0x0501
+#error _WIN32_WINNT is 0x0501
+choke me now;
+#endif
+#if _WIN32_WINNT+0 == 0x0502
+#error _WIN32_WINNT is 0x0502
+choke me now;
+#endif
+ ]],[[(void)0]])
+ ], [], [
+ mhd_w32_ver="WinXP"
+ mhd_w32_ver_msg="WinXP (selected by precompiler flags)"
+ ]
+ )
+ AS_VAR_SET_IF([mhd_w32_ver], [],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#if _WIN32_WINNT+0 < 0x0600
+#error _WIN32_WINNT is less than 0x0600 but greater than 0x0502
+choke me now;
+#endif
+ ]],[[(void)0]])
+ ], [], [
+ AC_MSG_ERROR([[_WIN32_WINNT value is wrong (less than 0x0600 but greater than 0x0502)]])
+ ]
+ )
+
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#if _WIN32_WINNT+0 == 0x0600
+#error _WIN32_WINNT is 0x0600
+choke me now;
+#endif
+ ]],[[(void)0]])
+ ], [], [
+ mhd_w32_ver="Vista"
+ mhd_w32_ver_msg="Vista (selected by precompiler flags)"
+ ]
+ )
+ ]
+ )
+
+ AS_VAR_SET_IF([mhd_w32_ver], [],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#if _WIN32_WINNT+0 > 0x0600
+#error _WIN32_WINNT is greater than 0x0600
+choke me now;
+#endif
+ ]],[[(void)0]])
+ ], [
+ mhd_w32_ver="unknown"
+ mhd_w32_ver_msg="unknown (cannot be detected)"
+ ], [
+ mhd_w32_ver="newer than Vista"
+ mhd_w32_ver_msg="newer than Vista (selected by precompiler flags)"
+ ]
+ )
+ ]
+ )
+ AC_MSG_RESULT([[${mhd_w32_ver}]])
+ ], [
+ mhd_w32_ver="Vista"
+ mhd_w32_ver_msg="Vista (default, override by CPPFLAGS=-D_WIN32_WINNT=0xNNNN)"
+ CPPFLAGS_ac="${CPPFLAGS_ac} -D_WIN32_WINNT=0x0600"
+ CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}"
+ AC_MSG_CHECKING([[whether headers accept _WIN32_WINNT=0x0600]])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+#include <stdio.h>
+ ]],[[(void)0]])
+ ], [
+ AC_MSG_RESULT([[yes]])
+ ], [
+ AC_MSG_RESULT([[no]])
+ AC_MSG_ERROR([Headers do not accept _WIN32_WINNT=0x0600. Consider override target W32 version by CPPFLAGS=-D_WIN32_WINNT=0xNNNN])
+ ]
+ )
+ ]
+ )
+ ]
+)
+
+AS_IF([test "x${os_is_windows}" = "xyes" && test "x${os_is_native_w32}" = "xyes"],
+ [
+ AC_CACHE_CHECK([W32 run-time library type], [mhd_cv_wctr_type],
+ [
+ AC_EGREP_CPP([MHDMARKER: UCRT run-time library in use!], [
+#include <stdio.h>
+#if defined(_UCRT)
+#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
+#endif
+#if defined(__MSVCRT_VERSION__)
+#if (__MSVCRT_VERSION__ >= 0xE00) && (__MSVCRT_VERSION__ < 0x1000)
+#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
+#endif
+#if (__MSVCRT_VERSION__ > 0x1400)
+#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
+#endif
+#endif
+
+#ifndef CRT_STR
+#define CRT_STR "MHDMARKER: MSVCRT run-time library in use!"
+#endif
+
+int main(void)
+{
+ printf ("%\n", CRT_STR);
+ return 0;
+}
+ ],
+ [mhd_cv_wctr_type="ucrt"], [mhd_cv_wctr_type="msvcrt"])
+ ]
+ )
+ mhd_host_os="${mhd_host_os}-${mhd_cv_wctr_type}"
+ AS_VAR_IF([mhd_cv_wctr_type], ["msvcrt"],
+ [
+ # Use CFLAGS here to override user-supplied wrong CPPFLAGS. Durty trick, but choice is limited.
+ AX_APPEND_COMPILE_FLAGS([-U__USE_MINGW_ANSI_STDIO -D__USE_MINGW_ANSI_STDIO=0], [CFLAGS_ac])
+ AC_SUBST([W32CRT], [MSVCRT])
+ ], [AC_SUBST([W32CRT], [UCRT])]
+ )
+
+ CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+ LDFLAGS="${user_LDFLAGS}"
+ AS_CASE([$mhd_w32_ver],
+ [WinXP],
+ [MHD_CHECK_ADD_CC_LDFLAG([-Wl,--major-subsystem-version,5,--minor-subsystem-version,1],[LDFLAGS_ac])],
+ [Vista],
+ [MHD_CHECK_ADD_CC_LDFLAG([-Wl,--major-subsystem-version,6,--minor-subsystem-version,0],[LDFLAGS_ac])]
+ )
+ LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}"
+ ]
+)
+CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+
+
+AC_ARG_WITH([threads],
+ [AS_HELP_STRING([--with-threads=LIB],[choose threading library (posix, w32, auto, none) [auto]])],
+ [], [with_threads='auto'])
+AS_CASE([[$with_threads]],
+ [[win32]], [[with_threads='w32']],
+ [[pthreads]], [[with_threads='posix']],
+ [[posix]], [[:]],
+ [[w32]], [[:]],
+ [[none]], [[with_threads='none']],
+ [[no]], [[with_threads='none']],
+ [[auto]], [[:]],
+ [AC_MSG_ERROR([[incorrect parameter "$with_threads" specified for --with-threads]])]
+)
# Check for posix threads support, regardless of configure parameters as
# testsuite uses only posix threads.
@@ -1354,7 +1809,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])
@@ -1601,6 +2056,8 @@ MHD_FIND_LIB([socket],
#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>
@@ -2146,14 +2603,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];
}
@@ -2187,6 +2644,8 @@ MHD_FIND_LIB([sendmsg],
#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>
@@ -2231,7 +2690,7 @@ AC_C_VARARRAYS
AC_CACHE_CHECK([[whether __func__ magic-macro is available]],
[[mhd_cv_macro___func___avail]], [dnl
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stddef.h>]],[[const char *funcname = __func__ ; if (NULL == funcname) return 1;]])],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[const char *funcname = __func__ ; if (!funcname) return 1;]])],
[[mhd_cv_macro___func___avail="yes"]],[[mhd_cv_macro___func___avail="no"]])
])
AS_VAR_IF([mhd_cv_macro___func___avail], ["yes"],
@@ -2239,7 +2698,7 @@ AS_VAR_IF([mhd_cv_macro___func___avail], ["yes"],
[
AC_CACHE_CHECK([[whether __FUNCTION__ magic-macro is available]],
[[mhd_cv_macro___function___avail]], [dnl
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stddef.h>]],[[const char *funcname = __FUNCTION__ ; if (NULL == funcname) return 1;]])],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[const char *funcname = __FUNCTION__ ; if (!funcname) return 1;]])],
[[mhd_cv_macro___function___avail="yes"]],[[mhd_cv_macro___function___avail="no"]])
])
AS_VAR_IF([mhd_cv_macro___function___avail], ["yes"],
@@ -2247,7 +2706,7 @@ AS_VAR_IF([mhd_cv_macro___func___avail], ["yes"],
[
AC_CACHE_CHECK([[whether __PRETTY_FUNCTION__ magic-macro is available]],
[[mhd_cv_macro___pretty_function___avail]], [dnl
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stddef.h>]],[[const char *funcname = __PRETTY_FUNCTION__ ; if (NULL == funcname) return 1;]])],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[const char *funcname = __PRETTY_FUNCTION__ ; if (!funcname) return 1;]])],
[[mhd_cv_macro___pretty_function___avail="yes"]],[[mhd_cv_macro___pretty_function___avail="no"]])
])
AS_VAR_IF([mhd_cv_macro___pretty_function___avail], ["yes"],
@@ -2259,14 +2718,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"]],
@@ -2344,70 +2803,166 @@ 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([[select]],
+ [
+ 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_LINK_IFELSE(
+ [
+ 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=${enableval}],
- [enable_poll='auto']
- )
-
-AS_IF([test "$enable_poll" != "no"],
[
- AS_IF([test "$os_is_native_w32" != "yes"],
+ 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_CHECK_HEADERS([poll.h],
- [
- MHD_CHECK_FUNC([poll],
- [[
+ 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]
+ ]],
+ [have_poll='yes'],[have_poll='no']
)
- ],
- [
- MHD_CHECK_FUNC([WSAPoll],
- [[
+ ],[],[AC_INCLUDES_DEFAULT]
+ )
+ ],
+ [
+ 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'
],
[
@@ -2419,8 +2974,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"],
[
@@ -2440,7 +2994,9 @@ AC_CACHE_CHECK([for supported 'noreturn' keyword], [mhd_cv_decl_noreturn],
[
mhd_cv_decl_noreturn="none"
CFLAGS="${CFLAGS_ac} ${user_CFLAGS} ${errattr_CFLAGS}"
- for decl_noret in '_Noreturn' '__attribute__((__noreturn__))' '__declspec(noreturn)'
+ MHD_SAVED_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag=yes
+ for decl_noret in ['[[noreturn]]'] '_Noreturn' '__attribute__((__noreturn__))' '__declspec(noreturn)'
do
AC_LINK_IFELSE([AC_LANG_SOURCE(
[[
@@ -2469,12 +3025,14 @@ int main (int argc, char *const *argv)
)
AS_IF([test "x${mhd_cv_decl_noreturn}" != "xnone"], [break])
done
- CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+ ac_c_werror_flag="$MHD_SAVED_ac_c_werror_flag"
+ AS_UNSET([MHD_SAVED_ac_c_werror_flag])
+ CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
]
)
AS_VAR_IF([mhd_cv_decl_noreturn], ["none"],
- [AC_DEFINE([_MHD_NORETURN], [], [Define to supported 'noreturn' function declaration])],
- [AC_DEFINE_UNQUOTED([_MHD_NORETURN], [${mhd_cv_decl_noreturn}], [Define to supported 'noreturn' function declaration])]
+ [AC_DEFINE([MHD_NORETURN_], [[/* empty */]], [Define to the supported 'noreturn' function declaration])],
+ [AC_DEFINE_UNQUOTED([MHD_NORETURN_], [${mhd_cv_decl_noreturn}], [Define to the supported 'noreturn' function declaration])]
)
# Check for types sizes
@@ -2509,8 +3067,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>]])
@@ -2560,6 +3118,8 @@ AC_CHECK_MEMBERS([struct sockaddr.sa_len, struct sockaddr_storage.ss_len,
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -2663,6 +3223,80 @@ int main(void)
[AC_DEFINE([[MHD_USE_GETSOCKNAME]], [[1]], [Define if you have usable `getsockname' function.])]
)
+AS_VAR_IF([[os_is_native_w32]], [["yes"]],[],
+ [
+ MHD_CHECK_FUNC([socketpair],
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+ ]],
+ [[
+ int arr[2];
+ i][f (0 != socketpair(0, SOCK_STREAM, 0, arr)) return 2;
+ close (arr[1]);
+ close (arr[0]);
+ ]],
+ [],
+ [AS_UNSET([mhd_cv_socketpair_usable])]
+ )
+ ]
+)
+
+AC_CHECK_DECLS([AF_UNIX,AF_LOCAL,SOMAXCONN,IPV6_V6ONLY,SO_REUSEADDR,SO_REUSEPORT,\
+ TCP_NODELAY,TCP_FASTOPEN],[],[],
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#ifdef HAVE_SOCKLIB_H
+#include <sockLib.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_INETLIB_H
+#include <inetLib.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#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
+#ifdef HAVE_AFUNIX_H
+#include <afunix.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+ ]]
+)
+
AC_CACHE_CHECK([for usable PAGESIZE macro], [mhd_cv_macro_pagesize_usable],
[
AC_LINK_IFELSE(
@@ -2763,543 +3397,1085 @@ choke me now
)
AS_VAR_IF([[mhd_cv_macro_page_size_usable]], [["yes"]],
[
- AC_DEFINE([[MHD_USE_PAGE_SIZE_MACRO]],[[1]],[Define if you have usable PAGE_SIZE macro])
- AC_CACHE_CHECK([whether PAGE_SIZE macro could be used for static init], [mhd_cv_macro_page_size_usable_static],
+ AC_DEFINE([[MHD_USE_PAGE_SIZE_MACRO]],[[1]],[Define if you have usable PAGE_SIZE macro])
+ AC_CACHE_CHECK([whether PAGE_SIZE macro could be used for static init], [mhd_cv_macro_page_size_usable_static],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifndef PAGE_SIZE
+#error No PAGE_SIZE macro defined
+choke me now
+#endif
+static long ac_pgsz = PAGE_SIZE + 0;
+ ]],
+ [[
+ if (1 > ac_pgsz) return 1;
+ ]]
+ )
+ ],
+ [[mhd_cv_macro_page_size_usable_static="yes"]], [[mhd_cv_macro_page_size_usable_static="no"]]
+ )
+ ]
+ )
+ AS_VAR_IF([[mhd_cv_macro_page_size_usable_static]], [["yes"]],
+ [AC_DEFINE([[MHD_USE_PAGE_SIZE_MACRO_STATIC]],[[1]],[Define if you have PAGE_SIZE macro usable for static init])]
+ )
+ ]
+ )
+ ]
+)
+
+# Check for inter-thread signaling type
+AC_ARG_ENABLE([[itc]],
+ [AS_HELP_STRING([[--enable-itc=TYPE]], [use TYPE of inter-thread communication (pipe, socketpair, eventfd) [auto]])], [],
+ [[enable_itc='auto']]
+)
+
+AS_CASE([[$enable_itc]],
+ [[pipe]], [[:]],
+ [[socketpair]], [[:]],
+ [[eventfd]], [[:]],
+ [[auto]], [AS_VAR_IF([[os_is_windows]], [["yes"]], [[enable_itc='socketpair']])],
+ [[eventFD]], [[enable_itc='eventfd']],
+ [[socket]], [[enable_itc='socketpair']],
+ [[no]], [AC_MSG_ERROR([[inter-thread communication cannot be disabled]])],
+ [AC_MSG_ERROR([[unrecognized type "$enable_itc" of inter-thread communication specified by "--enable-itc=$enable_itc"]])]
+)
+AS_UNSET([[use_itc]])
+
+AS_IF([[test "x$enable_itc" = "xeventfd" || test "x$enable_itc" = "xauto"]],
+ [
+ MHD_CHECK_LINK_RUN([[f][or working eventfd(2)]],[[mhd_cv_eventfd_usable]],[[mhd_cv_eventfd_usable='assuming no']],
+ [
+ AC_LANG_SOURCE([[
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+int main(void)
+{
+ unsigned char buf[8];
+ int ret;
+ int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+ if (0 > efd)
+ return 2;
+ ret = 0;
+ buf[3] = 1;
+ if (8 != write(efd, buf, 8))
+ ret = 3;
+ else
+ {
+ if (8 != read(efd, buf, 8))
+ ret = 4;
+ }
+ close(efd);
+ return ret;
+}
+ ]]
+ )
+ ],
+ [
+ use_itc='eventfd'
+ enable_itc="$use_itc"
+ 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]])])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_eventfd_usable],["assuming no"],
+ [AC_MSG_WARN([if you have 'eventfd' support enabled on your target system consider overriding test result by "mhd_cv_eventfd_usable=yes" configure parameter])]
+ )
+ ]
+)
+
+AS_IF([[test "x$enable_itc" = "xpipe" || test "x$enable_itc" = "xauto"]], [
+ AS_VAR_IF([[os_is_native_w32]], [["yes"]], [], [
+ AC_CACHE_CHECK([[whether pipe(3) is usable]], [[mhd_cv_pipe_usable]], [
+ AC_LINK_IFELSE([
+ AC_LANG_PROGRAM([
+AC_INCLUDES_DEFAULT
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+ ], [[
+ int arr[2];
+ int res;
+ res = pipe(arr);
+ if (res != 0) return 33;
+ close (arr[0]);
+ close (arr[1]);
+ ]])
+ ], [[mhd_cv_pipe_usable='yes']], [[mhd_cv_pipe_usable='no']])
+ ])
+ 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])
+ MHD_CHECK_LINK_RUN([[whether pipe2(2) is usable]],[[mhd_cv_pipe2_usable]],
+ [
+ # Cross-compiling
+ AS_CASE([${host_os}], [kfreebsd*-gnu], [[mhd_cv_pipe2_usable='assuming no']],
+ [[mhd_cv_pipe2_usable='assuming yes']])
+ ],
+ [
+ AC_LANG_PROGRAM([
+AC_INCLUDES_DEFAULT
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+ ], [[
+ int arr[2];
+ int res;
+ res = pipe2(arr, O_CLOEXEC | O_NONBLOCK);
+ if (res != 0) return 33;
+ close (arr[0]);
+ close (arr[1]);
+ ]]
+ )
+ ],
+ [AC_DEFINE([[HAVE_PIPE2_FUNC]], [[1]], [Define if you have usable pipe2(2) function])]
+ )
+ ], [
+ AS_VAR_IF([[enable_itc]], [["pipe"]], [AC_MSG_ERROR([[pipe(3) is not usable, consider using other type of inter-thread communication]])])
+ ])
+ ])
+])
+
+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_LINK_IFELSE([
AC_LANG_PROGRAM(
[[
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
#endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
#endif
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
#endif
-#ifndef PAGE_SIZE
-#error No PAGE_SIZE macro defined
-choke me now
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
#endif
-static long ac_pgsz = PAGE_SIZE + 0;
- ]],
- [[
- if (1 > ac_pgsz) return 1;
+ ]], [[
+ 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;
+#endif
+ if (res != 0) return 1;
+ close (arr[1]);
+ close (arr[0]);
]]
)
],
- [[mhd_cv_macro_page_size_usable_static="yes"]], [[mhd_cv_macro_page_size_usable_static="no"]]
+ [[mhd_cv_socketpair_usable='yes']],
+ [[mhd_cv_socketpair_usable='no']]
)
]
)
- AS_VAR_IF([[mhd_cv_macro_page_size_usable_static]], [["yes"]],
- [AC_DEFINE([[MHD_USE_PAGE_SIZE_MACRO_STATIC]],[[1]],[Define if you have PAGE_SIZE macro usable for static init])]
+ ],
+ [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]])]
)
]
)
]
)
-# Check for inter-thread signaling type
-AC_ARG_ENABLE([[itc]],
- [AS_HELP_STRING([[--enable-itc=TYPE]], [use TYPE of inter-thread communication (pipe, socketpair, eventfd) [auto]])], [],
- [[enable_itc='auto']]
+AS_IF([[test -z "$use_itc"]], [AC_MSG_ERROR([[cannot find usable type of inter-thread communication]])])
+
+
+MHD_CHECK_FUNC([accept4],
+ [[
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#include <sys/socket.h>
+ ]],
+ [[
+ struct sockaddr sk_addr;
+ socklen_t addr_size;
+ i][f (0 > accept4(0, &sk_addr, &addr_size, 0))
+ return 3;
+ ]]
)
+MHD_CHECK_FUNC([gmtime_r],
+ [[
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#include <time.h>
+ ]],
+ [[
+ time_t timer = (time_t) 0;
+ struct tm res;
-AS_CASE([[$enable_itc]],
- [[pipe]], [[:]],
- [[socketpair]], [[:]],
- [[eventfd]], [[:]],
- [[auto]], [AS_VAR_IF([[os_is_windows]], [["yes"]], [[enable_itc='socketpair']])],
- [[eventFD]], [[enable_itc='eventfd']],
- [[socket]], [[enable_itc='socketpair']],
- [[no]], [AC_MSG_ERROR([[inter-thread communication cannot be disabled]])],
- [AC_MSG_ERROR([[unrecognized type "$enable_itc" of inter-thread communication specified by "--enable-itc=$enable_itc"]])]
+ i][f (&res != gmtime_r(&timer, &res))
+ return 3;
+ ]]
+)
+MHD_CHECK_FUNC([memmem],
+ [[
+#if defined(HAVE_STDDEF_H)
+# include <stddef.h>
+#elif defined(HAVE_STDLIB_H)
+# include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#include <string.h>
+ ]],
+ [[
+ const char *haystack = "abc";
+ size_t hslen = 3;
+ const char *needle = "b";
+ size_t needlelen = 1;
+
+ i][f ((haystack + 1) != memmem(haystack, hslen, needle, needlelen))
+ return 3;
+ ]]
+)
+MHD_CHECK_FUNC([snprintf],
+ [[
+#include <stdio.h>
+ ]],
+ [[
+ char buf[2];
+
+ i][f (1 != snprintf(buf, 2, "a"))
+ return 3;
+ /* Do not use the next check to avoid compiler warning */
+ /* i][f (4 != snprintf(buf, 2, "abcd"))
+ return 4; */
+ ]]
+)
+AC_CHECK_DECL([gmtime_s],
+ [
+ AC_MSG_CHECKING([[whether gmtime_s is in C11 form]])
+ AC_LINK_IFELSE(
+ [ AC_LANG_PROGRAM(
+ [[
+#define __STDC_WANT_LIB_EXT1__ 1
+#include <time.h>
+#ifdef __cplusplus
+extern "C"
+#endif
+ struct tm* gmtime_s(const time_t* time, struct tm* result);
+ ]], [[
+ static struct tm res;
+ static time_t t = 0;
+ gmtime_s (&t, &res);
+ ]])
+ ],
+ [
+ AC_DEFINE([HAVE_C11_GMTIME_S], [1], [Define to 1 if you have the `gmtime_s' function in C11 form.])
+ AC_MSG_RESULT([[yes]])
+ ],
+ [
+ AC_MSG_RESULT([[no]])
+ AC_MSG_CHECKING([[whether gmtime_s is in W32 form]])
+ AC_LINK_IFELSE(
+ [ AC_LANG_PROGRAM(
+ [[
+#include <time.h>
+#ifdef __cplusplus
+extern "C"
+#endif
+errno_t gmtime_s(struct tm* _tm, const time_t* time);
+ ]], [[
+ static struct tm res;
+ static time_t t = 0;
+ gmtime_s (&res, &t);
+ ]])
+ ],
+ [
+ AC_DEFINE([HAVE_W32_GMTIME_S], [1], [Define to 1 if you have the `gmtime_s' function in W32 form.])
+ AC_MSG_RESULT([[yes]])
+ ],
+ [AC_MSG_RESULT([[no]])
+ ])
+ ])
+ ], [],
+ [[#define __STDC_WANT_LIB_EXT1__ 1
+#include <time.h>]])
+
+
+AC_CHECK_DECL([SOCK_NONBLOCK], [AC_DEFINE([HAVE_SOCK_NONBLOCK], [1], [SOCK_NONBLOCK is defined in a socket header])], [],
+ [[
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#if defined(HAVE_SYS_SOCKET_H)
+# include <sys/socket.h>
+#elif defined(HAVE_WINSOCK2_H)
+# include <winsock2.h>
+#elif defined(HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+ ]]
+)
+
+MHD_FIND_LIB([clock_gettime],[[#include <time.h>]],
+ [[
+ struct timespec tp;
+ i][f (0 > clock_gettime(CLOCK_REALTIME, &tp))
+ return 3;
+ ]],
+ [rt],
+ [
+ AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define to '1' if you have clock_gettime() function])
+ AS_VAR_IF([[mhd_cv_find_lib_clock_gettime]],[["none required"]], [],
+ [
+ MHD_LIBDEPS_PKGCFG="${mhd_cv_find_lib_clock_gettime} $MHD_LIBDEPS_PKGCFG"
+ ]
+ )
+ ],[],
+ [MHD_LIBDEPS]
+)
+
+MHD_CHECK_FUNC([clock_get_time],
+ [[
+#include <mach/clock.h>
+#include <mach/mach.h>
+ ]],
+ [[
+ clock_serv_t cs;
+ mach_timespec_t mt;
+ host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cs);
+ clock_get_time(cs, &mt);
+ mach_port_deallocate(mach_task_self(), cs);
+ ]]
+)
+
+MHD_CHECK_FUNC([gethrtime],
+ [[
+#ifdef HAVE_SYS_TIME_H
+/* Solaris define gethrtime() in sys/time.h */
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+#ifdef HAVE_TIME_H
+/* HP-UX define gethrtime() in time.h */
+#include <time.h>
+#endif /* HAVE_TIME_H */
+ ]],
+ [[
+ hrtime_t hrt = gethrtime();
+ i][f (0 == hrt)
+ return 3;
+ ]]
+)
+
+AS_VAR_IF([ac_cv_header_time_h], ["yes"],
+ [
+ MHD_CHECK_FUNC([timespec_get],
+ [[
+#include <time.h>
+
+#ifndef TIME_UTC
+#error TIME_UTC must be defined to use timespec_get()
+choke me now
+#endif
+ ]],
+ [[
+ struct timespec ts;
+ i][f (TIME_UTC != timespec_get (&ts, TIME_UTC))
+ return 3;
+ ]]
+ )
+ ]
)
-AS_UNSET([[use_itc]])
-AS_IF([[test "x$enable_itc" = "xeventfd" || test "x$enable_itc" = "xauto"]],
+MHD_CHECK_FUNC_GETTIMEOFDAY
+
+# IPv6
+AC_CACHE_CHECK([for IPv6],[mhd_cv_have_inet6],
[
- MHD_CHECK_LINK_RUN([[f][or working eventfd(2)]],[[mhd_cv_eventfd_usable]],[[mhd_cv_eventfd_usable='assuming no']],
- [
- AC_LANG_SOURCE([[
-#include <sys/eventfd.h>
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <stdio.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#elif defined(HAVE_UNISTD_H)
#include <unistd.h>
-
-int main(void)
-{
- unsigned char buf[8];
- int ret;
- int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
- if (0 > efd)
- return 2;
- ret = 0;
- buf[3] = 1;
- if (8 != write(efd, buf, 8))
- ret = 3;
- else
- {
- if (8 != read(efd, buf, 8))
- ret = 4;
- }
- close(efd);
- return ret;
-}
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+ ]], [[
+ int af=AF_INET6;
+ struct sockaddr_in6 sa;
+ printf("%d %p\n", (int) af, (void*) &sa);
]]
)
],
- [
- use_itc='eventfd'
- enable_itc="$use_itc"
- 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]])])
- ]
- )
- AS_VAR_IF([mhd_cv_eventfd_usable],["assuming no"],
- [AC_MSG_WARN([if you have 'eventfd' support enabled on your target system consider overriding test result by "mhd_cv_eventfd_usable=yes" configure parameter])]
+ [AS_VAR_SET([mhd_cv_have_inet6],["yes"])],
+ [AS_VAR_SET([mhd_cv_have_inet6],["no"])]
)
]
)
+AS_VAR_IF([mhd_cv_have_inet6],["yes"],
+ [AC_DEFINE([HAVE_INET6], [1], [Define to '1' if you have IPv6 headers])]
+)
-AS_IF([[test "x$enable_itc" = "xpipe" || test "x$enable_itc" = "xauto"]], [
- AS_VAR_IF([[os_is_native_w32]], [["yes"]], [], [
- AC_CACHE_CHECK([[whether pipe(3) is usable]], [[mhd_cv_pipe_usable]], [
- AC_LINK_IFELSE([
- AC_LANG_PROGRAM([
-AC_INCLUDES_DEFAULT
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+MHD_CHECK_FUNC([[sysconf]], [[#include <unistd.h>]], [[long a = sysconf(0); if (a) return 1;]])
+
+MHD_CHECK_FUNC([[sysctl]], [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
#endif
- ], [[
- int arr[2];
- int res;
- res = pipe(arr);
- if (res != 0) return 33;
- close (arr[0]);
- close (arr[1]);
- ]])
- ], [[mhd_cv_pipe_usable='yes']], [[mhd_cv_pipe_usable='no']])
- ])
- 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])
- MHD_CHECK_LINK_RUN([[whether pipe2(2) is usable]],[[mhd_cv_pipe2_usable]],
- [
- # Cross-compiling
- AS_CASE([${host_os}], [kfreebsd*-gnu], [[mhd_cv_pipe2_usable='assuming no']],
- [[mhd_cv_pipe2_usable='assuming yes']])
- ],
- [
- AC_LANG_PROGRAM([
-AC_INCLUDES_DEFAULT
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
#endif
-#ifdef HAVE_UNISTD_H
+#if defined(HAVE_STDDEF_H)
+#include <stddef.h>
+#elif defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#endif
+ ]], [[
+ int mib[2] = {0, 0}; /* Avoid any platform-specific values */
+ i][f (sysctl(mib, 2, NULL, NULL, NULL, 0)) return 1;
+ ]],
+ [
+ AC_CHECK_DECLS([CTL_NET,PF_INET,IPPROTO_ICMP,ICMPCTL_ICMPLIM],[],[],
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#elif defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
- ], [[
- int arr[2];
- int res;
- res = pipe2(arr, O_CLOEXEC | O_NONBLOCK);
- if (res != 0) return 33;
- close (arr[0]);
- close (arr[1]);
- ]]
- )
- ],
- [AC_DEFINE([[HAVE_PIPE2_FUNC]], [[1]], [Define if you have usable pipe2(2) function])]
- )
- ], [
- AS_VAR_IF([[enable_itc]], [["pipe"]], [AC_MSG_ERROR([[pipe(3) is not usable, consider using other type of inter-thread communication]])])
- ])
- ])
-])
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif /* HAVE_NETINET_IN_SYSTM_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+#ifdef HAVE_NETINET_IP_ICMP_H
+#include <netinet/ip_icmp.h>
+#endif /* HAVE_NETINET_IP_ICMP_H */
+#ifdef HAVE_NETINET_ICMP_VAR_H
+#include <netinet/icmp_var.h>
+#endif /* HAVE_NETINET_ICMP_VAR_H */
+ ]]
+ )
+ ]
+)
-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
+MHD_CHECK_FUNC([[sysctlbyname]], [[
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.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);
-#else
-#error AF_LOCAL and AF_UNIX are both undefined
- choke me now;
+#if defined(HAVE_STDDEF_H)
+#include <stddef.h>
+#elif defined(HAVE_STDLIB_H)
+#include <stdlib.h>
#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]])])
- ])
-])
+ ]], [[sysctlbyname("test", NULL, NULL, NULL, 0);]]
+)
-AS_IF([[test -z "$use_itc"]], [AC_MSG_ERROR([[cannot find usable type of inter-thread communication]])])
+MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
+MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
+
+# NOTE: require setting of errattr_CFLAGS above
+CFLAGS="${CFLAGS_ac} ${user_CFLAGS} ${errattr_CFLAGS}"
+AC_CACHE_CHECK([whether $CC supports __attribute__((used))],[mhd_cv_cc_attr_used],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+extern __attribute__((used)) int
+test_func(void);
+
+extern __attribute__((used)) int
+test_func(void) {return 0;}
+
+int main(void) {return test_func();}
+ ]])
+ ],
+ [mhd_cv_cc_attr_used="yes"],[mhd_cv_cc_attr_used="no"]
+ )
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_used],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_USED],[1],
+ [Define to '1' if your compiler supports __attribute__((used))]
+ )
+ ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__((pure))],[mhd_cv_cc_attr_pure],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+extern __attribute__((pure)) int
+test_func(const char *ptr);
+
+extern __attribute__((pure)) int
+test_func(const char *ptr) {return (0 == (*ptr));}
+
+int main(void) {return test_func("");}
+ ]])
+ ],
+ [mhd_cv_cc_attr_pure="yes"],[mhd_cv_cc_attr_pure="no"]
+ )
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_pure],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_PURE],[1],
+ [Define to '1' if your compiler supports __attribute__((pure))]
+ )
+ ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__((const))],[mhd_cv_cc_attr_const],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+extern __attribute__((const)) int
+test_func(int a);
+
+extern __attribute__((const)) int
+test_func(int a) {return a + 1;}
+
+int main(void) {return test_func(-1);}
+ ]])
+ ],
+ [mhd_cv_cc_attr_const="yes"],[mhd_cv_cc_attr_const="no"]
+ )
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_const],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_CONST],[1],
+ [Define to '1' if your compiler supports __attribute__((const))]
+ )
+ ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__((visibility("default")))],[mhd_cv_cc_attr_visibility_default],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+extern __attribute__((visibility("default"))) int
+test_extrn_func(void);
+
+extern __attribute__((visibility("default"))) int
+test_extrn_func(void) {return 0;}
+
+int main(void) {return test_extrn_func();}
+ ]])
+ ],
+ [mhd_cv_cc_attr_visibility_default="yes"],[mhd_cv_cc_attr_visibility_default="no"]
+ )
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_visibility_default],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_VISIBILITY_DEFAULT],[1],
+ [Define to '1' if your compiler supports __attribute__((visibility("default")))]
+ )
+
+ AC_CACHE_CHECK([whether $CC supports __attribute__((visibility("internal")))],[mhd_cv_cc_attr_visibility_internal],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+__attribute__((visibility("internal"))) int
+test_intern_func(void);
+
+__attribute__((visibility("internal"))) int
+test_intern_func(void) {return 0;}
+int main(void) {return test_intern_func();}
+ ]])
+ ],
+ [mhd_cv_cc_attr_visibility_internal="yes"],[mhd_cv_cc_attr_visibility_internal="no"]
+ )
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_attr_visibility_internal],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_VISIBILITY_INTERNAL],[1],
+ [Define to '1' if your compiler supports __attribute__((visibility("internal")))]
+ )
+ ]
+ )
-MHD_CHECK_FUNC([accept4],
- [[
-#if defined(HAVE_SYS_TYPES_H)
-# include <sys/types.h>
-#endif
-#include <sys/socket.h>
- ]],
- [[
- struct sockaddr sk_addr;
- socklen_t addr_size;
- i][f (0 > accept4(0, &sk_addr, &addr_size, 0))
- return 3;
- ]]
+ CFLAGS="${user_CFLAGS}"
+ MHD_CHECK_CC_CFLAG([-fvisibility=hidden],[CFLAGS_ac],
+ [
+ MHD_APPEND_FLAG_TO_VAR([MHD_LIB_CFLAGS],[-fvisibility=hidden])
+ ],
+ [
+ AC_MSG_WARN([[$CC supports __attribute__((visibility("default"))), but does not support -fvisibility=hidden. Check compiler and compiler flags.]])
+ ]
+ )
+ ]
)
-MHD_CHECK_FUNC([gmtime_r],
- [[
-#if defined(HAVE_SYS_TYPES_H)
-# include <sys/types.h>
-#endif
-#include <time.h>
- ]],
- [[
- time_t timer = (time_t) 0;
- struct tm res;
+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; }
- i][f (&res != gmtime_r(&timer, &res))
- return 3;
- ]]
+int main(void) { return test_func(); }
+ ]])
+ ],
+ [mhd_cv_cc_attr_warn_unused_res="yes"],[mhd_cv_cc_attr_warn_unused_res="no"]
+ )
+ ]
)
-MHD_CHECK_FUNC([memmem],
- [[
-#if defined(HAVE_STDDEF_H)
-# include <stddef.h>
-#elif defined(HAVE_STDLIB_H)
-# include <stdlib.h>
-#endif /* HAVE_STDLIB_H */
-#include <string.h>
- ]],
- [[
- const char *haystack = "abc";
- size_t hslen = 3;
- const char *needle = "b";
- size_t needlelen = 1;
+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;
+}
- i][f ((haystack + 1) != memmem(haystack, hslen, needle, needlelen))
- return 3;
- ]]
+int main(void) {
+ return *(test_func());
+}
+ ]])
+ ],
+ [mhd_cv_cc_attr_ret_nonnull="yes"],[mhd_cv_cc_attr_ret_nonnull="no"]
+ )
+ ]
)
-MHD_CHECK_FUNC([snprintf],
- [[
-#include <stdio.h>
- ]],
- [[
- char buf[2];
+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;}
- i][f (1 != snprintf(buf, 2, "a"))
- return 3;
- /* Do not use the next check to avoid compiler warning */
- /* i][f (4 != snprintf(buf, 2, "abcd"))
- return 4; */
- ]]
+int main(void) {
+ int i = 0;
+ return test_func(&i) ? 0 : 5;
+}
+ ]])
+ ],
+ [mhd_cv_cc_attr_nonnull="yes"],[mhd_cv_cc_attr_nonnull="no"]
+ )
+ ]
)
-AC_CHECK_DECL([gmtime_s],
+AS_VAR_IF([mhd_cv_cc_attr_nonnull],["yes"],
[
- AC_MSG_CHECKING([[whether gmtime_s is in C11 form]])
- AC_LINK_IFELSE(
- [ AC_LANG_PROGRAM(
- [[
-#define __STDC_WANT_LIB_EXT1__ 1
-#include <time.h>
-#ifdef __cplusplus
-extern "C"
-#endif
- struct tm* gmtime_s(const time_t* time, struct tm* result);
- ]], [[
- static struct tm res;
- static time_t t = 0;
- gmtime_s (&t, &res);
+ 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;
+}
]])
- ],
- [
- AC_DEFINE([HAVE_C11_GMTIME_S], [1], [Define to 1 if you have the `gmtime_s' function in C11 form.])
- AC_MSG_RESULT([[yes]])
- ],
- [
- AC_MSG_RESULT([[no]])
- AC_MSG_CHECKING([[whether gmtime_s is in W32 form]])
- AC_LINK_IFELSE(
- [ AC_LANG_PROGRAM(
- [[
-#include <time.h>
-#ifdef __cplusplus
-extern "C"
-#endif
-errno_t gmtime_s(struct tm* _tm, const time_t* time);
- ]], [[
- static struct tm res;
- static time_t t = 0;
- gmtime_s (&res, &t);
- ]])
- ],
- [
- AC_DEFINE([HAVE_W32_GMTIME_S], [1], [Define to 1 if you have the `gmtime_s' function in W32 form.])
- AC_MSG_RESULT([[yes]])
- ],
- [AC_MSG_RESULT([[no]])
- ])
- ])
- ], [],
- [[#define __STDC_WANT_LIB_EXT1__ 1
-#include <time.h>]])
+ ],
+ [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;}
-AC_CHECK_DECL([SOCK_NONBLOCK], [AC_DEFINE([HAVE_SOCK_NONBLOCK], [1], [SOCK_NONBLOCK is defined in a socket header])], [],
- [[
-#if defined(HAVE_SYS_TYPES_H)
-# include <sys/types.h>
-#endif
-#if defined(HAVE_SYS_SOCKET_H)
-# include <sys/socket.h>
-#elif defined(HAVE_WINSOCK2_H)
-# include <winsock2.h>
-#endif
- ]]
+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;}
-MHD_FIND_LIB([clock_gettime],[[#include <time.h>]],
- [[
- struct timespec tp;
- i][f (0 > clock_gettime(CLOCK_REALTIME, &tp))
- return 3;
- ]],
- [rt],
+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_CLOCK_GETTIME], [1], [Define to '1' if you have clock_gettime() function])
- AS_VAR_IF([[mhd_cv_find_lib_clock_gettime]],[["none required"]], [],
- [
- MHD_LIBDEPS_PKGCFG="${mhd_cv_find_lib_clock_gettime} $MHD_LIBDEPS_PKGCFG"
- ]
+ AC_DEFINE([HAVE_ATTR_ACCESS_READ_WRITE],[1],
+ [Define to '1' if your compiler supports __attribute__ ((access (read_write,NUM)))]
)
- ],[],
- [MHD_LIBDEPS]
+ ]
)
+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];}
-MHD_CHECK_FUNC([clock_get_time],
- [[
-#include <mach/clock.h>
-#include <mach/mach.h>
- ]],
- [[
- clock_serv_t cs;
- mach_timespec_t mt;
- host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cs);
- clock_get_time(cs, &mt);
- mach_port_deallocate(mach_task_self(), cs);
- ]]
+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;}
-MHD_CHECK_FUNC([gethrtime],
- [[
-#ifdef HAVE_SYS_TIME_H
-/* Solaris define gethrtime() in sys/time.h */
-#include <sys/time.h>
-#endif /* HAVE_SYS_TIME_H */
-#ifdef HAVE_TIME_H
-/* HP-UX define gethrtime() in time.h */
-#include <time.h>
-#endif /* HAVE_TIME_H */
- ]],
- [[
- hrtime_t hrt = gethrtime();
- i][f (0 == hrt)
- return 3;
- ]]
+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([ac_cv_header_time_h], ["yes"],
+AS_VAR_IF([mhd_cv_cc_attr_access_write_size],["yes"],
[
- MHD_CHECK_FUNC([timespec_get],
- [[
-#include <time.h>
-
-#ifndef TIME_UTC
-#error TIME_UTC must be defined to use timespec_get()
-choke me now
-#endif
- ]],
- [[
- struct timespec ts;
- i][f (TIME_UTC != timespec_get (&ts, TIME_UTC))
- return 3;
- ]]
+ AC_DEFINE([HAVE_ATTR_ACCESS_WRITE_SIZE],[1],
+ [Define to '1' if your compiler supports __attribute__ ((access (write_only,NUM,NUM_SIZE)))]
)
]
)
-
-MHD_CHECK_FUNC_GETTIMEOFDAY
-
-# IPv6
-AC_CACHE_CHECK([for IPv6],[mhd_cv_have_inet6],
+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_PROGRAM([[
-#include <stdio.h>
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef HAVE_WINSOCK2_H
-#include <winsock2.h>
-#endif
-#ifdef HAVE_WS2TCPIP_H
-#include <ws2tcpip.h>
-#endif
- ]], [[
- int af=AF_INET6;
- int pf=PF_INET6;
- struct sockaddr_in6 sa;
- printf("%d %d %p\n", af, pf, (void*) &sa);
- ]]
- )
+ 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];
+}
+ ]])
],
- [AS_VAR_SET([mhd_cv_have_inet6],["yes"])],
- [AS_VAR_SET([mhd_cv_have_inet6],["no"])]
+ [mhd_cv_cc_attr_access_read_write_size="yes"],[mhd_cv_cc_attr_access_read_write_size="no"]
)
]
)
-AS_VAR_IF([mhd_cv_have_inet6],["yes"],
- [AC_DEFINE([HAVE_INET6], [1], [Define to '1' if you have IPv6 headers])]
+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)))]
+ )
+ ]
)
-
-MHD_CHECK_FUNC([[sysconf]], [[#include <unistd.h>]], [[long a = sysconf(0); if (a) return 1;]])
-
-MHD_CHECK_FUNC([[sysctl]], [[
+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
-#ifdef HAVE_SYS_SYSCTL_H
-#include <sys/sysctl.h>
+#include <stdio.h>
+#ifdef HAVE_IO_H
+#include <io.h>
#endif
-#if defined(HAVE_STDDEF_H)
-#include <stddef.h>
-#elif defined(HAVE_STDLIB_H)
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
- ]], [[
- int mib[2] = {0, 0}; /* Avoid any platform-specific values */
- i][f (sysctl(mib, 2, NULL, NULL, NULL, 0)) return 1;
- ]],
+#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_CHECK_DECLS([CTL_NET,PF_INET,IPPROTO_ICMP,ICMPCTL_ICMPLIM],[],[],
- [[
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
-#ifdef HAVE_SYS_SYSCTL_H
-#include <sys/sysctl.h>
-#endif /* HAVE_SYS_SYSCTL_H */
-#ifdef HAVE_SYS_SYSCTL_H
-#include <sys/sysctl.h>
-#endif /* HAVE_SYS_SYSCTL_H */
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif /* HAVE_SYS_SOCKET_H */
-#ifdef HAVE_NETINET_IN_SYSTM_H
-#include <netinet/in_systm.h>
-#endif /* HAVE_NETINET_IN_SYSTM_H */
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif /* HAVE_NETINET_IN_H */
-#ifdef HAVE_NETINET_IP_H
-#include <netinet/ip.h>
-#endif /* HAVE_NETINET_IP_H */
-#ifdef HAVE_NETINET_IP_ICMP_H
-#include <netinet/ip_icmp.h>
-#endif /* HAVE_NETINET_IP_ICMP_H */
-#ifdef HAVE_NETINET_ICMP_VAR_H
-#include <netinet/icmp_var.h>
-#endif /* HAVE_NETINET_ICMP_VAR_H */
- ]]
+ 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;}
-MHD_CHECK_FUNC([[sysctlbyname]], [[
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_SYSCTL_H
-#include <sys/sysctl.h>
-#endif
-#if defined(HAVE_STDDEF_H)
-#include <stddef.h>
-#elif defined(HAVE_STDLIB_H)
-#include <stdlib.h>
-#endif
- ]], [[sysctlbyname("test", NULL, NULL, NULL, 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"
+ ac_c_werror_flag="yes"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+enum __attribute__((enum_extensibility (closed))) Test_Enum
+{Val_A = 0, Val_B = 3};
-MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
-MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
+int main(void)
+{
+ enum Test_Enum var = Val_A;
+ return (var != Val_B) ? 0 : 3;
+}
+ ]])
+ ],
+ [mhd_cv_cc_attr_enum_extns_closed="yes"],[mhd_cv_cc_attr_enum_extns_closed="no"]
+ )
+ ac_c_werror_flag="$SAVE_ac_c_werror_flag"
+ AS_UNSET([SAVE_ac_c_werror_flag])
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_enum_extns_closed],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_ENUM_EXTNS_CLOSED],[1],
+ [Define to '1' if your compiler supports __attribute__((enum_extensibility (closed)))]
+ )
+ ]
+)
+AC_CACHE_CHECK([whether $CC supports __attribute__((flag_enum))],[mhd_cv_cc_attr_flag_enum],
+ [
+ SAVE_ac_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag="yes"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_ATTR_ENUM_EXTNS_CLOSED
+#define ATTR_EXTNS_CLOSED_IF_SUPPORTED __attribute__((enum_extensibility (closed)))
+#else
+#define ATTR_EXTNS_CLOSED_IF_SUPPORTED /* empty */
+#endif
+enum ATTR_EXTNS_CLOSED_IF_SUPPORTED __attribute__((flag_enum)) Test_Enum
+{Flag_A = 1 << 0, Flag_B = 1 << 1, Flag_C = 1 << 2, Flag_D = 1 << 3};
-HIDDEN_VISIBILITY_CFLAGS=""
-AH_TEMPLATE([_MHD_EXTERN],[defines how to decorate public symbols w][hile building the library])
-CFLAGS="${user_CFLAGS}"
-MHD_CHECK_CC_CFLAG([-fvisibility=hidden],[CFLAGS_ac],
+int main(void)
+{
+ enum Test_Enum var = Flag_A;
+ var |= Flag_C;
+ var = var | Flag_D | Flag_A;
+ return (var != Flag_B) ? 0 : 3;
+}
+ ]])
+ ],
+ [mhd_cv_cc_attr_flag_enum="yes"],[mhd_cv_cc_attr_flag_enum="no"]
+ )
+ ac_c_werror_flag="$SAVE_ac_c_werror_flag"
+ AS_UNSET([SAVE_ac_c_werror_flag])
+ ]
+)
+AS_VAR_IF([mhd_cv_cc_attr_flag_enum],["yes"],
[
- # NOTE: require setting of errattr_CFLAGS above
- CFLAGS="${CFLAGS_ac} -fvisibility=hidden ${user_CFLAGS} ${errattr_CFLAGS}"
- AC_CACHE_CHECK([whether $CC supports __attribute__((visibility("default")))],[mhd_cv_cc_attr_visibility],
- [
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
-extern __attribute__((visibility("default"))) int test_extrn_func(void);
+ AC_DEFINE([HAVE_ATTR_FLAG_ENUM],[1],
+ [Define to '1' if your compiler supports __attribute__((flag_enum))]
+ )
+ ]
+)
+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 test_extrn_func(void) {return 0;}
- ]])
- ],
- [mhd_cv_cc_attr_visibility="yes"],[mhd_cv_cc_attr_visibility="no"]
- )
- ]
+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_attr_visibility],["yes"],
- [
- HIDDEN_VISIBILITY_CFLAGS="-fvisibility=hidden"
- AS_IF([test "x$os_is_native_w32" = "xyes" && test "x$enable_shared" = "xyes"],
- [AC_DEFINE([_MHD_EXTERN], [__attribute__((visibility("default"))) __declspec(dllexport) extern])],
- [AC_DEFINE([_MHD_EXTERN], [__attribute__((visibility("default"))) extern])]
- )
+ ]
+)
+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);
+}
+ ]])
],
- [
- AC_MSG_WARN([$CC supports -fvisibility, but does not support __attribute__((visibility("default"))). Check compiler and compiler flags.])
- AC_DEFINE([_MHD_EXTERN], [extern])
- ]
+ [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]]
)
- ],[AC_DEFINE([_MHD_EXTERN], [extern])]
+ ]
)
CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
-AC_SUBST([HIDDEN_VISIBILITY_CFLAGS])
# libcurl (required for testing)
AC_ARG_ENABLE([curl],
@@ -3406,10 +4582,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;
@@ -3448,18 +4624,24 @@ AS_VAR_IF([[found_sendfile]], [["no"]],
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[[
+#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>
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;
@@ -3467,7 +4649,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();
]]
@@ -3489,25 +4671,31 @@ AS_VAR_IF([[found_sendfile]], [["no"]],
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[[
+#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>
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();
]]
@@ -3526,24 +4714,30 @@ 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(
[AC_LANG_PROGRAM(
[[
+#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/sendfile.h>
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;
@@ -3557,19 +4751,30 @@ 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"
AC_MSG_RESULT([[yes]])
MHD_CHECK_FUNC([sendfile64],
[[
+#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/sendfile.h>
]],
[[
off64_t f_offset = (off64_t) 0;
+#ifndef sendfile64
+ (void)sendfile64;
+#endif
if (0 > sendfile64 (0, 1, &f_offset, 1))
return 3;
]]
@@ -3585,19 +4790,48 @@ AS_IF([[test "x$found_sendfile" = "xno" && test "x$enable_sendfile" = "xyes"]],
[AC_MSG_ERROR([[sendfile() usage was requested by configure parameter, but no usable sendfile() function is detected]])]
)
-# optional: enable error and informational messages
-AC_MSG_CHECKING([[whether to generate text messages]])
+# optional: disable log and HTTP automatic messages
AC_ARG_ENABLE([messages],
- [AS_HELP_STRING([--disable-messages],
- [disable error, warning messages and text bodies for ]
- [automatic HTTP responses (to reduce the binary size)])],
- [enable_messages=${enableval}],
- [enable_messages=yes])
-AS_IF([[test "x$enable_messages" = "xyes"]],
- [ AC_DEFINE([HAVE_MESSAGES],[1],[Define to 1 to enable support for text messages.]) ],
- [[ enable_messages=no ]])
-AC_MSG_RESULT([[$enable_messages]])
-AM_CONDITIONAL([HAVE_MESSAGES], [test "x$enable_messages" != "xno"])
+ [
+ AS_HELP_STRING([--disable-messages],
+ [disable log messages and text bodies for ]
+ [automatic HTTP responses (to reduce the binary size)]
+ )
+ ],
+ [],[enable_messages="yes"]
+)
+
+AC_MSG_CHECKING([[whether to support internal logging functionality and build messages for log]])
+AC_ARG_ENABLE([log-messages],
+ [
+ AS_HELP_STRING([--disable-log-messages],
+ [disable logger functionality and exclude log mesages from binary]
+ )
+ ],
+ [],[enable_log_messages="$enable_messages"]
+)
+AS_VAR_IF([enable_log_messages],["yes"],
+ [AC_DEFINE([HAVE_LOG_FUNCTIONALITY],[1],[Define to '1' to enable internal logging and log messages.])],
+ [enable_log_messages="no"]
+)
+AC_MSG_RESULT([[$enable_log_messages]])
+AM_CONDITIONAL([HAVE_LOG_FUNCTIONALITY], [test "x$enable_log_messages" != "xno"])
+
+AC_MSG_CHECKING([[whether to build text bodies for automatic HTTP response messages]])
+AC_ARG_ENABLE([http-messages],
+ [
+ AS_HELP_STRING([--disable-http-messages],
+ [use empty bodies for automatic HTTP responses (less verbose for clients)]
+ )
+ ],
+ [],[enable_http_messages="$enable_messages"]
+)
+AS_VAR_IF([enable_http_messages],["yes"],
+ [AC_DEFINE([HAVE_HTTP_AUTO_MESSAGES_BODIES],[1],[Define to '1' to enable verbose text bodies for automatic HTTP replies.])],
+ [enable_http_messages="no"]
+)
+AC_MSG_RESULT([[$enable_http_messages]])
+AM_CONDITIONAL([HAVE_HTTP_AUTO_MESSAGES_BODIES], [test "x$enable_http_messages" != "xno"])
# optional: have postprocessor?
@@ -4070,7 +5304,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;
]]
)
@@ -4409,7 +5643,13 @@ AS_IF([test "x$enable_dauth" != "xno"],
AC_CACHE_CHECK([[for calloc()]], [[mhd_cv_have_func_calloc]],
[
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#elif defined(HAVE_MALLOC_H)
+#include <malloc.h>
+#else
+#include <unistd.h>
+#endif
]],[[void * ptr = calloc(1, 2); if (ptr) return 1;]])
],
[[mhd_cv_have_func_calloc='yes']],
@@ -4419,6 +5659,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"]],
@@ -4511,6 +5752,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"],
[
@@ -4542,7 +5785,7 @@ AS_VAR_IF([enable_tools],["yes"],
i][f (1 != pstat_getdynamic(&psd_data, sizeof(psd_data), (size_t)1, 0))
return 2;
i][f (0 >= psd_data.psd_proc_cnt)
- return 3;
+ return 3;
]]
)
MHD_CHECK_FUNC([vxCpuEnabledGet],[[#include <vxCpuLib.h>]],
@@ -5948,15 +7191,22 @@ AM_CONDITIONAL([HAVE_EXPERIMENTAL], [test "x$enable_experimental" = "xyes"])
AC_CONFIG_FILES([
-libmicrohttpd.pc
+src/mhd2/libmicrohttpd2.pc
+w32/common/microhttpd_dll_res_vc.rc
Makefile
contrib/Makefile
doc/Makefile
doc/doxygen/libmicrohttpd.doxy
doc/doxygen/Makefile
+doc/examples/Makefile
m4/Makefile
src/Makefile
+src/incl_priv/Makefile
src/include/Makefile
+src/mhd2/Makefile
+src/tests/Makefile
+src/tests/basic/Makefile
+src/examples2/Makefile
])
AC_OUTPUT
@@ -6011,11 +7261,13 @@ 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}
- Messages: ${enable_messages}
+ Logging support: ${enable_log_messages}
+ Verbose auto replies: ${enable_http_messages}
Cookie parsing: ${enable_cookie}
Postproc: ${enable_postprocessor}
Basic auth.: ${enable_bauth}
@@ -6033,7 +7285,7 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary:
Build shared lib: ${enable_shared}
Build docs: ${enable_doc}
Build examples: ${enable_examples}
- Build tools: ${enable_examples}
+ Build tools: ${enable_tools}
Test with libcurl: ${MSG_CURL}
Heavy tests: ${use_heavy_tests_MSG}
Fuzzing tests: ${run_zzuf_tests_MSG=no}
diff --git a/m4/mhd_shutdown_socket_trigger.m4 b/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
diff --git a/src/Makefile.am b/src/Makefile.am
@@ -1,6 +1,14 @@
# This Makefile.am is in the public domain
-SUBDIRS = include .
+if BUILD_EXAMPLES
+EXAMPLES_DIRS = examples2
+endif
+
+SUBDIRS = . incl_priv include mhd2 $(EXAMPLES_DIRS) tests
+
+if BUILD_TOOLS
+#SUBDIRS += tools
+endif
EXTRA_DIST = \
datadir/cert-and-key.pem \
diff --git a/src/examples2/.gitignore b/src/examples2/.gitignore
@@ -0,0 +1 @@
+minimal_example2
diff --git a/src/examples2/Makefile.am b/src/examples2/Makefile.am
@@ -0,0 +1,32 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ $(CPPFLAGS_ac)
+
+AM_CFLAGS = $(CFLAGS_ac)
+
+AM_LDFLAGS = $(LDFLAGS_ac)
+
+MHD_CPU_COUNT_DEF = -DMHD_CPU_COUNT=$(CPU_COUNT)
+
+AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
+
+LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la
+
+if USE_COVERAGE
+ AM_CFLAGS += --coverage
+endif
+
+$(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
+
+
+# example programs
+noinst_PROGRAMS = \
+ minimal_example2
+
+minimal_example2_SOURCES = \
+ minimal_example2.c
diff --git a/src/examples2/minimal_example2.c b/src/examples2/minimal_example2.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 minimal_example2.c
+ * @brief Minimal example for libmicrohttpd v2
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <microhttpd2.h>
+
+static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3)
+const struct MHD_Action *
+req_cb (void *cls,
+ struct MHD_Request *MHD_RESTRICT request,
+ const struct MHD_String *MHD_RESTRICT path,
+ enum MHD_HTTP_Method method,
+ uint_fast64_t upload_size)
+{
+ static const char res_msg[] = "Hello there!\n";
+
+ (void) cls;
+ (void) path;
+ (void) method;
+ (void) upload_size; /* Unused */
+
+ return MHD_action_from_response (
+ request,
+ MHD_response_from_buffer_static (
+ MHD_HTTP_STATUS_OK,
+ sizeof(res_msg) / sizeof(char) - 1,
+ res_msg));
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ struct MHD_Daemon *d;
+ int port;
+
+ if (argc != 2)
+ {
+ fprintf (stderr,
+ "Usage:\n%s PORT\n",
+ argv[0]);
+ return 1;
+ }
+ port = atoi (argv[1]);
+ if ((1 > port) || (65535 < port))
+ {
+ fprintf (stderr,
+ "The PORT must be a numeric value between 1 and 65535.\n");
+ return 2;
+ }
+ d = MHD_daemon_create (&req_cb,
+ NULL);
+ if (NULL == d)
+ {
+ fprintf (stderr,
+ "Failed to create MHD daemon.\n");
+ return 3;
+ }
+ if (MHD_SC_OK !=
+ MHD_DAEMON_SET_OPTIONS (
+ d,
+ MHD_D_OPTION_WM_WORKER_THREADS (1),
+ MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
+ (uint_least16_t) port)))
+ {
+ fprintf (stderr,
+ "Failed to set MHD daemon run parameters.\n");
+ }
+ else
+ {
+ if (MHD_SC_OK !=
+ MHD_daemon_start (d))
+ {
+ fprintf (stderr,
+ "Failed to start MHD daemon.\n");
+ }
+ else
+ {
+ printf ("The MHD daemon is listening on port %d\n"
+ "Press ENTER to stop.\n", port);
+ (void) fgetc (stdin);
+ }
+ }
+ printf ("Stopping... ");
+ fflush (stdout);
+ MHD_daemon_destroy (d);
+ printf ("OK\n");
+ return 0;
+}
diff --git a/src/incl_priv/Makefile.am b/src/incl_priv/Makefile.am
@@ -0,0 +1,3 @@
+# This Makefile.am is in the public domain
+
+noinst_HEADERS = mhd_sys_options.h
diff --git a/src/incl_priv/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h
@@ -0,0 +1,550 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2016-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
+ 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/include/mhd_sys_options.h
+ * @brief additional automatic macros for MHD_config.h
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This file includes MHD_config.h and adds automatic macros based on values
+ * in MHD_config.h, compiler built-in macros and commandline-defined macros
+ * (but not based on values defined in other headers). Works also as a guard
+ * to prevent double inclusion of MHD_config.h
+ *
+ * This file must be included always before other headers as this header
+ * defines macros that control behaviour of other included headers.
+ */
+
+#ifndef MHD_SYS_OPTIONS_H
+#define MHD_SYS_OPTIONS_H 1
+
+#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
+ * we do not actually call gettext() in MHD, but we do make it
+ * easy to create a ".po" file so that applications that do want
+ * to translate error messages can do so.
+ */
+#define _(String) (String)
+
+#ifdef HAVE_ATTR_VISIBILITY_DEFAULT
+# define MHD_VISIBILITY_EXTERN __attribute__((visibility ("default")))
+#else
+# define MHD_VISIBILITY_EXTERN /* empty */
+#endif
+
+#if ! defined(_WIN32) || \
+ (! defined(DLL_EXPORT) /* Defined by libtool for shared version */ \
+ && ! defined(MHD_W32DLL) /* Defined by MS VS projects for MHD DLL */)
+# define MHD_EXPORTED /* empty */
+#else
+# define MHD_EXPORTED __declspec(dllexport)
+#endif
+
+#if defined(HAVE_ATTR_USED) \
+ && (defined(PIC) || defined(DLL_EXPORT) || defined(MHD_W32DLL))
+/* Used externally, only for functions in shared library */
+# define MHD_EXTERN_USED __attribute__((used))
+#else
+# define MHD_EXTERN_USED /* empty */
+#endif
+
+#if defined(MHD_EXTERN_) && defined(BUILDING_MHD_LIB)
+# undef MHD_EXTERN_
+#endif /* MHD_EXTERN_ && BUILDING_MHD_LIB */
+
+#ifndef MHD_EXTERN_
+# ifdef BUILDING_MHD_LIB
+/* Building MHD itself */
+# define MHD_EXTERN_ \
+ extern MHD_VISIBILITY_EXTERN MHD_EXPORTED MHD_EXTERN_USED
+# else /* ! BUILDING_MHD_LIB */
+/* Test or example code, using MHD as a library */
+# define MHD_EXTERN_ extern
+# endif /* ! BUILDING_MHD_LIB */
+#endif /* ! MHD_EXTERN_ */
+
+#ifdef HAVE_ATTR_VISIBILITY_INTERNAL
+/* To be used with internal non-static functions */
+# define MHD_VISIBILITY_INTERNAL __attribute__((visibility ("internal")))
+#else
+/* To be used with internal non-static functions */
+# 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
+# define MHD_FN_PURE_ /* empty */
+#endif
+
+#ifdef HAVE_ATTR_CONST
+# define MHD_FN_CONST_ __attribute__((const))
+#else
+# define MHD_FN_CONST_ MHD_FN_PURE_
+#endif
+
+#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_
+
+/* 'inline' and 'restrict' are defined in mhd_config.h if needed */
+#define MHD_INLINE inline
+
+#define MHD_RESTRICT restrict
+
+#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 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 */
+#elif defined(_WIN32) || defined(__CYGWIN__)
+/* Platform with WinSock and without overridden FD_SETSIZE */
+# 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
+#endif /* !FD_SETSIZE && !W32 */
+
+#if defined(HAVE_LINUX_SENDFILE) || defined(HAVE_FREEBSD_SENDFILE) || \
+ defined(HAVE_DARWIN_SENDFILE)
+/* Have any supported sendfile() function. */
+# 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
+# define MHD_USE_THREADS 1
+# 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
+#define _OPEN_MSGQ_EXT
+#define _LP64
+#endif
+
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+/* Declare POSIX-compatible names */
+# define _CRT_DECLARE_NONSTDC_NAMES 1
+/* Do not warn about POSIX name usage */
+# define _CRT_NONSTDC_NO_WARNINGS 1
+# ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0600
+# else /* _WIN32_WINNT */
+# if _WIN32_WINNT < 0x0501
+#error "Headers for Windows XP or later are required"
+# endif /* _WIN32_WINNT < 0x0501 */
+# endif /* _WIN32_WINNT */
+# ifndef WIN32_LEAN_AND_MEAN
+/* Do not include unneeded parts of W32 headers. */
+# define WIN32_LEAN_AND_MEAN 1
+# 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 */
+
+#if defined(LINUX) && (defined(HAVE_SENDFILE64) || defined(HAVE_LSEEK64)) && \
+ ! defined(_LARGEFILE64_SOURCE)
+/* On Linux, special macro is required to enable definitions of some xxx64 functions */
+#define _LARGEFILE64_SOURCE 1
+#endif
+
+#ifdef HAVE_C11_GMTIME_S
+/* Special macro is required to enable C11 definition of gmtime_s() function */
+#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.
+#error Cannot favor speed and size at the same time.
+#endif /* MHD_FAVOR_FAST_CODE && MHD_FAVOR_SMALL_CODE */
+
+/* Define MHD_FAVOR_FAST_CODE to force fast code path or
+ define MHD_FAVOR_SMALL_CODE to choose compact code path */
+#if ! defined(MHD_FAVOR_FAST_CODE) && ! defined(MHD_FAVOR_SMALL_CODE)
+/* Try to detect user preferences */
+/* Defined by GCC and many compatible compilers */
+# if defined(__OPTIMIZE_SIZE__)
+# define MHD_FAVOR_SMALL_CODE 1
+# elif defined(__OPTIMIZE__)
+# define MHD_FAVOR_FAST_CODE 1
+# endif /* __OPTIMIZE__ */
+#endif /* !MHD_FAVOR_FAST_CODE && !MHD_FAVOR_SMALL_CODE */
+
+#if ! defined(MHD_FAVOR_FAST_CODE) && ! defined(MHD_FAVOR_SMALL_CODE)
+/* Use faster code by default */
+# define MHD_FAVOR_FAST_CODE 1
+#endif /* !MHD_FAVOR_FAST_CODE && !MHD_FAVOR_SMALL_CODE */
+
+#if defined(MHD_FAVOR_SMALL_CODE) && defined(MHD_static_inline_)
+# undef MHD_static_inline_
+# define MHD_static_inline_ static inline /* give compiler more freedom */
+#endif
+
+#ifndef MHD_ASAN_ACTIVE
+#if (defined(__GNUC__) || defined(_MSC_VER)) && defined(__SANITIZE_ADDRESS__)
+#define MHD_ASAN_ACTIVE 1
+#elif defined(__has_feature)
+#if __has_feature (address_sanitizer)
+#define MHD_ASAN_ACTIVE 1
+#endif /* __has_feature(address_sanitizer) */
+#endif /* __has_feature */
+#endif /* MHD_ASAN_ACTIVE */
+
+#if defined(MHD_ASAN_ACTIVE) && defined(HAVE_SANITIZER_ASAN_INTERFACE_H) && \
+ (defined(FUNC_PTRCOMPARE_CAST_WORKAROUND_WORKS) || \
+ (defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \
+ defined(FUNC_ATTR_PTRSUBTRACT_WORKS)) || \
+ defined(FUNC_ATTR_NOSANITIZE_WORKS))
+#ifndef MHD_ASAN_POISON_ACTIVE
+/* User ASAN poisoning could be used */
+#warning User memory poisoning is not active
+#endif /* ! MHD_ASAN_POISON_ACTIVE */
+#else /* ! (MHD_ASAN_ACTIVE && HAVE_SANITIZER_ASAN_INTERFACE_H &&
+ (FUNC_ATTR_PTRCOMPARE_WORKS || FUNC_ATTR_NOSANITIZE_WORKS)) */
+#ifdef MHD_ASAN_POISON_ACTIVE
+#error User memory poisoning is active, but conditions are not suitable
+#endif /* MHD_ASAN_POISON_ACTIVE */
+#endif /* ! (MHD_ASAN_ACTIVE && HAVE_SANITIZER_ASAN_INTERFACE_H &&
+ (FUNC_ATTR_PTRCOMPARE_WORKS || FUNC_ATTR_NOSANITIZE_WORKS)) */
+
+#ifndef _MSC_FULL_VER
+# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ /* empty */
+# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ /* empty */
+#else /* _MSC_FULL_VER */
+# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ \
+ __pragma(runtime_checks("c", off))
+# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ \
+ __pragma(runtime_checks("c", restore))
+#endif /* _MSC_FULL_VER */
+
+/* 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
+ has non-zero value. Unlike HAVE_DECL_*, macros HAS_DECL_*
+ cannot have zero value. */
+#ifdef HAVE_DECL__SC_NPROCESSORS_ONLN
+# if 0 == HAVE_DECL__SC_NPROCESSORS_ONLN
+# undef HAVE_DECL__SC_NPROCESSORS_ONLN
+# else /* 0 != HAVE_DECL__SC_NPROCESSORS_ONLN */
+# define HAS_DECL__SC_NPROCESSORS_ONLN 1
+# endif /* 0 != HAVE_DECL__SC_NPROCESSORS_ONLN */
+#endif /* HAVE_DECL__SC_NPROCESSORS_ONLN */
+
+#ifdef HAVE_DECL__SC_NPROCESSORS_CONF
+# if 0 == HAVE_DECL__SC_NPROCESSORS_CONF
+# undef HAVE_DECL__SC_NPROCESSORS_CONF
+# else /* 0 != HAVE_DECL__SC_NPROCESSORS_CONF */
+# define HAS_DECL__SC_NPROCESSORS_CONF 1
+# endif /* 0 != HAVE_DECL__SC_NPROCESSORS_CONF */
+#endif /* HAVE_DECL__SC_NPROCESSORS_CONF */
+
+#ifdef HAVE_DECL__SC_NPROC_ONLN
+# if 0 == HAVE_DECL__SC_NPROC_ONLN
+# undef HAVE_DECL__SC_NPROC_ONLN
+# else /* 0 != HAVE_DECL__SC_NPROC_ONLN */
+# define HAS_DECL__SC_NPROC_ONLN 1
+# endif /* 0 != HAVE_DECL__SC_NPROC_ONLN */
+#endif /* HAVE_DECL__SC_NPROC_ONLN */
+
+#ifdef HAVE_DECL__SC_CRAY_NCPU
+# if 0 == HAVE_DECL__SC_CRAY_NCPU
+# undef HAVE_DECL__SC_CRAY_NCPU
+# else /* 0 != HAVE_DECL__SC_CRAY_NCPU */
+# define HAS_DECL__SC_CRAY_NCPU 1
+# endif /* 0 != HAVE_DECL__SC_CRAY_NCPU */
+#endif /* HAVE_DECL__SC_CRAY_NCPU */
+
+#ifdef HAVE_DECL_CTL_HW
+# if 0 == HAVE_DECL_CTL_HW
+# undef HAVE_DECL_CTL_HW
+# else /* 0 != HAVE_DECL_CTL_HW */
+# define HAS_DECL_CTL_HW 1
+# endif /* 0 != HAVE_DECL_CTL_HW */
+#endif /* HAVE_DECL_CTL_HW */
+
+#ifdef HAVE_DECL_HW_NCPUONLINE
+# if 0 == HAVE_DECL_HW_NCPUONLINE
+# undef HAVE_DECL_HW_NCPUONLINE
+# else /* 0 != HAVE_DECL_HW_NCPUONLINE */
+# define HAS_DECL_HW_NCPUONLINE 1
+# endif /* 0 != HAVE_DECL_HW_NCPUONLINE */
+#endif /* HAVE_DECL_HW_NCPUONLINE */
+
+#ifdef HAVE_DECL_HW_AVAILCPU
+# if 0 == HAVE_DECL_HW_AVAILCPU
+# undef HAVE_DECL_HW_AVAILCPU
+# else /* 0 != HAVE_DECL_HW_AVAILCPU */
+# define HAS_DECL_HW_AVAILCPU 1
+# endif /* 0 != HAVE_DECL_HW_AVAILCPU */
+#endif /* HAVE_DECL_HW_AVAILCPU */
+
+#ifdef HAVE_DECL_HW_NCPU
+# if 0 == HAVE_DECL_HW_NCPU
+# undef HAVE_DECL_HW_NCPU
+# else /* 0 != HAVE_DECL_HW_NCPU */
+# define HAS_DECL_HW_NCPU 1
+# endif /* 0 != HAVE_DECL_HW_NCPU */
+#endif /* HAVE_DECL_HW_NCPU */
+
+#ifdef HAVE_DECL_CPU_SETSIZE
+# if 0 == HAVE_DECL_CPU_SETSIZE
+# undef HAVE_DECL_CPU_SETSIZE
+# else /* 0 != HAVE_DECL_CPU_SETSIZE */
+# define HAS_DECL_CPU_SETSIZE 1
+# endif /* 0 != HAVE_DECL_CPU_SETSIZE */
+#endif /* HAVE_DECL_CPU_SETSIZE */
+
+#ifndef MHD_DAUTH_DEF_TIMEOUT_
+# define MHD_DAUTH_DEF_TIMEOUT_ 90
+#endif /* ! MHD_DAUTH_DEF_TIMEOUT_ */
+#ifndef MHD_DAUTH_DEF_MAX_NC_
+# 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 */
diff --git a/src/include/mhd_future.h b/src/include/mhd_future.h
@@ -0,0 +1,358 @@
+#ifndef MHD_EXTERN_
+# if ! defined(_WIN32)
+# define MHD_EXTERN_ extern
+# else /* defined(_WIN32) */
+# if ! defined(MHD_W32LIB)
+# define MHD_EXTERN_ extern
+# else /* defined(_WIN32) && efined(MHD_W32LIB) */
+/* Define MHD_W32DLL when using MHD as W32 .DLL to speed up linker a little */
+# define MHD_EXTERN_ extern __declspec(dllimport)
+# endif
+# endif
+#endif
+
+
+#ifndef MHD_FIXED_ENUM_
+# define MHD_FIXED_ENUM_ /* empty */
+#endif /* MHD_FIXED_ENUM_ */
+#ifndef MHD_FLAGS_ENUM_
+# define MHD_FLAGS_ENUM_ /* empty */
+#endif /* MHD_FLAGS_ENUM_ */
+
+#ifndef MHD_FIXED_FLAGS_ENUM_
+# define MHD_FIXED_FLAGS_ENUM_ MHD_FIXED_ENUM_ MHD_FLAGS_ENUM_
+#endif
+
+#ifndef MHD_FIXED_ENUM_APP_SET_
+/* The enum is set by an application to the fixed list of values */
+# define MHD_FIXED_ENUM_APP_SET_ MHD_FIXED_ENUM_
+#endif
+
+#ifndef MHD_FLAGS_ENUM_APP_SET_
+/* The enum is set by an application, it is a bitmap */
+# define MHD_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_
+#endif
+
+#ifndef MHD_FIXED_FLAGS_ENUM_APP_SET_
+/* The enum is set by an application to the fixed bitmap values */
+# define MHD_FIXED_FLAGS_ENUM_APP_SET_ MHD_FIXED_FLAGS_ENUM_
+#endif
+
+#ifndef MHD_FIXED_ENUM_MHD_SET_
+/* The enum is set by MHD to the fixed list of values */
+# define MHD_FIXED_ENUM_MHD_SET_ /* enum can be extended in next MHD versions */
+#endif
+
+#ifndef MHD_FLAGS_ENUM_MHD_SET_
+/* The enum is set by MHD, it is a bitmap */
+# define MHD_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_
+#endif
+
+#ifndef MHD_FIXED_FLAGS_ENUM_MHD_SET_
+/* The enum is set by MHD to the fixed bitmap values */
+# define MHD_FIXED_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_ /* enum can be extended in next MHD versions */
+#endif
+
+#ifndef MHD_FIXED_ENUM_MHD_APP_SET_
+/* The enum is set by both MHD and app to the fixed list of values */
+# define MHD_FIXED_ENUM_MHD_APP_SET_ /* enum can be extended in next MHD versions */
+#endif
+
+#ifndef MHD_FLAGS_ENUM_MHD_APP_SET_
+/* The enum is set by both MHD and app, it is a bitmap */
+# define MHD_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_
+#endif
+
+#ifndef MHD_FIXED_FLAGS_ENUM_MHD_APP_SET_
+/* The enum is set by both MHD and app to the fixed bitmap values */
+# define MHD_FIXED_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ /* enum can be extended in next MHD versions */
+#endif
+
+
+#ifndef MHD_FN_CONST_
+# define MHD_FN_CONST_ /* empty */
+#endif /* ! MHD_FN_CONST_ */
+#ifndef MHD_FN_PURE_
+# define MHD_FN_PURE_ /* empty */
+#endif /* ! MHD_FN_PURE_ */
+#ifndef MHD_FN_MUST_CHECK_RESULT_
+# define MHD_FN_MUST_CHECK_RESULT_ /* empty */
+#endif /* ! MHD_FN_MUST_CHECK_RESULT_ */
+#ifndef MHD_FN_PAR_NONNULL_
+# define MHD_FN_PAR_NONNULL_(param_num) /* empty */
+#endif /* ! MHD_FN_PAR_NONNULL_ */
+#ifndef MHD_FN_PAR_NONNULL_ALL_
+# define MHD_FN_PAR_NONNULL_ALL_ /* empty */
+#endif /* ! MHD_FN_PAR_NONNULL_ALL_ */
+#ifndef MHD_FN_PAR_IN_
+# define MHD_FN_PAR_IN_(param_num) /* empty */
+#endif /* !MHD_FN_PAR_IN_ */
+#ifndef MHD_FN_PAR_IN_SIZE_
+# define MHD_FN_PAR_IN_SIZE_(param_num,size_num) /* empty */
+#endif /* !MHD_FN_PAR_IN_SIZE_ */
+#ifndef MHD_FN_PAR_OUT_
+# define MHD_FN_PAR_OUT_(param_num) /* empty */
+#endif /* !MHD_FN_PAR_OUT_ */
+#ifndef MHD_FN_PAR_OUT_SIZE_
+# define MHD_FN_PAR_OUT_SIZE_(param_num,size_num) /* empty */
+#endif /* !MHD_FN_PAR_OUT_SIZE_ */
+#ifndef MHD_FN_PAR_INOUT_
+# define MHD_FN_PAR_INOUT_(param_num) /* empty */
+#endif /* !MHD_FN_PAR_INOUT_ */
+#ifndef MHD_FN_PAR_INOUT_SIZE_
+# define MHD_FN_PAR_INOUT_SIZE_(param_num,size_num) /* empty */
+#endif /* !MHD_FN_PAR_INOUT_SIZE_ */
+#ifndef MHD_FN_PAR_FD_READ_
+# define MHD_FN_PAR_FD_READ_(param_num) /* empty */
+#endif /* !MHD_FN_PAR_FD_READ_ */
+#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_ */
+
+
+/**
+ * Which threading and polling mode should be used by MHD?
+ */
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_ThreadingPollingMode
+{
+ /**
+ * Use an external event loop.
+ * Application uses #MHD_get_watched_fds()/#MHD_get_watched_fds_update()
+ * and #MHD_process_watched_fds() with level triggered sockets
+ * polling (like select() or poll()).
+ */
+ MHD_TM_EXTERNAL_EVENT_LOOP_WFD_LEVEL = 10
+ ,
+ /**
+ * Use an external event loop.
+ * Application uses #MHD_get_watched_fds()/#MHD_get_watched_fds_update()
+ * and #MHD_process_watched_fds() with edge triggered sockets polling.
+ */
+ MHD_TM_EXTERNAL_EVENT_LOOP_WFD_EDGE = 11
+ ,
+};
+
+/* FUTURE:
+ (with eventually option "number of masters")
+ MHD_TM_WORKER_THREADS_WITH_MASTER_LISTENER = 3 */
+
+
+// Alternative style
+
+struct MHD_WatchedFD
+{
+ /**
+ * The watched socket.
+ * Ignored if set by application to #MHD_INVALID_SOCKET. TODO: Improve wording
+ */
+ MHD_Socket fd;
+
+ /**
+ * Indicates that socket should be watched for specific network state
+ * (when set by #MHD_get_watched_fds(), #MHD_get_watched_fds_update())
+ * / the network state of the socket (when used for
+ * #MHD_process_watched_fds())
+ */
+ enum MHD_FdState state;
+};
+
+/**
+ * Get the full list of the sockets that must be watched by application.
+ *
+ * The application may use this function each time to get a full list of
+ * the sockets for watch or may use #MHD_get_watched_fds_update() to
+ * get the incremental updates.
+ *
+ * // TODO: add introspection reference
+ *
+ * @param daemon the daemon to get the list
+ * @param num_elements the number of elements in @a fds list
+ * @param[out] wfds the arrays of @a num_elements of sockets to be watched
+ * by application, the unused elements (if any) at
+ * the end of the array are filled with
+ * { MHD_INVALID_SOCKET, MHD_FD_STATE_NONE }
+ * @param[out] max_wait the pointer to value set to maximum wait time
+ * for the network events, in microseconds
+ * @return ::MHD_SC_OK on success,
+ * error code otherwise
+ * @ingroup event
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_get_watched_fds (
+ struct MHD_Daemon *daemon,
+ unsigned int num_elements,
+ struct MHD_WatchedFD wfds[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)],
+ uint_fast64_t *max_wait)
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_OUT_(3) MHD_FN_PAR_NONNULL_(3)
+MHD_FN_PAR_OUT_(4) MHD_FN_PAR_NONNULL_ (4);
+
+
+enum MHD_WatchedFdAction
+{
+ /**
+ * New watched FD, to be added to the list
+ */
+ MHD_WFA_ADD = 1
+ ,
+ /**
+ * Update watching interest in already watched FD
+ */
+ MHD_WFA_UPDATE = 2
+ ,
+ /**
+ * Delete FD from watching list
+ */
+ MHD_WFA_REMOVE = 3
+ ,
+ /**
+ * No action. Used to fill the end of the array
+ * The matching FD is always #MHD_INVALID_SOCKET.
+ */
+ MHD_WFA_NONE = 0
+};
+
+struct MHD_WatchedFdUpdate
+{
+ /**
+ * The required action: add/update/delete
+ */
+ enum MHD_WatchedFdAction action;
+
+ /**
+ * The watched FD to add, update or delete.
+ */
+ struct MHD_WatchedFD watched_fd;
+};
+
+/**
+ * Get the update of the list of the sockets that must be watched
+ * by application.
+ * This function provides an update to the list of watched sockets
+ * since the last call of #MHD_get_watched_fds() or
+ * #MHD_get_watched_fds_update().
+ * If this function is called before #MHD_get_watched_fds() then it
+ * returns full list of sockets to watch with action #MHD_WFA_ADD.
+ *
+ * @param daemon the daemon to get the list
+ * @param num_elements the number of elements in @a fds list
+ * @param[out] wfdus the arrays of @a num_elements to update the list
+ * of watched sockets, the unused elements (if any) at
+ * the end of the array are filled with
+ * { MHD_WFA_NONE, { MHD_INVALID_SOCKET, MHD_FD_STATE_NONE } }
+ * @param[out] max_wait the pointer to value set to maximum wait time
+ * for the network events, in microseconds
+ * @return ::MHD_SC_OK on success,
+ * error code otherwise
+ * @ingroup event
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_get_watched_fds_update (
+ struct MHD_Daemon *daemon,
+ unsigned int num_elements,
+ struct MHD_WatchedFdUpdate wfdus[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)],
+ uint_fast64_t *max_wait)
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_OUT_(3) MHD_FN_PAR_NONNULL_(3)
+MHD_FN_PAR_OUT_(4) MHD_FN_PAR_NONNULL_ (4);
+;
+
+
+/**
+ * Perform round of sockets processing, including receiving, sending,
+ * data processing, sockets closing and other things.
+ * @param daemon the daemon to process
+ * @param num_elements the number of elements in the @a fds array
+ * @param fds the array of watched sockets, must be complete list of
+ * all watched sockets level sockets triggering used or
+ * could be just partial list if edge sockets triggering used
+ * @return ::MHD_SC_OK on success,
+ * otherwise error code TODO: complete list of error codes
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_process_watched_fds (
+ struct MHD_Daemon *daemon,
+ unsigned int num_elements,
+ const struct MHD_WatchedFD fds[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_IN_(3) MHD_FN_PAR_NONNULL_ (3);
+
+
+// FIXME: convert introspection
+/**
+ * Obtain timeout value for polling function for this daemon.
+ *
+ * This function set value to amount of milliseconds for which polling
+ * function (`select()` or `poll()`) should at most block, not the
+ * timeout value set for connections.
+ * It is important to always use this function, even if connection
+ * timeout is not set, as in some cases MHD may already have more
+ * data to process on next turn (data pending in TLS buffers,
+ * connections are already ready with epoll etc.) and returned timeout
+ * will be zero.
+ *
+ * @param[in,out] daemon daemon to query for timeout
+ * @param[out] timeout set to the timeout (in milliseconds),
+ * #MHD_WAIT_INDEFINITELY if timeouts are // FIXME: redesigned
+ * not used (or no connections exist that would
+ * necessitate the use of a timeout right now)
+ * @return #MHD_SC_OK on success, otherwise
+ * an error code
+ * @ingroup event
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_daemon_ext_polling_get_max_wait (struct MHD_Daemon *daemon,
+ uint_fast64_t *timeout)
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_OUT_ (2);
+
+// FIXME: gana? table for RFC 7541...
+// TODO: extract https://www.rfc-editor.org/rfc/rfc7541.html#appendix-A
+enum MHD_PredefinedHeader;
+
+
+/**
+ * Get last occurrence of a particular header value under
+ * the given @a skt.
+ *
+ * The pointer to the string in @a value is valid until the response
+ * is queued. If the data is needed beyond this point, it should be copied.
+ *
+ * @param[in,out] request request to get values from
+ * @param kind what kind of value are we looking for
+ * @param skt the header to look for based on RFC 7541 Appendix A.
+ * @param[out] value the found value, the str pointer set to
+ * NULL if nothing is found
+ * @return #MHD_SC_OK if found,
+ * error code otherwise
+ * @ingroup request
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_request_lookup_value_by_static_header (struct MHD_Request *request,
+ enum MHD_ValueKind kind,
+ enum MHD_PredefinedHeader skt,
+ struct MHD_StringNullable *value)
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (4); // TODO: convert like previous
+
+
+/* FUTURE:
+ (with eventually option "number of masters")
+ MHD_TM_WORKER_THREADS_WITH_MASTER_LISTENER = 3 */
+
+
+// FIXME: remove completely?
+/**
+ * Configure DH parameters (dh.pem) to use for the TLS key
+ * exchange.
+ *
+ * @param daemon daemon to configure tls for
+ * @param dh parameters to use
+ * @return #MHD_SC_OK upon success; TODO: define failure modes
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_daemon_tls_mem_dhparams (struct MHD_Daemon *daemon,
+ const char *dh)
+MHD_FN_PAR_NONNULL_ (1);
diff --git a/src/include/this_API_is_TERRIBLE.txt b/src/include/this_API_is_TERRIBLE.txt
@@ -0,0 +1,137 @@
+- The header is NOT readable:
+-- Having the **third** version of the every type of options ("documenting...")
+ makes reading even worse. Absolutely unclear *why* the reader should treat
+ the excluded code as an actual code. Not clear how it is supposed to work.
+ I hardly believe that many readers will just blindly follow disabled part
+ of the header. In practice it would be the opposite: many readers just
+ ignore disabled parts of the headers (as IDEs gray-out or even hide them).
+-- IDEs will be cryptic as well when decoding hierarchical macros (the options
+ itself, then macros that set the options).
+-- IDEs will not point to "generated ... documenting", when one would try
+ to follow the definition/declaration
+-- Trying to "grep" for the keyword (as previously suggested way to find the
+ declaration) is giving too many false results. This makes understanding
+ even more complicated and even less straightforward.
+-- '#include "microhttpd2_generated_daemon_options.h"' is far from "generated
+ code documenting..."
+-- All macros that switch from "static inline" to macros with compound
+ literals are not clear for the user. Even right now it is not clear how
+ they are supposed to work. Not clear that one form is used for C compilers,
+ while another form is used for C++ compilers (and, actually, the third
+ possible version, for not compatible compilers, for C89, and before C++11
+ however it is not the largest concern). It is not clear that app code is
+ supposed to look the same in both C and C++. Actually, even with modern
+ compilers (and language versions) there are three possible combinations:
+ - macros for option with macro to make an array (C),
+ - static functions for options with macro to make an array (C++ with clang),
+ - static functions with vector (C++ in general).
+ And this is repeated for every section with the options.
+-- Functions with ALL CAPS (like MHD_D_OPTION_WORK_MODE) are hard to read. ALL
+ CAPS are usually macros.
+-- The "parts" of the main headers are badly visualised by IDEs as they are
+ incomplete (do not have macros used in declarations, like
+ MHD_FIXED_ENUM_APP_SET_).
+-- Having both MHD_D_O_XXX and (two times of each) MHD_D_OPTION_XXX is
+ confusion and not obvious. Even you was confused with similar names and
+ used MHD_D_OPTION_XXX as switch values, while it must be MHD_D_O_XXX.
+-- Try to work with simple things: find how to set "epoll" syscall; find how
+ to enable internal thread pool. The header is written in the way that
+ is very inconvenient to find needed options and the way how to set them.
+-- The API should be self-documenting. This is main goal defined everywhere.
+ This API is **NOT** self-documenting. Three levels of macros for basic
+ things, like "work mode" is not acceptable!
+
+- This design is less secure by nature. The sizes of arrays or memory
+ allocations cannot be checked by compilers (like
+ "size_t num, int arr[num]").
+- Automatic generated setting function cannot check for build-time configurable
+ features availability, neither for system/platform available features.
+ Only daemon_start() can make any checks leaving no chance for the application
+ to understand where the problem is. The result is less detailed and less
+ flexible API.
+
+
+- Some generated settings processing functions are incorrect. When setting is
+ documented to ignore some specific parameter value (like MHD_INVALID_SOCKET),
+ it must NOT override previously set value. This requires even more
+ complicated generation.
+- Some options (MHD_D_O_BIND_SA, MHD_D_O_RANDOM_ENTROPY) require copying of
+ user memory. Need either further heavy customizing or kind of manual
+ exceptions. Future "large" options will need malloc() and copy for each(!) of
+ them during "set" phase, without error checking for the options content.
+- Generated settings and set function cannot skip code parts excluded by
+ configure parameters, making the library less flexible and less "micro".
+- Some settings require different storage format and parameters format.
+ For example, when parameter is copied, the storage format shouldn't be
+ pointer to const as we need to free it.
+
+It is supposed to make the header maintainable so someone may take care about
+it later. BUT
+- The header is hardly maintainable:
+-- The structure of generated parts, static parts, databases and the build
+ system is not obvious at all. You need to dig very deep just to understand
+ now to start modifying it. It is NOT a good design.
+ The worst thing that actually the current version is not good enough for
+ the production. The production version has to be even more complicated,
+ which makes it even less maintainable.
+-- Simple question: what if it will be needed to change the formatting of
+ header? Which file should I edit? Is it obvious? For example, change
+ the indent.
+-- Adding new type of settings (for example, for the HTTP stream settings) is
+ complicated and multiply already large number of input files.
+-- It is always not nice that relatively often modifiable file (the main
+ header) cannot be modified directly. Every time when I change something
+ in the main header, I have to run the build system before starting using
+ the new header. This may require update of the generated makefiles or re-run
+ the configure. Currently re-run of configure takes 6 minutes on W32.
+ With the additional checks for build host compilers (and other new checks)
+ it will be even longer. So, more then 6 minutes after edit of some part
+ of the header before getting the result. No efficient at all!
+-- Quite inconvenient edit generated sources file by editing other files. Even
+ minor corrections (like change list of included files) becomes problematic,
+ as it may change the generation logic. In long term it is a bad design.
+-- To insert the "generated code documenting" in different parts of the header,
+ more slicing of the main header is required. Currently it must be at least
+ four parts of the header, with every additional type of option the number
+ will increase further. It is easy to lost between parts of the main header.
+ Editing of any part of the header becoming more time-consuming, as every
+ time it is required to find which part is needed to be edited.
+-- Too error-prone, like not working macro for "per_ip_limit". All macro
+ parameters must be different from any token used within the macro.
+-- The future parameters that require larger size will need pointers,
+ which break "the beauty" of the client use, which is the main goal.
+
+- The build system for the header is broken currently.
+-- Need to add the section to the configure for the build compilers, document
+ additional parameters, pass them correctly to makefiles, make custom build
+ rules for binaries for build host (libtool cannot be used), make everything
+ similar to rules for target host
+-- Need to fix makefiles for out-of-tree builds
+-- Need to fix makefiles for parallel builds (this is not easy! check the "po"
+ part)
+-- Need to fix the generator itself. Currently it is not POSIX, is using
+ unportable extensions. Need to make it with proper memory handling and
+ correct checking for all errors. "It works fast" will not be an excuse for
+ security audits. It sounds like "I'll commit a crime, but I'll do it very
+ quickly, so it will not be a crime". Memory leaks are not acceptable for
+ proper design, even on developers machines.
+ I expect also many reports from users that use any kind of "sanitizers" or
+ analysers that immaculately report tons of problems.
+ In short: we cannot afford this code in our repo.
+-- More inputs requires more checks to keep them in sync. More test, more
+ makefile rules, more problems with "make distcheck".
+-- All these fixes makes build system even more complicated, even less obvious,
+ even less readable, even harder maintainable. Problems will be multiplied
+ with additional types of the settings.
+
+! Errors: Doxy lines are too long.
+! Errors: Some doxy lines are broken (do not start with " *")
+! Errors: Several "@param"s are in single doxy line
+! Errors: MHD_D_OPTION_PER_IP_LIMIT does not work - parameter token used as member name
+! Wrong: The body of some static functions with single parameter are formed as macros
+
+Positive:
++ The exclude of "portability" macros is fine (actually, the excluded part is
+ not about the portability only). However, even this part is easier to use
+ when it is fully integrated. To keep it separated the scope must be reduced,
+ to avoid having types declarations in it.
diff --git a/src/mhd2/.gitignore b/src/mhd2/.gitignore
@@ -0,0 +1,2 @@
+/w32_lib_res.rc
+/libmicrohttpd2.pc
diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am
@@ -0,0 +1,160 @@
+# 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_file_fd.h sys_malloc.h \
+ sys_select.h sys_poll.h \
+ sys_sendfile.h \
+ compat_calloc.h \
+ mhd_assert.h \
+ mhd_tristate.h \
+ mhd_socket_type.h mhd_sockets_macros.h \
+ mhd_sockets_funcs.c mhd_sockets_funcs.h \
+ mhd_socket_error.c mhd_socket_error.h \
+ mhd_atomic_counter.c mhd_atomic_counter.h \
+ mhd_str.c mhd_str.h \
+ mhd_str_macros.h mhd_str_types.h \
+ mhd_buffer.h \
+ mhd_limits.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_connection.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_recv.c mhd_recv.h \
+ mhd_send.c mhd_send.h \
+ mhd_daemon.h \
+ mhd_public_api.h \
+ mhd_request.h mhd_reply.h mhd_response.h \
+ http_method.h http_prot_ver.h \
+ http_status_str.c http_status_str.h \
+ action.c mhd_action.h \
+ dcc_action.c mhd_dcc_action.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_get_info.c \
+ daemon_add_conn.c daemon_add_conn.h \
+ daemon_funcs.c daemon_funcs.h \
+ conn_data_process.c conn_data_process.h \
+ conn_data_recv.c conn_data_recv.h \
+ conn_data_send.c conn_data_send.h \
+ conn_mark_ready.h \
+ request_funcs.c request_funcs.h \
+ request_get_value.c request_get_value.h \
+ respond_with_error.c respond_with_error.h \
+ response_from.c response_from.h \
+ response_add_header.c response_add_header.h \
+ response_destroy.c response_destroy.h \
+ response_funcs.c response_funcs.h \
+ response_set_options.c response_set_options.h response_options.h \
+ stream_funcs.c stream_funcs.h \
+ stream_process_states.c stream_process_states.h \
+ stream_process_request.c stream_process_request.h \
+ stream_process_reply.c stream_process_reply.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_VER_CURRENT@:@LIB_VER_REVISION@:@LIB_VER_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
+
+EXTRA_DIST = \
+ libmicrohttpd2.pc.in
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libmicrohttpd2.pc
+
+$(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
diff --git a/src/mhd2/action.c b/src/mhd2/action.c
@@ -0,0 +1,184 @@
+/*
+ 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 "daemon_logger.h"
+
+#include "response_funcs.h"
+
+#include "mhd_public_api.h"
+
+
+MHD_EXTERN_ 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);
+ if (mhd_ACTION_NO_ACTION != head_act->act)
+ return (const struct MHD_Action *) NULL;
+
+ 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 (const struct MHD_Action *) NULL;
+ if (NULL == response)
+ return (const struct MHD_Action *) NULL;
+
+ mhd_response_check_frozen_freeze (response);
+
+ 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 (const struct MHD_Action *) NULL;
+ if (0 == large_buffer_size)
+ {
+ if (NULL != uc_full)
+ return (const struct MHD_Action *) NULL;
+ if (NULL == uc_inc)
+ return (const struct MHD_Action *) NULL;
+ }
+ else
+ {
+ if (NULL == uc_full)
+ return (const struct MHD_Action *) 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) const 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 (const struct MHD_Action *) NULL;
+ if (NULL == done_cb)
+ return (const struct MHD_Action *) 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_UploadAction *const restrict upl_act =
+ &(request->app_act.upl_act);
+ if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+ return (const struct MHD_UploadAction *) 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_UploadAction *const restrict upl_act =
+ &(request->app_act.upl_act);
+ if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+ return (const struct MHD_UploadAction *) NULL;
+
+ mhd_response_check_frozen_freeze (response);
+
+ upl_act->act = mhd_UPLOAD_ACTION_RESPONSE;
+ 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_UploadAction *const restrict upl_act =
+ &(request->app_act.upl_act);
+ if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+ return (const struct MHD_UploadAction *) NULL;
+
+ upl_act->act = mhd_UPLOAD_ACTION_CONTINUE;
+
+ return upl_act;
+}
diff --git a/src/mhd2/autoinit_funcs.h b/src/mhd2/autoinit_funcs.h
@@ -0,0 +1,303 @@
+/*
+ * AutoinitFuncs: Automatic Initialization and Deinitialization Functions
+ * Copyright(C) 2014-2023 Karlson2k (Evgeny Grin)
+ *
+ * This header 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 header 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 header; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ General usage is simple: include this header, declare or define two
+ functions with zero parameters (void) and any return type: one for
+ initialisation and one for deinitialisation, add
+ _SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code
+ and functions will be automatically called during application startup
+ and shutdown.
+ This is useful for libraries as libraries don't have direct access
+ to main() functions.
+ Example:
+ -------------------------------------------------
+ #include <stdlib.h>
+ #include "autoinit_funcs.h"
+
+ int someVar;
+ void* somePtr;
+
+ void libInit(void)
+ {
+ someVar = 3;
+ somePtr = malloc(100);
+ }
+
+ void libDeinit(void)
+ {
+ free(somePtr);
+ }
+
+ _SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit);
+ -------------------------------------------------
+
+ If initialiser or deinitialiser function is not needed, just define
+ it as empty function.
+
+ This header should work with GCC, clang, MSVC (2010 or later) and
+ SunPro / Sun Studio / Oracle Solaris Studio / Oracle Developer Studio
+ compiler.
+ Supported C and C++ languages; application, static and dynamic (DLL)
+ libraries; non-optimized (Debug) and optimised (Release) compilation
+ and linking.
+
+ For more information see header code and comments in code.
+ */
+#ifndef AUTOINIT_FUNCS_INCLUDED
+#define AUTOINIT_FUNCS_INCLUDED 1
+
+/**
+* Current version of the header in packed BCD form.
+* 0x01093001 = 1.9.30-1.
+*/
+#define AUTOINIT_FUNCS_VERSION 0x01001000
+
+#if defined(__GNUC__) || defined(__clang__)
+/* if possible - check for supported attribute */
+#ifdef __has_attribute
+#if ! __has_attribute (constructor) || ! __has_attribute (destructor)
+#define _GNUC_ATTR_CONSTR_NOT_SUPPORTED 1
+#endif /* !__has_attribute(constructor) || !__has_attribute(destructor) */
+#endif /* __has_attribute */
+#endif /* __GNUC__ */
+
+/* "__has_attribute__ ((constructor))" is supported by GCC, clang and
+ Sun/Oracle compiler starting from version 12.1. */
+#if ((defined(__GNUC__) || defined(__clang__)) && \
+ ! defined(_GNUC_ATTR_CONSTR_NOT_SUPPORTED)) || \
+ (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;}
+
+#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT (FI,FD)
+#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
+
+#elif defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1600
+
+/* Make sure that your project/sources define:
+ _LIB if building a static library (_LIB is ignored if _CONSOLE is defined);
+ _USRDLL if building DLL-library;
+ not defined both _LIB and _USRDLL if building an application */
+
+/* Define AUTOINIT_FUNCS_DECLARE_STATIC_REG if you need macro declaration
+ for registering static initialisation functions even if you building DLL */
+/* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro
+ _SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL */
+
+/* Stringify macros */
+#define _INSTRMACRO(a) #a
+#define _STRMACRO(a) _INSTRMACRO (a)
+
+#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_DECLARE_STATIC_REG) \
+ || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG)
+
+/* Use "C" linkage for variable to simplify variable decoration */
+#ifdef __cplusplus
+#define W32_INITVARDECL extern "C"
+#else
+#define W32_INITVARDECL extern
+#endif
+
+/* How variable is decorated by compiler */
+#if (defined(_WIN32) || defined(_WIN64)) \
+ && ! defined(_M_IX86) && ! defined(_X86_)
+#if ! defined(_M_X64) && ! defined(_M_AMD64) && ! defined(_x86_64_) \
+ && ! defined(_M_ARM) && ! defined(_M_ARM64)
+#pragma message(__FILE__ "(" _STRMACRO(__LINE__) ") : warning AIFW001 : " \
+ "Untested architecture, linker may fail with unresolved symbol")
+#endif /* ! _M_X64 && ! _M_AMD64 && ! _x86_64_ && ! _M_ARM && ! _M_ARM64 */
+#define W32_VARDECORPREFIX
+#define W32_DECORVARNAME(v) v
+#define W32_VARDECORPREFIXSTR ""
+#elif defined(_WIN32) && (defined(_M_IX86) || defined(_X86_))
+#define W32_VARDECORPREFIX _
+#define W32_DECORVARNAME(v) _ ## v
+#define W32_VARDECORPREFIXSTR "_"
+#else
+#error Do not know how to decorate symbols for this architecture
+#endif
+
+/* Internal variable prefix (can be any) */
+#define W32_INITHELPERVARNAME(f) _initHelperDummy_ ## f
+#define W32_INITHELPERVARNAMEDECORSTR(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 */
+/* Initialisation function must be declared as
+ 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
+
+/* Sections (segments) for pointers to initialisers/deinitialisers */
+
+/* Semi-officially suggested section for early initialisers (called before
+ C++ objects initialisers), "void" return type */
+#define W32_SEG_INIT_EARLY ".CRT$XCT"
+/* Semi-officially suggested section for late initialisers (called after
+ C++ objects initialisers), "void" return type */
+#define W32_SEG_INIT_LATE ".CRT$XCV"
+
+/* Unsafe sections (segments) for pointers to initialisers/deinitialisers */
+
+/* C++ lib initialisers, "void" return type (reserved by the system!) */
+#define W32_SEG_INIT_CXX_LIB ".CRT$XCL"
+/* C++ user initialisers, "void" return type (reserved by the system!) */
+#define W32_SEG_INIT_CXX_USER ".CRT$XCU"
+
+
+/* Declare section (segment), put variable pointing to init function to chosen segment,
+ force linker to always include variable to avoid omitting by optimiser */
+/* Initialisation function must be declared as
+ int __cdecl FuncName(void) */
+/* 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
+
+/* Unsafe sections (segments) for pointers to initialisers with
+ "int" return type */
+
+/* C lib initialisers, "int" return type (reserved by the system!).
+ These initialisers are called before others. */
+#define W32_SEG_INIT_C_LIB ".CRT$XIL"
+/* C user initialisers, "int" return type (reserved by the system!).
+ These initialisers are called before others. */
+#define W32_SEG_INIT_C_USER ".CRT$XIU"
+
+
+/* Declare macro for different initialisers sections */
+/* Macro can be used several times to register several initialisers */
+/* Once function is registered as initialiser, it will be called automatically
+ during application startup */
+#define W32_REG_INIT_EARLY(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_EARLY,F)
+#define W32_REG_INIT_LATE(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_LATE,F)
+
+
+/* Not recommended / unsafe */
+/* "lib" initialisers are called before "user" initialisers */
+/* "C" initialisers are called before "C++" initialisers */
+#define W32_REG_INIT_C_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_C_USER,F)
+#define W32_REG_INIT_C_LIB(F) W32_FPTR_IN_SEG (W32_SEG_INIT_C_LIB,F)
+#define W32_REG_INIT_CXX_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_USER,F)
+#define W32_REG_INIT_CXX_LIB(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_LIB,F)
+
+/* Choose main register macro based on language and program type */
+/* Assuming that _LIB or _USRDLL is defined for static or DLL-library */
+/* Macro can be used several times to register several initialisers */
+/* Once function is registered as initialiser, it will be called automatically
+ during application startup */
+/* Define AUTOINIT_FUNCS_FORCE_EARLY_INIT to force register as early
+ initialiser */
+/* Define AUTOINIT_FUNCS_FORCE_LATE_INIT to force register as late
+ initialiser */
+/* By default C++ static or DLL-library code and any C code and will be
+ registered as early initialiser, while C++ non-library code will be
+ registered as late initialiser */
+#if (! defined(__cplusplus) || \
+ ((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)
+#else
+#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE (F)
+#endif
+
+#endif /* ! _USRDLL || ! AUTOINIT_FUNCS_DECLARE_STATIC_REG
+ || AUTOINIT_FUNCS_FORCE_STATIC_REG */
+
+
+#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG)
+
+#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)
+#else /* _USRDLL */
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* WIN32_LEAN_AND_MEAN */
+
+#include <Windows.h> /* Required for DllMain */
+
+/* If DllMain is already present in code, define AUTOINIT_FUNCS_CALL_USR_DLLMAIN
+ 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;}
+#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;}
+#endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
+#endif /* _USRDLL */
+
+#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) W32_SET_INIT_AND_DEINIT (FI,FD)
+/* Indicate that automatic initialisers/deinitialisers are supported */
+#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
+
+#else /* !__GNUC__ && !_MSC_FULL_VER */
+
+/* Define EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED before inclusion of header to
+ abort compilation if automatic initialisers/deinitialisers are not supported */
+#ifdef EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED
+#error \
+ Compiler/platform does not support automatic calls of user-defined initializer and deinitializer
+#endif /* EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED */
+
+/* Do nothing */
+#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD)
+/* Indicate that automatic initialisers/deinitialisers are not supported */
+#define _AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1
+
+#endif /* !__GNUC__ && !_MSC_FULL_VER */
+#endif /* !AUTOINIT_FUNCS_INCLUDED */
diff --git a/src/mhd2/compat_calloc.c b/src/mhd2/compat_calloc.c
@@ -0,0 +1,63 @@
+/*
+ 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/compat_calloc.c
+ * @brief The implementation of the calloc() replacement
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "compat_calloc.h"
+#ifndef HAVE_CALLOC
+
+#include <string.h> /* for memset() */
+#include "sys_malloc.h"
+
+
+#ifdef __has_builtin
+# if __has_builtin (__builtin_mul_overflow)
+# define MHD_HAVE_MUL_OVERFLOW 1
+# endif
+#elif defined(__GNUC__) && __GNUC__ + 0 >= 5
+# define MHD_HAVE_MUL_OVERFLOW 1
+#endif /* __GNUC__ >= 5 */
+
+MHD_INTERNAL void *
+mhd_calloc (size_t nelem, size_t elsize)
+{
+ size_t alloc_size;
+ void *ptr;
+#ifdef MHD_HAVE_MUL_OVERFLOW
+ if (__builtin_mul_overflow (nelem, elsize, &alloc_size) || (0 == alloc_size))
+ return NULL;
+#else /* ! MHD_HAVE_MUL_OVERFLOW */
+ alloc_size = nelem * elsize;
+ if ((0 == alloc_size) || (elsize != alloc_size / nelem))
+ return NULL;
+#endif /* ! MHD_HAVE_MUL_OVERFLOW */
+ ptr = malloc (alloc_size);
+ if (NULL == ptr)
+ return NULL;
+ memset (ptr, 0, alloc_size);
+ return ptr;
+}
+
+
+#endif /* ! HAVE_CALLOC */
diff --git a/src/mhd2/compat_calloc.h b/src/mhd2/compat_calloc.h
@@ -0,0 +1,65 @@
+/*
+ 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/compat_calloc.h
+ * @brief The header for the calloc() or the calloc() replacement declarations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_COMPAT_CALLOC_H
+#define MHD_COMPAT_CALLOC_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_CALLOC
+# if defined(HAVE_STDLIB_H)
+# include <stdlib.h>
+# elif defined(HAVE_MALLOC_H)
+# include <malloc.h>
+# else
+/* Try some set of headers, hoping the right header is included */
+# if defined(HAVE_UNISTD_H)
+# include <unistd.h>
+# endif
+# include <stdio.h>
+# include <string.h>
+# endif
+
+# define mhd_calloc calloc
+#else
+
+# include "sys_base_types.h" /* for size_t, NULL */
+
+
+/**
+ * Allocate memory for an array of @a nelem objects of @a elsize size and
+ * initialise all bytes to zero in the allocated memory area.
+ * @param nelem the number of elements to allocate
+ * @param elsize the size of single element
+ * @return the pointer to allocated memory area on success,
+ * the NULL pointer on failure.
+ */
+MHD_INTERNAL void *
+mhd_calloc (size_t nelem, size_t elsize);
+
+#endif /* ! HAVE_CALLOC */
+
+#endif /* ! MHD_COMPAT_CALLOC_H */
diff --git a/src/mhd2/conn_data_process.c b/src/mhd2/conn_data_process.c
@@ -0,0 +1,181 @@
+/*
+ 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 "conn_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 "daemon_logger.h"
+
+#include "conn_data_recv.h"
+#include "conn_data_send.h"
+#include "stream_process_states.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
+{
+ const bool send_ready_state_known =
+ ((mhd_D_IS_USING_EDGE_TRIG (c->daemon)) ||
+ (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)));
+ const bool has_sock_err =
+ (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready));
+ bool data_processed;
+
+ data_processed = false;
+
+ if (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
+ {
+ bool use_recv;
+ use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready));
+ use_recv = use_recv ||
+ (has_sock_err && c->sk_nonblck);
+
+ if (use_recv)
+ {
+ mhd_conn_data_recv (c, has_sock_err);
+ if (! mhd_conn_process_data (c))
+ return false;
+ data_processed = true;
+ }
+ }
+
+ if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))
+ {
+ bool use_send;
+ /* 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
+ * + detected network error on the connection, to check to the error */
+ /* Assuming that after finishing receiving phase, connection send system
+ buffers should have some space as sending was performed before receiving
+ or has not been performed yet. */
+ use_send = (0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready));
+ use_send = use_send ||
+ (data_processed && (! send_ready_state_known) && c->sk_nonblck);
+ use_send = use_send ||
+ (has_sock_err && c->sk_nonblck);
+
+ if (use_send)
+ {
+ mhd_conn_data_send (c);
+ if (! mhd_conn_process_data (c))
+ return false;
+ data_processed = true;
+ }
+ }
+ if (! data_processed)
+ return mhd_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_UNCHUNKED_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_HAS_THR_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
+}
diff --git a/src/mhd2/conn_data_process.h b/src/mhd2/conn_data_process.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/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_conn_process_recv_send_data (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Update "active" state, move to the activity lists if necessary.
+ * Update "event loop info"
+ * @param c the connection to use
+ * @return true if data processed successfully,
+ * false if connection needs to be closed
+ */
+MHD_INTERNAL bool
+mhd_conn_update_active_state (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_DATA_PROCESS_H */
diff --git a/src/mhd2/conn_data_recv.c b/src/mhd2/conn_data_recv.c
@@ -0,0 +1,157 @@
+/*
+ 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/conn_data_recv.c
+ * @brief The implementation of data receiving functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "conn_data_recv.h"
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_connection.h"
+
+#include "mhd_recv.h"
+#include "stream_funcs.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_data_recv (struct MHD_Connection *restrict c,
+ bool has_err)
+{
+ 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);
+ mhd_assert (c->read_buffer_size > c->read_buffer_offset);
+ 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
+
+ buf = c->read_buffer + c->read_buffer_offset;
+ buf_size = c->read_buffer_size - c->read_buffer_offset;
+
+ res = mhd_recv (c,buf_size, buf, &received);
+
+ if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
+ {
+ /* Handle errors */
+ if ((mhd_SOCKET_ERR_NO_ERROR == res) && (0 == received))
+ {
+ c->sk_rmt_shut_wr = true;
+ res = mhd_SOCKET_ERR_REMT_DISCONN;
+ }
+ if (has_err && ! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
+ {
+ /* Re-try last time to detect the error */
+ uint_fast64_t dummy_buf;
+ res = mhd_recv (c, sizeof(dummy_buf), (char *) &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 == received)
+ c->sk_rmt_shut_wr = true;
+
+ c->read_buffer_offset += received;
+ mhd_stream_update_activity_mark (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
diff --git a/src/mhd2/conn_data_recv.h b/src/mhd2/conn_data_recv.h
@@ -0,0 +1,50 @@
+/*
+ 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/conn_data_recv.h
+ * @brief The definition of data receiving functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_CONN_DATA_RECV_H
+#define MHD_CONN_DATA_RECV_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declarations */
+
+/**
+ * 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
+ */
+MHD_INTERNAL void
+mhd_conn_data_recv (struct MHD_Connection *restrict c,
+ bool has_err)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* ! MHD_CONN_DATA_RECV_H */
diff --git a/src/mhd2/conn_data_send.c b/src/mhd2/conn_data_send.c
@@ -0,0 +1,308 @@
+/*
+ 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/conn_data_send.c
+ * @brief The implementation of data sending functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff, Evgeny Grin
+ * and other contributors.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "conn_data_send.h"
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "mhd_str_macros.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_connection.h"
+#include "mhd_response.h"
+
+#include "mhd_socket_error.h"
+
+#include "mhd_send.h"
+#include "stream_funcs.h"
+
+
+/**
+ * Check if we are done sending the write-buffer.
+ * If so, transition into "next_state".
+ *
+ * @param connection connection to check write status for
+ * @param next_state the next state to transition to
+ * @return #MHD_NO if we are not done, #MHD_YES if we are
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+check_write_done (struct MHD_Connection *restrict connection,
+ enum MHD_CONNECTION_STATE next_state)
+{
+ // TODO: integrate into states processing
+ if ( (connection->write_buffer_append_offset !=
+ connection->write_buffer_send_offset)
+ /* || data_in_tls_buffers == true */
+ )
+ return false;
+ connection->write_buffer_append_offset = 0;
+ connection->write_buffer_send_offset = 0;
+ connection->state = next_state;
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_data_send (struct MHD_Connection *restrict c)
+{
+ static const char http_100_continue_msg[] =
+ mdh_HTTP_1_1_100_CONTINUE_REPLY;
+ static const size_t http_100_continue_msg_len =
+ mhd_SSTR_LEN (mdh_HTTP_1_1_100_CONTINUE_REPLY);
+ enum mhd_SocketError res;
+ size_t sent;
+
+ // TODO: assert check suspended
+
+#ifdef HTTPS_SUPPORT
+ // TODO: TLS support, handshake
+#endif /* HTTPS_SUPPORT */
+
+ // TODO: MOVE out STATES PROCESSING
+
+ res = mhd_SOCKET_ERR_INTERNAL;
+
+ if (MHD_CONNECTION_CONTINUE_SENDING == c->state)
+ {
+ res = mhd_send_data (c,
+ http_100_continue_msg_len
+ - c->continue_message_write_offset,
+ http_100_continue_msg
+ + c->continue_message_write_offset,
+ true,
+ &sent);
+ if (mhd_SOCKET_ERR_NO_ERROR == res)
+ c->continue_message_write_offset += sent;
+ }
+ else if (MHD_CONNECTION_HEADERS_SENDING == c->state)
+ {
+ struct MHD_Response *const restrict resp = c->rp.response;
+ const size_t wb_ready = c->write_buffer_append_offset
+ - c->write_buffer_send_offset;
+ mhd_assert (c->write_buffer_append_offset >= \
+ c->write_buffer_send_offset);
+ mhd_assert (NULL != resp);
+ mhd_assert ((mhd_CONN_MUST_UPGRADE != c->conn_reuse) || \
+ (! c->rp.props.send_reply_body));
+
+ // TODO: support body generating alongside with header sending
+
+ if ((c->rp.props.send_reply_body) &&
+ (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc))
+ {
+ /* Send response headers alongside the response body, if the body
+ * data is available. */
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
+ mhd_assert (! c->rp.props.chunked);
+
+ res = mhd_send_hdr_and_body (c,
+ wb_ready,
+ c->write_buffer
+ + c->write_buffer_send_offset,
+ false,
+ resp->cntn_size,
+ (const char *) resp->cntn.buf,
+ true,
+ &sent);
+ }
+ else
+ {
+ /* This is response for HEAD request or reply body is not allowed
+ * for any other reason or reply body is dynamically generated. */
+ /* Do not send the body data even if it's available. */
+ res = mhd_send_hdr_and_body (c,
+ wb_ready,
+ c->write_buffer
+ + c->write_buffer_send_offset,
+ false,
+ 0,
+ NULL,
+ ((0 == resp->cntn_size) ||
+ (! c->rp.props.send_reply_body)),
+ &sent);
+ }
+ if (mhd_SOCKET_ERR_NO_ERROR == res)
+ {
+ mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state);
+
+ if (sent > wb_ready)
+ {
+ /* The complete header and some response data have been sent,
+ * update both offsets. */
+ mhd_assert (0 == c->rp.rsp_cntn_read_pos);
+ mhd_assert (! c->rp.props.chunked);
+ mhd_assert (c->rp.props.send_reply_body);
+ c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY;
+ c->write_buffer_send_offset += wb_ready;
+ c->rp.rsp_cntn_read_pos = sent - wb_ready;
+ if (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size)
+ c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+ }
+ else
+ {
+ c->write_buffer_send_offset += sent;
+ // TODO: move it to data processing
+ check_write_done (c,
+ MHD_CONNECTION_HEADERS_SENT);
+ }
+
+
+ }
+
+ }
+ else if ((MHD_CONNECTION_UNCHUNKED_BODY_READY == c->state) ||
+ (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
+ {
+ struct MHD_Response *const restrict resp = c->rp.response;
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size);
+ mhd_assert ((MHD_CONNECTION_CHUNKED_BODY_READY != c->state) || \
+ (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc));
+ if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
+ {
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
+
+ res = mhd_send_data (c,
+ c->rp.rsp_cntn_read_pos - resp->cntn_size,
+ (const char *) resp->cntn.buf
+ + c->rp.rsp_cntn_read_pos,
+ true,
+ &sent);
+ }
+ else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
+ {
+ mhd_assert (c->write_buffer_append_offset > \
+ c->write_buffer_send_offset);
+
+ res = mhd_send_data (c,
+ c->write_buffer_append_offset
+ - c->write_buffer_send_offset,
+ c->write_buffer + c->write_buffer_send_offset,
+ true,
+ &sent);
+ }
+ else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
+ {
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == resp->cntn_dtype);
+
+ res = mhd_send_iovec (c,
+ &c->rp.resp_iov,
+ true,
+ &sent);
+ }
+#if defined(MHD_USE_SENDFILE)
+ else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
+ {
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == resp->cntn_dtype);
+
+ res = mhd_send_sendfile (c, &sent);
+ if (mhd_SOCKET_ERR_INTR == res)
+ {
+ if (! c->rp.response->cntn.file.use_sf)
+ { /* Switch to filereader */
+ mhd_assert (! c->rp.props.chunked);
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+ c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY;
+ }
+ }
+ }
+#endif /* MHD_USE_SENDFILE */
+ else
+ {
+ mhd_assert (0 && "Should be unreachable");
+ res = mhd_SOCKET_ERR_INTERNAL;
+ }
+
+ if (mhd_SOCKET_ERR_NO_ERROR == res)
+ {
+ if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
+ {
+ enum MHD_CONNECTION_STATE next_state;
+ c->write_buffer_send_offset += sent;
+ // TODO: move it to data processing
+ if (MHD_CONNECTION_CHUNKED_BODY_READY == c->state)
+ next_state =
+ (c->rp.response->cntn_size == c->rp.rsp_cntn_read_pos) ?
+ MHD_CONNECTION_CHUNKED_BODY_SENT :
+ MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+ else
+ next_state =
+ (c->rp.rsp_cntn_read_pos == resp->cntn_size) ?
+ MHD_CONNECTION_FULL_REPLY_SENT :
+ MHD_CONNECTION_UNCHUNKED_BODY_UNREADY;
+ check_write_done (c,
+ next_state);
+ }
+ else
+ {
+ c->rp.rsp_cntn_read_pos += sent;
+ if (c->rp.rsp_cntn_read_pos == resp->cntn_size)
+ c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+ }
+ }
+
+ }
+ else if (MHD_CONNECTION_FOOTERS_SENDING == c->state)
+ {
+ res = mhd_send_data (c,
+ c->write_buffer_append_offset
+ - c->write_buffer_send_offset,
+ c->write_buffer
+ + c->write_buffer_send_offset,
+ true,
+ &sent);
+ if (mhd_SOCKET_ERR_NO_ERROR == res)
+ {
+ c->write_buffer_send_offset += sent;
+ // TODO: move it to data processing
+ check_write_done (c,
+ MHD_CONNECTION_FULL_REPLY_SENT);
+ }
+ }
+ else
+ {
+ mhd_assert (0 && "Should be unreachable");
+ res = mhd_SOCKET_ERR_INTERNAL;
+ }
+
+ if (mhd_SOCKET_ERR_NO_ERROR == res)
+ {
+ mhd_stream_update_activity_mark (c); // TODO: centralise activity mark updates
+ }
+ else 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);
+ }
+}
diff --git a/src/mhd2/conn_data_send.h b/src/mhd2/conn_data_send.h
@@ -0,0 +1,47 @@
+/*
+ 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/conn_data_send.h
+ * @brief The definition of data sending functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_CONN_DATA_SEND_H
+#define MHD_CONN_DATA_SEND_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declarations */
+
+/**
+ * Perform data sending for the connection and try to detect the socket error
+ * type.
+ *
+ * @param c the connection to use
+ */
+MHD_INTERNAL void
+mhd_conn_data_send (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* ! MHD_CONN_DATA_SEND_H */
diff --git a/src/mhd2/conn_mark_ready.h b/src/mhd2/conn_mark_ready.h
@@ -0,0 +1,97 @@
+/*
+ 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/conn_mark_ready.h
+ * @brief The definition of static functions to mark/unmark connection as
+ * "process ready".
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_CONN_MARK_READY_H
+#define MHD_CONN_MARK_READY_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_null_macro.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_connection.h"
+#include "mhd_daemon.h"
+#include "mhd_dlinked_list.h"
+
+/**
+ * Mark connection as "ready to process" and add it to the end of the
+ * "process ready" list if connection is not in the list.
+ * @param c the connection to mark
+ * @param d the daemon for the connection
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_mark_ready (struct MHD_Connection *restrict c,
+ struct MHD_Daemon *restrict d)
+{
+ mhd_assert (d == c->daemon);
+ if (c->in_proc_ready)
+ {
+ mhd_assert ((NULL != mhd_DLINKEDL_GET_NEXT (c, proc_ready)) || \
+ (NULL != mhd_DLINKEDL_GET_PREV (c, proc_ready)) || \
+ (c == mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready)));
+ return; /* Already marked and in the list */
+ }
+ mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready));
+ mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, proc_ready));
+ mhd_assert (c != mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready));
+ mhd_assert (c != mhd_DLINKEDL_GET_LAST (&(d->events), proc_ready));
+
+ mhd_DLINKEDL_INS_LAST (&(d->events), c, proc_ready);
+ c->in_proc_ready = true;
+}
+
+
+/**
+ * Mark connection as "not ready to process" and remove it from the "process
+ * ready" list if connection is in the list.
+ * @param c the connection to mark
+ * @param d the daemon for the connection
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_mark_unready (struct MHD_Connection *restrict c,
+ struct MHD_Daemon *restrict d)
+{
+ mhd_assert (d == c->daemon);
+ if (! c->in_proc_ready)
+ {
+ mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready));
+ mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, proc_ready));
+ mhd_assert (c != mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready));
+ mhd_assert (c != mhd_DLINKEDL_GET_LAST (&(d->events), proc_ready));
+ return; /* Already unmarked and not in the list */
+ }
+ mhd_assert ((NULL != mhd_DLINKEDL_GET_NEXT (c, proc_ready)) || \
+ (NULL != mhd_DLINKEDL_GET_PREV (c, proc_ready)) || \
+ (c == mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready)));
+
+ mhd_DLINKEDL_DEL (&(d->events), c, proc_ready);
+ c->in_proc_ready = false;
+}
+
+
+#endif /* ! MHD_CONN_MARK_READY_H */
diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c
@@ -0,0 +1,951 @@
+/*
+ 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 "sys_ip_headers.h"
+
+#include <string.h>
+#ifdef MHD_USE_EPOLL
+# include <sys/epoll.h>
+#endif
+
+#include "compat_calloc.h"
+
+#include "mhd_sockets_macros.h"
+#include "mhd_sockets_funcs.h"
+
+#include "mhd_panic.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "daemon_logger.h"
+#include "mhd_mono_clock.h"
+#include "mhd_mempool.h"
+#include "events_process.h"
+
+#include "response_from.h"
+#include "response_destroy.h"
+#include "conn_mark_ready.h"
+
+#include "mhd_public_api.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->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
+ 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
+}
+
+
+/**
+ * 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 = mdh_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->events.data.epoll.e_fd,
+ EPOLL_CTL_ADD,
+ connection->socket_fd,
+ &event))
+ {
+ mhd_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED,
+ "Failed to add connection socket to epoll.");
+ 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_conn_mark_ready (connection, daemon);
+ }
+ 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));
+ mhd_assert (mhd_FD_FITS_DAEMON (daemon, client_socket));
+
+ 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))
+ (void) 0; // FIXME: remove already pending connections?
+
+ if (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type)
+ && daemon->conns.block_new)
+ {
+ (void) mhd_socket_close (client_socket);
+ 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.");
+ (void) mhd_socket_close (client_socket);
+ 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'.");
+ (void) mhd_socket_close (client_socket);
+ 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.");
+ (void) mhd_socket_close (client_socket);
+ 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'.");
+ (void) mhd_socket_close (client_socket);
+ 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_FD_FITS_DAEMON (daemon, client_socket))
+ {
+ mhd_LOG_MSG (daemon, MHD_SC_NEW_CONN_FD_OUTSIDE_OF_SET_RANGE, \
+ "The new connection FD value is higher than allowed");
+ (void) mhd_socket_close (client_socket);
+ return MHD_SC_NEW_CONN_FD_OUTSIDE_OF_SET_RANGE;
+ }
+
+ 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 restrict 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 */
+ (void) mhd_socket_close (client_socket);
+ 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_SUCCESS : mhd_DAEMON_ACCEPT_FAILED;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_close_final (struct MHD_Connection *restrict c)
+{
+ mhd_assert (c->dbg.pre_closed);
+ mhd_assert (c->dbg.pre_cleaned);
+ mhd_assert (NULL == c->rp.response);
+ mhd_assert (! c->rq.app_aware);
+ mhd_assert (! c->in_proc_ready);
+ mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready));
+ mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, proc_ready));
+ mhd_assert (c != mhd_DLINKEDL_GET_FIRST (&(c->daemon->events), proc_ready));
+ mhd_assert (c != mhd_DLINKEDL_GET_LAST (&(c->daemon->events), proc_ready));
+
+ if (mhd_D_HAS_THR_PER_CONN (c->daemon))
+ {
+ mhd_assert (0 && "Not implemented yet");
+ // TODO: Support "thread per connection"
+ }
+ mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, by_timeout));
+ mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, by_timeout));
+ mhd_assert (NULL == c->pool);
+
+ mhd_DLINKEDL_DEL (&(c->daemon->conns), c, all_conn);
+
+ // TODO: update per-IP limits
+ if (NULL != c->addr)
+ free (c->addr);
+ mhd_socket_close (c->socket_fd);
+
+ c->daemon->conns.count--;
+ c->daemon->conns.block_new = false;
+
+ free (c);
+}
diff --git a/src/mhd2/daemon_add_conn.h b/src/mhd2/daemon_add_conn.h
@@ -0,0 +1,96 @@
+/*
+ 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 */
+struct MHD_Connection; /* 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);
+
+/**
+ * Finally close and clean-up connection.
+ * Must be performed only when connection thread (for thread-per-connection)
+ * has stopped.
+ * The connection data deallocated by this function and cannot be used anymore.
+ * The function must be the last function called for connection object.
+ * @param c the connection to close
+ */
+MHD_INTERNAL void
+mhd_conn_close_final (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* ! MHD_DAEMON_ADD_CONN */
diff --git a/src/mhd2/daemon_create.c b/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_cfg.cb = req_cb;
+ d->req_cfg.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"
diff --git a/src/mhd2/daemon_funcs.c b/src/mhd2/daemon_funcs.c
@@ -0,0 +1,182 @@
+/*
+ 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"
+
+#include "daemon_funcs.h"
+
+#include "sys_base_types.h"
+#include "sys_malloc.h"
+
+#include "mhd_assert.h"
+#include "mhd_itc.h"
+#include "mhd_daemon.h"
+#include "daemon_logger.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_MUST_CHECK_RESULT_ struct MHD_Daemon *
+mhd_daemon_get_master_daemon (struct MHD_Daemon *restrict d)
+{
+#ifdef MHD_USE_THREADS
+ if (mhd_D_HAS_MASTER (d))
+ return d->threading.hier.master;
+#endif /* MHD_USE_THREADS */
+ return d;
+}
+
+
+#ifdef MHD_USE_THREADS
+
+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 */
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_daemon_resume_conns (struct MHD_Daemon *restrict d)
+{
+ (void) d;
+ mhd_assert (0 && "Not implemented yet");
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_MUST_CHECK_RESULT_ bool
+mhd_daemon_claim_lbuf (struct MHD_Daemon *d,
+ size_t requested_size)
+{
+ bool ret;
+ struct MHD_Daemon *const masterd = mhd_daemon_get_master_daemon (d);
+ mhd_assert (0 != requested_size);
+ if (0 == masterd->req_cfg.large_buf.space_left)
+ return false; /* Shortcut for typical use without large buffer */
+
+ ret = false;
+ mhd_mutex_lock_chk (&(masterd->req_cfg.large_buf.lock));
+ if (masterd->req_cfg.large_buf.space_left >= requested_size)
+ {
+ masterd->req_cfg.large_buf.space_left -= requested_size;
+ ret = true;
+ }
+ mhd_mutex_unlock_chk (&(masterd->req_cfg.large_buf.lock));
+ return ret;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_daemon_reclaim_lbuf (struct MHD_Daemon *d,
+ size_t reclaimed_size)
+{
+ struct MHD_Daemon *const masterd = mhd_daemon_get_master_daemon (d);
+ mhd_assert (0 != reclaimed_size);
+ mhd_mutex_lock_chk (&(masterd->req_cfg.large_buf.lock));
+ masterd->req_cfg.large_buf.space_left += reclaimed_size;
+ mhd_mutex_unlock_chk (&(masterd->req_cfg.large_buf.lock));
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+MHD_FN_PAR_OUT_ (3) bool
+mhd_daemon_get_lbuf (struct MHD_Daemon *restrict d,
+ size_t requested_size,
+ struct mhd_Buffer *restrict buf)
+{
+ if (! mhd_daemon_claim_lbuf (d, requested_size))
+ {
+ buf->size = 0;
+ buf->buf = NULL;
+ return false;
+ }
+ buf->buf = (char *) malloc (requested_size);
+ if (NULL == buf->buf)
+ {
+ buf->size = 0;
+ mhd_daemon_reclaim_lbuf (d, requested_size);
+ return false;
+ }
+ buf->size = requested_size;
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+MHD_FN_PAR_INOUT_ (3) bool
+mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
+ size_t grow_size,
+ struct mhd_Buffer *restrict buf)
+{
+ void *new_alloc;
+ mhd_assert (NULL != buf->buf || 0 == buf->size);
+ mhd_assert (0 != buf->size || NULL == buf->buf);
+
+ if (! mhd_daemon_claim_lbuf (d, grow_size))
+ return false;
+
+ if (NULL == buf->buf)
+ new_alloc = malloc (grow_size);
+ else
+ new_alloc = realloc (buf->buf, buf->size + grow_size);
+ if (NULL == new_alloc)
+ {
+ mhd_daemon_reclaim_lbuf (d, grow_size);
+ return false;
+ }
+
+ buf->buf = (char *) new_alloc;
+ buf->size += grow_size;
+
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) void
+mhd_daemon_free_lbuf (struct MHD_Daemon *restrict d,
+ struct mhd_Buffer *restrict buf)
+{
+ if (0 == buf->size)
+ {
+ mhd_assert (NULL == buf->buf);
+ return;
+ }
+ free (buf->buf);
+ buf->buf = NULL;
+ mhd_daemon_reclaim_lbuf (d, buf->size);
+ buf->size = 0;
+}
diff --git a/src/mhd2/daemon_funcs.h b/src/mhd2/daemon_funcs.h
@@ -0,0 +1,138 @@
+/*
+ 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"
+
+#include "sys_bool_type.h"
+
+#include "mhd_buffer.h"
+
+struct MHD_Daemon; /* forward declaration */
+
+
+/**
+ * Get controlling daemon
+ * @param d the daemon to get controlling daemon
+ * @return the master daemon (possible the same as the @a d)
+ */
+MHD_INTERNAL struct MHD_Daemon *
+mhd_daemon_get_master_daemon (struct MHD_Daemon *restrict d)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_;
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * 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 */
+
+
+/**
+ * Check whether any resuming connections are pending and resume them
+ * @param d the daemon to use
+ */
+MHD_INTERNAL void
+mhd_daemon_resume_conns (struct MHD_Daemon *restrict d)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Request allocation of the large buffer
+ * @param d the daemon to use
+ * @param requested_size the requested size of allocation
+ * @return true if allocation allowed and counted,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_daemon_claim_lbuf (struct MHD_Daemon *restrict d,
+ size_t requested_size)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_;
+
+
+/**
+ * Reclaim the large buffer allocation.
+ * Must be called when the allocation has been already freed.
+ * @param d the daemon to use
+ * @param reclaimed_size the deallocated size
+ */
+MHD_INTERNAL void
+mhd_daemon_reclaim_lbuf (struct MHD_Daemon *restrict d,
+ size_t reclaimed_size)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Allocate the large buffer
+ * @param d the daemon to use
+ * @param requested_size the requested size of allocation
+ * @param[out] buf the buffer to allocate
+ * @return true if buffer is allocated,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_daemon_get_lbuf (struct MHD_Daemon *restrict d,
+ size_t requested_size,
+ struct mhd_Buffer *restrict buf)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+ MHD_FN_PAR_OUT_ (3);
+
+/**
+ * Grow the large buffer, which previously was allocated
+ * @param d the daemon to use
+ * @param grow_size the requested size of grow
+ * @param[in,out] buf the buffer to grow
+ * @return true if buffer has been grown,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
+ size_t grow_size,
+ struct mhd_Buffer *restrict buf)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+ MHD_FN_PAR_INOUT_ (3);
+
+
+/**
+ * Free large buffer.
+ * @param d the daemon to use
+ * @param[in,out] buf the buffer to free
+ */
+MHD_INTERNAL void
+mhd_daemon_free_lbuf (struct MHD_Daemon *restrict d,
+ struct mhd_Buffer *restrict buf)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (2);
+
+
+#endif /* ! MHD_DAEMON_FUNCS_H */
diff --git a/src/mhd2/daemon_get_info.c b/src/mhd2/daemon_get_info.c
@@ -0,0 +1,87 @@
+/*
+ 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_get_info.c
+ * @brief The implementation of MHD_daemon_get_info_*() functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "sys_sockets_types.h"
+
+#include "mhd_socket_type.h"
+#include "mhd_daemon.h"
+
+#include "mhd_public_api.h"
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3)
+MHD_FN_PURE_ enum MHD_StatusCode
+MHD_daemon_get_info_fixed_sz (struct MHD_Daemon *daemon,
+ enum MHD_DaemonInfoFixedType info_type,
+ union MHD_DaemonInfoFixedData *return_value,
+ size_t return_value_size)
+{
+ switch (info_type)
+ {
+ case MHD_DAEMON_INFO_FIXED_LISTEN_SOCKET:
+ if (MHD_INVALID_SOCKET == daemon->net.listen.fd)
+ return MHD_SC_INFO_GET_TYPE_UNSUPPORTED;
+ if (sizeof(MHD_Socket) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_socket = daemon->net.listen.fd;
+ return MHD_SC_OK;
+ case MHD_DAEMON_INFO_FIXED_AGGREAGATE_FD:
+#ifdef MHD_USE_EPOLL
+ if (! mhd_D_IS_USING_EPOLL (daemon))
+ return MHD_SC_INFO_GET_TYPE_UNSUPPORTED;
+ if (sizeof(int) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_fd = daemon->events.data.epoll.e_fd;
+ return MHD_SC_OK;
+#else
+ return MHD_SC_INFO_GET_TYPE_NOT_SUPP_BY_BUILD;
+#endif
+ break;
+ case MHD_DAEMON_INFO_FIXED_BIND_PORT:
+ if (MHD_INVALID_SOCKET == daemon->net.listen.fd)
+ return MHD_SC_INFO_GET_TYPE_UNSUPPORTED;
+ if (mhd_SOCKET_TYPE_UNKNOWN > daemon->net.listen.type)
+ return MHD_SC_INFO_GET_TYPE_UNSUPPORTED;
+ if (0 == daemon->net.listen.port)
+ {
+ if (mhd_SOCKET_TYPE_IP != daemon->net.listen.type)
+ return MHD_SC_INFO_GET_TYPE_UNSUPPORTED;
+ return MHD_SC_INFO_GET_TYPE_UNAVAILALBE;
+ }
+ if (sizeof(return_value->v_port) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_port = daemon->net.listen.port;
+ return MHD_SC_OK;
+ case MHD_DAEMON_INFO_FIXED_SENTINEL:
+ default:
+ break;
+ }
+ return MHD_SC_INFO_TYPE_UNKNOWN;
+}
diff --git a/src/mhd2/daemon_logger.c b/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 */
diff --git a/src/mhd2/daemon_logger.h b/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 */
diff --git a/src/mhd2/daemon_logger_default.c b/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 */
diff --git a/src/mhd2/daemon_logger_default.h b/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 */
diff --git a/src/mhd2/daemon_options.h b/src/mhd2/daemon_options.h
@@ -0,0 +1,290 @@
+/* This is generated code, it is still under LGPLv2.1+.
+ Do not edit directly! */
+/* *INDENT-OFF* */
+/**
+ * @file daemon_options.h
+ * @author daemon-options-generator.c
+ */
+
+#ifndef MHD_DAEMON_OPTIONS_H
+#define MHD_DAEMON_OPTIONS_H 1
+
+#include "mhd_sys_options.h"
+#include "mhd_public_api.h"
+
+struct DaemonOptions
+{
+ /**
+ * Value for #MHD_D_O_WORK_MODE.
+ * the object created by one of the next functions/macros: #MHD_WM_OPTION_EXTERNAL_PERIODIC(), #MHD_WM_OPTION_EXTERNAL_EVENT_LOOP_CB_LEVEL(), #MHD_WM_OPTION_EXTERNAL_EVENT_LOOP_CB_EDGE(), #MHD_WM_OPTION_EXTERNAL_SINGLE_FD_WATCH(), #MHD_WM_OPTION_WORKER_THREADS(), #MHD_WM_OPTION_THREAD_PER_CONNECTION()
+ */
+ struct MHD_WorkModeWithParam work_mode;
+
+
+ /**
+ * Value for #MHD_D_O_POLL_SYSCALL.
+ * FIXME
+ */
+ enum MHD_SockPollSyscall poll_syscall;
+
+
+ /**
+ * Value for #MHD_D_O_LOG_CALLBACK.
+ * the callback to use for logging,
+ * NULL to disable logging
+ */
+ struct MHD_DaemonOptionValueLog log_callback;
+
+
+ /**
+ * Value for #MHD_D_O_BIND_PORT.
+ * the address family to use,
+ * the #MHD_AF_NONE to disable listen socket (the same effect as if this option is not used)
+ */
+ struct MHD_DaemonOptionValueBind bind_port;
+
+
+ /**
+ * Value for #MHD_D_O_BIND_SA.
+ * the size of the socket address pointed by @a sa.
+ */
+ struct MHD_DaemonOptionValueSA bind_sa;
+
+
+ /**
+ * Value for #MHD_D_O_LISTEN_SOCKET.
+ * the listen socket to use, ignored if set to #MHD_INVALID_SOCKET
+ */
+ MHD_Socket listen_socket;
+
+
+ /**
+ * Value for #MHD_D_O_LISTEN_ADDR_REUSE.
+ * FIXME
+ */
+ enum MHD_DaemonOptionBindType listen_addr_reuse;
+
+
+ /**
+ * Value for #MHD_D_O_TCP_FASTOPEN.
+ * the type use of of TCP FastOpen
+ */
+ struct MHD_DaemonOptionValueTFO tcp_fastopen;
+
+
+ /**
+ * Value for #MHD_D_O_LISTEN_BACKLOG.
+ * FIXME
+ */
+ unsigned int listen_backlog;
+
+
+ /**
+ * Value for #MHD_D_O_SIGPIPE_SUPPRESSED.
+ */
+ enum MHD_Bool sigpipe_suppressed;
+
+
+ /**
+ * Value for #MHD_D_O_TLS.
+ * the TLS backend to use,
+ * #MHD_TLS_BACKEND_NONE for non-TLS (plain TCP) connections
+ */
+ enum MHD_TlsBackend tls;
+
+
+ /**
+ * Value for #MHD_D_O_TLS_KEY_CERT.
+ * the private key loaded into memory (not a filename)
+ */
+ struct MHD_DaemonOptionValueTlsCert tls_key_cert;
+
+
+ /**
+ * Value for #MHD_D_O_TLS_CLIENT_CA.
+ * the CA certificate in memory (not a filename)
+ */
+ const char *tls_client_ca;
+
+
+ /**
+ * Value for #MHD_D_O_TLS_PSK_CALLBACK.
+ * the function to call to obtain pre-shared key
+ */
+ struct MHD_DaemonOptionValueTlsPskCB tls_psk_callback;
+
+
+ /**
+ * Value for #MHD_D_O_NO_ALPN.
+ */
+ enum MHD_Bool no_alpn;
+
+
+ /**
+ * Value for #MHD_D_O_DEFAULT_TIMEOUT.
+ * the in seconds, zero for no timeout
+ */
+ unsigned int default_timeout;
+
+
+ /**
+ * Value for #MHD_D_O_GLOBAL_CONNECTION_LIMIT.
+ * FIXME
+ */
+ unsigned int global_connection_limit;
+
+
+ /**
+ * Value for #MHD_D_O_PER_IP_LIMIT.
+ * FIXME
+ */
+ unsigned int per_ip_limit;
+
+
+ /**
+ * Value for #MHD_D_O_ACCEPT_POLICY.
+ * the accept policy callback
+ */
+ struct MHD_DaemonOptionValueAcceptPol accept_policy;
+
+
+ /**
+ * Value for #MHD_D_O_PROTOCOL_STRICT_LEVEL.
+ * the level of strictness
+ */
+ struct MHD_DaemonOptionValueStrctLvl protocol_strict_level;
+
+
+ /**
+ * Value for #MHD_D_O_EARLY_URI_LOGGER.
+ * the early URI callback
+ */
+ struct MHD_DaemonOptionValueUriCB early_uri_logger;
+
+
+ /**
+ * Value for #MHD_D_O_DISABLE_URI_QUERY_PLUS_AS_SPACE.
+ */
+ enum MHD_Bool disable_uri_query_plus_as_space;
+
+
+ /**
+ * Value for #MHD_D_O_SUPPRESS_DATE_HEADER.
+ */
+ enum MHD_Bool suppress_date_header;
+
+
+ /**
+ * Value for #MHD_D_O_ENABLE_SHOUTCAST.
+ */
+ enum MHD_Bool enable_shoutcast;
+
+
+ /**
+ * Value for #MHD_D_O_CONN_MEMORY_LIMIT.
+ */
+ size_t conn_memory_limit;
+
+
+ /**
+ * Value for #MHD_D_O_LARGE_POOL_SIZE.
+ */
+ size_t large_pool_size;
+
+
+ /**
+ * Value for #MHD_D_O_STACK_SIZE.
+ */
+ size_t stack_size;
+
+
+ /**
+ * Value for #MHD_D_O_FD_NUMBER_LIMIT.
+ * FIXME
+ */
+ MHD_Socket fd_number_limit;
+
+
+ /**
+ * Value for #MHD_D_O_TURBO.
+ */
+ enum MHD_Bool turbo;
+
+
+ /**
+ * Value for #MHD_D_O_DISABLE_THREAD_SAFETY.
+ */
+ enum MHD_Bool disable_thread_safety;
+
+
+ /**
+ * Value for #MHD_D_O_DISALLOW_UPGRADE.
+ */
+ enum MHD_Bool disallow_upgrade;
+
+
+ /**
+ * Value for #MHD_D_O_DISALLOW_SUSPEND_RESUME.
+ */
+ enum MHD_Bool disallow_suspend_resume;
+
+
+ /**
+ * Value for #MHD_D_O_DAEMON_READY_CALLBACK.
+ * the pre-start callback
+ */
+ struct MHD_DaemonOptionValueReadyCB daemon_ready_callback;
+
+
+ /**
+ * Value for #MHD_D_O_NOTIFY_CONNECTION.
+ * the callback for notifications
+ */
+ struct MHD_DaemonOptionValueNotifConnCB notify_connection;
+
+
+ /**
+ * Value for #MHD_D_O_NOTIFY_STREAM.
+ * the callback for notifications
+ */
+ struct MHD_DaemonOptionValueNotifStreamCB notify_stream;
+
+
+ /**
+ * Value for #MHD_D_O_RANDOM_ENTROPY.
+ * the size of the buffer
+ */
+ struct MHD_DaemonOptionEntropySeed random_entropy;
+
+
+ /**
+ * Value for #MHD_D_O_DAUTH_MAP_SIZE.
+ * the size of the map array
+ */
+ size_t dauth_map_size;
+
+
+ /**
+ * Value for #MHD_D_O_DAUTH_NONCE_BIND_TYPE.
+ * FIXME
+ */
+ enum MHD_DaemonOptionValueDAuthBindNonce dauth_nonce_bind_type;
+
+
+ /**
+ * Value for #MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT.
+ * FIXME
+ */
+ unsigned int dauth_def_nonce_timeout;
+
+
+ /**
+ * Value for #MHD_D_O_DAUTH_DEF_MAX_NC.
+ * FIXME
+ */
+ uint_fast32_t dauth_def_max_nc;
+
+
+};
+
+#endif /* ! MHD_DAEMON_OPTIONS_H 1 */
diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c
@@ -0,0 +1,208 @@
+/* This is generated code, it is still under LGPLv2.1+.
+ Do not edit directly! */
+/* *INDENT-OFF* */
+/**
+ * @file daemon_set_options.c
+ * @author daemon-options-generator.c
+ */
+
+#include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "sys_malloc.h"
+#include <string.h>
+#include "mhd_daemon.h"
+#include "daemon_options.h"
+#include "mhd_public_api.h"
+
+MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_
+enum MHD_StatusCode
+MHD_daemon_set_options (
+ struct MHD_Daemon *daemon,
+ const struct MHD_DaemonOptionAndValue *options,
+ size_t options_max_num)
+{
+ struct DaemonOptions *restrict settings = daemon->settings;
+ enum MHD_StatusCode res = MHD_SC_OK;
+ size_t i;
+
+ if (mhd_DAEMON_STATE_NOT_STARTED != daemon->state)
+ return MHD_SC_TOO_LATE;
+
+ for (i = 0; i < options_max_num; i++)
+ {
+ const struct MHD_DaemonOptionAndValue *const option
+ = options + i;
+ switch (option->opt)
+ {
+ case MHD_D_O_END:
+ i = options_max_num - 1;
+ break;
+ case MHD_D_O_WORK_MODE:
+ settings->work_mode = option->val.work_mode;
+ continue;
+ case MHD_D_O_POLL_SYSCALL:
+ settings->poll_syscall = option->val.poll_syscall;
+ continue;
+ case MHD_D_O_LOG_CALLBACK:
+ /* Note: set directly to the daemon */
+ daemon->log_params = option->val.log_callback;
+ continue;
+ case MHD_D_O_BIND_PORT:
+ settings->bind_port.v_af = option->val.bind_port.v_af;
+ settings->bind_port.v_port = option->val.bind_port.v_port;
+ continue;
+ case MHD_D_O_BIND_SA:
+ /* custom setter */
+ if (0 != option->val.bind_sa.v_sa_len)
+ {
+ if (NULL != settings->bind_sa.v_sa)
+ free (settings->bind_sa.v_sa);
+ settings->bind_sa.v_sa = malloc (option->val.bind_sa.v_sa_len);
+ if (NULL == settings->bind_sa.v_sa)
+ return MHD_SC_DAEMON_MALLOC_FAILURE;
+ memcpy (settings->bind_sa.v_sa, option->val.bind_sa.v_sa,
+ option->val.bind_sa.v_sa_len);
+ settings->bind_sa.v_sa_len = option->val.bind_sa.v_sa_len;
+ settings->bind_sa.v_dual = option->val.bind_sa.v_dual;
+ }
+ continue;
+ case MHD_D_O_LISTEN_SOCKET:
+ settings->listen_socket = option->val.listen_socket;
+ continue;
+ case MHD_D_O_LISTEN_ADDR_REUSE:
+ settings->listen_addr_reuse = option->val.listen_addr_reuse;
+ continue;
+ case MHD_D_O_TCP_FASTOPEN:
+ settings->tcp_fastopen.v_option = option->val.tcp_fastopen.v_option;
+ settings->tcp_fastopen.v_queue_length = option->val.tcp_fastopen.v_queue_length;
+ continue;
+ case MHD_D_O_LISTEN_BACKLOG:
+ settings->listen_backlog = option->val.listen_backlog;
+ continue;
+ case MHD_D_O_SIGPIPE_SUPPRESSED:
+ settings->sigpipe_suppressed = option->val.sigpipe_suppressed;
+ continue;
+ case MHD_D_O_TLS:
+ settings->tls = option->val.tls;
+ continue;
+ case MHD_D_O_TLS_KEY_CERT:
+ settings->tls_key_cert.v_mem_key = option->val.tls_key_cert.v_mem_key;
+ settings->tls_key_cert.v_mem_cert = option->val.tls_key_cert.v_mem_cert;
+ settings->tls_key_cert.v_mem_pass = option->val.tls_key_cert.v_mem_pass;
+ continue;
+ case MHD_D_O_TLS_CLIENT_CA:
+ settings->tls_client_ca = option->val.tls_client_ca;
+ continue;
+ case MHD_D_O_TLS_PSK_CALLBACK:
+ settings->tls_psk_callback.v_psk_cb = option->val.tls_psk_callback.v_psk_cb;
+ settings->tls_psk_callback.v_psk_cb_cls = option->val.tls_psk_callback.v_psk_cb_cls;
+ continue;
+ case MHD_D_O_NO_ALPN:
+ settings->no_alpn = option->val.no_alpn;
+ continue;
+ case MHD_D_O_DEFAULT_TIMEOUT:
+ settings->default_timeout = option->val.default_timeout;
+ continue;
+ case MHD_D_O_GLOBAL_CONNECTION_LIMIT:
+ settings->global_connection_limit = option->val.global_connection_limit;
+ continue;
+ case MHD_D_O_PER_IP_LIMIT:
+ settings->per_ip_limit = option->val.per_ip_limit;
+ continue;
+ case MHD_D_O_ACCEPT_POLICY:
+ settings->accept_policy.v_apc = option->val.accept_policy.v_apc;
+ settings->accept_policy.v_apc_cls = option->val.accept_policy.v_apc_cls;
+ continue;
+ case MHD_D_O_PROTOCOL_STRICT_LEVEL:
+ settings->protocol_strict_level.v_sl = option->val.protocol_strict_level.v_sl;
+ settings->protocol_strict_level.v_how = option->val.protocol_strict_level.v_how;
+ continue;
+ case MHD_D_O_EARLY_URI_LOGGER:
+ settings->early_uri_logger.v_cb = option->val.early_uri_logger.v_cb;
+ settings->early_uri_logger.v_cls = option->val.early_uri_logger.v_cls;
+ continue;
+ case MHD_D_O_DISABLE_URI_QUERY_PLUS_AS_SPACE:
+ settings->disable_uri_query_plus_as_space = option->val.disable_uri_query_plus_as_space;
+ continue;
+ case MHD_D_O_SUPPRESS_DATE_HEADER:
+ settings->suppress_date_header = option->val.suppress_date_header;
+ continue;
+ case MHD_D_O_ENABLE_SHOUTCAST:
+ settings->enable_shoutcast = option->val.enable_shoutcast;
+ continue;
+ case MHD_D_O_CONN_MEMORY_LIMIT:
+ settings->conn_memory_limit = option->val.conn_memory_limit;
+ continue;
+ case MHD_D_O_LARGE_POOL_SIZE:
+ settings->large_pool_size = option->val.large_pool_size;
+ continue;
+ case MHD_D_O_STACK_SIZE:
+ settings->stack_size = option->val.stack_size;
+ continue;
+ case MHD_D_O_FD_NUMBER_LIMIT:
+ settings->fd_number_limit = option->val.fd_number_limit;
+ continue;
+ case MHD_D_O_TURBO:
+ settings->turbo = option->val.turbo;
+ continue;
+ case MHD_D_O_DISABLE_THREAD_SAFETY:
+ settings->disable_thread_safety = option->val.disable_thread_safety;
+ continue;
+ case MHD_D_O_DISALLOW_UPGRADE:
+ settings->disallow_upgrade = option->val.disallow_upgrade;
+ continue;
+ case MHD_D_O_DISALLOW_SUSPEND_RESUME:
+ settings->disallow_suspend_resume = option->val.disallow_suspend_resume;
+ continue;
+ case MHD_D_O_DAEMON_READY_CALLBACK:
+ settings->daemon_ready_callback.v_cb = option->val.daemon_ready_callback.v_cb;
+ settings->daemon_ready_callback.v_cb_cls = option->val.daemon_ready_callback.v_cb_cls;
+ continue;
+ case MHD_D_O_NOTIFY_CONNECTION:
+ settings->notify_connection.v_ncc = option->val.notify_connection.v_ncc;
+ settings->notify_connection.v_cls = option->val.notify_connection.v_cls;
+ continue;
+ case MHD_D_O_NOTIFY_STREAM:
+ settings->notify_stream.v_nsc = option->val.notify_stream.v_nsc;
+ settings->notify_stream.v_cls = option->val.notify_stream.v_cls;
+ continue;
+ case MHD_D_O_RANDOM_ENTROPY:
+ /* custom setter */
+ /* The is not an easy for automatic generations */
+ if (0 != option->val.random_entropy.v_buf_size)
+ {
+ if (NULL != settings->random_entropy.v_buf)
+ free (settings->random_entropy.v_buf);
+ settings->random_entropy.v_buf =
+ malloc (option->val.random_entropy.v_buf_size);
+ if (NULL == settings->random_entropy.v_buf)
+ return MHD_SC_DAEMON_MALLOC_FAILURE;
+ memcpy (settings->random_entropy.v_buf,
+ option->val.random_entropy.v_buf,
+ option->val.random_entropy.v_buf_size);
+ settings->random_entropy.v_buf_size =
+ option->val.random_entropy.v_buf_size;
+ }
+ continue;
+ case MHD_D_O_DAUTH_MAP_SIZE:
+ settings->dauth_map_size = option->val.dauth_map_size;
+ continue;
+ case MHD_D_O_DAUTH_NONCE_BIND_TYPE:
+ settings->dauth_nonce_bind_type = option->val.dauth_nonce_bind_type;
+ continue;
+ case MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT:
+ settings->dauth_def_nonce_timeout = option->val.dauth_def_nonce_timeout;
+ continue;
+ case MHD_D_O_DAUTH_DEF_MAX_NC:
+ settings->dauth_def_max_nc = option->val.dauth_def_max_nc;
+ continue;
+ case MHD_D_O_SENTINEL:
+ default: /* for -Wswitch-default -Wswitch-enum */
+ res = MHD_SC_OPTION_UNKNOWN;
+ i = options_max_num - 1;
+ break;
+ }
+ }
+ return res;
+}
diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c
@@ -0,0 +1,2868 @@
+/*
+ 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_types.h"
+#include "sys_sockets_headers.h"
+#include "mhd_sockets_macros.h"
+#include "sys_ip_headers.h"
+
+#ifdef MHD_POSIX_SOCKETS
+# include "sys_errno.h"
+#endif
+#ifdef MHD_USE_EPOLL
+# include <sys/epoll.h>
+#endif
+
+#ifdef MHD_POSIX_SOCKETS
+# include <fcntl.h>
+# 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
+
+#include "mhd_limits.h"
+
+#include "mhd_daemon.h"
+#include "daemon_options.h"
+
+#include "mhd_assert.h"
+#include "mhd_sockets_funcs.h"
+#include "daemon_logger.h"
+
+#ifdef MHD_USE_THREADS
+# include "mhd_itc.h"
+# include "mhd_threads.h"
+# include "events_process.h"
+# include "daemon_funcs.h"
+#endif
+
+#include "mhd_public_api.h"
+
+
+/**
+ * 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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_least16_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_least16_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_least16_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, SOL_SOCKET, 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, SOL_SOCKET,
+#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, SOL_SOCKET,
+#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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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_least16_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_least16_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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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 (EPOLL_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);
+ /* 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+ MHD_FN_MUST_CHECK_RESULT_ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+ MHD_FN_MUST_CHECK_RESULT_ 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
+
+/**
+ * Initialise large buffer tracking.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ * the error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
+daemon_init_large_buf (struct MHD_Daemon *restrict d,
+ struct DaemonOptions *restrict s)
+{
+ mhd_assert (! mhd_D_HAS_MASTER (d));
+ d->req_cfg.large_buf.space_left = s->large_pool_size;
+ if (0 == d->req_cfg.large_buf.space_left) // TODO: USE SETTINGS!
+ d->req_cfg.large_buf.space_left = 1024 * 1024U; // TODO: USE SETTINGS!
+ if (! mhd_mutex_init_short (&(d->req_cfg.large_buf.lock)))
+ {
+ mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
+ "Failed to initialise mutex for the global large buffer.");
+ return MHD_SC_MUTEX_INIT_FAILURE;
+ }
+ return MHD_SC_OK;
+}
+
+
+/**
+ * Initialise large buffer tracking.
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ * the error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+daemon_deinit_large_buf (struct MHD_Daemon *restrict d)
+{
+ mhd_mutex_destroy_chk (&(d->req_cfg.large_buf.lock));
+}
+
+
+/**
+ * Finish initialisation of events processing
+ * @param d the daemon object
+ * @return #MHD_SC_OK on success,
+ * the error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
+allocate_events (struct MHD_Daemon *restrict d)
+{
+#if defined(MHD_USE_POLL) || defined(MHD_USE_EPOLL)
+ /**
+ * The number of elements to be monitored by sockets polling function
+ */
+ unsigned int num_elements;
+ num_elements = 0;
+#ifdef MHD_USE_THREADS
+ ++num_elements; /* For the ITC */
+#endif
+ if (MHD_INVALID_SOCKET != d->net.listen.fd)
+ ++num_elements; /* For the listening socket */
+ if (! mhd_D_HAS_THR_PER_CONN (d))
+ num_elements += d->conns.cfg.count_limit;
+#endif /* MHD_USE_POLL || MHD_USE_EPOLL */
+
+ 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.num_events_elements = FD_SETSIZE;
+ 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 ((num_elements > d->conns.cfg.count_limit) /* Check for value overflow */
+ || (mhd_D_HAS_THR_PER_CONN (d)))
+ {
+ d->events.data.poll.fds =
+ (struct pollfd *) malloc (sizeof(struct pollfd) * num_elements);
+ if (NULL != d->events.data.poll.fds)
+ {
+ d->events.data.poll.rel =
+ (union mhd_SocketRelation *) malloc (sizeof(union mhd_SocketRelation)
+ * num_elements);
+ if (NULL != d->events.data.poll.rel)
+ {
+#ifndef NDEBUG
+ d->dbg.num_events_elements = num_elements;
+ 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 poll fds 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:
+ mhd_assert (! mhd_D_HAS_THR_PER_CONN (d));
+ /* 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 ((num_elements > d->conns.cfg.count_limit) /* Check for value overflow */
+ || (mhd_D_HAS_THR_PER_CONN (d)))
+ {
+ const unsigned int upper_limit = (sizeof(void*) >= 8) ? 4096 : 1024;
+
+ /* 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.num_events_elements = num_elements;
+ d->dbg.events_allocated = true;
+#endif
+ return MHD_SC_OK; /* Success exit point */
+ }
+ }
+ mhd_LOG_MSG (d, MHD_SC_EPOLL_EVENTS_MEMORY_ALLOCATE_FAILURE, \
+ "Failed to allocate memory for epoll events for the daemon");
+ return MHD_SC_EPOLL_EVENTS_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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ 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;
+ }
+ if (! mhd_FD_FITS_DAEMON (d,mhd_itc_r_fd (d->threading.itc)))
+ {
+ mhd_LOG_MSG (d, MHD_SC_ITC_FD_OUTSIDE_OF_SET_RANGE, \
+ "The inter-thread communication FD value is " \
+ "higher than allowed");
+ (void) mhd_itc_destroy (d->threading.itc);
+ mhd_itc_set_invalid (&(d->threading.itc));
+ return MHD_SC_ITC_FD_OUTSIDE_OF_SET_RANGE;
+ }
+#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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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));
+ (void) 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
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ 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 = POLLIN;
+ 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 = POLLIN;
+ 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), ®_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, ®_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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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_HAS_MASTER (d));
+ 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 = UINT_MAX;
+ limit_by_select = UINT_MAX;
+
+ error_by_fd_setsize = false;
+#ifdef MHD_POSIX_SOCKETS
+ if (1)
+ {
+ limit_by_num = (unsigned int) 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 > (unsigned int) 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) INT_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 = 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);
+ mhd_assert (mhd_POLL_TYPE_EPOLL != 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);
+ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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
+}
+
+
+/**
+ * Nullify worker daemon member that should be set only in master daemon
+ * @param d
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+reset_master_only_areas (struct MHD_Daemon *restrict d)
+{
+ /* Not needed. It is initialised later */
+ /* memset (&(d->req_cfg.large_buf), 0, sizeof(d->req_cfg.large_buf)); */
+ (void) d;
+}
+
+
+/**
+ * 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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));
+ reset_master_only_areas (worker);
+
+ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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 */
+ }
+ if (MHD_SC_OK == res)
+ {
+ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ 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));
+
+ d->threading.stop_requested = true;
+
+ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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' */
+ d->threading.hier.pool.workers[i].threading.stop_requested = true;
+ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_MUST_CHECK_RESULT_ 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) 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
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_MUST_CHECK_RESULT_ 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_init_large_buf (d, s);
+ if (MHD_SC_OK == res)
+ {
+ res = daemon_start_threads (d);
+ if (MHD_SC_OK == res)
+ {
+ return MHD_SC_OK;
+ }
+
+ /* Below is a clean-up path */
+ daemon_deinit_large_buf (d);
+ }
+ daemon_deinit_threading_and_conn (d);
+ }
+
+
+ daemon_deinit_net (d);
+ mhd_assert (MHD_SC_OK != res);
+ return res;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_MUST_CHECK_RESULT_ 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_EXTERN_ MHD_FN_PAR_NONNULL_ALL_ 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_large_buf (daemon);
+
+ daemon_deinit_net (daemon);
+ }
+ daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */
+
+ free (daemon);
+}
diff --git a/src/mhd2/dcc_action.c b/src/mhd2/dcc_action.c
@@ -0,0 +1,150 @@
+/*
+ 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/dcc_action.c
+ * @brief The definition of the MHD_DCC_action_*()
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include <string.h>
+
+#include "mhd_dcc_action.h"
+#include "mhd_response.h"
+#include "mhd_reply.h"
+#include "mhd_connection.h"
+
+#include "mhd_public_api.h"
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_CSTR_ (4) const struct MHD_DynamicContentCreatorAction *
+MHD_DCC_action_continue_zc (
+ struct MHD_DynamicContentCreatorContext *ctx,
+ size_t data_size,
+ const struct MHD_DynContentZCIoVec *iov_data,
+ const char *MHD_RESTRICT chunk_ext)
+{
+ struct MHD_DynamicContentCreatorAction *ret;
+
+ ret = NULL;
+ do
+ {
+ if ((&(ctx->connection->rp.app_act_ctx)) != ctx)
+ break;
+ if (mhd_DCC_ACTION_NO_ACTION != ctx->connection->rp.app_act.act)
+ break; /* The action already has been created */
+ if (NULL != iov_data)
+ {
+ mhd_assert (0 && "Not implemented yet");
+ break;
+ }
+ if (0 == data_size)
+ { /* The total size must be non-zero */
+ if (NULL == iov_data)
+ break;
+ mhd_assert (0 && "Not implemented yet");
+ (void) iov_data->iov_count;
+ // TODO: add check for iov data total size
+ break;
+ }
+ if (NULL != chunk_ext)
+ {
+ if (ctx->connection->rp.props.chunked)
+ {
+ mhd_assert (0 && "Not implemented yet");
+ // TODO: copy 'chunk_ext' directly to the output buffer
+ break;
+ }
+ }
+ ret = &(ctx->connection->rp.app_act);
+ ret->act = mhd_DCC_ACTION_CONTINUE;
+ ret->data.cntnue.buf_data_size = data_size;
+ ret->data.cntnue.iov_data = iov_data;
+ } while (0);
+
+ if (NULL == ret)
+ {
+ /* Call application clean-up */
+ if ((NULL != iov_data) && (NULL != iov_data->iov_fcb))
+ iov_data->iov_fcb (iov_data->iov_fcb_cls);
+ }
+
+ return ret;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+const struct MHD_DynamicContentCreatorAction *
+MHD_DCC_action_finish_with_footer (
+ struct MHD_DynamicContentCreatorContext *ctx,
+ size_t num_footers,
+ const struct MHD_NameValueCStr *MHD_RESTRICT footers)
+{
+ struct MHD_DynamicContentCreatorAction *ret;
+
+ if ((&(ctx->connection->rp.app_act_ctx)) != ctx)
+ return NULL;
+ if (mhd_DCC_ACTION_NO_ACTION != ctx->connection->rp.app_act.act)
+ return NULL; /* The action already has been created */
+ if ((0 != num_footers) && (NULL == footers))
+ return NULL;
+
+ if (MHD_SIZE_UNKNOWN != ctx->connection->rp.response->cntn_size)
+ {
+ mhd_assert (ctx->connection->rp.rsp_cntn_read_pos <
+ ctx->connection->rp.response->cntn_size);
+ return NULL;
+ }
+
+ if (0 != num_footers)
+ {
+ mhd_assert (0 && "Not implemented yet");
+ // TODO: build response footer and use footers directly
+ return NULL;
+ }
+
+ ret = &(ctx->connection->rp.app_act);
+ ret->act = mhd_DCC_ACTION_FINISH;
+
+ return ret;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_RETURNS_NONNULL_ const struct MHD_DynamicContentCreatorAction *
+MHD_DCC_action_suspend (struct MHD_DynamicContentCreatorContext *ctx)
+{
+ struct MHD_DynamicContentCreatorAction *ret;
+
+ if ((&(ctx->connection->rp.app_act_ctx)) != ctx)
+ return NULL;
+ if (mhd_DCC_ACTION_NO_ACTION != ctx->connection->rp.app_act.act)
+ return NULL; /* The action already has been created */
+
+ ret = &(ctx->connection->rp.app_act);
+ ret->act = mhd_DCC_ACTION_SUSPEND;
+
+ return ret;
+}
diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c
@@ -0,0 +1,1104 @@
+/*
+ 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 "mhd_sys_options.h"
+#include "events_process.h"
+
+#include "mhd_socket_type.h"
+#include "sys_poll.h"
+#include "sys_select.h"
+#ifdef MHD_USE_EPOLL
+# include <sys/epoll.h>
+#endif
+#ifdef MHD_POSIX_SOCKETS
+# include "sys_errno.h"
+#endif
+
+#include "mhd_itc.h"
+
+#include "mhd_panic.h"
+
+#include "mhd_sockets_macros.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "conn_mark_ready.h"
+#include "daemon_logger.h"
+#include "daemon_add_conn.h"
+#include "daemon_funcs.h"
+#include "conn_data_process.h"
+#include "stream_funcs.h"
+
+#include "mhd_public_api.h"
+
+static int
+get_max_wait (struct MHD_Daemon *restrict d)
+{
+ bool zero_wait = d->events.zero_wait;
+ if (d->events.act_req.accept && d->conns.block_new)
+ d->events.act_req.accept = false;
+
+ d->events.zero_wait = false; /* Reset as this pending data will be processed */
+ if (d->events.act_req.accept)
+ return 0;
+ if (zero_wait)
+ return 0;
+ if (NULL != mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready))
+ return 0;
+
+ return INT_MAX; // TODO: calculate correct timeout value
+}
+
+
+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 ((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_conn_mark_ready (c, d);
+ else
+ mhd_conn_mark_unready (c, d);
+}
+
+
+/**
+ * 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);
+ if (slots_left < num_to_accept)
+ num_to_accept = slots_left;
+ }
+#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_active_conns (struct MHD_Daemon *restrict d)
+{
+ struct MHD_Connection *c;
+ mhd_assert (! mhd_D_HAS_WORKERS (d));
+
+ c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready);
+ while (NULL != c)
+ {
+ struct MHD_Connection *next;
+ next = mhd_DLINKEDL_GET_NEXT (c, proc_ready); /* The current connection can be closed */
+ if (! mhd_conn_process_recv_send_data (c))
+ mhd_conn_close_final (c);
+
+ c = next;
+ }
+ return true;
+}
+
+
+static void
+close_all_daemon_conns (struct MHD_Daemon *d)
+{
+ struct MHD_Connection *c;
+
+ for (c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn);
+ NULL != c;
+ c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn))
+ {
+ mhd_conn_pre_close_d_shutdown (c);
+ mhd_conn_close_final (c);
+ }
+}
+
+
+#ifdef MHD_USE_SELECT
+
+/**
+ * Add socket to the fd_set
+ * @param fd the socket to add
+ * @param fs the pointer to fd_set
+ * @param max the pointer to variable to be updated with maximum FD value (or
+ * set to non-zero in case of WinSock)
+ * @param d the daemon object
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2)
+MHD_FN_PAR_INOUT_ (3) void
+fd_set_wrap (MHD_Socket fd,
+ fd_set *restrict fs,
+ int *restrict max,
+ struct MHD_Daemon *restrict d)
+{
+ mhd_assert (mhd_FD_FITS_DAEMON (d, fd)); /* Must be checked for every FD before
+ it is added */
+ mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type);
+ (void) d; /* Unused with non-debug builds */
+#if defined(MHD_POSIX_SOCKETS)
+ FD_SET (fd, fs);
+ if (*max < fd)
+ *max = fd;
+#elif defined(MHD_WINSOCK_SOCKETS)
+ /* Use custom set function to take advantage of know uniqueness of
+ * used sockets (to skip useless (for this function) check for duplicated
+ * sockets implemented in system's macro). */
+ mhd_assert (fs->fd_count < FD_SETSIZE - 1); /* Daemon limits set to always fit FD_SETSIZE */
+ mhd_assert (! FD_ISSET (fd, fs)); /* All sockets must be unique */
+ fs->fd_array[fs->fd_count++] = fd;
+ *max = 1;
+#else
+#error Unknown sockets type
+#endif
+}
+
+
+/**
+ * Set daemon's FD_SETs to monitor all daemon's sockets
+ * @param d the daemon to use
+ * @param listen_only set to 'true' if connections's sockets should NOT
+ * be monitored
+ * @return with POSIX sockets: the maximum number of the socket used in
+ * the FD_SETs;
+ * with winsock: non-zero if at least one socket has been added to
+ * the FD_SETs,
+ * zero if no sockets in the FD_SETs
+ */
+static MHD_FN_PAR_NONNULL_ (1) int
+select_update_fdsets (struct MHD_Daemon *restrict d,
+ bool listen_only)
+{
+ struct MHD_Connection *c;
+ fd_set *const restrict rfds = d->events.data.select.rfds;
+ fd_set *const restrict wfds = d->events.data.select.wfds;
+ fd_set *const restrict efds = d->events.data.select.efds;
+ int ret;
+
+ mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type);
+ mhd_assert (NULL != rfds);
+ mhd_assert (NULL != wfds);
+ mhd_assert (NULL != efds);
+ FD_ZERO (rfds);
+ FD_ZERO (wfds);
+ FD_ZERO (efds);
+
+ ret = 0;
+#ifdef MHD_USE_THREADS
+ mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+ fd_set_wrap (mhd_itc_r_fd (d->threading.itc),
+ rfds,
+ &ret,
+ d);
+ fd_set_wrap (mhd_itc_r_fd (d->threading.itc),
+ efds,
+ &ret,
+ d);
+#endif
+ if (MHD_INVALID_SOCKET != d->net.listen.fd)
+ {
+ fd_set_wrap (d->net.listen.fd,
+ rfds,
+ &ret,
+ d);
+ fd_set_wrap (d->net.listen.fd,
+ efds,
+ &ret,
+ d);
+ }
+ if (listen_only)
+ return ret;
+
+ for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn); NULL != c;
+ c = mhd_DLINKEDL_GET_NEXT (c,all_conn))
+ {
+ mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+
+ if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
+ fd_set_wrap (c->socket_fd,
+ rfds,
+ &ret,
+ d);
+ if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE))
+ fd_set_wrap (c->socket_fd,
+ wfds,
+ &ret,
+ d);
+ fd_set_wrap (c->socket_fd,
+ efds,
+ &ret,
+ d);
+ }
+
+ return ret;
+}
+
+
+static MHD_FN_PAR_NONNULL_ (1) bool
+select_update_statuses_from_fdsets (struct MHD_Daemon *d,
+ int num_events)
+{
+ struct MHD_Connection *c;
+ fd_set *const restrict rfds = d->events.data.select.rfds;
+ fd_set *const restrict wfds = d->events.data.select.wfds;
+ fd_set *const restrict efds = d->events.data.select.efds;
+
+ mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type);
+ mhd_assert (0 <= num_events);
+ mhd_assert (((unsigned int) num_events) <= d->dbg.num_events_elements);
+
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (0 == num_events)
+ return true;
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+#ifdef MHD_USE_THREADS
+ mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+ if (FD_ISSET (mhd_itc_r_fd (d->threading.itc), efds))
+ {
+ 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 (FD_ISSET (mhd_itc_r_fd (d->threading.itc), rfds))
+ {
+ --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);
+ }
+
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (0 == num_events)
+ return true;
+#endif /* MHD_FAVOR_SMALL_CODE */
+#endif /* MHD_USE_THREADS */
+
+ if (MHD_INVALID_SOCKET != d->net.listen.fd)
+ {
+ if (FD_ISSET (d->net.listen.fd, efds))
+ {
+ --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_HAS_MASTER (d))
+ 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 (FD_ISSET (d->net.listen.fd, rfds))
+ {
+ --num_events;
+ d->events.act_req.accept = true;
+ }
+ }
+
+ mhd_assert ((0 == num_events) || \
+ (mhd_DAEMON_TYPE_LISTEN_ONLY != d->threading.d_type));
+
+#ifdef MHD_FAVOR_SMALL_CODE
+ (void) num_events;
+ num_events = 1; /* Use static value to optimise out the next look */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+
+ for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns), all_conn);
+ (NULL != c) && (0 != num_events);
+ c = mhd_DLINKEDL_GET_NEXT (c, all_conn))
+ {
+ const MHD_Socket sk = c->socket_fd;
+ bool recv_ready = FD_ISSET (sk, rfds);
+ bool send_ready = FD_ISSET (sk, wfds);
+ bool err_state = FD_ISSET (sk, efds);
+
+ update_conn_net_status (d,
+ c,
+ recv_ready,
+ send_ready,
+ err_state);
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (recv_ready || send_ready || err_state)
+ --num_events;
+#endif /* MHD_FAVOR_SMALL_CODE */
+ }
+
+ #ifndef MHD_FAVOR_SMALL_CODE
+ mhd_assert (0 == num_events);
+#endif /* MHD_FAVOR_SMALL_CODE */
+ return true;
+}
+
+
+/**
+ * Update states of all connections, check for connection pending
+ * to be accept()'ed, check for the events on ITC.
+ * @param listen_only set to 'true' if connections's sockets should NOT
+ * be monitored
+ * @return 'true' if processed successfully,
+ * 'false' is unrecoverable error occurs and the daemon must be
+ * closed
+ */
+static MHD_FN_PAR_NONNULL_ (1) bool
+get_all_net_updates_by_select (struct MHD_Daemon *restrict d,
+ bool listen_only)
+{
+ int max_socket;
+ int max_wait;
+ struct timeval tmvl;
+ int num_events;
+ mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type);
+
+ max_socket = select_update_fdsets (d,
+ listen_only);
+
+ max_wait = get_max_wait (d); // TODO: use correct timeout value
+
+#ifdef MHD_WINSOCK_SOCKETS
+ if (0 == max_socket)
+ {
+ Sleep ((unsigned int) max_wait);
+ return true;
+ }
+#endif /* MHD_WINSOCK_SOCKETS */
+
+ tmvl.tv_sec = max_wait / 1000;
+#ifndef MHD_WINSOCK_SOCKETS
+ tmvl.tv_usec = (uint_least16_t) ((max_wait % 1000) * 1000);
+#else
+ tmvl.tv_usec = (int) ((max_wait % 1000) * 1000);
+#endif
+
+ num_events = select (max_socket + 1,
+ d->events.data.select.rfds,
+ d->events.data.select.wfds,
+ d->events.data.select.efds,
+ &tmvl);
+
+ 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_EBADF_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) ||
+ (WSANOTINITIALISED == err));
+#endif
+ if (! is_ignored_error)
+ {
+ if (is_hard_error)
+ {
+ mhd_LOG_MSG (d, MHD_SC_SELECT_HARD_ERROR, \
+ "The select() encountered unrecoverable error.");
+ return false;
+ }
+ mhd_LOG_MSG (d, MHD_SC_SELECT_SOFT_ERROR, \
+ "The select() encountered error.");
+ return true;
+ }
+ }
+
+ return select_update_statuses_from_fdsets (d, num_events);
+}
+
+
+#endif /* MHD_USE_SELECT */
+
+
+#ifdef MHD_USE_POLL
+
+static MHD_FN_PAR_NONNULL_ (1) 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);
+ mhd_assert (i_c < d->dbg.num_events_elements);
+ mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+
+ d->events.data.poll.fds[i_c].fd = c->socket_fd;
+ d->events.data.poll.rel[i_c].connection = c;
+ events = 0;
+ 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));
+ mhd_assert (i_c <= d->dbg.num_events_elements);
+ return i_c;
+}
+
+
+static MHD_FN_PAR_NONNULL_ (1) 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);
+ mhd_assert (((unsigned int) num_events) <= d->dbg.num_events_elements);
+
+ 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 | POLLIN)))
+ {
+ --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_HAS_MASTER (d))
+ 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 | POLLIN)))
+ {
+ --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 (i_c < d->dbg.num_events_elements);
+ 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 | POLLIN)));
+ send_ready = (0 != (revents & (MHD_POLL_OUT | POLLOUT)));
+#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 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
+ 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 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ)))
+ 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 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE)))
+ 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));
+ mhd_assert (i_c <= d->dbg.num_events_elements);
+ return true;
+}
+
+
+static MHD_FN_PAR_NONNULL_ (1) 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,
+ get_max_wait (d)); // TODO: use correct timeout value
+ 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.");
+ }
+ return true;
+ }
+
+ return poll_update_statuses_from_fds (d, num_events);
+}
+
+
+#endif /* MHD_USE_POLL */
+
+#ifdef MHD_USE_EPOLL
+
+/**
+ * Map events provided by epoll to connection states, ITC and
+ * listen socket states
+ */
+static MHD_FN_PAR_NONNULL_ (1) bool
+poll_update_statuses_from_eevents (struct MHD_Daemon *restrict d,
+ unsigned int num_events)
+{
+ unsigned int i;
+ struct epoll_event *const restrict events =
+ d->events.data.epoll.events;
+ for (i = 0; num_events > i; ++i)
+ {
+ struct epoll_event *const e = events + i;
+ if (((uint64_t) mhd_SOCKET_REL_MARKER_ITC) == e->data.u64) /* uint64_t is in the system header */
+ {
+ if (0 != (e->events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
+ {
+ 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 != (e->events & EPOLLIN))
+ {
+ /* 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);
+ }
+ }
+ else if (((uint64_t) mhd_SOCKET_REL_MARKER_LISTEN) == e->data.u64) /* uint64_t is in the system header */
+ {
+ if (0 != (e->events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
+ {
+ 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.");
+
+ // TODO: remove listen from epoll monitoring
+
+ /* Close the listening socket unless the master daemon should close it */
+ if (! mhd_D_HAS_MASTER (d))
+ mhd_socket_close (d->net.listen.fd);
+
+ /* Stop monitoring socket to avoid spinning with busy-waiting */
+ d->net.listen.fd = MHD_INVALID_SOCKET;
+ }
+ if (0 != (e->events & EPOLLIN))
+ d->events.act_req.accept = true;
+ }
+ else
+ {
+ bool recv_ready;
+ bool send_ready;
+ bool err_state;
+ struct MHD_Connection *const restrict c =
+ (struct MHD_Connection *) e->data.ptr;
+ recv_ready = (0 != (e->events & (EPOLLIN | EPOLLERR | EPOLLHUP)));
+ send_ready = (0 != (e->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)));
+ err_state = (0 != (e->events & (EPOLLERR | EPOLLHUP)));
+
+ update_conn_net_status (d, c, recv_ready, send_ready, err_state);
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Update states of all connections, check for connection pending
+ * to be accept()'ed, check for the events on ITC.
+ */
+static MHD_FN_PAR_NONNULL_ (1) bool
+get_all_net_updates_by_epoll (struct MHD_Daemon *restrict d)
+{
+ int num_events;
+ unsigned int events_processed;
+ int max_wait;
+ mhd_assert (mhd_POLL_TYPE_EPOLL == d->events.poll_type);
+ mhd_assert (0 < ((int) d->events.data.epoll.num_elements));
+ mhd_assert (d->events.data.epoll.num_elements == \
+ (size_t) ((int) d->events.data.epoll.num_elements));
+ mhd_assert (0 != d->events.data.epoll.num_elements);
+ mhd_assert (0 != d->conns.cfg.count_limit);
+ mhd_assert (d->events.data.epoll.num_elements == d->dbg.num_events_elements);
+
+ // TODO: add listen socket enable/disable
+
+ events_processed = 0;
+ max_wait = get_max_wait (d); // TODO: use correct timeout value
+ do
+ {
+ num_events = epoll_wait (d->events.data.epoll.e_fd,
+ d->events.data.epoll.events,
+ (int) d->events.data.epoll.num_elements,
+ max_wait);
+ max_wait = 0;
+ if (0 > num_events)
+ {
+ const int err = errno;
+ if (EINTR != err)
+ {
+ mhd_LOG_MSG (d, MHD_SC_EPOLL_HARD_ERROR, \
+ "The epoll_wait() encountered unrecoverable error.");
+ return false;
+ }
+ return true; /* EINTR, try next time */
+ }
+ if (! poll_update_statuses_from_eevents (d, (unsigned int) num_events))
+ return false;
+
+ events_processed += (unsigned int) num_events; /* Avoid reading too many events */
+ } while ((((unsigned int) num_events) == d->events.data.epoll.num_elements) &&
+ (events_processed < d->conns.cfg.count_limit + 2));
+
+ return true;
+}
+
+
+#endif /* MHD_USE_EPOLL */
+
+
+static MHD_FN_PAR_NONNULL_ (1) 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:
+ if (! get_all_net_updates_by_select (d, false))
+ return false;
+ 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:
+ if (! get_all_net_updates_by_epoll (d))
+ return false;
+ 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_active_conns (d);
+ return ! d->threading.stop_requested;
+}
+
+
+/**
+ * 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_thread_handle_ID_set_current_thread_ID (&(d->threading.tid));
+ 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 (d->threading.resume_requested)
+ mhd_daemon_resume_conns (d);
+
+ 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 for the daemon.");
+ }
+ close_all_daemon_conns (d);
+
+ return (mhd_THRD_RTRN_TYPE) 0;
+}
+
+
+static MHD_FN_PAR_NONNULL_ (1) 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_thread_handle_ID_set_current_thread_ID (&(d->threading.tid));
+
+ 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)
+{
+ (void) cls;
+ mhd_assert (0 && "Not yet implemented");
+ return (mhd_THRD_RTRN_TYPE) 0;
+}
diff --git a/src/mhd2/events_process.h b/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 */
diff --git a/src/mhd2/http_method.h b/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 */
diff --git a/src/mhd2/http_prot_ver.h b/src/mhd2/http_prot_ver.h
@@ -0,0 +1,71 @@
+/*
+ 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 = 3
+ ,
+ MHD_HTTP_VERSION_3 = 4
+ ,
+ MHD_HTTP_VERSION_FUTURE = 255
+};
+
+
+#define MHD_HTTP_PROTOCOL_VER_DEFINED 1
+#endif /* ! MHD_HTTP_PROTOCOL_VER_DEFINED */
+
+/**
+ * Check whether version of HTTP protocol is supported
+ */
+#define MHD_HTTP_VERSION_IS_SUPPORTED(v) \
+ ((MHD_HTTP_VERSION_1_0 <= (v)) && (MHD_HTTP_VERSION_1_1 >= (v)))
+
+/**
+ * Check whether version of HTTP protocol is valid
+ */
+#define MHD_HTTP_VERSION_IS_VALID(v) \
+ (((MHD_HTTP_VERSION_1_0 <= (v)) && (MHD_HTTP_VERSION_3 >= (v))) || \
+ (MHD_HTTP_VERSION_FUTURE == (v)))
+
+#endif /* ! MHD_HTTP_PROT_VER_H */
diff --git a/src/mhd2/http_status_str.c b/src/mhd2/http_status_str.c
@@ -0,0 +1,209 @@
+/*
+ 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 "http_status_str.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 num_elmnts;
+ 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_HTTP_StatusCode code)
+{
+ const 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].num_elmnts <= (code_i % 100))
+ return NULL;
+ res = statuses[code_i / 100].data + (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 (uint_fast16_t code)
+{
+ static const struct MHD_String no_status =
+ mhd_MSTR_INIT ("Nonstandard Status");
+ const struct MHD_String *res;
+
+ res = MHD_HTTP_status_code_to_string ((enum MHD_HTTP_StatusCode) code);
+ if (NULL != res)
+ return res;
+
+ return &no_status;
+}
diff --git a/src/mhd2/http_status_str.h b/src/mhd2/http_status_str.h
@@ -0,0 +1,47 @@
+/*
+ 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 "sys_base_types.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 (uint_fast16_t code)
+MHD_FN_CONST_;
+
+
+#endif /* ! MHD_HTTP_STATUS_STR_H */
diff --git a/libmicrohttpd.pc.in b/src/mhd2/libmicrohttpd2.pc.in
diff --git a/src/mhd2/mhd_action.h b/src/mhd2/mhd_action.h
@@ -0,0 +1,308 @@
+/*
+ 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
+ ,
+ /**
+ * Process clients upload by application callback
+ */
+ mhd_ACTION_UPLOAD
+ ,
+ /**
+ * Process clients upload by POST processor
+ */
+ mhd_ACTION_POST_PROCESS
+ ,
+ /**
+ * Suspend requests (connection)
+ */
+ mhd_ACTION_SUSPEND
+ ,
+ /**
+ * Hard close request with no response
+ */
+ mhd_ACTION_ABORT
+};
+
+/**
+ * Check whether provided mhd_ActionType value is valid
+ */
+#define mhd_ACTION_IS_VALID(act) \
+ ((mhd_ACTION_RESPONSE <= (act)) && (mhd_ACTION_ABORT >= (act)))
+
+
+struct MHD_Response; /* forward declaration */
+struct MHD_Request; /* 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
+ ,
+ /**
+ * Continue processing the upload
+ */
+ mhd_UPLOAD_ACTION_CONTINUE
+ ,
+ /**
+ * 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_ABORT
+};
+
+/**
+ * Check whether provided mhd_UploadActionType value is valid
+ */
+#define mhd_UPLOAD_ACTION_IS_VALID(act) \
+ ((mhd_UPLOAD_ACTION_CONTINUE <= (act)) && \
+ (mhd_UPLOAD_ACTION_ABORT >= (act)))
+
+
+/**
+ * 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 */
diff --git a/src/mhd2/mhd_assert.h b/src/mhd2/mhd_assert.h
@@ -0,0 +1,88 @@
+/*
+ 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 */
+
+#ifdef _DEBUG
+# ifdef MHD_UNREACHABLE_
+# undef MHD_UNREACHABLE_
+# endif
+# define MHD_UNREACHABLE_ ((void) 0)
+#endif
+
+#endif /* ! MHD_ASSERT_H */
diff --git a/src/mhd2/mhd_atomic_counter.c b/src/mhd2/mhd_atomic_counter.c
@@ -0,0 +1,78 @@
+/*
+ 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_atomic_counter.c
+ * @brief The definition of the atomic counter functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_atomic_counter.h"
+
+#if defined(mhd_ATOMIC_BY_LOCKS)
+
+#include "mhd_assert.h"
+
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt)
+{
+ mhd_ATOMIC_COUNTER_TYPE ret;
+
+ mhd_mutex_lock_chk (&(pcnt->lock));
+ ret = ++(pcnt->count);
+ mhd_mutex_unlock_chk (&(pcnt->lock));
+
+ mhd_assert (0 != ret); /* check for overflow */
+
+ return ret;
+}
+
+
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt)
+{
+ mhd_ATOMIC_COUNTER_TYPE ret;
+
+ mhd_mutex_lock_chk (&(pcnt->lock));
+ ret = --(pcnt->count);
+ mhd_mutex_unlock_chk (&(pcnt->lock));
+
+ mhd_assert (mhd_ATOMIC_COUNTER_MAX != ret); /* check for underflow */
+
+ return ret;
+}
+
+
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt)
+{
+ mhd_ATOMIC_COUNTER_TYPE ret;
+
+ mhd_mutex_lock_chk (&(pcnt->lock));
+ ret = pcnt->count;
+ mhd_mutex_unlock_chk (&(pcnt->lock));
+
+ return ret;
+}
+
+
+#endif /* mhd_ATOMIC_BY_LOCKS */
diff --git a/src/mhd2/mhd_atomic_counter.h b/src/mhd2/mhd_atomic_counter.h
@@ -0,0 +1,205 @@
+/*
+ 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_atomic_counter.h
+ * @brief The definition of the atomic counter type and related functions
+ * declarations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_ATOMIC_COUNTER_H
+#define MHD_ATOMIC_COUNTER_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h" /* for size_t */
+
+/* Use 'size_t' to make sure it would never overflow when used for
+ * MHD needs. */
+
+/**
+ * The type used to contain the counter value.
+ * Always unsigned.
+ */
+#define mhd_ATOMIC_COUNTER_TYPE size_t
+/**
+ * The maximum counter value
+ */
+#define mhd_ATOMIC_COUNTER_MAX \
+ ((mhd_ATOMIC_COUNTER_TYPE) (~((mhd_ATOMIC_COUNTER_TYPE) 0)))
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * Atomic operations are based on locks
+ */
+# define mhd_ATOMIC_BY_LOCKS 1
+
+#else /* ! MHD_USE_THREADS */
+
+/**
+ * Atomic because single thread environment is used
+ */
+# define mhd_ATOMIC_SINGLE_THREAD 1
+#endif /* ! MHD_USE_THREADS */
+
+
+#if defined(mhd_ATOMIC_BY_LOCKS)
+# include "mhd_locks.h"
+# include "sys_bool_type.h"
+
+/**
+ * The atomic counter
+ */
+struct mhd_AtomicCounter
+{
+ /**
+ * Counter value.
+ * Must be read or written only with @a lock held.
+ */
+ volatile mhd_ATOMIC_COUNTER_TYPE count;
+ /**
+ * The mutex.
+ */
+ mhd_mutex lock;
+};
+
+#elif defined(mhd_ATOMIC_SINGLE_THREAD)
+
+/**
+ * The atomic counter
+ */
+struct mhd_AtomicCounter
+{
+ /**
+ * Counter value.
+ */
+ volatile mhd_ATOMIC_COUNTER_TYPE count;
+};
+
+#endif /* mhd_ATOMIC_SINGLE_THREAD */
+
+
+#if defined(mhd_ATOMIC_BY_LOCKS)
+
+/**
+ * Initialise the counter to specified value.
+ * @param pcnt the pointer to the counter to initialise
+ * @param initial_value the initial value for the counter
+ * @return 'true' if succeed, "false' if failed
+ * @warning Must not be called for the counters that has been initialised
+ * already.
+ */
+# define mhd_atomic_counter_init(pcnt,initial_value) \
+ ((pcnt)->count = (initial_value), \
+ mhd_mutex_init_short (&((pcnt)->lock)))
+
+/**
+ * Deinitialise the counter.
+ * @param pcnt the pointer to the counter to deinitialise
+ * @warning Must be called only for the counters that has been initialised.
+ */
+# define mhd_atomic_counter_deinit(pcnt) \
+ mhd_mutex_destroy_chk (&((pcnt)->lock))
+
+/**
+ * Atomically increment the value of the counter
+ * @param pcnt the pointer to the counter to increment
+ */
+# define mhd_atomic_counter_inc(pcnt) do { \
+ mhd_mutex_lock_chk (&((pcnt)->lock)); \
+ ++(pcnt->count); \
+ mhd_mutex_unlock_chk (&((pcnt)->lock)); } while (0)
+
+/**
+ * Atomically increment the value of the counter and return the result
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt);
+
+/**
+ * Atomically decrement the value of the counter and return the result
+ * @param pcnt the pointer to the counter to decrement
+ * @return the final/resulting counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt);
+
+/**
+ * Atomically get the value of the counter
+ * @param pcnt the pointer to the counter to get
+ * @return the counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt);
+
+#elif defined(mhd_ATOMIC_SINGLE_THREAD)
+
+/**
+ * Initialise the counter to specified value.
+ * @param pcnt the pointer to the counter to initialise
+ * @param initial_value the initial value for the counter
+ * @return 'true' if succeed, "false' if failed
+ * @warning Must not be called for the counters that has been initialised
+ * already.
+ */
+# define mhd_atomic_counter_init(pcnt,initial_value) \
+ ((pcnt)->count = (initial_value), (! 0))
+
+/**
+ * Deinitialise the counter.
+ * @param pcnt the pointer to the counter to deinitialise
+ * @warning Must be called only for the counters that has been initialised.
+ */
+# define mhd_atomic_counter_deinit(pcnt) ((void) 0)
+
+/**
+ * Atomically increment the value of the counter
+ * @param pcnt the pointer to the counter to increment
+ */
+# define mhd_atomic_counter_inc(pcnt) do { ++(pcnt->count); } while (0)
+
+/**
+ * Atomically increment the value of the counter and return the result
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+# define mhd_atomic_counter_inc_get(pcnt) (++((pcnt)->count))
+
+/**
+ * Atomically decrement the value of the counter and return the result
+ * @param pcnt the pointer to the counter to decrement
+ * @return the final/resulting counter value
+ */
+# define mhd_atomic_counter_dec_get(pcnt) (--((pcnt)->count))
+
+/**
+ * Atomically get the value of the counter
+ * @param pcnt the pointer to the counter to get
+ * @return the counter value
+ */
+# define mhd_atomic_counter_get(pcnt) ((pcnt)->count)
+
+#endif /* mhd_ATOMIC_SINGLE_THREAD */
+
+#endif /* ! MHD_ATOMIC_COUNTER_H */
diff --git a/src/mhd2/mhd_buffer.h b/src/mhd2/mhd_buffer.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/mhd_buffer.h
+ * @brief The definition of the MHD_Buffer type
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_BUFFER_H
+#define MHD_BUFFER_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+
+/**
+ * The buffer with size
+ */
+struct mhd_Buffer
+{
+ /**
+ * The size of the allocated @a buf buffer
+ */
+ size_t size;
+
+ /**
+ * The pointer to the allocation
+ */
+ char *buf;
+};
+
+#endif /* ! MHD_BUFFER_H */
diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h
@@ -0,0 +1,627 @@
+/*
+ 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_socket_type.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"
+
+/**
+ * Minimum reasonable size by which MHD tries to increment read/write buffers.
+ * We usually begin with half the available pool space for the
+ * IO-buffer, but if absolutely needed we additively grow by the
+ * number of bytes given here (up to -- theoretically -- the full pool
+ * space).
+ *
+ * Currently set to reasonable maximum MSS size.
+ */
+#define mhd_BUF_INC_SIZE 1500
+
+/**
+ * Message to transmit when http 1.1 request is received
+ */
+#define mdh_HTTP_1_1_100_CONTINUE_REPLY "HTTP/1.1 100 Continue\r\n\r\n"
+
+
+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
+};
+
+
+/**
+ * The reason for the socket closure
+ */
+enum mhd_SocketClosureReason
+{
+ /**
+ * The socket is not closed / closing.
+ */
+ mhd_SCOKET_CLOSURE_REASON_NO_CLOSURE = 0
+ ,
+ /**
+ * Socket has to be closed because HTTP protocol successfully finished data
+ * exchange.
+ */
+ mhd_SCOKET_CLOSURE_REASON_PROTOCOL_SUCCESS
+ ,
+ /**
+ * Socket has to be closed because remote side violated some HTTP
+ * specification requirements or request processed with an error.
+ * The HTTP error response should be sent.
+ */
+ mhd_SCOKET_CLOSURE_REASON_PROTOCOL_FAILURE_SOFT
+ ,
+ /**
+ * Timeout expired
+ */
+ mhd_SCOKET_CLOSURE_REASON_TIMEOUT
+ ,
+ /**
+ * Socket has to be closed because received data cannot be interpreted as
+ * valid HTTP data.
+ */
+ mhd_SCOKET_CLOSURE_REASON_PROTOCOL_FAILURE_HARD
+ ,
+ /**
+ * Unrecoverable TLS error
+ */
+ mhd_SCOKET_CLOSURE_REASON_TLS_ERROR
+ ,
+ /**
+ * The remote side closed connection in abortive way
+ */
+ mhd_SCOKET_CLOSURE_REASON_REMOTE_HARD_DISCONN
+ ,
+ /**
+ * The connection has been broken for some reason
+ */
+ mhd_SCOKET_CLOSURE_REASON_CONN_BROKEN
+};
+
+/**
+ * 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,
+
+ /**
+ * 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,
+
+ /**
+ * Receiving request headers. Wait for the rest of the headers.
+ */
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING,
+
+ /**
+ * We got the request headers. Process them.
+ */
+ MHD_CONNECTION_HEADERS_RECEIVED,
+
+ /**
+ * We have processed the request headers. Call application callback.
+ */
+ MHD_CONNECTION_HEADERS_PROCESSED,
+
+ /**
+ * We have processed the headers and need to send 100 CONTINUE.
+ */
+ MHD_CONNECTION_CONTINUE_SENDING,
+
+ /**
+ * We have sent 100 CONTINUE (or do not need to). Read the message body.
+ */
+ MHD_CONNECTION_BODY_RECEIVING,
+
+ /**
+ * We got the request body.
+ *
+ * A milestone state. No received data is processed in this state.
+ */
+ MHD_CONNECTION_BODY_RECEIVED,
+
+ /**
+ * We are reading the request footers.
+ */
+ MHD_CONNECTION_FOOTERS_RECEIVING,
+
+ /**
+ * We received the entire footer.
+ *
+ * A milestone state. No data is receiving in this state.
+ */
+ MHD_CONNECTION_FOOTERS_RECEIVED,
+
+ /**
+ * We received the entire request.
+ *
+ * A milestone state. No data is receiving in this state.
+ */
+ MHD_CONNECTION_FULL_REQ_RECEIVED,
+
+ /**
+ * Finished receiving request data: either complete request received or
+ * MHD is going to send reply early, without getting full request.
+ */
+ MHD_CONNECTION_REQ_RECV_FINISHED,
+
+ /**
+ * 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,
+
+ /**
+ * We have prepared the response headers in the write buffer.
+ * Send the response headers.
+ */
+ MHD_CONNECTION_HEADERS_SENDING,
+
+ /**
+ * We have sent the response headers. Get ready to send the body.
+ */
+ MHD_CONNECTION_HEADERS_SENT,
+
+ /**
+ * We are waiting for the client to provide more
+ * data of a non-chunked body.
+ */
+ MHD_CONNECTION_UNCHUNKED_BODY_UNREADY,
+
+ /**
+ * We are ready to send a part of a non-chunked body. Send it.
+ */
+ MHD_CONNECTION_UNCHUNKED_BODY_READY,
+
+ /**
+ * We are waiting for the client to provide a chunk of the body.
+ */
+ MHD_CONNECTION_CHUNKED_BODY_UNREADY,
+
+ /**
+ * We are ready to send a chunk.
+ */
+ MHD_CONNECTION_CHUNKED_BODY_READY,
+
+ /**
+ * We have sent the chunked response body. Prepare the footers.
+ */
+ MHD_CONNECTION_CHUNKED_BODY_SENT,
+
+ /**
+ * We have prepared the response footer. Send it.
+ */
+ MHD_CONNECTION_FOOTERS_SENDING,
+
+ /**
+ * We have sent the entire reply.
+ * Shutdown connection or restart processing to get a new request.
+ */
+ MHD_CONNECTION_FULL_REPLY_SENT,
+
+ /**
+ * This connection is to be closed.
+ */
+ MHD_CONNECTION_CLOSED
+
+};
+
+struct mhd_ConnDebugData
+{
+ bool pre_closed;
+ bool pre_cleaned;
+};
+
+/**
+ * Ability to use same connection for next request
+ */
+enum MHD_FIXED_ENUM_ mhd_ConnReuse
+{
+ /**
+ * Connection must be closed after sending response.
+ */
+ mhd_CONN_MUST_CLOSE = -1
+ ,
+ /**
+ * KeepAlive state is possible
+ */
+ mhd_CONN_KEEPALIVE_POSSIBLE = 0
+ ,
+ /**
+ * Connection will be upgraded
+ */
+ mhd_CONN_MUST_UPGRADE = 1
+};
+
+/**
+ * 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 the remote has been read__.
+ */
+ bool sk_rmt_shut_wr;
+
+ /**
+ * 'true' if connection is in 'process ready' list,
+ * 'false' otherwise
+ */
+ bool in_proc_ready;
+
+ /**
+ * 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);
+
+ /**
+ * True if connection is suspended
+ */
+ bool suspended;
+
+ /**
+ * True if connection is resuming
+ */
+ bool resuming;
+
+ /**
+ * 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 mhd_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 "KeepAlive" to "Must close",
+ * but no functions reset value "Must Close" to any other value.
+ */
+ enum mhd_ConnReuse conn_reuse;
+
+ /**
+ * 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 @a socket_fd is non-blocking, false otherwise.
+ */
+ bool sk_nonblck;
+
+ /**
+ * true if connection socket has set SIGPIPE suppression
+ */
+ bool sk_spipe_suppress;
+
+// #ifndef MHD_WINSOCK_SOCKETS // TODO: conditionally use in the code
+ /**
+ * Tracks TCP_CORK / TCP_NOPUSH of the connection socket.
+ */
+ enum mhd_Tristate sk_corked;
+// #endif
+
+ /**
+ * Tracks TCP_NODELAY state of the connection socket.
+ */
+ enum mhd_Tristate sk_nodelay;
+
+ /**
+ * 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;
+
+#ifndef NDEBUG
+ /**
+ * Debugging data
+ */
+ struct mhd_ConnDebugData dbg;
+#endif
+};
+
+
+#endif /* ! MHD_CONNECTION_H */
diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h
@@ -0,0 +1,969 @@
+/*
+ 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"
+# include "mhd_locks.h"
+#endif
+
+#include "sys_select.h"
+#include "sys_poll.h"
+#ifdef MHD_USE_EPOLL
+# include <sys/epoll.h>
+#endif
+
+#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);
+
+/**
+ * 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;
+
+ /**
+ * Indicate that daemon already has some data to be processed on the next
+ * cycle
+ */
+ bool zero_wait;
+
+ /**
+ * 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_least16_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 MHD_USE_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;
+
+ /**
+ * 'True' if resuming of any connection has been requested.
+ */
+ volatile bool resume_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 connections sorted by last activity
+ */
+ mhd_DLNKDL_LIST (MHD_Connection,def_timeout);
+
+ /**
+ * The list of connections with custom timeouts
+ */
+ mhd_DLNKDL_LIST (MHD_Connection,cust_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;
+};
+
+/**
+ * Early URI callback
+ */
+struct mhd_DaemonRequestUriCB
+{
+ /**
+ * The callback
+ */
+ MHD_EarlyUriLogCallback cb;
+ /**
+ * The callback closure
+ */
+ void *cls;
+};
+
+/**
+ * Shared large buffer data
+ */
+struct mhd_DeamonLargeBuffer
+{
+ /**
+ * The amount of memory left allowed to be allocated for the large buffer
+ */
+ size_t space_left;
+
+#ifdef MHD_USE_THREADS
+ /**
+ * The mutex to change or check the @a space_left value
+ */
+ mhd_mutex lock;
+#endif
+};
+
+/**
+ * Settings for requests processing
+ */
+struct mhd_DaemonRequestProcessingSettings
+{
+ /**
+ * Request callback.
+ * The main request processing callback.
+ */
+ MHD_RequestCallback cb;
+
+ /**
+ * The closure for @a req_cb
+ */
+ void *cb_cls;
+
+ /**
+ * Protocol strictness enforced by MHD on clients.
+ */
+ enum MHD_ProtocolStrictLevel strictnees;
+
+ /**
+ * Early URI callback
+ */
+ struct mhd_DaemonRequestUriCB uri_cb; // TODO: set from settings
+
+ /**
+ * Shared large buffer data
+ */
+ struct mhd_DeamonLargeBuffer large_buf; // TODO: set from settings
+
+ /**
+ * Suppress "Date:" header in responses
+ */
+ bool suppress_date; // TODO: set from settings
+};
+
+
+#ifndef NDEBUG
+/**
+ * Various debugging data
+ */
+struct mhd_daemon_debug
+{
+ bool net_inited;
+ bool net_deinited;
+ bool events_allocated;
+ unsigned int num_events_elements;
+ 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 */
+
+ /**
+ * Settings for requests processing
+ */
+ struct mhd_DaemonRequestProcessingSettings req_cfg;
+
+ /* 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
+
+#ifdef MHD_USE_THREADS
+# define mhd_D_HAS_THR_PER_CONN(d) \
+ (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION == \
+ ((d)->wmode_int))
+#else
+# define mhd_D_HAS_THR_PER_CONN(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)))
diff --git a/src/mhd2/mhd_dcc_action.h b/src/mhd2/mhd_dcc_action.h
@@ -0,0 +1,173 @@
+/*
+ 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_dcc_action.h
+ * @brief The definition of the MHD_Action and MHD_UploadAction structures
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DCC_ACTION_H
+#define MHD_DCC_ACTION_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * The context used for Dynamic Content Creator callback
+ */
+struct MHD_DynamicContentCreatorContext
+{
+ struct MHD_Connection *connection;
+};
+
+/**
+ * The type of the dynamic content creator action requested by application
+ */
+enum mhd_DccActionType
+{
+ /**
+ * Action has not been set yet.
+ */
+ mhd_DCC_ACTION_NO_ACTION = 0
+ ,
+ /**
+ * Send new portion of data, provided by application
+ */
+ mhd_DCC_ACTION_CONTINUE
+ ,
+ /**
+ * Signal the end of the data stream.
+ */
+ mhd_DCC_ACTION_FINISH
+ ,
+ /**
+ * Suspend requests (connection)
+ */
+ mhd_DCC_ACTION_SUSPEND
+ ,
+ /**
+ * Hard close request with no or partial response
+ */
+ mhd_DCC_ACTION_ABORT
+};
+
+/**
+ * Check whether provided mhd_ActionType value is valid
+ */
+#define mhd_DCC_ACTION_IS_VALID(act) \
+ ((mhd_DCC_ACTION_CONTINUE <= (act)) && (mhd_DCC_ACTION_ABORT >= (act)))
+
+
+#ifndef MHD_FREECALLBACK_DEFINED
+
+/**
+ * This method is called by libmicrohttpd when response with dynamic content
+ * is being destroyed. It should be used to free resources associated
+ * with the dynamic content.
+ *
+ * @param[in] free_cls closure
+ * @ingroup response
+ */
+typedef void
+(*MHD_FreeCallback) (void *free_cls);
+
+#define MHD_FREECALLBACK_DEFINED 1
+#endif /* ! MHD_FREECALLBACK_DEFINED */
+#ifndef MHD_DYNCONTENTZCIOVEC_DEFINED
+
+
+/**
+ * Structure for iov type of the response.
+ * Used for zero-copy response content data.
+ */
+struct MHD_DynContentZCIoVec
+{
+ /**
+ * The number of elements in @a iov
+ */
+ unsigned int iov_count;
+ /**
+ * The pointer to the array with @a iov_count elements.
+ */
+ const struct MHD_IoVec *iov;
+ /**
+ * The callback to free resources.
+ * It is called once the full array of iov elements is sent.
+ * No callback is called if NULL.
+ */
+ MHD_FreeCallback iov_fcb;
+ /**
+ * The parameter for @a iov_fcb
+ */
+ void *iov_fcb_cls;
+};
+
+#define MHD_DYNCONTENTZCIOVEC_DEFINED 1
+#endif /* ! MHD_DYNCONTENTZCIOVEC_DEFINED */
+
+/**
+ * The data for DCC "continue" action
+ */
+struct mhd_DccActionContinueData
+{
+ /**
+ * The size of the content data in the buffer
+ */
+ size_t buf_data_size;
+ /**
+ * Zero-copy content data data
+ */
+ const struct MHD_DynContentZCIoVec *iov_data;
+};
+
+
+/**
+ * The data for the DCC application action
+ */
+union mhd_DccActionData
+{
+ /**
+ * The data for the action #mhd_DCC_ACTION_CONTINUE
+ */
+ struct mhd_DccActionContinueData cntnue;
+};
+
+
+/**
+ * The action type returned by Dynamic Content Creator callback
+ */
+struct MHD_DynamicContentCreatorAction
+{
+ /**
+ * The action
+ */
+ enum mhd_DccActionType act;
+
+ /**
+ * The data for the @a act action
+ */
+ union mhd_DccActionData data;
+};
+
+#endif /* ! MHD_DCC_ACTION_H */
diff --git a/src/mhd2/mhd_dlinked_list.h b/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 */
diff --git a/src/mhd2/mhd_iovec.h b/src/mhd2/mhd_iovec.h
@@ -0,0 +1,114 @@
+/*
+ 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"
+#include "sys_base_types.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
+
+#include "mhd_limits.h"
+
+#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_elmn_size;
+#define mhd_IOV_RET_MAX_SIZE LONG_MAX
+typedef long mhd_iov_ret_type;
+#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_elmn_size;
+#define mhd_IOV_RET_MAX_SIZE SSIZE_MAX
+typedef ssize_t mhd_iov_ret_type;
+#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_elmn_size;
+#define mhd_IOV_RET_MAX_SIZE SSIZE_MAX
+typedef ssize_t mhd_iov_ret_type;
+#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 */
diff --git a/src/mhd2/mhd_itc.c b/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_ */
diff --git a/src/mhd2/mhd_itc.h b/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 "sys_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 "sys_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_sys_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 */
diff --git a/src/mhd2/mhd_itc_types.h b/src/mhd2/mhd_itc_types.h
@@ -0,0 +1,94 @@
+/*
+ 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_types.h
+ * @brief Types for platform-independent inter-thread communication
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ *
+ * Provides basic types for inter-thread communication.
+ * Designed to be included by other headers.
+ */
+#ifndef MHD_ITC_TYPES_H
+#define MHD_ITC_TYPES_H 1
+#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 native W32 platform
+#endif /* _WIN32 && !__CYGWIN__ && !MHD_ITC_SOCKETPAIR_ */
+
+#if defined(MHD_ITC_EVENTFD_)
+/* **************** Optimised ITC implementation by eventfd ********** */
+
+/**
+ * Data type for a MHD ITC.
+ */
+struct mhd_itc
+{
+ int fd;
+};
+
+/**
+ * Static initialiser for struct mhd_itc
+ */
+# define mhd_ITC_STATIC_INIT_INVALID { -1 }
+
+
+#elif defined(MHD_ITC_PIPE_)
+/* **************** Standard UNIX ITC implementation by pipe ********** */
+
+/**
+ * Data type for a MHD ITC.
+ */
+struct mhd_itc
+{
+ int fd[2];
+};
+
+/**
+ * Static initialiser for struct mhd_itc
+ */
+# define mhd_ITC_STATIC_INIT_INVALID { { -1, -1 } }
+
+
+#elif defined(MHD_ITC_SOCKETPAIR_)
+/* **************** ITC implementation by socket pair ********** */
+
+# include "mhd_socket_type.h"
+
+/**
+ * Data type for a MHD ITC.
+ */
+struct mhd_itc
+{
+ MHD_Socket sk[2];
+};
+
+/**
+ * Static initialiser for struct mhd_itc
+ */
+# define mhd_ITC_STATIC_INIT_INVALID \
+ { { MHD_INVALID_SOCKET, MHD_INVALID_SOCKET } }
+
+#endif /* MHD_ITC_SOCKETPAIR_ */
+
+#endif /* ! MHD_ITC_TYPES_H */
diff --git a/src/mhd2/mhd_lib_init.c b/src/mhd2/mhd_lib_init.c
@@ -0,0 +1,89 @@
+/*
+ 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"
+#include "mhd_send.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 */
+ MHD_monotonic_msec_counter_init();
+ mhd_send_init_static_vars();
+}
+
+
+void
+mhd_lib_global_deinit (void)
+{
+ MHD_monotonic_msec_counter_finish();
+#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 */
diff --git a/src/mhd2/mhd_lib_init.h b/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 */
diff --git a/src/mhd2/mhd_lib_init_impl.h b/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 */
diff --git a/src/mhd2/mhd_limits.h b/src/mhd2/mhd_limits.h
@@ -0,0 +1,177 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2015-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_limits.h
+ * @brief limits values definitions
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This file provides maximum types values as macros. Macros may not work
+ * in preprocessor expressions, while macros always work in compiler
+ * expressions.
+ * This file does not include <stdint.h> and other type-definitions files.
+ * For best portability, make sure that this file is included after required
+ * type-definitions files.
+ */
+
+#ifndef MHD_LIMITS_H
+#define MHD_LIMITS_H
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#define mhd_UNSIGNED_TYPE_MAX(type) ((type)(~((type) 0)))
+
+/* Assume 8 bits per byte, no padding bits. */
+#define mhd_SIGNED_TYPE_MAX(type) \
+ ( (type) ((( ((type) 1) << (sizeof(type) * 8 - 2)) - 1) * 2 + 1) )
+
+/* The maximum value for signed type, based on knowledge of unsigned counterpart
+ type */
+#define mhd_SIGNED_TYPE_MAX2(type,utype) ((type)(((utype)(~((utype)0))) >> 1))
+
+#define mhd_IS_TYPE_SIGNED(type) (((type) 0) > ((type) - 1))
+
+#if defined(__GNUC__) || defined(__clang__)
+# define mhd_USE_PREDEF_LIMITS
+#endif
+
+#ifndef INT_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT_MAX__)
+# define INT_MAX __INT_MAX__
+# else /* ! __INT_MAX__ */
+# define INT_MAX mhd_SIGNED_TYPE_MAX2 (int, unsigned int)
+# endif /* ! __INT_MAX__ */
+#endif /* ! INT_MAX */
+
+#ifndef UINT_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT_MAX__)
+# define UINT_MAX __UINT_MAX__
+# else /* ! __UINT_MAX__ */
+# define UINT_MAX mhd_UNSIGNED_TYPE_MAX (unsigned int)
+# endif /* ! __UINT_MAX__ */
+#endif /* !UINT_MAX */
+
+#ifndef LONG_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__LONG_MAX__)
+# define LONG_MAX __LONG_MAX__
+# else /* ! __LONG_MAX__ */
+# define LONG_MAX mhd_SIGNED_TYPE_MAX2 (long, unsigned long)
+# endif /* ! __LONG_MAX__ */
+#endif /* !LONG_MAX */
+
+#ifndef ULONG_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__ULONG_MAX__)
+# define ULONG_MAX ULONG_MAX
+# else /* ! __ULONG_MAX__ */
+# define ULONG_MAX mhd_UNSIGNED_TYPE_MAX (unsigned long)
+# endif /* ! __ULONG_MAX__ */
+#endif /* !ULONG_MAX */
+
+#ifndef ULLONG_MAX
+# ifdef ULONGLONG_MAX
+# define ULLONG_MAX ULONGLONG_MAX
+# else /* ! ULONGLONG_MAX */
+# define ULLONG_MAX mhd_UNSIGNED_TYPE_MAX (unsigned long long)
+# endif /* ! ULONGLONG_MAX */
+#endif /* !ULLONG_MAX */
+
+#ifndef INT32_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT32_MAX__)
+# define INT32_MAX __INT32_MAX__
+# else /* ! __INT32_MAX__ */
+# define INT32_MAX ((int32_t) 0x7FFFFFFF)
+# endif /* ! __INT32_MAX__ */
+#endif /* !INT32_MAX */
+
+#ifndef UINT32_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT32_MAX__)
+# define UINT32_MAX __UINT32_MAX__
+# else /* ! __UINT32_MAX__ */
+# define UINT32_MAX ((uint32_t) 0xFFFFFFFFU)
+# endif /* ! __UINT32_MAX__ */
+#endif /* !UINT32_MAX */
+
+#ifndef INT64_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT64_MAX__)
+# define INT64_MAX __INT64_MAX__
+# else /* ! __INT64_MAX__ */
+# define INT64_MAX ((int64_t) 0x7FFFFFFFFFFFFFFF)
+# endif /* ! __UINT64_MAX__ */
+#endif /* !INT64_MAX */
+
+#ifndef UINT64_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT64_MAX__)
+# define UINT64_MAX __UINT64_MAX__
+# else /* ! __UINT64_MAX__ */
+# define UINT64_MAX ((uint64_t) 0xFFFFFFFFFFFFFFFFU)
+# endif /* ! __UINT64_MAX__ */
+#endif /* !UINT64_MAX */
+
+#ifndef SIZE_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__SIZE_MAX__)
+# define SIZE_MAX __SIZE_MAX__
+# else /* ! __SIZE_MAX__ */
+# define SIZE_MAX mhd_UNSIGNED_TYPE_MAX (size_t)
+# endif /* ! __SIZE_MAX__ */
+#endif /* !SIZE_MAX */
+
+#ifndef SSIZE_MAX
+# if defined(mhd_USE_PREDEF_LIMITS) && defined(__SSIZE_MAX__)
+# define SSIZE_MAX __SSIZE_MAX__
+# else
+# define SSIZE_MAX mhd_SIGNED_TYPE_MAX2 (ssize_t, size_t)
+# endif
+#endif /* ! SSIZE_MAX */
+
+#ifndef OFF_T_MAX
+# ifdef OFF_MAX
+# define OFF_T_MAX OFF_MAX
+# elif defined(OFFT_MAX)
+# define OFF_T_MAX OFFT_MAX
+# elif defined(__APPLE__) && defined(__MACH__)
+# define OFF_T_MAX INT64_MAX
+# else
+# define OFF_T_MAX mhd_SIGNED_TYPE_MAX (off_t)
+# endif
+#endif /* !OFF_T_MAX */
+
+#if defined(_LARGEFILE64_SOURCE) && ! defined(OFF64_T_MAX)
+# define OFF64_T_MAX mhd_SIGNED_TYPE_MAX (off64_t)
+#endif /* _LARGEFILE64_SOURCE && !OFF64_T_MAX */
+
+#ifndef TIME_T_MAX
+# define TIME_T_MAX ((time_t) \
+ (mhd_IS_TYPE_SIGNED (time_t) ? \
+ mhd_SIGNED_TYPE_MAX (time_t) : \
+ mhd_UNSIGNED_TYPE_MAX (time_t)))
+#endif /* !TIME_T_MAX */
+
+#ifndef TIMEVAL_TV_SEC_MAX
+# ifndef _WIN32
+# define TIMEVAL_TV_SEC_MAX TIME_T_MAX
+# else /* _WIN32 */
+# define TIMEVAL_TV_SEC_MAX LONG_MAX
+# endif /* _WIN32 */
+#endif /* !TIMEVAL_TV_SEC_MAX */
+
+#endif /* MHD_LIMITS_H */
diff --git a/src/mhd2/mhd_locks.h b/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 */
diff --git a/src/mhd2/mhd_mempool.c b/src/mhd2/mhd_mempool.c
@@ -0,0 +1,809 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007--2024 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2014--2024 Evgeny Grin (Karlson2k)
+
+ 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/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 "mhd_mempool.h"
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#include <string.h>
+#include "mhd_assert.h"
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+#ifdef _WIN32
+# 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 */
+#endif /* HAVE_SYSCONF */
+
+#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_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 */
+
+#include "mhd_limits.h"
+
+/**
+ * Fallback value of page size
+ */
+#define _MHD_FALLBACK_PAGE_SIZE (4096)
+
+#if defined(MHD_USE_PAGESIZE_MACRO)
+#define MHD_DEF_PAGE_SIZE_ PAGESIZE
+#elif defined(MHD_USE_PAGE_SIZE_MACRO)
+#define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
+#else /* ! PAGESIZE */
+#define MHD_DEF_PAGE_SIZE_ _MHD_FALLBACK_PAGE_SIZE
+#endif /* ! PAGESIZE */
+
+
+#ifdef MHD_ASAN_POISON_ACTIVE
+#include <sanitizer/asan_interface.h>
+#endif /* MHD_ASAN_POISON_ACTIVE */
+
+/* define MAP_ANONYMOUS for Mac OS X */
+#if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+#if defined(_WIN32)
+#define MAP_FAILED NULL
+#elif ! defined(MAP_FAILED)
+#define MAP_FAILED ((void*) -1)
+#endif
+
+/**
+ * Align to 2x word size (as GNU libc does).
+ */
+#define ALIGN_SIZE (2 * sizeof(void*))
+
+/**
+ * Round up 'n' to a multiple of ALIGN_SIZE.
+ */
+#define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \
+ / (ALIGN_SIZE) *(ALIGN_SIZE))
+
+
+#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
+/**
+ * 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)))
+/**
+ * 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))))
+#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 _MHD_POISON_MEMORY(pointer, size) \
+ ASAN_POISON_MEMORY_REGION ((pointer), (size))
+#define _MHD_UNPOISON_MEMORY(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))))
+/**
+ * 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)))))
+#elif defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \
+ defined(FUNC_ATTR_PTRSUBTRACT_WORKS)
+#ifdef _DEBUG
+/**
+ * Boolean 'true' if the first pointer is less or equal the second pointer
+ */
+__attribute__((no_sanitize ("pointer-compare"))) static bool
+mp_ptr_le_ (const void *p1, const void *p2)
+{
+ return (((const uint8_t *) p1) <= ((const uint8_t *) p2));
+}
+
+
+#endif /* _DEBUG */
+
+
+/**
+ * The difference in bytes between positions of the first and
+ * the second pointers
+ */
+__attribute__((no_sanitize ("pointer-subtract"))) static size_t
+mp_ptr_diff_ (const void *p1, const void *p2)
+{
+ return (size_t) (((const uint8_t *) p1) - ((const uint8_t *) p2));
+}
+
+
+#elif defined(FUNC_ATTR_NOSANITIZE_WORKS)
+#ifdef _DEBUG
+/**
+ * Boolean 'true' if the first pointer is less or equal the second pointer
+ */
+__attribute__((no_sanitize ("address"))) static bool
+mp_ptr_le_ (const void *p1, const void *p2)
+{
+ return (((const uint8_t *) p1) <= ((const uint8_t *) p2));
+}
+
+
+#endif /* _DEBUG */
+
+/**
+ * The difference in bytes between positions of the first and
+ * the second pointers
+ */
+__attribute__((no_sanitize ("address"))) static size_t
+mp_ptr_diff_ (const void *p1, const void *p2)
+{
+ return (size_t) (((const uint8_t *) p1) - ((const uint8_t *) p2));
+}
+
+
+#else /* ! FUNC_ATTR_NOSANITIZE_WORKS */
+#error User-poisoning cannot be used
+#endif /* ! FUNC_ATTR_NOSANITIZE_WORKS */
+#endif /* MHD_ASAN_POISON_ACTIVE */
+
+/**
+ * Size of memory page
+ */
+static size_t MHD_sys_page_size_ = (size_t)
+#if defined(MHD_USE_PAGESIZE_MACRO_STATIC)
+ PAGESIZE;
+#elif defined(MHD_USE_PAGE_SIZE_MACRO_STATIC)
+ PAGE_SIZE;
+#else /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
+ _MHD_FALLBACK_PAGE_SIZE; /* Default fallback value */
+#endif /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
+
+/**
+ * Initialise values for memory pools
+ */
+void
+mhd_init_mem_pools (void)
+{
+#ifdef MHD_SC_PAGESIZE
+ long result;
+ result = sysconf (MHD_SC_PAGESIZE);
+ if (-1 != result)
+ MHD_sys_page_size_ = (size_t) result;
+ else
+ MHD_sys_page_size_ = (size_t) MHD_DEF_PAGE_SIZE_;
+#elif defined(_WIN32)
+ SYSTEM_INFO si;
+ GetSystemInfo (&si);
+ MHD_sys_page_size_ = (size_t) si.dwPageSize;
+#else
+ MHD_sys_page_size_ = (size_t) MHD_DEF_PAGE_SIZE_;
+#endif /* _WIN32 */
+ mhd_assert (0 == (MHD_sys_page_size_ % ALIGN_SIZE));
+}
+
+
+/**
+ * Handle for a memory pool. Pools are not reentrant and must not be
+ * used by multiple threads.
+ */
+struct mhd_MemoryPool
+{
+
+ /**
+ * Pointer to the pool's memory
+ */
+ uint8_t *memory;
+
+ /**
+ * Size of the pool.
+ */
+ size_t size;
+
+ /**
+ * Offset of the first unallocated byte.
+ */
+ size_t pos;
+
+ /**
+ * Offset of the byte after the last unallocated byte.
+ */
+ size_t end;
+
+ /**
+ * 'false' if pool was malloc'ed, 'true' if mmapped (VirtualAlloc'ed for W32).
+ */
+ bool is_mmap;
+
+ // TODO: implement *optional* zeroing on reset on reallocs
+};
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+MHD_INTERNAL struct mhd_MemoryPool *
+mdh_pool_create (size_t max)
+{
+ struct mhd_MemoryPool *pool;
+ size_t alloc_size;
+
+ mhd_assert (max > 0);
+ alloc_size = 0;
+ pool = malloc (sizeof (struct mhd_MemoryPool));
+ if (NULL == pool)
+ return NULL;
+#if defined(MAP_ANONYMOUS) || defined(_WIN32)
+ if ( (max <= 32 * 1024) ||
+ (max < MHD_sys_page_size_ * 4 / 3) )
+ {
+ pool->memory = MAP_FAILED;
+ }
+ else
+ {
+ /* Round up allocation to page granularity. */
+ alloc_size = max + MHD_sys_page_size_ - 1;
+ alloc_size -= alloc_size % MHD_sys_page_size_;
+#if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
+ pool->memory = mmap (NULL,
+ alloc_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0);
+#elif defined(_WIN32)
+ pool->memory = VirtualAlloc (NULL,
+ alloc_size,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE);
+#endif /* _WIN32 */
+ }
+#else /* ! _WIN32 && ! MAP_ANONYMOUS */
+ pool->memory = MAP_FAILED;
+#endif /* ! _WIN32 && ! MAP_ANONYMOUS */
+ if (MAP_FAILED == pool->memory)
+ {
+ alloc_size = ROUND_TO_ALIGN (max);
+ pool->memory = malloc (alloc_size);
+ if (NULL == pool->memory)
+ {
+ free (pool);
+ return NULL;
+ }
+ pool->is_mmap = false;
+ }
+#if defined(MAP_ANONYMOUS) || defined(_WIN32)
+ else
+ {
+ pool->is_mmap = true;
+ }
+#endif /* _WIN32 || MAP_ANONYMOUS */
+ mhd_assert (0 == (((uintptr_t) pool->memory) % ALIGN_SIZE));
+ pool->pos = 0;
+ pool->end = alloc_size;
+ pool->size = alloc_size;
+ mhd_assert (0 < alloc_size);
+ _MHD_POISON_MEMORY (pool->memory, pool->size);
+ return pool;
+}
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+MHD_INTERNAL void
+mhd_pool_destroy (struct mhd_MemoryPool *restrict pool)
+{
+ if (NULL == pool)
+ return;
+
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
+ _MHD_UNPOISON_MEMORY (pool->memory, pool->size);
+ if (! pool->is_mmap)
+ free (pool->memory);
+ else
+#if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
+ munmap (pool->memory,
+ pool->size);
+#elif defined(_WIN32)
+ VirtualFree (pool->memory,
+ 0,
+ MEM_RELEASE);
+#else
+ abort ();
+#endif
+ free (pool);
+}
+
+
+/**
+ * Check how much memory is left in the @a pool
+ *
+ * @param pool pool to check
+ * @return number of bytes still available in @a pool
+ */
+MHD_INTERNAL size_t
+mhd_pool_get_free (struct mhd_MemoryPool *restrict pool)
+{
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
+#ifdef MHD_ASAN_POISON_ACTIVE
+ if ((pool->end - pool->pos) <= _MHD_RED_ZONE_SIZE)
+ return 0;
+#endif /* MHD_ASAN_POISON_ACTIVE */
+ return (pool->end - pool->pos) - _MHD_RED_ZONE_SIZE;
+}
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to 'true');
+ * use this for small, persistent allocations that
+ * will never be reallocated
+ * @return NULL if the pool cannot support size more
+ * bytes
+ */
+MHD_INTERNAL void *
+mhd_pool_allocate (struct mhd_MemoryPool *restrict pool,
+ size_t size,
+ bool from_end)
+{
+ void *ret;
+ size_t asize;
+
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
+ asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
+ if ( (0 == asize) && (0 != size) )
+ return NULL; /* size too close to SIZE_MAX */
+ if (asize > pool->end - pool->pos)
+ return NULL;
+ if (from_end)
+ {
+ ret = &pool->memory[pool->end - asize];
+ pool->end -= asize;
+ }
+ else
+ {
+ ret = &pool->memory[pool->pos];
+ pool->pos += asize;
+ }
+ _MHD_UNPOISON_MEMORY (ret, size);
+ return ret;
+}
+
+
+/**
+ * Checks whether allocated block is re-sizable in-place.
+ * If block is not re-sizable in-place, it still could be shrunk, but freed
+ * memory will not be re-used until reset of the pool.
+ * @param pool the memory pool to use
+ * @param block the pointer to the allocated block to check
+ * @param block_size the size of the allocated @a block
+ * @return true if block can be resized in-place in the optimal way,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_pool_is_resizable_inplace (struct mhd_MemoryPool *restrict pool,
+ void *restrict block,
+ size_t block_size)
+{
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (block != NULL || block_size == 0);
+ mhd_assert (pool->size >= block_size);
+ if (NULL != block)
+ {
+ const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
+ mhd_assert (mp_ptr_le_ (pool->memory, block));
+ mhd_assert (pool->size >= block_offset);
+ mhd_assert (pool->size >= block_offset + block_size);
+ return (pool->pos ==
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size));
+ }
+ return false; /* Unallocated blocks cannot be resized in-place */
+}
+
+
+/**
+ * Try to allocate @a size bytes memory area from the @a pool.
+ *
+ * If allocation fails, @a required_bytes is updated with size required to be
+ * freed in the @a pool from rellocatable area to allocate requested number
+ * of bytes.
+ * Allocated memory area is always not rellocatable ("from end").
+ *
+ * @param pool memory pool to use for the operation
+ * @param size the size of memory in bytes to allocate
+ * @param[out] required_bytes the pointer to variable to be updated with
+ * the size of the required additional free
+ * memory area, set to 0 if function succeeds.
+ * Cannot be NULL.
+ * @return the pointer to allocated memory area if succeed,
+ * NULL if the pool doesn't have enough space, required_bytes is updated
+ * with amount of space needed to be freed in rellocatable area or
+ * set to SIZE_MAX if requested size is too large for the pool.
+ */
+MHD_INTERNAL void *
+mhd_pool_try_alloc (struct mhd_MemoryPool *restrict pool,
+ size_t size,
+ size_t *restrict required_bytes)
+{
+ void *ret;
+ size_t asize;
+
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
+ asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
+ if ( (0 == asize) && (0 != size) )
+ { /* size is too close to SIZE_MAX, very unlikely */
+ *required_bytes = SIZE_MAX;
+ return NULL;
+ }
+ if (asize > pool->end - pool->pos)
+ {
+ mhd_assert ((pool->end - pool->pos) == \
+ ROUND_TO_ALIGN (pool->end - pool->pos));
+ if (asize <= pool->end)
+ *required_bytes = asize - (pool->end - pool->pos);
+ else
+ *required_bytes = SIZE_MAX;
+ return NULL;
+ }
+ *required_bytes = 0;
+ ret = &pool->memory[pool->end - asize];
+ pool->end -= asize;
+ _MHD_UNPOISON_MEMORY (ret, size);
+ return ret;
+}
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recently
+ * (re)allocated block, the memory of the previous
+ * allocation may be not released until the pool is
+ * destroyed or reset.
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ * NULL if the pool cannot support @a new_size
+ * bytes (old continues to be valid for @a old_size)
+ */
+MHD_INTERNAL void *
+mhd_pool_reallocate (struct mhd_MemoryPool *pool,
+ void *restrict old,
+ size_t old_size,
+ size_t new_size)
+{
+ size_t asize;
+ uint8_t *new_blc;
+
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (old != NULL || old_size == 0);
+ mhd_assert (pool->size >= old_size);
+ mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
+#if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
+ mhd_assert (NULL == __asan_region_is_poisoned (old, old_size));
+#endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
+
+ if (NULL != old)
+ { /* Have previously allocated data */
+ const size_t old_offset = mp_ptr_diff_ (old, pool->memory);
+ const bool shrinking = (old_size > new_size);
+
+ mhd_assert (mp_ptr_le_ (pool->memory, old));
+ /* (pool->memory + pool->size >= (uint8_t*) old + old_size) */
+ mhd_assert ((pool->size - _MHD_RED_ZONE_SIZE) >= (old_offset + old_size));
+ /* Blocks "from the end" must not be reallocated */
+ /* (old_size == 0 || pool->memory + pool->pos > (uint8_t*) old) */
+ mhd_assert ((old_size == 0) || \
+ (pool->pos > old_offset));
+ mhd_assert ((old_size == 0) || \
+ ((pool->end - _MHD_RED_ZONE_SIZE) >= (old_offset + old_size)));
+ /* Try resizing in-place */
+ if (shrinking)
+ { /* Shrinking in-place, zero-out freed part */
+ memset ((uint8_t *) old + new_size, 0, old_size - new_size);
+ _MHD_POISON_MEMORY ((uint8_t *) old + new_size, old_size - new_size);
+ }
+ if (pool->pos ==
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + old_size))
+ { /* "old" block is the last allocated block */
+ const size_t new_apos =
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size);
+ if (! shrinking)
+ { /* Grow in-place, check for enough space. */
+ if ( (new_apos > pool->end) ||
+ (new_apos < pool->pos) ) /* Value wrap */
+ return NULL; /* No space */
+ }
+ /* Resized in-place */
+ pool->pos = new_apos;
+ _MHD_UNPOISON_MEMORY (old, new_size);
+ return old;
+ }
+ if (shrinking)
+ return old; /* Resized in-place, freed part remains allocated */
+ }
+ /* Need to allocate new block */
+ asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
+ if ( ( (0 == asize) &&
+ (0 != new_size) ) || /* Value wrap, too large new_size. */
+ (asize > pool->end - pool->pos) ) /* Not enough space */
+ return NULL;
+
+ new_blc = pool->memory + pool->pos;
+ pool->pos += asize;
+
+ _MHD_UNPOISON_MEMORY (new_blc, new_size);
+ if (0 != old_size)
+ {
+ /* Move data to new block, old block remains allocated */
+ memcpy (new_blc, old, old_size);
+ /* Zero-out old block */
+ memset (old, 0, old_size);
+ _MHD_POISON_MEMORY (old, old_size);
+ }
+ return new_blc;
+}
+
+
+/**
+ * Deallocate a block of memory obtained from the pool.
+ *
+ * If the given block is not the most recently
+ * (re)allocated block, the memory of the this block
+ * allocation may be not released until the pool is
+ * destroyed or reset.
+ *
+ * @param pool memory pool to use for the operation
+ * @param block the allocated block, the NULL is tolerated
+ * @param block_size the size of the allocated block
+ */
+MHD_INTERNAL void
+mhd_pool_deallocate (struct mhd_MemoryPool *restrict pool,
+ void *restrict block,
+ size_t block_size)
+{
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (block != NULL || block_size == 0);
+ mhd_assert (pool->size >= block_size);
+ mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
+
+ if (NULL != block)
+ { /* Have previously allocated data */
+ const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
+ mhd_assert (mp_ptr_le_ (pool->memory, block));
+ mhd_assert (block_offset <= pool->size);
+ mhd_assert ((block_offset != pool->pos) || (block_size == 0));
+ /* Zero-out deallocated region */
+ if (0 != block_size)
+ {
+ memset (block, 0, block_size);
+ _MHD_POISON_MEMORY (block, block_size);
+ }
+#if ! defined(MHD_FAVOR_SMALL_CODE) && ! defined(MHD_ASAN_POISON_ACTIVE)
+ else
+ return; /* Zero size, no need to do anything */
+#endif /* ! MHD_FAVOR_SMALL_CODE && ! MHD_ASAN_POISON_ACTIVE */
+ if (block_offset <= pool->pos)
+ {
+ /* "Normal" block, not allocated "from the end". */
+ const size_t alg_end =
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
+ mhd_assert (alg_end <= pool->pos);
+ if (alg_end == pool->pos)
+ {
+ /* The last allocated block, return deallocated block to the pool */
+ size_t alg_start = ROUND_TO_ALIGN (block_offset);
+ mhd_assert (alg_start >= block_offset);
+#if defined(MHD_ASAN_POISON_ACTIVE)
+ if (alg_start != block_offset)
+ {
+ _MHD_POISON_MEMORY (pool->memory + block_offset, \
+ alg_start - block_offset);
+ }
+ else if (0 != alg_start)
+ {
+ bool need_red_zone_before;
+ mhd_assert (_MHD_RED_ZONE_SIZE <= alg_start);
+#if defined(HAVE___ASAN_REGION_IS_POISONED)
+ need_red_zone_before =
+ (NULL == __asan_region_is_poisoned (pool->memory
+ + alg_start
+ - _MHD_RED_ZONE_SIZE,
+ _MHD_RED_ZONE_SIZE));
+#elif defined(HAVE___ASAN_ADDRESS_IS_POISONED)
+ need_red_zone_before =
+ (0 == __asan_address_is_poisoned (pool->memory + alg_start - 1));
+#else /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
+ need_red_zone_before = true; /* Unknown, assume new red zone needed */
+#endif /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
+ if (need_red_zone_before)
+ {
+ _MHD_POISON_MEMORY (pool->memory + alg_start, _MHD_RED_ZONE_SIZE);
+ alg_start += _MHD_RED_ZONE_SIZE;
+ }
+ }
+#endif /* MHD_ASAN_POISON_ACTIVE */
+ mhd_assert (alg_start <= pool->pos);
+ mhd_assert (alg_start == ROUND_TO_ALIGN (alg_start));
+ pool->pos = alg_start;
+ }
+ }
+ else
+ {
+ /* Allocated "from the end" block. */
+ /* The size and the pointers of such block should not be manipulated by
+ MHD code (block split is disallowed). */
+ mhd_assert (block_offset >= pool->end);
+ mhd_assert (ROUND_TO_ALIGN (block_offset) == block_offset);
+ if (block_offset == pool->end)
+ {
+ /* The last allocated block, return deallocated block to the pool */
+ const size_t alg_end =
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
+ pool->end = alg_end;
+ }
+ }
+ }
+}
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for @a keep of the given @a copy_bytes. The pointer
+ * returned should be a buffer of @a new_size where
+ * the first @a copy_bytes are from @a keep.
+ *
+ * @param pool memory pool to use for the operation
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param copy_bytes how many bytes need to be kept at this address
+ * @param new_size how many bytes should the allocation we return have?
+ * (should be larger or equal to @a copy_bytes)
+ * @return addr new address of @a keep (if it had to change)
+ */
+MHD_INTERNAL void *
+mhd_pool_reset (struct mhd_MemoryPool *restrict pool,
+ void *restrict keep,
+ size_t copy_bytes,
+ size_t new_size)
+{
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ mhd_assert (copy_bytes <= new_size);
+ mhd_assert (copy_bytes <= pool->size);
+ mhd_assert (keep != NULL || copy_bytes == 0);
+ mhd_assert (keep == NULL || mp_ptr_le_ (pool->memory, keep));
+ /* (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep + copy_bytes) */
+ mhd_assert ((keep == NULL) || \
+ (pool->size >= mp_ptr_diff_ (keep, pool->memory) + copy_bytes));
+#if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
+ mhd_assert (NULL == __asan_region_is_poisoned (keep, copy_bytes));
+#endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
+ _MHD_UNPOISON_MEMORY (pool->memory, new_size);
+ if ( (NULL != keep) &&
+ (keep != pool->memory) )
+ {
+ if (0 != copy_bytes)
+ memmove (pool->memory,
+ keep,
+ copy_bytes);
+ }
+ /* technically not needed, but safer to zero out */
+ if (pool->size > copy_bytes)
+ {
+ size_t to_zero; /** Size of area to zero-out */
+
+ to_zero = pool->size - copy_bytes;
+ _MHD_UNPOISON_MEMORY (pool->memory + copy_bytes, to_zero);
+#ifdef _WIN32
+ if (pool->is_mmap)
+ {
+ size_t to_recommit; /** Size of decommitted and re-committed area. */
+ uint8_t *recommit_addr;
+ /* Round down to page size */
+ to_recommit = to_zero - to_zero % MHD_sys_page_size_;
+ recommit_addr = pool->memory + pool->size - to_recommit;
+
+ /* De-committing and re-committing again clear memory and make
+ * pages free / available for other needs until accessed. */
+ if (VirtualFree (recommit_addr,
+ to_recommit,
+ MEM_DECOMMIT))
+ {
+ to_zero -= to_recommit;
+
+ if (recommit_addr != VirtualAlloc (recommit_addr,
+ to_recommit,
+ MEM_COMMIT,
+ PAGE_READWRITE))
+ abort (); /* Serious error, must never happen */
+ }
+ }
+#endif /* _WIN32 */
+ memset (&pool->memory[copy_bytes],
+ 0,
+ to_zero);
+ }
+ pool->pos = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
+ pool->end = pool->size;
+ _MHD_POISON_MEMORY (((uint8_t *) pool->memory) + new_size, \
+ pool->size - new_size);
+ return pool->memory;
+}
+
+
+/* end of memorypool.c */
diff --git a/src/mhd2/mhd_mempool.h b/src/mhd2/mhd_mempool.h
@@ -0,0 +1,197 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007--2024 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2016--2024 Evgeny Grin (Karlson2k)
+
+ 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/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)
+ */
+
+#ifndef MHD_MEMPOOL_H
+#define MHD_MEMPOOL_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+
+/**
+ * Opaque handle for a memory pool.
+ * Pools are not reentrant and must not be used
+ * by multiple threads.
+ */
+struct mhd_MemoryPool;
+
+/**
+ * Initialize values for memory pools
+ */
+void
+mhd_init_mem_pools (void);
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+MHD_INTERNAL struct mhd_MemoryPool *
+mdh_pool_create (size_t max);
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+MHD_INTERNAL void
+mhd_pool_destroy (struct mhd_MemoryPool *restrict pool);
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to 'true');
+ * use this for small, persistent allocations that
+ * will never be reallocated
+ * @return NULL if the pool cannot support size more
+ * bytes
+ */
+MHD_INTERNAL void *
+mhd_pool_allocate (struct mhd_MemoryPool *restrict pool,
+ size_t size,
+ bool from_end);
+
+/**
+ * Checks whether allocated block is re-sizable in-place.
+ * If block is not re-sizable in-place, it still could be shrunk, but freed
+ * memory will not be re-used until reset of the pool.
+ * @param pool the memory pool to use
+ * @param block the pointer to the allocated block to check
+ * @param block_size the size of the allocated @a block
+ * @return true if block can be resized in-place in the optimal way,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_pool_is_resizable_inplace (struct mhd_MemoryPool *restrict pool,
+ void *restrict block,
+ size_t block_size);
+
+/**
+ * Try to allocate @a size bytes memory area from the @a pool.
+ *
+ * If allocation fails, @a required_bytes is updated with size required to be
+ * freed in the @a pool from rellocatable area to allocate requested number
+ * of bytes.
+ * Allocated memory area is always not rellocatable ("from end").
+ *
+ * @param pool memory pool to use for the operation
+ * @param size the size of memory in bytes to allocate
+ * @param[out] required_bytes the pointer to variable to be updated with
+ * the size of the required additional free
+ * memory area, set to 0 if function succeeds.
+ * Cannot be NULL.
+ * @return the pointer to allocated memory area if succeed,
+ * NULL if the pool doesn't have enough space, required_bytes is updated
+ * with amount of space needed to be freed in rellocatable area or
+ * set to SIZE_MAX if requested size is too large for the pool.
+ */
+MHD_INTERNAL void *
+mhd_pool_try_alloc (struct mhd_MemoryPool *restrict pool,
+ size_t size,
+ size_t *restrict required_bytes);
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recently
+ * (re)allocated block, the memory of the previous
+ * allocation may be not released until the pool is
+ * destroyed or reset.
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ * NULL if the pool cannot support @a new_size
+ * bytes (old continues to be valid for @a old_size)
+ */
+MHD_INTERNAL void *
+mhd_pool_reallocate (struct mhd_MemoryPool *restrict pool,
+ void *restrict old,
+ size_t old_size,
+ size_t new_size);
+
+
+/**
+ * Check how much memory is left in the @a pool
+ *
+ * @param pool pool to check
+ * @return number of bytes still available in @a pool
+ */
+MHD_INTERNAL size_t
+mhd_pool_get_free (struct mhd_MemoryPool *restrict pool);
+
+
+/**
+ * Deallocate a block of memory obtained from the pool.
+ *
+ * If the given block is not the most recently
+ * (re)allocated block, the memory of the this block
+ * allocation may be not released until the pool is
+ * destroyed or reset.
+ *
+ * @param pool memory pool to use for the operation
+ * @param block the allocated block, the NULL is tolerated
+ * @param block_size the size of the allocated block
+ */
+MHD_INTERNAL void
+mhd_pool_deallocate (struct mhd_MemoryPool *restrict pool,
+ void *restrict block,
+ size_t block_size);
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for @a keep of the given @a copy_bytes. The pointer
+ * returned should be a buffer of @a new_size where
+ * the first @a copy_bytes are from @a keep.
+ *
+ * @param pool memory pool to use for the operation
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param copy_bytes how many bytes need to be kept at this address
+ * @param new_size how many bytes should the allocation we return have?
+ * (should be larger or equal to @a copy_bytes)
+ * @return addr new address of @a keep (if it had to change)
+ */
+MHD_INTERNAL void *
+mhd_pool_reset (struct mhd_MemoryPool *restrict pool,
+ void *restrict keep,
+ size_t copy_bytes,
+ size_t new_size);
+
+#endif /* ! MHD_MEMPOOL_H */
diff --git a/src/mhd2/mhd_mono_clock.c b/src/mhd2/mhd_mono_clock.c
@@ -0,0 +1,450 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2015-2022 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 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 */
+#endif /* _WIN32 && ! __CYGWIN__ */
+
+#ifdef HAVE_TIME_H
+# include <time.h>
+#endif /* HAVE_TIME_H */
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+
+#ifdef HAVE_CLOCK_GET_TIME
+# include <mach/mach.h>
+/* for host_get_clock_service(), mach_host_self(), mach_task_self() */
+# include <mach/clock.h>
+/* for clock_get_time() */
+
+# 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
+# include <windows.h>
+#endif /* _WIN32 */
+
+#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 */
+
+static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK;
+#endif /* HAVE_CLOCK_GETTIME */
+
+/* sync clocks; reduce chance of value wrap */
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || \
+ 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
+static uint64_t tick_start;
+# else /* _WIN32_WINNT < 0x0600 */
+static uint64_t perf_freq;
+static uint64_t perf_start;
+# endif /* _WIN32_WINNT < 0x0600 */
+#endif /* _WIN32 */
+
+
+/**
+ * Type of monotonic clock source
+ */
+enum _MHD_mono_clock_source
+{
+ /**
+ * No monotonic clock
+ */
+ _MHD_CLOCK_NO_SOURCE = 0,
+
+ /**
+ * clock_gettime() with specific clock
+ */
+ _MHD_CLOCK_GETTIME,
+
+ /**
+ * clock_get_time() with specific clock service
+ */
+ _MHD_CLOCK_GET_TIME,
+
+ /**
+ * gethrtime() / 1000000000
+ */
+ _MHD_CLOCK_GETHRTIME,
+
+ /**
+ * GetTickCount64() / 1000
+ */
+ _MHD_CLOCK_GETTICKCOUNT64,
+
+ /**
+ * QueryPerformanceCounter() / QueryPerformanceFrequency()
+ */
+ _MHD_CLOCK_PERFCOUNTER
+};
+
+
+/**
+ * Initialise milliseconds counters.
+ */
+void
+MHD_monotonic_msec_counter_init (void)
+{
+#ifdef HAVE_CLOCK_GET_TIME
+ mach_timespec_t cur_time;
+#endif /* HAVE_CLOCK_GET_TIME */
+ enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE;
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+ mono_clock_id = _MHD_UNWANTED_CLOCK;
+#endif /* HAVE_CLOCK_GETTIME */
+#ifdef HAVE_CLOCK_GET_TIME
+ mono_clock_service = _MHD_INVALID_CLOCK_SERV;
+#endif /* HAVE_CLOCK_GET_TIME */
+
+ /* just a little syntactic trick to get the
+ various following ifdef's to work out nicely */
+ if (0)
+ {
+ (void) 0; /* Mute possible compiler warning */
+ }
+ else
+#ifdef HAVE_CLOCK_GETTIME
+#ifdef CLOCK_MONOTONIC_COARSE
+ /* Linux-specific fast value-getting clock */
+ /* Can be affected by frequency adjustment and don't count time in suspend, */
+ /* but preferred since it's fast */
+ if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE,
+ &ts))
+ {
+ mono_clock_id = CLOCK_MONOTONIC_COARSE;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_MONOTONIC_COARSE */
+#ifdef CLOCK_MONOTONIC_FAST
+ /* FreeBSD/DragonFly fast value-getting clock */
+ /* Can be affected by frequency adjustment, but preferred since it's fast */
+ if (0 == clock_gettime (CLOCK_MONOTONIC_FAST,
+ &ts))
+ {
+ mono_clock_id = CLOCK_MONOTONIC_FAST;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_MONOTONIC_COARSE */
+#ifdef CLOCK_MONOTONIC_RAW_APPROX
+ /* Darwin-specific clock */
+ /* Not affected by frequency adjustment, returns clock value cached at
+ * context switch. Can be "milliseconds old", but it's fast. */
+ if (0 == clock_gettime (CLOCK_MONOTONIC_RAW_APPROX,
+ &ts))
+ {
+ mono_clock_id = CLOCK_MONOTONIC_RAW_APPROX;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_MONOTONIC_RAW */
+#ifdef CLOCK_MONOTONIC_RAW
+ /* Linux and Darwin clock */
+ /* Not affected by frequency adjustment,
+ * on Linux don't count time in suspend */
+ if (0 == clock_gettime (CLOCK_MONOTONIC_RAW,
+ &ts))
+ {
+ mono_clock_id = CLOCK_MONOTONIC_RAW;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_MONOTONIC_RAW */
+#ifdef CLOCK_BOOTTIME
+ /* Count time in suspend on Linux so it's real monotonic, */
+ /* but can be slower value-getting than other clocks */
+ if (0 == clock_gettime (CLOCK_BOOTTIME,
+ &ts))
+ {
+ mono_clock_id = CLOCK_BOOTTIME;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_BOOTTIME */
+#ifdef CLOCK_MONOTONIC
+ /* Monotonic clock */
+ /* Widely supported, may be affected by frequency adjustment */
+ /* On Linux it's not truly monotonic as it doesn't count time in suspend */
+ if (0 == clock_gettime (CLOCK_MONOTONIC,
+ &ts))
+ {
+ mono_clock_id = CLOCK_MONOTONIC;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_MONOTONIC */
+#ifdef CLOCK_UPTIME
+ /* non-Linux clock */
+ /* Doesn't count time in suspend */
+ if (0 == clock_gettime (CLOCK_UPTIME,
+ &ts))
+ {
+ mono_clock_id = CLOCK_UPTIME;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_BOOTTIME */
+#endif /* HAVE_CLOCK_GETTIME */
+#ifdef HAVE_CLOCK_GET_TIME
+ /* Darwin-specific monotonic clock */
+ /* Should be monotonic as clock_set_time function always unconditionally */
+ /* failed on latest kernels */
+ if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self (),
+ SYSTEM_CLOCK,
+ &mono_clock_service)) &&
+ (KERN_SUCCESS == clock_get_time (mono_clock_service,
+ &cur_time)) )
+ {
+ mono_clock_start = cur_time.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GET_TIME;
+ }
+ else
+#endif /* HAVE_CLOCK_GET_TIME */
+#ifdef _WIN32
+#if _WIN32_WINNT >= 0x0600
+ /* W32 Vista or later specific monotonic clock */
+ /* Available since Vista, ~15ms accuracy */
+ if (1)
+ {
+ tick_start = GetTickCount64 ();
+ mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64;
+ }
+ else
+#else /* _WIN32_WINNT < 0x0600 */
+ /* W32 specific monotonic clock */
+ /* Available on Windows 2000 and later */
+ if (1)
+ {
+ LARGE_INTEGER freq;
+ LARGE_INTEGER perf_counter;
+
+ QueryPerformanceFrequency (&freq); /* never fail on XP and later */
+ QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */
+ perf_freq = (uint64_t) freq.QuadPart;
+ perf_start = (uint64_t) perf_counter.QuadPart;
+ mono_clock_source = _MHD_CLOCK_PERFCOUNTER;
+ }
+ else
+#endif /* _WIN32_WINNT < 0x0600 */
+#endif /* _WIN32 */
+#ifdef HAVE_CLOCK_GETTIME
+#ifdef CLOCK_HIGHRES
+ /* Solaris-specific monotonic high-resolution clock */
+ /* Not preferred due to be potentially resource-hungry */
+ if (0 == clock_gettime (CLOCK_HIGHRES,
+ &ts))
+ {
+ mono_clock_id = CLOCK_HIGHRES;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_HIGHRES */
+#endif /* HAVE_CLOCK_GETTIME */
+#ifdef HAVE_GETHRTIME
+ /* HP-UX and Solaris monotonic clock */
+ /* Not preferred due to be potentially resource-hungry */
+ if (1)
+ {
+ hrtime_start = gethrtime ();
+ mono_clock_source = _MHD_CLOCK_GETHRTIME;
+ }
+ else
+#endif /* HAVE_GETHRTIME */
+ {
+ /* no suitable clock source was found */
+ mono_clock_source = _MHD_CLOCK_NO_SOURCE;
+ }
+
+#ifdef HAVE_CLOCK_GET_TIME
+ if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) &&
+ (_MHD_INVALID_CLOCK_SERV != mono_clock_service) )
+ {
+ /* clock service was initialised but clock_get_time failed */
+ mach_port_deallocate (mach_task_self (),
+ mono_clock_service);
+ mono_clock_service = _MHD_INVALID_CLOCK_SERV;
+ }
+#else
+ (void) mono_clock_source; /* avoid compiler warning */
+#endif /* HAVE_CLOCK_GET_TIME */
+
+#ifdef HAVE_TIMESPEC_GET
+ if (1)
+ {
+ struct timespec tsg;
+ if (TIME_UTC == timespec_get (&tsg, TIME_UTC))
+ gettime_start = tsg.tv_sec;
+ else
+ gettime_start = 0;
+ }
+#elif defined(HAVE_GETTIMEOFDAY)
+ if (1)
+ {
+ struct timeval tv;
+ if (0 == gettimeofday (&tv, NULL))
+ gettime_start = tv.tv_sec;
+ else
+ gettime_start = 0;
+ }
+#endif /* HAVE_GETTIMEOFDAY */
+ sys_clock_start = time (NULL);
+}
+
+
+/**
+ * Deinitialise milliseconds counters by freeing any allocated resources
+ */
+void
+MHD_monotonic_msec_counter_finish (void)
+{
+#ifdef HAVE_CLOCK_GET_TIME
+ if (_MHD_INVALID_CLOCK_SERV != mono_clock_service)
+ {
+ mach_port_deallocate (mach_task_self (),
+ mono_clock_service);
+ mono_clock_service = _MHD_INVALID_CLOCK_SERV;
+ }
+#endif /* HAVE_CLOCK_GET_TIME */
+}
+
+
+/**
+ * Monotonic milliseconds counter, useful for timeout calculation.
+ * Tries to be not affected by manually setting the system real time
+ * clock or adjustments by NTP synchronization.
+ *
+ * @return number of microseconds from some fixed moment
+ */
+uint_fast64_t
+MHD_monotonic_msec_counter (void)
+{
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET)
+ struct timespec ts;
+#endif /* HAVE_CLOCK_GETTIME || HAVE_TIMESPEC_GET */
+
+#ifdef HAVE_CLOCK_GETTIME
+ if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) &&
+ (0 == clock_gettime (mono_clock_id,
+ &ts)) )
+ 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)
+ {
+ mach_timespec_t cur_time;
+
+ if (KERN_SUCCESS == clock_get_time (mono_clock_service,
+ &cur_time))
+ 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 (uint_fast64_t) (GetTickCount64 () - tick_start);
+#else /* _WIN32_WINNT < 0x0600 */
+ if (0 != perf_freq)
+ {
+ LARGE_INTEGER perf_counter;
+ uint_fast64_t num_ticks;
+
+ QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */
+ num_ticks = (uint_fast64_t) (perf_counter.QuadPart - perf_start);
+ return ((num_ticks / perf_freq) * 1000)
+ + ((num_ticks % perf_freq) / (perf_freq / 1000));
+ }
+#endif /* _WIN32_WINNT < 0x0600 */
+#endif /* _WIN32 */
+#ifdef HAVE_GETHRTIME
+ if (1)
+ 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 (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 (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 (uint_fast64_t) (time (NULL) - sys_clock_start) * 1000;
+}
diff --git a/src/mhd2/mhd_mono_clock.h b/src/mhd2/mhd_mono_clock.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of libmicrohttpd
+ 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
+ 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/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_sys_options.h"
+
+#include "sys_base_types.h"
+
+/**
+ * Initialise milliseconds counters.
+ */
+void
+MHD_monotonic_msec_counter_init (void);
+
+
+/**
+ * Deinitialise milliseconds counters by freeing any allocated resources
+ */
+void
+MHD_monotonic_msec_counter_finish (void);
+
+
+/**
+ * Monotonic milliseconds counter, useful for timeout calculation.
+ * Tries to be not affected by manually setting the system real time
+ * clock or adjustments by NTP synchronization.
+ *
+ * @return number of microseconds from some fixed moment
+ */
+uint_fast64_t
+MHD_monotonic_msec_counter (void);
+
+#endif /* MHD_MONO_CLOCK_H */
diff --git a/src/mhd2/mhd_panic.c b/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 ();
+}
diff --git a/src/mhd2/mhd_panic.h b/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 */
diff --git a/src/mhd2/mhd_public_api.h b/src/mhd2/mhd_public_api.h
@@ -0,0 +1,41 @@
+/*
+ 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_public_api.h
+ * @brief The header for the MHD public API
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header acts as a wrapper for "microhttpd2.h", with correct system
+ * headers included for the types used in the public API.
+ */
+
+#ifndef MHD_PUBLIC_API_H
+#define MHD_PUBLIC_API_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "mhd_socket_type.h"
+#include "sys_sockets_types.h"
+
+#include "microhttpd2.h"
+
+#endif /* ! MHD_PUBLIC_API_H */
diff --git a/src/mhd2/mhd_recv.c b/src/mhd2/mhd_recv.c
@@ -0,0 +1,92 @@
+/*
+ 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 "mhd_limits.h"
+#include "mhd_socket_error.h"
+
+
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_plain_recv (struct MHD_Connection *restrict c,
+ size_t buf_size,
+ char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+ size_t *restrict received)
+{
+ /* Plain TCP connection */
+ ssize_t res;
+ enum mhd_SocketError 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) /* Clear 'recv-ready' */
+ (((unsigned int) c->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_RECV_READY));
+ return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */
+ }
+
+ err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
+
+ if (mhd_SOCKET_ERR_AGAIN == err)
+ c->sk_ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
+ (((unsigned int) c->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_RECV_READY));
+
+ return err; /* Failure exit point */
+}
+
+
+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,
+ char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+ size_t *restrict received)
+{
+ mhd_assert (MHD_INVALID_SOCKET != c->socket_fd);
+ mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+
+ // TODO: implement TLS support
+
+ return mhd_plain_recv (c, buf_size, buf, received);
+}
diff --git a/src/mhd2/mhd_recv.h b/src/mhd2/mhd_recv.h
@@ -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/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,
+ char buffer[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)],
+ 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 */
diff --git a/src/mhd2/mhd_reply.h b/src/mhd2/mhd_reply.h
@@ -0,0 +1,151 @@
+/*
+ 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_dcc_action.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 */
+ bool end_by_closing; /**< Signal end of content (only) by closing connection */
+};
+
+/**
+ * The location of the reply content
+ */
+enum MHD_FIXED_ENUM_ mhd_ReplyContentLocation
+{
+ /**
+ * Reply content is absent
+ */
+ mhd_REPLY_CNTN_LOC_NOWHERE = 0
+ ,
+ /**
+ * Reply content is in the response buffer
+ */
+ mhd_REPLY_CNTN_LOC_RESP_BUF
+ ,
+ /**
+ * Reply content is in the connection buffer
+ */
+ mhd_REPLY_CNTN_LOC_CONN_BUF
+ ,
+ /**
+ * Reply content is in the vector data
+ */
+ mhd_REPLY_CNTN_LOC_IOV
+ ,
+ /**
+ * Reply content is in the file, to be used with sendfile() function
+ */
+ mhd_REPLY_CNTN_LOC_FILE
+};
+
+
+/**
+ * Reply-specific values.
+ *
+ * Meaningful for the current reply only.
+ */
+struct MHD_Reply
+{
+ /**
+ * The action provided by application when content is dynamically created.
+ * Used only when mhd_RESPONSE_CONTENT_DATA_CALLBACK == response->cntn_dtype
+ */
+ struct MHD_DynamicContentCreatorAction app_act;
+
+ /**
+ * The context provided for application callback for dynamic content.
+ * Used only when mhd_RESPONSE_CONTENT_DATA_CALLBACK == response->cntn_dtype
+ */
+ struct MHD_DynamicContentCreatorContext app_act_ctx;
+
+ /**
+ * Response to transmit (initially NULL).
+ */
+ struct MHD_Response *response;
+
+ /**
+ * The "ICY" response.
+ * Reply begins with the SHOUTcast "ICY" line instead of "HTTP".
+ */
+ bool responseIcy;
+
+ /**
+ * Current rest position in the actual content (should be 0 while
+ * sending headers).
+ * When sending buffers located in the connection buffers, it is updated
+ * when the data copied to the buffers. In other cases it is updated when
+ * data is actually sent.
+ */
+ uint_fast64_t rsp_cntn_read_pos;
+
+ /**
+ * 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;
+
+ /**
+ * The location of the reply content
+ */
+ enum mhd_ReplyContentLocation cntn_loc;
+
+ /**
+ * Reply-specific properties
+ */
+ struct MHD_Reply_Properties props;
+};
+
+#endif /* ! MHD_REPLY_H */
diff --git a/src/mhd2/mhd_request.h b/src/mhd2/mhd_request.h
@@ -0,0 +1,375 @@
+/*
+ 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_public_api.h"
+
+#include "mhd_dlinked_list.h"
+
+#include "http_prot_ver.h"
+#include "http_method.h"
+#include "mhd_action.h"
+#include "mhd_buffer.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_RequestField; /* forward declarations */
+
+mhd_DLINKEDL_LINKS_DEF (mhd_RequestField);
+
+/**
+ * Header, footer, or cookie for HTTP request.
+ */
+struct mhd_RequestField
+{
+ /**
+ * The field data
+ */
+ struct MHD_NameValueKind field;
+
+ /**
+ * Headers are kept in a double-linked list.
+ */
+ mhd_DLNKDL_LINKS (mhd_RequestField,fields);
+};
+
+mhd_DLINKEDL_LIST_DEF (mhd_RequestField);
+
+
+/**
+ * The request content data
+ */
+struct mhd_ReqContentData
+{
+ /**
+ * The pointer to the large buffer
+ * Must be NULL if large buffer is not allocated.
+ */
+ struct mhd_Buffer lbuf;
+
+ /**
+ * The total size of the request content.
+ * #MHD_SIZE_UNKNOWN if the size is not yet known (chunked upload).
+ */
+ uint_fast64_t cntn_size;
+
+ /**
+ * The size of the received content
+ */
+ uint_fast64_t recv_size;
+
+ /**
+ * The size of the processed content
+ */
+ uint_fast64_t proc_size;
+};
+
+
+/**
+ * Request-specific values.
+ *
+ * Meaningful for the current request only.
+ */
+struct MHD_Request
+{
+ /**
+ * Linked list of parsed headers.
+ */
+ mhd_DLNKDL_LIST (mhd_RequestField,fields);
+
+ /**
+ * The action set by the application
+ */
+ struct mhd_ApplicationAction app_act;
+
+ /**
+ * The request content data
+ */
+ struct mhd_ReqContentData cntn;
+
+ /**
+ * Set to true if request is too large to be handled
+ */
+ bool too_large;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * 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 *app_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 app_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 */
diff --git a/src/mhd2/mhd_response.h b/src/mhd2/mhd_response.h
@@ -0,0 +1,362 @@
+/*
+ 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"
+
+#ifdef MHD_USE_THREADS
+# include "mhd_locks.h"
+#endif
+
+#include "mhd_atomic_counter.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_ResponseContentDataType
+{
+ mhd_RESPONSE_CONTENT_DATA_INVALID = 0
+ ,
+ mhd_RESPONSE_CONTENT_DATA_BUFFER
+ ,
+ mhd_RESPONSE_CONTENT_DATA_IOVEC
+ ,
+ mhd_RESPONSE_CONTENT_DATA_FILE
+ ,
+ mhd_RESPONSE_CONTENT_DATA_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 I/O vector data
+ */
+ 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
+ */
+ volatile 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.
+ */
+ struct mhd_AtomicCounter counter;
+
+#ifdef MHD_USE_THREADS
+ /**
+ * 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 close_forced;
+
+ /**
+ * Use "HTTP/1.0" in the reply header
+ * @a chunked is 'false' if this flag set.
+ * @a close_forced is 'true' is this flag set.
+ */
+ bool mode_1_0;
+
+ /**
+ * The (possible incorrect) content length is provided by application
+ */
+ bool cnt_len_by_app;
+
+ /**
+ * Response has "Date:" header
+ */
+ bool has_hdr_date; // TODO: set the member
+
+ /**
+ * Response has "Connection:" header
+ */
+ bool has_hdr_conn; // TODO: set the member
+
+ /**
+ * Response is internal-only error response
+ */
+ bool int_err_resp;
+};
+
+/**
+ * Special data for internal error responses
+ */
+struct mhd_ResponseInternalErrData
+{
+ /**
+ * The length of the @a spec_hdr
+ */
+ size_t spec_hdr_len;
+ /**
+ * The special header string.
+ * The final CRLF is not included.
+ * Must be deallocated if not NULL.
+ */
+ char *spec_hdr;
+};
+
+#ifndef NDEBUG
+struct mhd_ResponseDebug
+{
+ bool is_internal;
+};
+#endif
+
+mhd_DLINKEDL_LIST_DEF (mhd_ResponseHeader);
+
+// TODO: Group members in structs
+
+struct MHD_Response
+{
+ /**
+ * The response HTTP status code
+ */
+ enum MHD_HTTP_StatusCode sc;
+
+ /**
+ * The size of the response.
+ * #MHD_SIZE_UNKNOWN if size is undefined
+ */
+ uint_fast64_t cntn_size;
+
+ /**
+ * The type of the content data
+ */
+ enum mhd_ResponseContentDataType cntn_dtype;
+
+ /**
+ * 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);
+
+ /**
+ * Special data for internal error responses
+ */
+ struct mhd_ResponseInternalErrData special_resp;
+
+ #ifndef NDEBUG
+ struct mhd_ResponseDebug dbg;
+#endif
+};
+
+#endif /* ! MHD_RESPONSE_H */
diff --git a/src/mhd2/mhd_send.c b/src/mhd2/mhd_send.c
@@ -0,0 +1,1621 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2017-2024 Karlson2k (Evgeny Grin), Full re-write of buffering
+ and pushing, many bugs fixes, optimisations,
+ sendfile() porting
+ Copyright (C) 2019 ng0 <ng0@n0.is>, Initial version of send() wrappers
+
+ 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_send.c
+ * @brief Implementation of send() wrappers and helper functions.
+ * @author Karlson2k (Evgeny Grin)
+ * @author ng0 (N. Gillmann)
+ * @author Christian Grothoff
+ */
+
+/* Worth considering for future improvements and additions:
+ * NetBSD has no sendfile or sendfile64. The way to work
+ * with this seems to be to mmap the file and write(2) as
+ * large a chunk as possible to the socket. Alternatively,
+ * use madvise(..., MADV_SEQUENTIAL). */
+
+#include "mhd_sys_options.h"
+
+#include <string.h>
+
+#include "mhd_send.h"
+#include "sys_sockets_headers.h"
+#include "sys_ip_headers.h"
+#include "mhd_sockets_macros.h"
+#include "daemon_logger.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+#include "mhd_response.h"
+
+#include "mhd_iovec.h"
+#ifdef HAVE_LINUX_SENDFILE
+# include <sys/sendfile.h>
+#endif /* HAVE_LINUX_SENDFILE */
+
+#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/uio.h>
+#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
+#ifdef HAVE_SYS_PARAM_H
+/* For FreeBSD version identification */
+# include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#ifdef HAVE_SYSCONF
+# include <unistd.h>
+#endif /* HAVE_SYSCONF */
+#include "mhd_assert.h"
+
+#include "mhd_limits.h"
+
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
+ defined(MHD_WINSOCK_SOCKETS)
+# define mhd_USE_VECT_SEND 1
+#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_WINSOCK_SOCKETS */
+
+
+#ifdef mhd_USE_VECT_SEND
+# if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
+ defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
+ defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
+# define mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1
+# endif /* (!HAVE_SENDMSG || !MSG_NOSIGNAL) &&
+ mhd_SEND_SPIPE_SUPPRESS_POSSIBLE && mhd_SEND_SPIPE_SUPPRESS_NEEDED */
+#endif /* mhd_USE_VECT_SEND */
+
+/**
+ * sendfile() chuck size
+ */
+#define mhd_SENFILE_CHUNK_SIZE (0x20000)
+
+/**
+ * sendfile() chuck size for thread-per-connection
+ */
+#define mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C (0x200000)
+
+#if defined(HAVE_FREEBSD_SENDFILE) && defined(SF_FLAGS)
+/**
+ * FreeBSD sendfile() flags
+ */
+static int freebsd_sendfile_flags_;
+
+/**
+ * FreeBSD sendfile() flags for thread-per-connection
+ */
+static int freebsd_sendfile_flags_thd_p_c_;
+
+
+/**
+ * Initialises variables for FreeBSD's sendfile()
+ */
+static void
+freebsd_sendfile_init_ (void)
+{
+ long sys_page_size = sysconf (_SC_PAGESIZE);
+ if (0 >= sys_page_size)
+ { /* Failed to get page size. */
+ freebsd_sendfile_flags_ = SF_NODISKIO;
+ freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO;
+ }
+ else
+ {
+ freebsd_sendfile_flags_ =
+ SF_FLAGS ((uint_least16_t) \
+ ((mhd_SENFILE_CHUNK_SIZE + sys_page_size - 1) / sys_page_size) \
+ & 0xFFFFU, SF_NODISKIO);
+ freebsd_sendfile_flags_thd_p_c_ =
+ SF_FLAGS ((uint_least16_t) \
+ ((mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C + sys_page_size - 1) \
+ / sys_page_size) & 0xFFFFU, SF_NODISKIO);
+ }
+}
+
+
+#else /* ! HAVE_FREEBSD_SENDFILE || ! SF_FLAGS */
+# define freebsd_sendfile_init_() (void) 0
+#endif /* HAVE_FREEBSD_SENDFILE */
+
+
+#if defined(HAVE_SYSCONF) && defined(_SC_IOV_MAX)
+/**
+ * Current IOV_MAX system value
+ */
+static unsigned long mhd_iov_max_ = 0;
+
+static void
+iov_max_init_ (void)
+{
+ long res = sysconf (_SC_IOV_MAX);
+ if (res >= 0)
+ mhd_iov_max_ = (unsigned long) res;
+ else
+ {
+# if defined(IOV_MAX)
+ mhd_iov_max_ = IOV_MAX;
+# else /* ! IOV_MAX */
+ mhd_iov_max_ = 8; /* Should be the safe limit */
+# endif /* ! IOV_MAX */
+ }
+}
+
+
+/**
+ * IOV_MAX (run-time) value
+ */
+# define mhd_IOV_MAX mhd_iov_max_
+#else /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */
+# define iov_max_init_() (void) 0
+# if defined(IOV_MAX)
+
+/**
+ * IOV_MAX (static) value
+ */
+# define mhd_IOV_MAX IOV_MAX
+# endif /* IOV_MAX */
+#endif /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */
+
+
+/**
+ * Initialises static variables
+ */
+void
+mhd_send_init_static_vars (void)
+{
+ /* FreeBSD 11 and later allow to specify read-ahead size
+ * and handles SF_NODISKIO differently.
+ * SF_FLAGS defined only on FreeBSD 11 and later. */
+ freebsd_sendfile_init_ ();
+
+ iov_max_init_ ();
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_connection_set_nodelay_state (struct MHD_Connection *connection,
+ bool nodelay_state)
+{
+#ifdef TCP_NODELAY
+ static const mhd_SCKT_OPT_BOOL off_val = 0;
+ static const mhd_SCKT_OPT_BOOL on_val = 1;
+ int err_code;
+
+ if (mhd_T_IS_YES (connection->is_nonip))
+ return false;
+
+ if (0 == setsockopt (connection->socket_fd,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (const void *) (nodelay_state ? &on_val : &off_val),
+ sizeof (off_val)))
+ {
+ connection->sk_nodelay = nodelay_state ? mhd_T_YES : mhd_T_NO;
+ return true;
+ }
+
+ err_code = mhd_SCKT_GET_LERR ();
+ if ((mhd_T_IS_NOT_YES (connection->is_nonip)) &&
+ (mhd_SCKT_ERR_IS_EINVAL (err_code) ||
+ mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) ||
+ mhd_SCKT_ERR_IS_NOTSOCK (err_code)))
+ {
+ connection->is_nonip = mhd_T_YES;
+ }
+ else
+ {
+ mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_NODELAY_FAILED, \
+ "Failed to set required TCP_NODELAY option for the socket.");
+ }
+#else /* ! TCP_NODELAY */
+ (void) nodelay_state; /* Mute compiler warnings */
+ connection->sk_nodelay = mhd_T_NO;
+#endif /* ! TCP_NODELAY */
+ return false;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_connection_set_cork_state (struct MHD_Connection *connection,
+ bool cork_state)
+{
+#if defined(mhd_TCP_CORK_NOPUSH)
+ static const mhd_SCKT_OPT_BOOL off_val = 0;
+ static const mhd_SCKT_OPT_BOOL on_val = 1;
+ int err_code;
+
+ if (mhd_T_IS_YES (connection->is_nonip))
+ return false;
+
+ if (0 == setsockopt (connection->socket_fd,
+ IPPROTO_TCP,
+ mhd_TCP_CORK_NOPUSH,
+ (const void *) (cork_state ? &on_val : &off_val),
+ sizeof (off_val)))
+ {
+ connection->sk_corked = cork_state ? mhd_T_YES : mhd_T_NO;
+ return true;
+ }
+
+ err_code = mhd_SCKT_GET_LERR ();
+ if ((mhd_T_IS_NOT_YES (connection->is_nonip)) &&
+ (mhd_SCKT_ERR_IS_EINVAL (err_code) ||
+ mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) ||
+ mhd_SCKT_ERR_IS_NOTSOCK (err_code)))
+ {
+ connection->is_nonip = mhd_T_YES;
+ }
+ else
+ {
+# ifdef TCP_CORK
+ mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
+ "Failed to set required TCP_CORK option for the socket.");
+# else
+ mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
+ "Failed to set required TCP_NOPUSH option for the socket.");
+# endif
+ }
+
+#else /* ! mhd_TCP_CORK_NOPUSH */
+ (void) cork_state; /* Mute compiler warnings. */
+ connection->sk_corked = mhd_T_NO;
+#endif /* ! mhd_TCP_CORK_NOPUSH */
+ return false;
+}
+
+
+/**
+ * Handle pre-send setsockopt calls.
+ *
+ * @param connection the MHD_Connection structure
+ * @param plain_send set to true if plain send() or sendmsg() will be called,
+ * set to false if TLS socket send(), sendfile() or
+ * writev() will be called.
+ * @param push_data whether to push data to the network from buffers after
+ * the next call of send function.
+ */
+static void
+pre_send_setopt (struct MHD_Connection *connection,
+ bool plain_send,
+ bool push_data)
+{
+ /* Try to buffer data if not sending the final piece.
+ * Final piece is indicated by push_data == true. */
+ const bool buffer_data = (! push_data);
+
+ if (mhd_T_IS_YES (connection->is_nonip))
+ return;
+
+ // TODO: support inheriting of TCP_NODELAY and TCP_NOPUSH
+
+ /* The goal is to minimise the total number of additional sys-calls
+ * before and after send().
+ * The following tricky (over-)complicated algorithm typically use zero,
+ * one or two additional sys-calls (depending on OS) for each response. */
+
+ if (buffer_data)
+ {
+ /* Need to buffer data if possible. */
+#ifdef mhd_USE_MSG_MORE
+ if (plain_send)
+ return; /* Data is buffered by send() with MSG_MORE flag.
+ * No need to check or change anything. */
+#else /* ! mhd_USE_MSG_MORE */
+ (void) plain_send; /* Mute compiler warning. */
+#endif /* ! mhd_USE_MSG_MORE */
+
+#ifdef mhd_TCP_CORK_NOPUSH
+ if (mhd_T_IS_YES (connection->sk_corked))
+ return; /* The connection was already corked. */
+
+ /* Prefer 'cork' over 'no delay' as the 'cork' buffers better, regardless
+ * of the number of received ACKs. */
+ if (mhd_connection_set_cork_state (connection, true))
+ return; /* The connection has been corked. */
+
+ /* Failed to cork the connection.
+ * Really unlikely to happen on TCP connections. */
+#endif /* mhd_TCP_CORK_NOPUSH */
+ if (mhd_T_IS_NO (connection->sk_nodelay))
+ return; /* TCP_NODELAY was not set for the socket.
+ * Nagle's algorithm will buffer some data. */
+
+ /* Try to reset TCP_NODELAY state for the socket.
+ * Ignore possible error as no other options exist to
+ * buffer data. */
+ mhd_connection_set_nodelay_state (connection, false);
+ /* TCP_NODELAY has been (hopefully) reset for the socket.
+ * Nagle's algorithm will buffer some data. */
+ return;
+ }
+
+ /* Need to push data after the next send() */
+ /* If additional sys-call is required, prefer to make it only after the send()
+ * (if possible) as this send() may consume only part of the prepared data and
+ * more send() calls will be used. */
+#ifdef mhd_TCP_CORK_NOPUSH
+# ifdef mhd_CORK_RESET_PUSH_DATA
+# ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS
+ /* Data can be pushed immediately by uncorking socket regardless of
+ * cork state before. */
+ /* This is typical for Linux, no other kernel with
+ * such behaviour are known so far. */
+
+ /* No need to check the current state of TCP_CORK / TCP_NOPUSH
+ * as reset of cork will push the data anyway. */
+ return; /* Data may be pushed by resetting of
+ * TCP_CORK / TCP_NOPUSH after send() */
+# else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
+ /* Reset of TCP_CORK / TCP_NOPUSH will push the data
+ * only if socket is corked. */
+
+# ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
+ /* Data can be pushed immediately by setting TCP_NODELAY regardless
+ * of TCP_NODDELAY or corking state before. */
+
+ /* Dead code currently, no known kernels with such behaviour and without
+ * pushing by uncorking. */
+ return; /* Data may be pushed by setting of TCP_NODELAY after send().
+ No need to make extra sys-calls before send().*/
+# else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
+
+/* These next comment blocks are just generic description for the possible
+ * choices for the code below. */
+# ifdef mhd_NODELAY_SET_PUSH_DATA
+ /* Setting of TCP_NODELAY will push the data only if
+ * both TCP_NODELAY and TCP_CORK / TCP_NOPUSH were not set. */
+
+ /* Data can be pushed immediately by uncorking socket if
+ * socket was corked before or by setting TCP_NODELAY if
+ * socket was not corked and TCP_NODELAY was not set before. */
+
+ /* This combination not possible currently as Linux is the only kernel that
+ * pushes data by setting of TCP_NODELAY and Linux pushes data always
+ * by TCP_NODELAY, regardless previous TCP_NODELAY state. */
+# else /* ! mhd_NODELAY_SET_PUSH_DATA */
+ /* Data can be pushed immediately by uncorking socket or
+ * can be pushed by send() on uncorked socket if
+ * TCP_NODELAY was set *before*. */
+
+ /* This is typical modern FreeBSD and OpenBSD behaviour. */
+# endif /* ! mhd_NODELAY_SET_PUSH_DATA */
+
+ if (mhd_T_IS_YES (connection->sk_corked))
+ return; /* Socket is corked. Data can be pushed by resetting of
+ * TCP_CORK / TCP_NOPUSH after send() */
+ else if (mhd_T_IS_NO (connection->sk_corked))
+ {
+ /* The socket is not corked. */
+ if (mhd_T_IS_YES (connection->sk_nodelay))
+ return; /* TCP_NODELAY was already set,
+ * data will be pushed automatically by the next send() */
+# ifdef mhd_NODELAY_SET_PUSH_DATA
+ else if (mhd_T_IS_MAYBE (connection->sk_nodelay))
+ {
+ /* Setting TCP_NODELAY may push data NOW.
+ * Cork socket here and uncork after send(). */
+ if (mhd_connection_set_cork_state (connection, true))
+ return; /* The connection has been corked.
+ * Data can be pushed by resetting of
+ * TCP_CORK / TCP_NOPUSH after send() */
+ else
+ {
+ /* The socket cannot be corked.
+ * Really unlikely to happen on TCP connections */
+ /* Have to set TCP_NODELAY.
+ * If TCP_NODELAY real system state was OFF then
+ * already buffered data may be pushed NOW, but it is unlikely
+ * to happen as this is only a backup solution when corking has failed.
+ * Ignore possible error here as no other options exist to
+ * push data. */
+ mhd_connection_set_nodelay_state (connection, true);
+ /* TCP_NODELAY has been (hopefully) set for the socket.
+ * The data will be pushed by the next send(). */
+ return;
+ }
+ }
+# endif /* mhd_NODELAY_SET_PUSH_DATA */
+ else
+ {
+# ifdef mhd_NODELAY_SET_PUSH_DATA
+ /* The socket is not corked and TCP_NODELAY is switched off. */
+# else /* ! mhd_NODELAY_SET_PUSH_DATA */
+ /* The socket is not corked and TCP_NODELAY is not set or unknown. */
+# endif /* ! mhd_NODELAY_SET_PUSH_DATA */
+
+ /* At least one additional sys-call before send() is required. */
+ /* Setting TCP_NODELAY is optimal here as data will be pushed
+ * automatically by the next send() and no additional
+ * sys-call are needed after the send(). */
+ if (mhd_connection_set_nodelay_state (connection, true))
+ return;
+ else
+ {
+ /* Failed to set TCP_NODELAY for the socket.
+ * Really unlikely to happen on TCP connections. */
+ /* Cork the socket here and make additional sys-call
+ * to uncork the socket after send(). This will push the data. */
+ /* Ignore possible error here as no other options exist to
+ * push data. */
+ mhd_connection_set_cork_state (connection, true);
+ /* The connection has been (hopefully) corked.
+ * Data can be pushed by resetting of TCP_CORK / TCP_NOPUSH
+ * after send() */
+ return;
+ }
+ }
+ }
+ /* Corked state is unknown. Need to make a sys-call here otherwise
+ * data may not be pushed. */
+ if (mhd_connection_set_cork_state (connection, true))
+ return; /* The connection has been corked.
+ * Data can be pushed by resetting of
+ * TCP_CORK / TCP_NOPUSH after send() */
+ /* The socket cannot be corked.
+ * Really unlikely to happen on TCP connections */
+ if (mhd_T_IS_YES (connection->sk_nodelay))
+ return; /* TCP_NODELAY was already set,
+ * data will be pushed by the next send() */
+
+ /* Have to set TCP_NODELAY. */
+# ifdef mhd_NODELAY_SET_PUSH_DATA
+ /* If TCP_NODELAY state was unknown (external connection) then
+ * already buffered data may be pushed here, but this is unlikely
+ * to happen as it is only a backup solution when corking has failed. */
+# endif /* mhd_NODELAY_SET_PUSH_DATA */
+ /* Ignore possible error here as no other options exist to
+ * push data. */
+ mhd_connection_set_nodelay_state (connection, true);
+ /* TCP_NODELAY has been (hopefully) set for the socket.
+ * The data will be pushed by the next send(). */
+ return;
+# endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
+# endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
+# else /* ! mhd_CORK_RESET_PUSH_DATA */
+
+# ifndef mhd_NODELAY_SET_PUSH_DATA
+ /* Neither uncorking the socket or setting TCP_NODELAY
+ * push the data immediately. */
+ /* The only way to push the data is to use send() on uncorked
+ * socket with TCP_NODELAY switched on . */
+
+ /* This is old FreeBSD and Darwin behaviour. */
+
+ /* Uncork socket if socket wasn't uncorked. */
+ if (mhd_T_IS_NOT_NO (connection->sk_corked))
+ mhd_connection_set_cork_state (connection, false);
+
+ /* Set TCP_NODELAY if it wasn't set. */
+ if (mhd_T_IS_NOT_YES (connection->sk_nodelay))
+ mhd_connection_set_nodelay_state (connection, true);
+
+ return;
+# else /* mhd_NODELAY_SET_PUSH_DATA */
+ /* Setting TCP_NODELAY push the data immediately. */
+
+ /* Dead code currently as Linux kernel is only kernel which push by
+ * setting TCP_NODELAY. The same kernel push data by resetting TCP_CORK. */
+# ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
+ return; /* Data may be pushed by setting of TCP_NODELAY after send().
+ No need to make extra sys-calls before send().*/
+# else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
+ /* Cannot set TCP_NODELAY here as it would push data NOW.
+ * Set TCP_NODELAY after the send(), together if uncorking if necessary. */
+# endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
+# endif /* mhd_NODELAY_SET_PUSH_DATA */
+# endif /* ! mhd_CORK_RESET_PUSH_DATA */
+#else /* ! mhd_TCP_CORK_NOPUSH */
+ /* Buffering of data is controlled only by
+ * Nagel's algorithm. */
+ /* Set TCP_NODELAY if it wasn't set. */
+ if (mhd_T_IS_NOT_YES (connection->sk_nodelay))
+ mhd_connection_set_nodelay_state (connection, true);
+#endif /* ! mhd_TCP_CORK_NOPUSH */
+}
+
+
+#ifndef mhd_CORK_RESET_PUSH_DATA_ALWAYS
+/**
+ * Send zero-sized data
+ *
+ * This function use send of zero-sized data to kick data from the socket
+ * buffers to the network. The socket must not be corked and must have
+ * TCP_NODELAY switched on.
+ * Used only as last resort option, when other options are failed due to
+ * some errors.
+ * Should not be called on typical data processing.
+ * @return true if succeed, false if failed
+ */
+static bool
+zero_send (struct MHD_Connection *connection)
+{
+ static const int dummy = 0;
+
+ if (mhd_T_IS_YES (connection->is_nonip))
+ return false;
+ mhd_assert (mhd_T_IS_NO (connection->sk_corked));
+ mhd_assert (mhd_T_IS_YES (connection->sk_nodelay));
+ if (0 == mhd_sys_send (connection->socket_fd, &dummy, 0))
+ return true;
+ mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \
+ "Failed to push the data by zero-sized send.");
+ return false;
+}
+
+
+#endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
+
+/**
+ * Handle post-send setsockopt calls.
+ *
+ * @param connection the MHD_Connection structure
+ * @param plain_send_next set to true if plain send() or sendmsg() will be
+ * called next,
+ * set to false if TLS socket send(), sendfile() or
+ * writev() will be called next.
+ * @param push_data whether to push data to the network from buffers
+ */
+static void
+post_send_setopt (struct MHD_Connection *connection,
+ bool plain_send_next,
+ bool push_data)
+{
+ /* Try to buffer data if not sending the final piece.
+ * Final piece is indicated by push_data == true. */
+ const bool buffer_data = (! push_data);
+
+ if (mhd_T_IS_YES (connection->is_nonip))
+ return;
+ if (buffer_data)
+ return; /* Nothing to do after the send(). */
+
+#ifndef mhd_USE_MSG_MORE
+ (void) plain_send_next; /* Mute compiler warning */
+#endif /* ! mhd_USE_MSG_MORE */
+
+ /* Need to push data. */
+#ifdef mhd_TCP_CORK_NOPUSH
+ if (mhd_T_IS_YES (connection->sk_nodelay) && \
+ mhd_T_IS_NO (connection->sk_corked))
+ return; /* Data has been already pushed by last send(). */
+
+# ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS
+# ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
+# ifdef mhd_USE_MSG_MORE
+ /* This is Linux kernel.
+ * The socket is corked (or unknown) or 'no delay' is not set (or unknown).
+ * There are options:
+ * * Push the data by setting of TCP_NODELAY (without change
+ * of the cork on the socket),
+ * * Push the data by resetting of TCP_CORK.
+ * The optimal choice depends on the next final send functions
+ * used on the same socket.
+ *
+ * In general on Linux kernel TCP_NODELAY always enabled is preferred,
+ * as buffering is controlled by MSG_MORE or cork/uncork.
+ *
+ * If next send function will not support MSG_MORE (like sendfile()
+ * or TLS-connection) than push data by setting TCP_NODELAY
+ * so the socket may remain corked (no additional sys-call before
+ * next send()).
+ *
+ * If send()/sendmsg() will be used next than push data by
+ * resetting of TCP_CORK so next final send without MSG_MORE will push
+ * data to the network (without additional sys-call to push data). */
+
+ if (mhd_T_IS_NOT_YES (connection->sk_nodelay) ||
+ (! plain_send_next))
+ {
+ if (mhd_connection_set_nodelay_state (connection, true))
+ return; /* Data has been pushed by TCP_NODELAY. */
+ /* Failed to set TCP_NODELAY for the socket.
+ * Really unlikely to happen on TCP connections. */
+ if (mhd_connection_set_cork_state (connection, false))
+ return; /* Data has been pushed by uncorking the socket. */
+ /* Failed to uncork the socket.
+ * Really unlikely to happen on TCP connections. */
+
+ /* The socket cannot be uncorked, no way to push data */
+ }
+ else
+ {
+ if (mhd_connection_set_cork_state (connection, false))
+ return; /* Data has been pushed by uncorking the socket. */
+ /* Failed to uncork the socket.
+ * Really unlikely to happen on TCP connections. */
+ if (mhd_connection_set_nodelay_state (connection, true))
+ return; /* Data has been pushed by TCP_NODELAY. */
+ /* Failed to set TCP_NODELAY for the socket.
+ * Really unlikely to happen on TCP connections. */
+
+ /* The socket cannot be uncorked, no way to push data */
+ }
+# else /* ! mhd_USE_MSG_MORE */
+ /* Push data by setting TCP_NODELAY here as uncorking here
+ * would require corking the socket before sending the next response. */
+ if (mhd_connection_set_nodelay_state (connection, true))
+ return; /* Data was pushed by TCP_NODELAY. */
+ /* Failed to set TCP_NODELAY for the socket.
+ * Really unlikely to happen on TCP connections. */
+ if (mhd_connection_set_cork_state (connection, false))
+ return; /* Data was pushed by uncorking the socket. */
+ /* Failed to uncork the socket.
+ * Really unlikely to happen on TCP connections. */
+
+ /* The socket remains corked, no way to push data */
+# endif /* ! mhd_USE_MSG_MORE */
+# else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
+ if (mhd_connection_set_cork_state (connection, false))
+ return; /* Data was pushed by uncorking the socket. */
+ /* Failed to uncork the socket.
+ * Really unlikely to happen on TCP connections. */
+
+ /* Socket remains corked, no way to push data */
+# endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
+# else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
+ /* This is old FreeBSD or Darwin kernel. */
+
+ if (mhd_T_IS_NO (connection->sk_corked))
+ {
+ mhd_assert (mhd_T_IS_NOT_YES (connection->sk_nodelay));
+
+ /* Unlikely to reach this code.
+ * TCP_NODELAY should be turned on before send(). */
+ if (mhd_connection_set_nodelay_state (connection, true))
+ {
+ /* TCP_NODELAY has been set on uncorked socket.
+ * Use zero-send to push the data. */
+ if (zero_send (connection))
+ return; /* The data has been pushed by zero-send. */
+ }
+
+ /* Failed to push the data by all means. */
+ /* There is nothing left to try. */
+ }
+ else
+ {
+#ifdef mhd_CORK_RESET_PUSH_DATA
+ enum mhd_Tristate old_cork_state = connection->sk_corked;
+#endif /* mhd_CORK_RESET_PUSH_DATA */
+ /* The socket is corked or cork state is unknown. */
+
+ if (mhd_connection_set_cork_state (connection, false))
+ {
+#ifdef mhd_CORK_RESET_PUSH_DATA
+ /* Modern FreeBSD or OpenBSD kernel */
+ if (mhd_T_IS_YES (old_cork_state))
+ return; /* Data has been pushed by uncorking the socket. */
+#endif /* mhd_CORK_RESET_PUSH_DATA */
+
+ /* Unlikely to reach this code.
+ * The data should be pushed by uncorking (FreeBSD) or
+ * the socket should be uncorked before send(). */
+ if (mhd_T_IS_YES (connection->sk_nodelay) ||
+ (mhd_connection_set_nodelay_state (connection, true)))
+ {
+ /* TCP_NODELAY is turned ON on uncorked socket.
+ * Use zero-send to push the data. */
+ if (zero_send (connection))
+ return; /* The data has been pushed by zero-send. */
+ }
+ }
+ /* Data cannot be pushed. */
+ }
+#endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
+#else /* ! mhd_TCP_CORK_NOPUSH */
+ /* Corking is not supported. Buffering is controlled
+ * by TCP_NODELAY only. */
+ mhd_assert (mhd_T_IS_NOT_YES (connection->sk_corked));
+ if (mhd_T_IS_YES (connection->sk_nodelay))
+ return; /* Data was already pushed by send(). */
+
+ /* Unlikely to reach this code.
+ * TCP_NODELAY should be turned on before send(). */
+ if (mhd_connection_set_nodelay_state (connection, true))
+ {
+ /* TCP_NODELAY has been set.
+ * Use zero-send to try to push the data. */
+ if (zero_send (connection))
+ return; /* The data has been pushed by zero-send. */
+ }
+
+ /* Failed to push the data. */
+#endif /* ! mhd_TCP_CORK_NOPUSH */
+ mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED, \
+ "Failed to force flush the last part of the response header " \
+ "or the response content that might have been buffered by " \
+ "the kernel. The client may experience some delay (usually " \
+ "in range 200ms - 5 sec).");
+ return;
+}
+
+
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2)
+MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
+mhd_plain_send (struct MHD_Connection *restrict c,
+ size_t buf_size,
+ const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+ bool push_data,
+ size_t *restrict sent)
+{
+ /* plaintext transmission */
+ ssize_t res;
+ bool full_buf_sent;
+
+ if (buf_size > MHD_SCKT_SEND_MAX_SIZE_)
+ {
+ buf_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
+ push_data = false; /* Incomplete send */
+ }
+
+ pre_send_setopt (c, true, push_data);
+#ifdef mhd_USE_MSG_MORE
+ res = mhd_sys_send4 (c->socket_fd,
+ buf,
+ buf_size,
+ push_data ? 0 : MSG_MORE);
+#else
+ res = mhd_sys_send4 (c->socket_fd,
+ buf,
+ buf_size,
+ 0);
+#endif
+
+ if (0 >= res)
+ {
+ enum mhd_SocketError err;
+
+ err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
+
+ if (mhd_SOCKET_ERR_AGAIN == err)
+ c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) c->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
+ return err;
+ }
+ *sent = (size_t) res;
+
+ full_buf_sent = (buf_size == (size_t) res);
+
+ if (! full_buf_sent)
+ c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) c->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
+ /* If there is a need to push the data from network buffers
+ * call post_send_setopt(). */
+ /* It's unknown whether sendfile() (or other send function without
+ * MSG_MORE support) will be used for the next reply so assume
+ * that next sending will be the same, like this call. */
+ if (push_data && full_buf_sent)
+ post_send_setopt (c, false, push_data);
+
+ return mhd_SOCKET_ERR_NO_ERROR;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2)
+MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
+mhd_send_data (struct MHD_Connection *restrict connection,
+ size_t buf_size,
+ const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+ bool push_data,
+ size_t *restrict sent)
+{
+ const bool tls_conn = false; // TODO: TLS support
+
+ mhd_assert (MHD_INVALID_SOCKET != connection->socket_fd);
+ mhd_assert (MHD_CONNECTION_CLOSED != connection->state);
+
+ if (tls_conn)
+ {
+ enum mhd_SocketError ret;
+
+#ifdef HTTPS_SUPPORT
+ pre_send_setopt (connection,
+ (! tls_conn),
+ push_data);
+ ret = mhd_SOCKET_ERR_OTHER;
+ mhd_assert (0 && "Not implemented yet");
+#else /* ! HTTPS_SUPPORT */
+ ret = mhd_SOCKET_ERR_NOTCONN;
+#endif /* ! HTTPS_SUPPORT */
+ return ret;
+ }
+
+ return mhd_plain_send (connection,
+ buf_size,
+ buf,
+ push_data,
+ sent);
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3)
+MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_IN_SIZE_ (6,5) enum mhd_SocketError
+mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
+ size_t header_size,
+ const char *restrict header,
+ bool never_push_hdr,
+ size_t body_size,
+ const char *restrict body,
+ bool complete_response,
+ size_t *restrict sent)
+{
+ mhd_iov_ret_type res;
+ bool send_error;
+ bool push_hdr;
+ bool push_body;
+ MHD_Socket s = connection->socket_fd;
+#ifdef mhd_USE_VECT_SEND
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+ struct iovec vector[2];
+#ifdef HAVE_SENDMSG
+ struct msghdr msg;
+#endif /* HAVE_SENDMSG */
+#endif /* HAVE_SENDMSG || HAVE_WRITEV */
+#ifdef _WIN32
+ WSABUF vector[2];
+ DWORD vec_sent;
+#endif /* _WIN32 */
+ bool no_vec; /* Is vector-send() disallowed? */
+
+ no_vec = false;
+#ifdef HTTPS_SUPPORT
+ no_vec = no_vec || (false); // TODO: TLS support
+#endif /* HTTPS_SUPPORT */
+#if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \
+ defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
+ defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
+ no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
+ ! connection->sk_spipe_suppress);
+#endif /* (!HAVE_SENDMSG || ! MSG_NOSIGNAL) &&
+ mhd_SEND_SPIPE_SUPPRESS_POSSIBLE &&
+ mhd_SEND_SPIPE_SUPPRESS_NEEDED */
+#endif /* mhd_USE_VECT_SEND */
+
+ mhd_assert ( (NULL != body) || (0 == body_size) );
+
+ mhd_assert (MHD_INVALID_SOCKET != s);
+ mhd_assert (MHD_CONNECTION_CLOSED != connection->state);
+
+ push_body = complete_response;
+
+ if (! never_push_hdr)
+ {
+ if (! complete_response)
+ push_hdr = true; /* Push the header as the client may react
+ * on header alone while the body data is
+ * being prepared. */
+ else
+ {
+ if (1400 > (header_size + body_size))
+ push_hdr = false; /* Do not push the header as complete
+ * reply is already ready and the whole
+ * reply most probably will fit into
+ * the single IP packet. */
+ else
+ push_hdr = true; /* Push header alone so client may react
+ * on it while reply body is being delivered. */
+ }
+ }
+ else
+ push_hdr = false;
+
+ if (complete_response && (0 == body_size))
+ push_hdr = true; /* The header alone is equal to the whole response. */
+
+#ifndef mhd_USE_VECT_SEND
+ no_vec = (no_vec || true);
+#else /* mhd_USE_VECT_SEND */
+ no_vec = (no_vec || (0 == body_size));
+ no_vec = (no_vec || ((sizeof(mhd_iov_elmn_size) <= sizeof(size_t)) &&
+ (((size_t) mhd_IOV_ELMN_MAX_SIZE) < header_size)));
+#endif /* mhd_USE_VECT_SEND */
+
+
+ if (no_vec)
+ {
+ enum mhd_SocketError ret;
+ ret = mhd_send_data (connection,
+ header_size,
+ header,
+ push_hdr,
+ sent);
+
+ // TODO: check 'send-ready'
+ if ((mhd_SOCKET_ERR_NO_ERROR == ret) &&
+ (header_size == *sent) &&
+ (0 != body_size) &&
+ (header_size < header_size + body_size) &&
+ (connection->sk_nonblck))
+ {
+ size_t sent_b;
+ /* The header has been sent completely.
+ * Try to send the reply body without waiting for
+ * the next round. */
+
+ ret = mhd_send_data (connection,
+ body_size,
+ body,
+ push_body,
+ &sent_b);
+
+ if (mhd_SOCKET_ERR_NO_ERROR == ret)
+ *sent += sent_b;
+ else if (mhd_SOCKET_ERR_IS_HARD (ret))
+ return ret; /* Unrecoverable error */
+
+ return mhd_SOCKET_ERR_NO_ERROR; /* The header has been sent successfully */
+ }
+ return ret;
+ }
+#ifdef mhd_USE_VECT_SEND
+
+ if (header_size > (header_size + body_size))
+ {
+ /* Return value limit */
+ body_size = SIZE_MAX - header_size;
+ complete_response = false;
+ push_body = complete_response;
+ }
+ if (((mhd_iov_ret_type) (header_size + body_size)) < 0 ||
+ ((size_t) (mhd_iov_ret_type) (header_size + body_size)) !=
+ (header_size + body_size))
+ {
+ /* Send sys-call total amount limit */
+ body_size = mhd_IOV_RET_MAX_SIZE - header_size;
+ complete_response = false;
+ push_body = complete_response;
+ }
+
+ pre_send_setopt (connection,
+#ifdef HAVE_SENDMSG
+ true,
+#else /* ! HAVE_SENDMSG */
+ false,
+#endif /* ! HAVE_SENDMSG */
+ push_hdr || push_body);
+ send_error = false;
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+ vector[0].iov_base = mhd_DROP_CONST (header);
+ vector[0].iov_len = header_size;
+ vector[1].iov_base = mhd_DROP_CONST (body);
+ vector[1].iov_len = body_size;
+
+#if defined(HAVE_SENDMSG)
+ memset (&msg, 0, sizeof(msg));
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = vector;
+ msg.msg_iovlen = 2;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ res = sendmsg (s, &msg, mhd_MSG_NOSIGNAL
+ | ((push_hdr || push_body) ? 0 : mhd_MSG_MORE));
+#elif defined(HAVE_WRITEV)
+ res = writev (s, vector, 2);
+#endif /* HAVE_WRITEV */
+ if (0 < res)
+ *sent = (size_t) res;
+ else
+ send_error = true;
+#endif /* HAVE_SENDMSG || HAVE_WRITEV */
+#ifdef _WIN32
+ if (((mhd_iov_elmn_size) body_size) != body_size)
+ {
+ /* Send item size limit */
+ body_size = mhd_IOV_ELMN_MAX_SIZE;
+ complete_response = false;
+ push_body = complete_response;
+ }
+ vector[0].buf = (char *) mhd_DROP_CONST (header);
+ vector[0].len = (unsigned long) header_size;
+ vector[1].buf = (char *) mhd_DROP_CONST (body);
+ vector[1].len = (unsigned long) body_size;
+
+ res = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
+ if (0 == res)
+ *sent = (size_t) vec_sent;
+ else
+ send_error = true;
+#endif /* _WIN32 */
+
+ if (send_error)
+ {
+ enum mhd_SocketError err;
+
+ err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
+
+ if (mhd_SOCKET_ERR_AGAIN == err)
+ connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) connection->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
+ return err;
+ }
+ if ((header_size + body_size) > *sent)
+ connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) connection->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
+ /* If there is a need to push the data from network buffers
+ * call post_send_setopt(). */
+ if ( (push_body) &&
+ ((header_size + body_size) == *sent) )
+ {
+ /* Complete reply has been sent. */
+ /* If TLS connection is used then next final send() will be
+ * without MSG_MORE support. If non-TLS connection is used
+ * it's unknown whether next 'send' will be plain send() / sendmsg() or
+ * sendfile() will be used so assume that next final send() will be
+ * the same, like for this response. */
+ post_send_setopt (connection,
+#ifdef HAVE_SENDMSG
+ true, /* Assume the same type of the send function */
+#else /* ! HAVE_SENDMSG */
+ false, /* Assume the same type of the send function */
+#endif /* ! HAVE_SENDMSG */
+ true);
+ }
+ else if ( (push_hdr) &&
+ (header_size <= *sent))
+ {
+ /* The header has been sent completely and there is a
+ * need to push the header data. */
+ /* Luckily the type of send function will be used next is known. */
+ post_send_setopt (connection,
+ true,
+ true);
+ }
+
+ return mhd_SOCKET_ERR_NO_ERROR;
+#else /* ! mhd_USE_VECT_SEND */
+ mhd_assert (0 && "Should be unreachable");
+ return mhd_SOCKET_ERR_INTERNAL; /* Unreachable. Mute warnings. */
+#endif /* ! mhd_USE_VECT_SEND */
+}
+
+
+#if defined(MHD_USE_SENDFILE)
+
+#if defined(HAVE_LINUX_SENDFILE) && defined(HAVE_SENDFILE64)
+# define mhd_off_t off64_t
+#else
+# define mhd_off_t off_t
+#endif
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) enum mhd_SocketError
+mhd_send_sendfile (struct MHD_Connection *restrict c,
+ size_t *restrict sent)
+{
+ enum mhd_SocketError ret;
+ const bool used_thr_p_c =
+ mhd_D_HAS_THR_PER_CONN (c->daemon);
+ const size_t chunk_size =
+ used_thr_p_c ?
+ mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C : mhd_SENFILE_CHUNK_SIZE;
+ const int file_fd = c->rp.response->cntn.file.fd;
+ mhd_off_t offset;
+ size_t send_size;
+ size_t sent_bytes;
+ bool push_data;
+ bool fallback_to_filereader;
+ mhd_assert (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc);
+ mhd_assert (MHD_SIZE_UNKNOWN != c->rp.response->cntn_size);
+ mhd_assert (chunk_size <= (size_t) SSIZE_MAX);
+ // mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS)); // TODO: TLS support
+
+ send_size = 0;
+ push_data = true;
+ if (1)
+ {
+ bool too_large;
+ uint_fast64_t left;
+
+ offset = (mhd_off_t)
+ (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset);
+ too_large = (((uint_fast64_t) offset) < c->rp.rsp_cntn_read_pos);
+ too_large = too_large ||
+ (((uint_fast64_t) offset) !=
+ (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset));
+ too_large = too_large || (0 > offset);
+ if (too_large)
+ { /* Retry to send with file reader and standard 'send()'. */
+ c->rp.response->cntn.file.use_sf = false;
+ return mhd_SOCKET_ERR_INTR;
+ }
+
+ left = c->rp.response->cntn_size - c->rp.rsp_cntn_read_pos;
+
+ /* Do not allow system to stick sending on single fast connection:
+ * use 128KiB chunks (2MiB for thread-per-connection). */
+ if (chunk_size < left) /* This also limit to SSIZE_MAX automatically */
+ {
+ send_size = chunk_size;
+ push_data = false; /* No need to push data, there is more to send. */
+ }
+ else
+ send_size = (size_t) left;
+ }
+ mhd_assert (0 != send_size);
+
+ pre_send_setopt (c, false, push_data);
+
+ sent_bytes = 0;
+ ret = mhd_SOCKET_ERR_NO_ERROR;
+ fallback_to_filereader = false;
+#if defined(HAVE_LINUX_SENDFILE)
+ if (1)
+ {
+ ssize_t res;
+#ifndef HAVE_SENDFILE64
+ ret = sendfile (c->socket_fd,
+ file_fd,
+ &offset,
+ send_size);
+#else /* HAVE_SENDFILE64 */
+ res = sendfile64 (c->socket_fd,
+ file_fd,
+ &offset,
+ send_size);
+#endif /* HAVE_SENDFILE64 */
+ if (0 > res)
+ {
+ const int sk_err = mhd_SCKT_GET_LERR ();
+
+ if ((EINVAL == sk_err) ||
+ (EOVERFLOW == sk_err) ||
+#ifdef EIO
+ (EIO == sk_err) ||
+#endif
+#ifdef EAFNOSUPPORT
+ (EAFNOSUPPORT == sk_err) ||
+#endif
+ (EOPNOTSUPP == sk_err))
+ fallback_to_filereader = true;
+ else
+ ret = mhd_socket_error_get_from_sys_err (sk_err);
+ }
+ else
+ sent_bytes = (size_t) res;
+ }
+#elif defined(HAVE_FREEBSD_SENDFILE)
+ if (1)
+ {
+ off_t sent_bytes_offt = 0;
+ int flags = 0;
+ bool sent_something = false;
+#ifdef SF_FLAGS
+ flags = used_thr_p_c ?
+ freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
+#endif /* SF_FLAGS */
+ if (0 != sendfile (file_fd,
+ c->socket_fd,
+ offset,
+ send_size,
+ NULL,
+ &sent_bytes_offt,
+ flags))
+ {
+ const int sk_err = mhd_SCKT_GET_LERR ();
+
+ sent_something =
+ (((EAGAIN == sk_err) || (EBUSY == sk_err) || (EINTR == sk_err)) &&
+ (0 != sent_bytes_offt));
+
+ if (! sent_something)
+ {
+ enum mhd_SocketError err;
+ if ((EINVAL == sk_err) ||
+ (EIO == sk_err) ||
+ (EOPNOTSUPP == sk_err))
+ fallback_to_filereader = true;
+ else
+ ret = mhd_socket_error_get_from_sys_err (sk_err);
+ }
+ }
+ else
+ sent_something = true;
+
+ if (sent_something)
+ {
+ mhd_assert (0 <= sent_bytes_offt);
+ mhd_assert (SIZE_MAX >= sent_bytes_offt);
+ sent_bytes = (size_t) sent_bytes_offt;
+ }
+ }
+#elif defined(HAVE_DARWIN_SENDFILE)
+ if (1)
+ {
+ off_t len;
+ bool sent_something;
+
+ sent_something = false;
+ len = (off_t) send_size; /* chunk always fit */
+
+ if (0 != sendfile (file_fd,
+ c->socket_fd,
+ offset,
+ &len,
+ NULL,
+ 0))
+ {
+ const int sk_err = mhd_SCKT_GET_LERR ();
+
+ sent_something =
+ ((EAGAIN == sk_err) || (EINTR == sk_err)) &&
+ (0 != len);
+
+ if (! sent_something)
+ {
+ enum mhd_SocketError err;
+ if ((ENOTSUP == sk_err) ||
+ (EOPNOTSUPP == sk_err))
+ fallback_to_filereader = true;
+ else
+ ret = mhd_socket_error_get_from_sys_err (sk_err);
+ }
+ }
+ else
+ sent_something = true;
+
+ if (sent_something)
+ {
+ mhd_assert (0 <= len);
+ mhd_assert (SIZE_MAX >= len);
+ sent_bytes = (size_t) len;
+ }
+ }
+#else
+#error No sendfile() function
+#endif
+
+ mhd_assert (send_size >= sent_bytes);
+
+ /* Some platforms indicate "beyond of the end of the file" by returning
+ * success with zero bytes. Let filereader to re-detect this kind of error. */
+ if ((fallback_to_filereader) ||
+ ((mhd_SOCKET_ERR_NO_ERROR == ret) && (0 == sent_bytes)))
+ { /* Retry to send with file reader and standard 'send()'. */
+ c->rp.response->cntn.file.use_sf = false;
+ return mhd_SOCKET_ERR_INTR;
+ }
+
+ if ((mhd_SOCKET_ERR_AGAIN == ret) ||
+ ((mhd_SOCKET_ERR_NO_ERROR == ret) && (send_size > sent_bytes)))
+ c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) c->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
+ if (mhd_SOCKET_ERR_NO_ERROR != ret)
+ return ret;
+
+ /* If there is a need to push the data from network buffers
+ * call post_send_setopt(). */
+ /* It's unknown whether sendfile() will be used in the next
+ * response so assume that next response will be the same. */
+ if ((push_data) &&
+ (send_size == sent_bytes))
+ post_send_setopt (c, true, push_data);
+
+ *sent = sent_bytes;
+ return ret;
+}
+
+
+#endif /* MHD_USE_SENDFILE */
+
+#if defined(mhd_USE_VECT_SEND)
+
+
+/**
+ * Function sends iov data by system sendmsg or writev function.
+ *
+ * Connection must be in non-TLS (non-HTTPS) mode.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov data structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ * system buffers (usually set for the last piece of data),
+ * set to false to prefer holding incomplete network packets
+ * (more data will be send for the same reply).
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ * the sent size) or socket error
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+send_iov_nontls (struct MHD_Connection *restrict connection,
+ struct mhd_iovec_track *const restrict r_iov,
+ bool push_data,
+ size_t *restrict sent)
+{
+ bool send_error;
+ size_t items_to_send;
+#ifndef MHD_WINSOCK_SOCKETS
+ ssize_t res;
+#endif
+#ifdef HAVE_SENDMSG
+ struct msghdr msg;
+#elif defined(MHD_WINSOCK_SOCKETS)
+ DWORD bytes_sent;
+ DWORD cnt_w;
+#endif /* MHD_WINSOCK_SOCKETS */
+
+ // TODO: assert for non-TLS
+
+ mhd_assert (MHD_INVALID_SOCKET != connection->socket_fd);
+ mhd_assert (MHD_CONNECTION_CLOSED != connection->state);
+
+ send_error = false;
+ items_to_send = r_iov->cnt - r_iov->sent;
+#ifdef mhd_IOV_MAX
+ if (mhd_IOV_MAX < items_to_send)
+ {
+ mhd_assert (0 < mhd_IOV_MAX);
+ if (0 == mhd_IOV_MAX)
+ return mhd_SOCKET_ERR_INTERNAL; /* Should never happen */
+ items_to_send = mhd_IOV_MAX;
+ push_data = false; /* Incomplete response */
+ }
+#endif /* mhd_IOV_MAX */
+#ifdef HAVE_SENDMSG
+ memset (&msg, 0, sizeof(struct msghdr));
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = r_iov->iov + r_iov->sent;
+ msg.msg_iovlen = items_to_send;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ pre_send_setopt (connection, true, push_data);
+ res = sendmsg (connection->socket_fd, &msg,
+ mhd_MSG_NOSIGNAL | (push_data ? 0 : mhd_MSG_MORE));
+ if (0 < res)
+ *sent = (size_t) res;
+ else
+ send_error = true;
+#elif defined(HAVE_WRITEV)
+ pre_send_setopt (connection, false, push_data);
+ res = writev (connection->socket_fd, r_iov->iov + r_iov->sent,
+ items_to_send);
+ if (0 < res)
+ *sent = (size_t) res;
+ else
+ send_error = true;
+#elif defined(MHD_WINSOCK_SOCKETS)
+#ifdef _WIN64
+ if (items_to_send > ULONG_MAX)
+ {
+ cnt_w = ULONG_MAX;
+ push_data = false; /* Incomplete response */
+ }
+ else
+ cnt_w = (DWORD) items_to_send;
+#else /* ! _WIN64 */
+ cnt_w = (DWORD) items_to_send;
+#endif /* ! _WIN64 */
+ pre_send_setopt (connection, true, push_data);
+ if (0 == WSASend (connection->socket_fd,
+ (LPWSABUF) (r_iov->iov + r_iov->sent),
+ cnt_w,
+ &bytes_sent, 0, NULL, NULL))
+ *sent = (size_t) bytes_sent;
+ else
+ send_error = true;
+#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_WINSOCK_SOCKETS */
+#error No vector-send function available
+#endif
+
+ if (send_error)
+ {
+ enum mhd_SocketError err;
+
+ err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
+
+ if (mhd_SOCKET_ERR_AGAIN == err)
+ connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) connection->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
+ return err;
+ }
+
+ /* Some data has been sent */
+ if (1)
+ {
+ size_t track_sent = (size_t) *sent;
+ /* Adjust the internal tracking information for the iovec to
+ * take this last send into account. */
+ while ((0 != track_sent) && (r_iov->iov[r_iov->sent].iov_len <= track_sent))
+ {
+ track_sent -= r_iov->iov[r_iov->sent].iov_len;
+ r_iov->sent++; /* The iov element has been completely sent */
+ mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == track_sent));
+ }
+
+ if (r_iov->cnt == r_iov->sent)
+ post_send_setopt (connection, true, push_data);
+ else
+ {
+ connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) connection->sk_ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+ if (0 != track_sent)
+ {
+ mhd_assert (r_iov->cnt > r_iov->sent);
+ /* The last iov element has been partially sent */
+ r_iov->iov[r_iov->sent].iov_base =
+ (void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + track_sent);
+ r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) track_sent;
+ }
+ }
+ }
+
+ return mhd_SOCKET_ERR_NO_ERROR;
+}
+
+
+#endif /* mhd_USE_VECT_SEND */
+
+#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \
+ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+
+
+/**
+ * Function sends iov data by sending buffers one-by-one by standard
+ * data send function.
+ *
+ * Connection could be in HTTPS or non-HTTPS mode.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov data structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ * system buffers (usually set for the last piece of data),
+ * set to false to prefer holding incomplete network packets
+ * (more data will be send for the same reply).
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ * the sent size) or socket error
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+send_iov_emu (struct MHD_Connection *restrict connection,
+ struct mhd_iovec_track *const restrict r_iov,
+ bool push_data,
+ size_t *restrict sent)
+{
+ const bool non_blk = connection->sk_nonblck;
+ size_t total_sent;
+ size_t max_elelements_to_sent;
+
+ mhd_assert (NULL != r_iov->iov);
+ total_sent = 0;
+ max_elelements_to_sent = 8; /* Do not make too many sys-calls for just one connection */
+ do
+ {
+ enum mhd_SocketError res;
+ size_t sent_el_size;
+
+ if (total_sent > (size_t) (r_iov->iov[r_iov->sent].iov_len + total_sent))
+ break; /* return value would overflow */
+
+ res = mhd_send_data (connection,
+ r_iov->iov[r_iov->sent].iov_len,
+ r_iov->iov[r_iov->sent].iov_base,
+ push_data && (r_iov->cnt == r_iov->sent + 1),
+ &sent_el_size);
+ if (mhd_SOCKET_ERR_NO_ERROR == res)
+ {
+ /* Result is an error */
+ if (0 == total_sent)
+ return res; /* Nothing was sent, return error as is */
+
+ if (mhd_SOCKET_ERR_IS_HARD (res))
+ return res; /* Any kind of a hard error */
+
+ break; /* Return the amount of the sent data */
+ }
+
+ total_sent += sent_el_size;
+
+ if (r_iov->iov[r_iov->sent].iov_len != sent_el_size)
+ {
+ /* Incomplete buffer has been sent.
+ * Adjust buffer of the last element. */
+ r_iov->iov[r_iov->sent].iov_base =
+ (void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + sent_el_size);
+ r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) sent_el_size;
+
+ break; /* Return the amount of the sent data */
+ }
+ /* The iov element has been completely sent */
+ r_iov->sent++;
+ } while ((r_iov->cnt > r_iov->sent) && 0 != (--max_elelements_to_sent) &&
+ (non_blk));
+
+ mhd_assert (0 != total_sent);
+ *sent = total_sent;
+ return mhd_SOCKET_ERR_NO_ERROR;
+}
+
+
+#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT
+ || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_send_iovec (struct MHD_Connection *restrict connection,
+ struct mhd_iovec_track *const restrict r_iov,
+ bool push_data,
+ size_t *restrict sent)
+{
+#ifdef mhd_USE_VECT_SEND
+#if defined(HTTPS_SUPPORT) || \
+ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+ bool use_iov_send = true;
+#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+#endif /* mhd_USE_VECT_SEND */
+
+ mhd_assert (NULL != connection->rp.resp_iov.iov);
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == \
+ connection->rp.response->cntn_dtype);
+ mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent);
+#ifdef mhd_USE_VECT_SEND
+#if defined(HTTPS_SUPPORT) || \
+ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+#ifdef HTTPS_SUPPORT
+ use_iov_send = use_iov_send &&
+ (true); // TODO: TLS support
+#endif /* HTTPS_SUPPORT */
+#ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
+ use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
+ connection->sk_spipe_suppress);
+#endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+ if (use_iov_send)
+#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+ return send_iov_nontls (connection, r_iov, push_data, sent);
+#endif /* mhd_USE_VECT_SEND */
+
+#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \
+ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+ return send_iov_emu (connection, r_iov, push_data, sent);
+#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT
+ || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+}
diff --git a/src/mhd2/mhd_send.h b/src/mhd2/mhd_send.h
@@ -0,0 +1,174 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2017-2024 Evgeny Grin (Karlson2k)
+ Copyright (C) 2019 ng0
+
+ 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_send.h
+ * @brief Declarations of send() wrappers.
+ * @author Karlson2k (Evgeny Grin)
+ * @author ng0 (N. Gillmann)
+ */
+
+#ifndef MHD_SEND_H
+#define MHD_SEND_H
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+#include "mhd_socket_error.h"
+
+struct MHD_Connection; /* forward declaration */
+struct mhd_iovec_track; /* forward declaration */
+
+/**
+ * Initialises static variables
+ */
+void
+mhd_send_init_static_vars (void);
+
+
+/**
+ * Send buffer to the client, push data from network buffer if requested
+ * and full buffer is sent.
+ *
+ * @param connection the MHD_Connection structure
+ * @param buffer_size the size of the @a buffer (in bytes)
+ * @param buffer content of the buffer to send
+ * @param push_data set to true to force push the data to the network from
+ * system buffers (usually set for the last piece of data),
+ * set to false to prefer holding incomplete network packets
+ * (more data will be send for the same reply).
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ * the sent size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_send_data (struct MHD_Connection *restrict connection,
+ size_t buf_size,
+ const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+ bool push_data,
+ size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (5);
+
+
+/**
+ * Send reply header with optional reply body.
+ *
+ * @param connection the MHD_Connection structure
+ * @param header content of header to send
+ * @param header_size the size of the @a header (in bytes)
+ * @param never_push_hdr set to true to disable internal algorithm
+ * that can push automatically header data
+ * alone to the network
+ * @param body content of the body to send (optional, may be NULL)
+ * @param body_size the size of the @a body (in bytes)
+ * @param complete_response set to true if complete response
+ * is provided by @a header and @a body,
+ * set to false if additional body data
+ * will be sent later
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * in total (from both buffers combined)
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ * the sent size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
+ size_t header_size,
+ const char *restrict header,
+ bool never_push_hdr,
+ size_t body_size,
+ const char *restrict body,
+ bool complete_response,
+ size_t *restrict sent)
+MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_NONNULL_(3)
+MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_IN_SIZE_ (6,5) MHD_FN_PAR_OUT_ (8);
+
+#if defined(MHD_USE_SENDFILE)
+/**
+ * Function for sending responses backed by file FD.
+ *
+ * @param connection the MHD connection structure
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * in total (from both buffers combined)
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ * the sent size) or socket error
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2) enum mhd_SocketError
+mhd_send_sendfile (struct MHD_Connection *restrict c,
+ size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif
+
+
+/**
+ * Function for sending responses backed by a an array of memory buffers.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov response structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ * system buffers (usually set for the last piece of data),
+ * set to false to prefer holding incomplete network packets
+ * (more data will be send for the same reply).
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * in total (from both buffers combined)
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ * the sent size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_send_iovec (struct MHD_Connection *restrict connection,
+ struct mhd_iovec_track *const restrict r_iov,
+ bool push_data,
+ size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4);
+
+/**
+ * Set required TCP_NODELAY state for connection socket
+ *
+ * The function automatically updates sk_nodelay state.
+ * @param connection the connection to manipulate
+ * @param nodelay_state the requested new state of socket
+ * @return true if succeed, false if failed or not supported
+ * by the current platform / kernel.
+ */
+MHD_INTERNAL bool
+mhd_connection_set_nodelay_state (struct MHD_Connection *connection,
+ bool nodelay_state)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Set required cork state for connection socket
+ *
+ * The function automatically updates sk_corked state.
+ *
+ * @param connection the connection to manipulate
+ * @param cork_state the requested new state of socket
+ * @return true if succeed, false if failed or not supported
+ * by the current platform / kernel.
+ */
+MHD_INTERNAL bool
+mhd_connection_set_cork_state (struct MHD_Connection *connection,
+ bool cork_state)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* MHD_SEND_H */
diff --git a/src/mhd2/mhd_socket_error.c b/src/mhd2/mhd_socket_error.c
@@ -0,0 +1,86 @@
+/*
+ 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.c
+ * @brief The definition of mhd_SocketError-related functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "mhd_socket_error.h"
+#include "sys_sockets_headers.h"
+#include "mhd_sockets_macros.h"
+#include "sys_sockets_types.h"
+
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_sys_err (int socket_err)
+{
+ if (mhd_SCKT_ERR_IS_EAGAIN (socket_err))
+ return mhd_SOCKET_ERR_AGAIN;
+ else if (mhd_SCKT_ERR_IS_CONNRESET (socket_err))
+ return mhd_SOCKET_ERR_CONNRESET;
+ else if (mhd_SCKT_ERR_IS_EINTR (socket_err))
+ return mhd_SOCKET_ERR_INTR;
+ else if (mhd_SCKT_ERR_IS_CONN_BROKEN (socket_err))
+ return mhd_SOCKET_ERR_CONN_BROKEN;
+ else if (mhd_SCKT_ERR_IS_PIPE (socket_err))
+ return mhd_SOCKET_ERR_PIPE;
+ else if (mhd_SCKT_ERR_IS_NOTCONN (socket_err))
+ return mhd_SOCKET_ERR_NOTCONN;
+ else if (mhd_SCKT_ERR_IS_LOW_MEM (socket_err))
+ return mhd_SOCKET_ERR_NOMEM;
+ else if (mhd_SCKT_ERR_IS_BADF (socket_err))
+ return mhd_SOCKET_ERR_BADF;
+ else if (mhd_SCKT_ERR_IS_EINVAL (socket_err))
+ return mhd_SOCKET_ERR_INVAL;
+ else if (mhd_SCKT_ERR_IS_OPNOTSUPP (socket_err))
+ return mhd_SOCKET_ERR_OPNOTSUPP;
+ else if (mhd_SCKT_ERR_IS_NOTSOCK (socket_err))
+ return mhd_SOCKET_ERR_NOTSOCK;
+
+ return mhd_SOCKET_ERR_OTHER;
+}
+
+
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_socket (MHD_Socket fd)
+{
+#if defined(SOL_SOCKET) && defined(SO_ERROR)
+ enum mhd_SocketError err;
+ int sock_err;
+ socklen_t optlen = sizeof (sock_err);
+
+ sock_err = 0;
+ if ((0 == getsockopt (fd,
+ SOL_SOCKET,
+ SO_ERROR,
+ (void *) &sock_err,
+ &optlen))
+ && (sizeof(sock_err) == optlen))
+ return mhd_socket_error_get_from_sys_err (sock_err);
+
+ err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
+ if ((mhd_SOCKET_ERR_NOTSOCK == err) ||
+ (mhd_SOCKET_ERR_BADF == err))
+ return err;
+#endif /* SOL_SOCKET && SO_ERROR */
+ return mhd_SOCKET_ERR_NOT_CHECKED;
+}
diff --git a/src/mhd2/mhd_socket_error.h b/src/mhd2/mhd_socket_error.h
@@ -0,0 +1,161 @@
+/*
+ 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 and related macros and
+ * declarations of related functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SOCKET_ERROR_H
+#define MHD_SOCKET_ERROR_H 1
+
+#include "mhd_sys_options.h"
+#include "mhd_socket_type.h"
+
+// TODO: better classification, when clearer local closing / network aborts
+/**
+ * Recognised socket errors for recv() and send()
+ */
+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 gracefully closed by remote peer
+ */
+ mhd_SOCKET_ERR_REMT_DISCONN
+ ,
+ /**
+ * 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
+ ,
+ /**
+ * General TLS encryption or decryption error
+ */
+ mhd_SOCKET_ERR_TLS
+ ,
+ /**
+ * The socket has been shut down for writing or no longer connected
+ * Only for 'send()'.
+ */
+ mhd_SOCKET_ERR_PIPE
+ ,
+ /**
+ * The error status reported, but concrete code error has not been
+ * checked by MHD
+ */
+ mhd_SOCKET_ERR_NOT_CHECKED
+ ,
+ /**
+ * 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
+ ,
+ /**
+ * Other socket error
+ */
+ mhd_SOCKET_ERR_OTHER
+ ,
+ /**
+ * Internal (MHD) error
+ * Not actually reported by the OS
+ */
+ mhd_SOCKET_ERR_INTERNAL
+
+};
+
+/**
+ * Check whether the socket error is unrecoverable
+ */
+#define mhd_SOCKET_ERR_IS_HARD(err) (mhd_SOCKET_ERR_REMT_DISCONN <= (err))
+
+/**
+ * Check whether the socket error is unexpected
+ */
+#define mhd_SOCKET_ERR_IS_BAD(err) (mhd_SOCKET_ERR_BADF <= (err))
+
+/**
+ * Map recv() / send() system socket error to the enum value
+ * @param socket_err the system socket error
+ * @return the enum value for the @a socket_err
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_sys_err (int socket_err);
+
+/**
+ * Get the last socket error recoded for the given socket
+ * @param fd the socket to check for the error
+ * @return the recorded error @a fd,
+ * #mhd_SOCKET_ERR_NOT_CHECKED if not possible to check @a fd for
+ * the error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_socket (MHD_Socket fd);
+
+#endif /* ! MHD_SOCKET_ERROR_H */
diff --git a/src/mhd2/mhd_socket_type.h b/src/mhd2/mhd_socket_type.h
@@ -0,0 +1,61 @@
+/*
+ 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_sockets_types.h
+ * @brief The header for MHD_Socket types and relevant macros
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header provides 'MHD_Socket' type and 'MHD_INVALID_SOCKET' value.
+ */
+
+#ifndef MHD_SOCKET_TYPE_H
+#define MHD_SOCKET_TYPE_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_INVALID_SOCKET
+# if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
+# define MHD_POSIX_SOCKETS 1 /* The POSIX-style sockets are used */
+/**
+ * MHD_Socket is type for socket FDs
+ *
+ * This type is always 'int' on POSIX platforms.
+ */
+typedef int MHD_Socket;
+/**
+ * Invalid value for MHD_Socket
+ */
+# define MHD_INVALID_SOCKET (-1)
+# else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+# define MHD_WINSOCK_SOCKETS 1 /* The WinSock-style sockets are used */
+# include <winsock2.h>
+/**
+ * MHD_Socket is type for socket FDs
+ */
+typedef SOCKET MHD_Socket;
+/**
+ * Invalid value for MHD_Socket
+ */
+# define MHD_INVALID_SOCKET (INVALID_SOCKET)
+# endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#endif /* MHD_INVALID_SOCKET */
+
+#endif /* ! MHD_SOCKET_TYPE_H */
diff --git a/src/mhd2/mhd_sockets_funcs.c b/src/mhd2/mhd_sockets_funcs.c
@@ -0,0 +1,285 @@
+/*
+ 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 "sys_sockets_types.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 = (get_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 > get_flags)
+ return false;
+
+ set_flags = (get_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 */
+}
+
+
+MHD_INTERNAL bool
+mhd_socket_set_hard_close (MHD_Socket sckt)
+{
+ // TODO: detect constants in configure
+#if defined(SOL_SOCKET) && defined(SO_LINGER)
+ struct linger par;
+
+ par.l_onoff = 1;
+ par.l_linger = 0;
+
+ return 0 == setsockopt (sckt, SOL_SOCKET, SO_LINGER,
+ (const void *) &par, sizeof (par));
+#else /* ! TCP_NODELAY */
+ (void) sock;
+ return false;
+#endif /* ! TCP_NODELAY */
+}
+
+
+MHD_INTERNAL bool
+mhd_socket_shut_wr (MHD_Socket sckt)
+{
+#if defined(SHUT_WR) // TODO: detect constants in configure
+ return 0 == shutdown (sckt, SHUT_WR);
+#elif defined(SD_SEND) // TODO: detect constants in configure
+ return 0 == shutdown (sckt, SD_SEND);
+#else
+ return false;
+#endif
+}
+
+
+#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 */
diff --git a/src/mhd2/mhd_sockets_funcs.h b/src/mhd2/mhd_sockets_funcs.h
@@ -0,0 +1,104 @@
+/*
+ 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);
+
+/**
+ * Change socket options to be closed "hard".
+ *
+ * @param sock the socket to manipulate
+ * @return true on success, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_socket_set_hard_close (MHD_Socket sckt);
+
+/**
+ * Shutdown sending on socket
+ *
+ * @param sock the socket to manipulate
+ * @return true on success, false otherwise
+ */
+MHD_INTERNAL bool
+mhd_socket_shut_wr (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 */
diff --git a/src/mhd2/mhd_sockets_macros.h b/src/mhd2/mhd_sockets_macros.h
@@ -0,0 +1,339 @@
+/*
+ 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
+# include "sys_errno.h"
+#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_sys_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_sys_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))
+
+/**
+ * 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 ENOPROTOOPT
+# define mhd_SCKT_ERR_IS_NOPROTOOPT(err) (ENOPROTOOPT == (err))
+# else
+# define mhd_SCKT_ERR_IS_NOPROTOOPT(err) ((void) (err), ! ! 0)
+# endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+# define mhd_SCKT_ERR_IS_NOPROTOOPT(err) (WSAENOPROTOOPT == (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
+
+#if defined(MHD_POSIX_SOCKETS)
+# ifdef EPIPE
+# define mhd_SCKT_ERR_IS_PIPE(err) (EPIPE == (err))
+# else
+# define mhd_SCKT_ERR_IS_PIPE(err) ((void) (err), ! ! 0)
+# endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+# define mhd_SCKT_ERR_IS_PIPE(err) (WSAESHUTDOWN == (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
+
+#if defined(SOL_SOCKET) && defined(SO_NOSIGPIPE)
+/**
+ * Helper for mhd_socket_nosignal()
+ */
+# ifdef HAVE_COMPOUND_LITERALS_LVALUES
+# define mhd_socket_nosig_helper_int_one ((int){1})
+# else
+/**
+ * Internal static const helper for mhd_socket_nosignal()
+ */
+static const int mhd_socket_nosig_helper_int_one = 1;
+# endif
+
+
+/**
+ * Change socket options to no signal on remote disconnect / broken connection.
+ *
+ * @param sock socket to manipulate
+ * @return non-zero if succeeded, zero otherwise
+ */
+# define mhd_socket_nosignal(sock) \
+ (! setsockopt ((sock),SOL_SOCKET,SO_NOSIGPIPE, \
+ &mhd_socket_nosig_helper_int_one, sizeof(int)))
+#endif /* SOL_SOCKET && SO_NOSIGPIPE */
+
+
+#endif /* ! MHD_SOCKETS_MACROS_H */
diff --git a/src/mhd2/mhd_str.c b/src/mhd2/mhd_str.c
@@ -0,0 +1,2317 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2015-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 microhttpd/mhd_str.c
+ * @brief Functions implementations for string manipulating
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_str.h"
+
+#include <string.h>
+
+#include "mhd_assert.h"
+#include "mhd_limits.h"
+#include "mhd_assert.h"
+
+#ifdef MHD_FAVOR_SMALL_CODE
+# ifdef MHD_static_inline_
+# undef MHD_static_inline_
+# endif /* MHD_static_inline_ */
+/* Do not force inlining and do not use macro functions, use normal static
+ functions instead.
+ This may give more flexibility for size optimizations. */
+# define MHD_static_inline_ static
+# ifndef HAVE_INLINE_FUNCS
+# define HAVE_INLINE_FUNCS 1
+# endif /* !HAVE_INLINE_FUNCS */
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+/*
+ * Block of functions/macros that use US-ASCII charset as required by HTTP
+ * standards. Not affected by current locale settings.
+ */
+
+#ifdef HAVE_INLINE_FUNCS
+
+#if 0 /* Disable unused functions. */
+/**
+ * Check whether character is lower case letter in US-ASCII
+ *
+ * @param c character to check
+ * @return non-zero if character is lower case letter, zero otherwise
+ */
+MHD_static_inline_ bool
+isasciilower (char c)
+{
+ return (c >= 'a') && (c <= 'z');
+}
+
+
+#endif /* Disable unused functions. */
+
+
+/**
+ * Check whether character is upper case letter in US-ASCII
+ *
+ * @param c character to check
+ * @return non-zero if character is upper case letter, zero otherwise
+ */
+MHD_static_inline_ bool
+isasciiupper (char c)
+{
+ return (c <= 'Z') && (c >= 'A');
+}
+
+
+#if 0 /* Disable unused functions. */
+/**
+ * Check whether character is letter in US-ASCII
+ *
+ * @param c character to check
+ * @return non-zero if character is letter in US-ASCII, zero otherwise
+ */
+MHD_static_inline_ bool
+isasciialpha (char c)
+{
+ return isasciilower (c) || isasciiupper (c);
+}
+
+
+#endif /* Disable unused functions. */
+
+
+/**
+ * Check whether character is decimal digit in US-ASCII
+ *
+ * @param c character to check
+ * @return non-zero if character is decimal digit, zero otherwise
+ */
+MHD_static_inline_ bool
+isasciidigit (char c)
+{
+ return (c <= '9') && (c >= '0');
+}
+
+
+#if 0 /* Disable unused functions. */
+/**
+ * Check whether character is hexadecimal digit in US-ASCII
+ *
+ * @param c character to check
+ * @return non-zero if character is decimal digit, zero otherwise
+ */
+MHD_static_inline_ bool
+isasciixdigit (char c)
+{
+ return isasciidigit (c) ||
+ ( (c <= 'F') && (c >= 'A') ) ||
+ ( (c <= 'f') && (c >= 'a') );
+}
+
+
+/**
+ * Check whether character is decimal digit or letter in US-ASCII
+ *
+ * @param c character to check
+ * @return non-zero if character is decimal digit or letter, zero otherwise
+ */
+MHD_static_inline_ bool
+isasciialnum (char c)
+{
+ return isasciialpha (c) || isasciidigit (c);
+}
+
+
+#endif /* Disable unused functions. */
+
+
+#if 0 /* Disable unused functions. */
+/**
+ * Convert US-ASCII character to lower case.
+ * If character is upper case letter in US-ASCII than it's converted to lower
+ * case analog. If character is NOT upper case letter than it's returned
+ * unmodified.
+ *
+ * @param c character to convert
+ * @return converted to lower case character
+ */
+MHD_static_inline_ char
+toasciilower (char c)
+{
+ return isasciiupper (c) ? (c - 'A' + 'a') : c;
+}
+
+
+/**
+ * Convert US-ASCII character to upper case.
+ * If character is lower case letter in US-ASCII than it's converted to upper
+ * case analog. If character is NOT lower case letter than it's returned
+ * unmodified.
+ *
+ * @param c character to convert
+ * @return converted to upper case character
+ */
+MHD_static_inline_ char
+toasciiupper (char c)
+{
+ return isasciilower (c) ? (c - 'a' + 'A') : c;
+}
+
+
+#endif /* Disable unused functions. */
+
+
+#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in mhd_str_to_uvalue_n() */
+/**
+ * Convert US-ASCII decimal digit to its value.
+ *
+ * @param c character to convert
+ * @return value of decimal digit or -1 if @ c is not decimal digit
+ */
+MHD_static_inline_ int
+todigitvalue (char c)
+{
+ if (isasciidigit (c))
+ return (unsigned char) (c - '0');
+
+ return -1;
+}
+
+
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+/**
+ * Convert US-ASCII hexadecimal digit to its value.
+ *
+ * @param c character to convert
+ * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
+ */
+MHD_static_inline_ int
+toxdigitvalue (char c)
+{
+#if ! defined(MHD_FAVOR_SMALL_CODE)
+ switch ((unsigned char) c)
+ {
+#if 0 /* Disabled to give the compiler a hint about low probability */
+ case 0x00U: /* NUL */
+ case 0x01U: /* SOH */
+ case 0x02U: /* STX */
+ case 0x03U: /* ETX */
+ case 0x04U: /* EOT */
+ case 0x05U: /* ENQ */
+ case 0x06U: /* ACK */
+ case 0x07U: /* BEL */
+ case 0x08U: /* BS */
+ case 0x09U: /* HT */
+ case 0x0AU: /* LF */
+ case 0x0BU: /* VT */
+ case 0x0CU: /* FF */
+ case 0x0DU: /* CR */
+ case 0x0EU: /* SO */
+ case 0x0FU: /* SI */
+ case 0x10U: /* DLE */
+ case 0x11U: /* DC1 */
+ case 0x12U: /* DC2 */
+ case 0x13U: /* DC3 */
+ case 0x14U: /* DC4 */
+ case 0x15U: /* NAK */
+ case 0x16U: /* SYN */
+ case 0x17U: /* ETB */
+ case 0x18U: /* CAN */
+ case 0x19U: /* EM */
+ case 0x1AU: /* SUB */
+ case 0x1BU: /* ESC */
+ case 0x1CU: /* FS */
+ case 0x1DU: /* GS */
+ case 0x1EU: /* RS */
+ case 0x1FU: /* US */
+ case 0x20U: /* ' ' */
+ case 0x21U: /* '!' */
+ case 0x22U: /* '"' */
+ case 0x23U: /* '#' */
+ case 0x24U: /* '$' */
+ case 0x25U: /* '%' */
+ case 0x26U: /* '&' */
+ case 0x27U: /* '\'' */
+ case 0x28U: /* '(' */
+ case 0x29U: /* ')' */
+ case 0x2AU: /* '*' */
+ case 0x2BU: /* '+' */
+ case 0x2CU: /* ',' */
+ case 0x2DU: /* '-' */
+ case 0x2EU: /* '.' */
+ case 0x2FU: /* '/' */
+ return -1;
+#endif
+ case 0x30U: /* '0' */
+ return 0;
+ case 0x31U: /* '1' */
+ return 1;
+ case 0x32U: /* '2' */
+ return 2;
+ case 0x33U: /* '3' */
+ return 3;
+ case 0x34U: /* '4' */
+ return 4;
+ case 0x35U: /* '5' */
+ return 5;
+ case 0x36U: /* '6' */
+ return 6;
+ case 0x37U: /* '7' */
+ return 7;
+ case 0x38U: /* '8' */
+ return 8;
+ case 0x39U: /* '9' */
+ return 9;
+#if 0 /* Disabled to give the compiler a hint about low probability */
+ case 0x3AU: /* ':' */
+ case 0x3BU: /* ';' */
+ case 0x3CU: /* '<' */
+ case 0x3DU: /* '=' */
+ case 0x3EU: /* '>' */
+ case 0x3FU: /* '?' */
+ case 0x40U: /* '@' */
+ return -1;
+#endif
+ case 0x41U: /* 'A' */
+ return 0xAU;
+ case 0x42U: /* 'B' */
+ return 0xBU;
+ case 0x43U: /* 'C' */
+ return 0xCU;
+ case 0x44U: /* 'D' */
+ return 0xDU;
+ case 0x45U: /* 'E' */
+ return 0xEU;
+ case 0x46U: /* 'F' */
+ return 0xFU;
+#if 0 /* Disabled to give the compiler a hint about low probability */
+ case 0x47U: /* 'G' */
+ case 0x48U: /* 'H' */
+ case 0x49U: /* 'I' */
+ case 0x4AU: /* 'J' */
+ case 0x4BU: /* 'K' */
+ case 0x4CU: /* 'L' */
+ case 0x4DU: /* 'M' */
+ case 0x4EU: /* 'N' */
+ case 0x4FU: /* 'O' */
+ case 0x50U: /* 'P' */
+ case 0x51U: /* 'Q' */
+ case 0x52U: /* 'R' */
+ case 0x53U: /* 'S' */
+ case 0x54U: /* 'T' */
+ case 0x55U: /* 'U' */
+ case 0x56U: /* 'V' */
+ case 0x57U: /* 'W' */
+ case 0x58U: /* 'X' */
+ case 0x59U: /* 'Y' */
+ case 0x5AU: /* 'Z' */
+ case 0x5BU: /* '[' */
+ case 0x5CU: /* '\' */
+ case 0x5DU: /* ']' */
+ case 0x5EU: /* '^' */
+ case 0x5FU: /* '_' */
+ case 0x60U: /* '`' */
+ return -1;
+#endif
+ case 0x61U: /* 'a' */
+ return 0xAU;
+ case 0x62U: /* 'b' */
+ return 0xBU;
+ case 0x63U: /* 'c' */
+ return 0xCU;
+ case 0x64U: /* 'd' */
+ return 0xDU;
+ case 0x65U: /* 'e' */
+ return 0xEU;
+ case 0x66U: /* 'f' */
+ return 0xFU;
+#if 0 /* Disabled to give the compiler a hint about low probability */
+ case 0x67U: /* 'g' */
+ case 0x68U: /* 'h' */
+ case 0x69U: /* 'i' */
+ case 0x6AU: /* 'j' */
+ case 0x6BU: /* 'k' */
+ case 0x6CU: /* 'l' */
+ case 0x6DU: /* 'm' */
+ case 0x6EU: /* 'n' */
+ case 0x6FU: /* 'o' */
+ case 0x70U: /* 'p' */
+ case 0x71U: /* 'q' */
+ case 0x72U: /* 'r' */
+ case 0x73U: /* 's' */
+ case 0x74U: /* 't' */
+ case 0x75U: /* 'u' */
+ case 0x76U: /* 'v' */
+ case 0x77U: /* 'w' */
+ case 0x78U: /* 'x' */
+ case 0x79U: /* 'y' */
+ case 0x7AU: /* 'z' */
+ case 0x7BU: /* '{' */
+ case 0x7CU: /* '|' */
+ case 0x7DU: /* '}' */
+ case 0x7EU: /* '~' */
+ case 0x7FU: /* DEL */
+ case 0x80U: /* EXT */
+ case 0x81U: /* EXT */
+ case 0x82U: /* EXT */
+ case 0x83U: /* EXT */
+ case 0x84U: /* EXT */
+ case 0x85U: /* EXT */
+ case 0x86U: /* EXT */
+ case 0x87U: /* EXT */
+ case 0x88U: /* EXT */
+ case 0x89U: /* EXT */
+ case 0x8AU: /* EXT */
+ case 0x8BU: /* EXT */
+ case 0x8CU: /* EXT */
+ case 0x8DU: /* EXT */
+ case 0x8EU: /* EXT */
+ case 0x8FU: /* EXT */
+ case 0x90U: /* EXT */
+ case 0x91U: /* EXT */
+ case 0x92U: /* EXT */
+ case 0x93U: /* EXT */
+ case 0x94U: /* EXT */
+ case 0x95U: /* EXT */
+ case 0x96U: /* EXT */
+ case 0x97U: /* EXT */
+ case 0x98U: /* EXT */
+ case 0x99U: /* EXT */
+ case 0x9AU: /* EXT */
+ case 0x9BU: /* EXT */
+ case 0x9CU: /* EXT */
+ case 0x9DU: /* EXT */
+ case 0x9EU: /* EXT */
+ case 0x9FU: /* EXT */
+ case 0xA0U: /* EXT */
+ case 0xA1U: /* EXT */
+ case 0xA2U: /* EXT */
+ case 0xA3U: /* EXT */
+ case 0xA4U: /* EXT */
+ case 0xA5U: /* EXT */
+ case 0xA6U: /* EXT */
+ case 0xA7U: /* EXT */
+ case 0xA8U: /* EXT */
+ case 0xA9U: /* EXT */
+ case 0xAAU: /* EXT */
+ case 0xABU: /* EXT */
+ case 0xACU: /* EXT */
+ case 0xADU: /* EXT */
+ case 0xAEU: /* EXT */
+ case 0xAFU: /* EXT */
+ case 0xB0U: /* EXT */
+ case 0xB1U: /* EXT */
+ case 0xB2U: /* EXT */
+ case 0xB3U: /* EXT */
+ case 0xB4U: /* EXT */
+ case 0xB5U: /* EXT */
+ case 0xB6U: /* EXT */
+ case 0xB7U: /* EXT */
+ case 0xB8U: /* EXT */
+ case 0xB9U: /* EXT */
+ case 0xBAU: /* EXT */
+ case 0xBBU: /* EXT */
+ case 0xBCU: /* EXT */
+ case 0xBDU: /* EXT */
+ case 0xBEU: /* EXT */
+ case 0xBFU: /* EXT */
+ case 0xC0U: /* EXT */
+ case 0xC1U: /* EXT */
+ case 0xC2U: /* EXT */
+ case 0xC3U: /* EXT */
+ case 0xC4U: /* EXT */
+ case 0xC5U: /* EXT */
+ case 0xC6U: /* EXT */
+ case 0xC7U: /* EXT */
+ case 0xC8U: /* EXT */
+ case 0xC9U: /* EXT */
+ case 0xCAU: /* EXT */
+ case 0xCBU: /* EXT */
+ case 0xCCU: /* EXT */
+ case 0xCDU: /* EXT */
+ case 0xCEU: /* EXT */
+ case 0xCFU: /* EXT */
+ case 0xD0U: /* EXT */
+ case 0xD1U: /* EXT */
+ case 0xD2U: /* EXT */
+ case 0xD3U: /* EXT */
+ case 0xD4U: /* EXT */
+ case 0xD5U: /* EXT */
+ case 0xD6U: /* EXT */
+ case 0xD7U: /* EXT */
+ case 0xD8U: /* EXT */
+ case 0xD9U: /* EXT */
+ case 0xDAU: /* EXT */
+ case 0xDBU: /* EXT */
+ case 0xDCU: /* EXT */
+ case 0xDDU: /* EXT */
+ case 0xDEU: /* EXT */
+ case 0xDFU: /* EXT */
+ case 0xE0U: /* EXT */
+ case 0xE1U: /* EXT */
+ case 0xE2U: /* EXT */
+ case 0xE3U: /* EXT */
+ case 0xE4U: /* EXT */
+ case 0xE5U: /* EXT */
+ case 0xE6U: /* EXT */
+ case 0xE7U: /* EXT */
+ case 0xE8U: /* EXT */
+ case 0xE9U: /* EXT */
+ case 0xEAU: /* EXT */
+ case 0xEBU: /* EXT */
+ case 0xECU: /* EXT */
+ case 0xEDU: /* EXT */
+ case 0xEEU: /* EXT */
+ case 0xEFU: /* EXT */
+ case 0xF0U: /* EXT */
+ case 0xF1U: /* EXT */
+ case 0xF2U: /* EXT */
+ case 0xF3U: /* EXT */
+ case 0xF4U: /* EXT */
+ case 0xF5U: /* EXT */
+ case 0xF6U: /* EXT */
+ case 0xF7U: /* EXT */
+ case 0xF8U: /* EXT */
+ case 0xF9U: /* EXT */
+ case 0xFAU: /* EXT */
+ case 0xFBU: /* EXT */
+ case 0xFCU: /* EXT */
+ case 0xFDU: /* EXT */
+ case 0xFEU: /* EXT */
+ case 0xFFU: /* EXT */
+ return -1;
+ default:
+ mhd_assert (0);
+ break; /* Should be unreachable */
+#else
+ default:
+ break;
+#endif
+ }
+ return -1;
+#else /* MHD_FAVOR_SMALL_CODE */
+ if (c <= 9)
+ {
+ if (c >= 0)
+ return (unsigned char) (c - '0');
+ }
+ else if (c <= 'F')
+ {
+ if (c >= 'A')
+ return (unsigned char) (c - 'A' + 10);
+ }
+ else if (c <= 'F')
+ {
+ if (c >= 'a')
+ return (unsigned char) (c - 'a' + 10);
+ }
+
+ return -1;
+#endif /* MHD_FAVOR_SMALL_CODE */
+}
+
+
+/**
+ * Caseless compare two characters.
+ *
+ * @param c1 the first char to compare
+ * @param c2 the second char to compare
+ * @return boolean 'true' if chars are caseless equal, false otherwise
+ */
+MHD_static_inline_ bool
+charsequalcaseless (const char c1, const char c2)
+{
+ return ( (c1 == c2) ||
+ (isasciiupper (c1) ?
+ ((c1 - 'A' + 'a') == c2) :
+ ((c1 == (c2 - 'A' + 'a')) && isasciiupper (c2))) );
+}
+
+
+#else /* !HAVE_INLINE_FUNCS */
+
+
+/**
+ * Checks whether character is lower case letter in US-ASCII
+ *
+ * @param c character to check
+ * @return boolean true if character is lower case letter,
+ * boolean false otherwise
+ */
+#define isasciilower(c) ((((char) (c)) >= 'a') && (((char) (c)) <= 'z'))
+
+
+/**
+ * Checks whether character is upper case letter in US-ASCII
+ *
+ * @param c character to check
+ * @return boolean true if character is upper case letter,
+ * boolean false otherwise
+ */
+#define isasciiupper(c) ((((char) (c)) <= 'Z') && (((char) (c)) >= 'A'))
+
+
+/**
+ * Checks whether character is letter in US-ASCII
+ *
+ * @param c character to check
+ * @return boolean true if character is letter, boolean false
+ * otherwise
+ */
+#define isasciialpha(c) (isasciilower (c) || isasciiupper (c))
+
+
+/**
+ * Check whether character is decimal digit in US-ASCII
+ *
+ * @param c character to check
+ * @return boolean true if character is decimal digit, boolean false
+ * otherwise
+ */
+#define isasciidigit(c) ((((char) (c)) <= '9') && (((char) (c)) >= '0'))
+
+
+/**
+ * Check whether character is hexadecimal digit in US-ASCII
+ *
+ * @param c character to check
+ * @return boolean true if character is hexadecimal digit,
+ * boolean false otherwise
+ */
+#define isasciixdigit(c) (isasciidigit ((c)) || \
+ (((char) (c)) <= 'F' && ((char) (c)) >= 'A') || \
+ (((char) (c)) <= 'f' && ((char) (c)) >= 'a'))
+
+
+/**
+ * Check whether character is decimal digit or letter in US-ASCII
+ *
+ * @param c character to check
+ * @return boolean true if character is decimal digit or letter,
+ * boolean false otherwise
+ */
+#define isasciialnum(c) (isasciialpha (c) || isasciidigit (c))
+
+
+/**
+ * Convert US-ASCII character to lower case.
+ * If character is upper case letter in US-ASCII than it's converted to lower
+ * case analog. If character is NOT upper case letter than it's returned
+ * unmodified.
+ *
+ * @param c character to convert
+ * @return converted to lower case character
+ */
+#define toasciilower(c) ((isasciiupper (c)) ? (((char) (c)) - 'A' + 'a') : \
+ ((char) (c)))
+
+
+/**
+ * Convert US-ASCII character to upper case.
+ * If character is lower case letter in US-ASCII than it's converted to upper
+ * case analog. If character is NOT lower case letter than it's returned
+ * unmodified.
+ *
+ * @param c character to convert
+ * @return converted to upper case character
+ */
+#define toasciiupper(c) ((isasciilower (c)) ? (((char) (c)) - 'a' + 'A') : \
+ ((char) (c)))
+
+
+/**
+ * Convert US-ASCII decimal digit to its value.
+ *
+ * @param c character to convert
+ * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
+ */
+#define todigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \
+ (int) (-1))
+
+
+/**
+ * Convert US-ASCII hexadecimal digit to its value.
+ * @param c character to convert
+ * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
+ */
+#define toxdigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \
+ ( (((char) (c)) >= 'A' && ((char) (c)) <= 'F') ? \
+ (int) (((unsigned char) (c)) - 'A' + 10) : \
+ ( (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ? \
+ (int) (((unsigned char) (c)) - 'a' + 10) : \
+ (int) (-1) )))
+
+/**
+ * Caseless compare two characters.
+ *
+ * @param c1 the first char to compare
+ * @param c2 the second char to compare
+ * @return boolean 'true' if chars are caseless equal, false otherwise
+ */
+#define charsequalcaseless(c1, c2) \
+ ( ((c1) == (c2)) || \
+ (isasciiupper (c1) ? \
+ (((c1) - 'A' + 'a') == (c2)) : \
+ (((c1) == ((c2) - 'A' + 'a')) && isasciiupper (c2))) )
+
+#endif /* !HAVE_INLINE_FUNCS */
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+MHD_INTERNAL bool
+mhd_str_equal_caseless (const char *str1,
+ const char *str2)
+{
+ while (0 != (*str1))
+ {
+ const char c1 = *str1;
+ const char c2 = *str2;
+ if (charsequalcaseless (c1, c2))
+ {
+ str1++;
+ str2++;
+ }
+ else
+ return false;
+ }
+ return 0 == (*str2);
+}
+
+
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+
+
+MHD_INTERNAL bool
+mhd_str_equal_caseless_n (const char *const str1,
+ const char *const str2,
+ size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i < maxlen; ++i)
+ {
+ const char c1 = str1[i];
+ const char c2 = str2[i];
+ if (0 == c2)
+ return 0 == c1;
+ if (charsequalcaseless (c1, c2))
+ continue;
+ else
+ return false;
+ }
+ return true;
+}
+
+
+MHD_INTERNAL bool
+mhd_str_equal_caseless_bin_n (const char *const str1,
+ const char *const str2,
+ size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i)
+ {
+ const char c1 = str1[i];
+ const char c2 = str2[i];
+ if (charsequalcaseless (c1, c2))
+ continue;
+ else
+ return 0;
+ }
+ return ! 0;
+}
+
+
+MHD_INTERNAL bool
+mhd_str_has_token_caseless (const char *str,
+ const char *const token,
+ size_t token_len)
+{
+ if (0 == token_len)
+ return false;
+
+ while (0 != *str)
+ {
+ size_t i;
+ /* Skip all whitespaces and empty tokens. */
+ while (' ' == *str || '\t' == *str || ',' == *str)
+ str++;
+
+ /* Check for token match. */
+ i = 0;
+ while (1)
+ {
+ const char sc = *(str++);
+ const char tc = token[i++];
+
+ if (0 == sc)
+ return false;
+ if (! charsequalcaseless (sc, tc))
+ break;
+ if (i >= token_len)
+ {
+ /* Check whether substring match token fully or
+ * has additional unmatched chars at tail. */
+ while (' ' == *str || '\t' == *str)
+ str++;
+ /* End of (sub)string? */
+ if ((0 == *str) || (',' == *str) )
+ return true;
+ /* Unmatched chars at end of substring. */
+ break;
+ }
+ }
+ /* Find next substring. */
+ while (0 != *str && ',' != *str)
+ str++;
+ }
+ return false;
+}
+
+
+MHD_INTERNAL bool
+mhd_str_remove_token_caseless (const char *restrict str,
+ size_t str_len,
+ const char *const restrict token,
+ const size_t token_len,
+ char *restrict buf,
+ ssize_t *restrict buf_size)
+{
+ const char *s1; /**< the "input" string / character */
+ char *s2; /**< the "output" string / character */
+ size_t t_pos; /**< position of matched character in the token */
+ bool token_removed;
+
+ mhd_assert (NULL == memchr (token, 0, token_len));
+ mhd_assert (NULL == memchr (token, ' ', token_len));
+ mhd_assert (NULL == memchr (token, '\t', token_len));
+ mhd_assert (NULL == memchr (token, ',', token_len));
+ mhd_assert (0 <= *buf_size);
+
+ if (SSIZE_MAX <= ((str_len / 2) * 3 + 3))
+ {
+ /* The return value may overflow, refuse */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ s1 = str;
+ s2 = buf;
+ token_removed = false;
+
+ while ((size_t) (s1 - str) < str_len)
+ {
+ const char *cur_token; /**< the first char of current token */
+ size_t copy_size;
+
+ /* Skip any initial whitespaces and empty tokens */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ ((' ' == *s1) || ('\t' == *s1) || (',' == *s1)) )
+ s1++;
+
+ /* 's1' points to the first char of token in the input string or
+ * points just beyond the end of the input string */
+
+ if ((size_t) (s1 - str) >= str_len)
+ break; /* Nothing to copy, end of the input string */
+
+ /* 's1' points to the first char of token in the input string */
+
+ cur_token = s1; /* the first char of input token */
+
+ /* Check the token with case-insensetive match */
+ t_pos = 0;
+ while ( ((size_t) (s1 - str) < str_len) && (token_len > t_pos) &&
+ (charsequalcaseless (*s1, token[t_pos])) )
+ {
+ s1++;
+ t_pos++;
+ }
+ /* s1 may point just beyond the end of the input string */
+ if ( (token_len == t_pos) && (0 != token_len) )
+ {
+ /* 'token' matched, check that current input token does not have
+ * any suffixes */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ ((' ' == *s1) || ('\t' == *s1)) )
+ s1++;
+ /* 's1' points to the first non-whitespace char after the token matched
+ * requested token or points just beyond the end of the input string after
+ * the requested token */
+ if (((size_t) (s1 - str) == str_len) || (',' == *s1))
+ {/* full token match, do not copy current token to the output */
+ token_removed = true;
+ continue;
+ }
+ }
+
+ /* 's1' points to first non-whitespace char, to some char after
+ * first non-whitespace char in the token in the input string, to
+ * the ',', or just beyond the end of the input string */
+ /* The current token in the input string does not match the token
+ * to exclude, it must be copied to the output string */
+ /* the current token size excluding leading whitespaces and current char */
+ copy_size = (size_t) (s1 - cur_token);
+ if (buf == s2)
+ { /* The first token to copy to the output */
+ if ((size_t) *buf_size < copy_size)
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ }
+ else
+ { /* Some token was already copied to the output buffer */
+ mhd_assert (s2 > buf);
+ if ((size_t) *buf_size < ((size_t) (s2 - buf)) + copy_size + 2)
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ *(s2++) = ',';
+ *(s2++) = ' ';
+ }
+ /* Copy non-matched token to the output */
+ if (0 != copy_size)
+ {
+ memcpy (s2, cur_token, copy_size);
+ s2 += copy_size;
+ }
+
+ while ( ((size_t) (s1 - str) < str_len) && (',' != *s1))
+ {
+ /* 's1' points to first non-whitespace char, to some char after
+ * first non-whitespace char in the token in the input string */
+ /* Copy all non-whitespace chars from the current token in
+ * the input string */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ (',' != *s1) && (' ' != *s1) && ('\t' != *s1) )
+ {
+ mhd_assert (s2 >= buf);
+ if ((size_t) *buf_size <= (size_t) (s2 - buf)) /* '<= s2' equals '< s2 + 1' */
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ *(s2++) = *(s1++);
+ }
+ /* 's1' points to some whitespace char in the token in the input
+ * string, to the ',', or just beyond the end of the input string */
+ /* Skip all whitespaces */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ ((' ' == *s1) || ('\t' == *s1)) )
+ s1++;
+
+ /* 's1' points to the first non-whitespace char in the input string
+ * after whitespace chars, to the ',', or just beyond the end of
+ * the input string */
+ if (((size_t) (s1 - str) < str_len) && (',' != *s1))
+ { /* Not the end of the current token */
+ mhd_assert (s2 >= buf);
+ if ((size_t) *buf_size <= (size_t) (s2 - buf)) /* '<= s2' equals '< s2 + 1' */
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ *(s2++) = ' ';
+ }
+ }
+ }
+ mhd_assert (((ssize_t) (s2 - buf)) <= *buf_size);
+ *buf_size = (ssize_t) (s2 - buf);
+ return token_removed;
+}
+
+
+MHD_INTERNAL bool
+mhd_str_remove_tokens_caseless (char *restrict str,
+ size_t *restrict str_len,
+ const char *const restrict tkns,
+ const size_t tokens_len)
+{
+ size_t pt; /**< position in @a tokens */
+ bool token_removed;
+
+ mhd_assert (NULL == memchr (tkns, 0, tokens_len));
+
+ token_removed = false;
+ pt = 0;
+
+ while (pt < tokens_len && *str_len != 0)
+ {
+ const char *tkn; /**< the current token */
+ size_t tkn_len;
+
+ /* Skip any initial whitespaces and empty tokens in 'tokens' */
+ while ( (pt < tokens_len) &&
+ ((' ' == tkns[pt]) || ('\t' == tkns[pt]) || (',' == tkns[pt])) )
+ pt++;
+
+ if (pt >= tokens_len)
+ break; /* No more tokens, nothing to remove */
+
+ /* Found non-whitespace char which is not a comma */
+ tkn = tkns + pt;
+ do
+ {
+ do
+ {
+ pt++;
+ } while (pt < tokens_len &&
+ (' ' != tkns[pt] && '\t' != tkns[pt] && ',' != tkns[pt]));
+ /* Found end of the token string, space, tab, or comma */
+ tkn_len = pt - (size_t) (tkn - tkns);
+
+ /* Skip all spaces and tabs */
+ while (pt < tokens_len && (' ' == tkns[pt] || '\t' == tkns[pt]))
+ pt++;
+ /* Found end of the token string or non-whitespace char */
+ } while (pt < tokens_len && ',' != tkns[pt]);
+
+ /* 'tkn' is the input token with 'tkn_len' chars */
+ mhd_assert (0 != tkn_len);
+
+ if (*str_len == tkn_len)
+ {
+ if (mhd_str_equal_caseless_bin_n (str, tkn, tkn_len))
+ {
+ *str_len = 0;
+ token_removed = true;
+ }
+ continue;
+ }
+ /* 'tkn' cannot match part of 'str' if length of 'tkn' is larger
+ * than length of 'str'.
+ * It's know that 'tkn' is not equal to the 'str' (was checked previously).
+ * As 'str' is normalized when 'tkn' is not equal to the 'str'
+ * it is required that 'str' to be at least 3 chars larger then 'tkn'
+ * (the comma, the space and at least one additional character for the next
+ * token) to remove 'tkn' from the 'str'. */
+ if (*str_len > tkn_len + 2)
+ { /* Remove 'tkn' from the input string */
+ size_t pr; /**< the 'read' position in the @a str */
+ size_t pw; /**< the 'write' position in the @a str */
+
+ pr = 0;
+ pw = 0;
+
+ do
+ {
+ mhd_assert (pr >= pw);
+ mhd_assert ((*str_len) >= (pr + tkn_len));
+ if ( ( ((*str_len) == (pr + tkn_len)) || (',' == str[pr + tkn_len]) ) &&
+ mhd_str_equal_caseless_bin_n (str + pr, tkn, tkn_len) )
+ {
+ /* current token in the input string matches the 'tkn', skip it */
+ mhd_assert ((*str_len == pr + tkn_len) || \
+ (' ' == str[pr + tkn_len + 1])); /* 'str' must be normalized */
+ token_removed = true;
+ /* Advance to the next token in the input string or beyond
+ * the end of the input string. */
+ pr += tkn_len + 2;
+ }
+ else
+ {
+ /* current token in the input string does not match the 'tkn',
+ * copy to the output */
+ if (0 != pw)
+ { /* not the first output token, add ", " to separate */
+ if (pr != pw + 2)
+ {
+ str[pw++] = ',';
+ str[pw++] = ' ';
+ }
+ else
+ pw += 2; /* 'str' is not yet modified in this round */
+ }
+ do
+ {
+ if (pr != pw)
+ str[pw] = str[pr];
+ pr++;
+ pw++;
+ } while (pr < *str_len && ',' != str[pr]);
+ /* Advance to the next token in the input string or beyond
+ * the end of the input string. */
+ pr += 2;
+ }
+ /* 'pr' should point to the next token in the input string or beyond
+ * the end of the input string */
+ if ((*str_len) < (pr + tkn_len))
+ { /* The rest of the 'str + pr' is too small to match 'tkn' */
+ if ((*str_len) > pr)
+ { /* Copy the rest of the string */
+ size_t copy_size;
+ copy_size = *str_len - pr;
+ if (0 != pw)
+ { /* not the first output token, add ", " to separate */
+ if (pr != pw + 2)
+ {
+ str[pw++] = ',';
+ str[pw++] = ' ';
+ }
+ else
+ pw += 2; /* 'str' is not yet modified in this round */
+ }
+ if (pr != pw)
+ memmove (str + pw, str + pr, copy_size);
+ pw += copy_size;
+ }
+ *str_len = pw;
+ break;
+ }
+ mhd_assert ((' ' != str[0]) && ('\t' != str[0]));
+ mhd_assert ((0 == pr) || (3 <= pr));
+ mhd_assert ((0 == pr) || (' ' == str[pr - 1]));
+ mhd_assert ((0 == pr) || (',' == str[pr - 2]));
+ } while (1);
+ }
+ }
+
+ return token_removed;
+}
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+/* Use individual function for each case */
+
+MHD_INTERNAL size_t
+mhd_str_to_uint64 (const char *restrict str,
+ uint_fast64_t *restrict out_val)
+{
+ const char *const start = str;
+ uint_fast64_t res;
+
+ if (! str || ! out_val || ! isasciidigit (str[0]))
+ return 0;
+
+ res = 0;
+ do
+ {
+ const int digit = (unsigned char) (*str) - '0';
+ uint_fast64_t prev_res = res;
+
+ res *= 10;
+ if (res / 10 != prev_res)
+ return 0;
+ res += (unsigned int) digit;
+ if (res < (unsigned int) digit)
+ return 0;
+
+ str++;
+ } while (isasciidigit (*str));
+
+ *out_val = res;
+ return (size_t) (str - start);
+}
+
+
+MHD_INTERNAL size_t
+mhd_str_to_uint64_n (const char *restrict str,
+ size_t maxlen,
+ uint_fast64_t *restrict out_val)
+{
+ uint_fast64_t res;
+ size_t i;
+
+ if (! str || ! maxlen || ! out_val || ! isasciidigit (str[0]))
+ return 0;
+
+ res = 0;
+ i = 0;
+ do
+ {
+ const int digit = (unsigned char) str[i] - '0';
+ uint_fast64_t prev_res = res;
+
+ res *= 10;
+ if (res / 10 != prev_res)
+ return 0;
+ res += (unsigned int) digit;
+ if (res < (unsigned int) digit)
+ return 0;
+ i++;
+ } while ( (i < maxlen) &&
+ isasciidigit (str[i]) );
+
+ *out_val = res;
+ return i;
+}
+
+
+MHD_INTERNAL size_t
+mhd_strx_to_uint32 (const char *restrict str,
+ uint_fast32_t *restrict out_val)
+{
+ const char *const start = str;
+ uint_fast32_t res;
+ int digit;
+
+ if (! str || ! out_val)
+ return 0;
+
+ res = 0;
+ digit = toxdigitvalue (*str);
+ while (digit >= 0)
+ {
+ uint_fast32_t prev_res = res;
+
+ res *= 16;
+ if (res / 16 != prev_res)
+ return 0;
+ res += (unsigned int) digit;
+ if (res < (unsigned int) digit)
+ return 0;
+
+ str++;
+ digit = toxdigitvalue (*str);
+ }
+
+ if (str - start > 0)
+ *out_val = res;
+ return (size_t) (str - start);
+}
+
+
+MHD_INTERNAL size_t
+mhd_strx_to_uint32_n (const char *restrict str,
+ size_t maxlen,
+ uint_fast32_t *restrict out_val)
+{
+ size_t i;
+ uint_fast32_t res;
+ int digit;
+ if (! str || ! out_val)
+ return 0;
+
+ res = 0;
+ i = 0;
+ while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
+ {
+ uint_fast32_t prev_res = res;
+
+ res *= 16;
+ if (res / 16 != prev_res)
+ return 0;
+ res += (unsigned int) digit;
+ if (res < (unsigned int) digit)
+ return 0;
+
+ res *= 16;
+ res += (unsigned int) digit;
+ i++;
+ }
+
+ if (i)
+ *out_val = res;
+ return i;
+}
+
+
+/**
+ * Convert hexadecimal US-ASCII digits in string to number in uint_fast64_t.
+ * Conversion stopped at first non-digit character.
+ *
+ * @param str string to convert
+ * @param[out] out_val pointer to uint_fast64_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast64_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_strx_to_uint64 (const char *restrict str,
+ uint_fast64_t *restrict out_val)
+{
+ const char *const start = str;
+ uint_fast64_t res;
+ int digit;
+ if (! str || ! out_val)
+ return 0;
+
+ res = 0;
+ digit = toxdigitvalue (*str);
+ while (digit >= 0)
+ {
+ uint_fast64_t prev_res = res;
+
+ res *= 16;
+ if (res / 16 != prev_res)
+ return 0;
+ res += (unsigned int) digit;
+ if (res < (unsigned int) digit)
+ return 0;
+
+ str++;
+ digit = toxdigitvalue (*str);
+ }
+
+ if (str - start > 0)
+ *out_val = res;
+ return (size_t) (str - start);
+}
+
+
+/**
+ * Convert not more then @a maxlen hexadecimal US-ASCII digits in string
+ * to number in uint_fast64_t.
+ * Conversion stopped at first non-digit character or after @a maxlen
+ * digits.
+ *
+ * @param str string to convert
+ * @param maxlen maximum number of characters to process
+ * @param[out] out_val pointer to uint_fast64_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast64_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_strx_to_uint64_n (const char *restrict str,
+ size_t maxlen,
+ uint_fast64_t *restrict out_val)
+{
+ size_t i;
+ uint_fast64_t res;
+ int digit;
+ if (! str || ! out_val)
+ return 0;
+
+ res = 0;
+ i = 0;
+ while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
+ {
+ uint_fast64_t prev_res = res;
+
+ res *= 16;
+ if (res / 16 != prev_res)
+ return 0;
+ res += (unsigned int) digit;
+ if (res < (unsigned int) digit)
+ return 0;
+ i++;
+ }
+
+ if (i)
+ *out_val = res;
+ return i;
+}
+
+
+#else /* MHD_FAVOR_SMALL_CODE */
+
+/**
+ * Generic function for converting not more then @a maxlen
+ * hexadecimal or decimal US-ASCII digits in string to number.
+ * Conversion stopped at first non-digit character or after @a maxlen
+ * digits.
+ * To be used only within macro.
+ *
+ * @param str the string to convert
+ * @param maxlen the maximum number of characters to process
+ * @param out_val the pointer to variable to store result of conversion
+ * @param val_size the size of variable pointed by @a out_val, in bytes, 4 or 8
+ * @param max_val the maximum decoded number
+ * @param base the numeric base, 10 or 16
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then @a max_val, @a val_size is not 4/8 or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_str_to_uvalue_n (const char *restrict str,
+ size_t maxlen,
+ void *restrict out_val,
+ size_t val_size,
+ uint_fast64_t max_val,
+ unsigned int base)
+{
+ size_t i;
+ uint_fast64_t res;
+ const uint_fast64_t max_v_div_b = max_val / base;
+ const uint_fast64_t max_v_mod_b = max_val % base;
+
+ if (! str || ! out_val ||
+ ((base != 16) && (base != 10)) )
+ return 0;
+
+ res = 0;
+ i = 0;
+ while (maxlen > i)
+ {
+ const int digit = (base == 16) ?
+ toxdigitvalue (str[i]) : todigitvalue (str[i]);
+
+ if (0 > digit)
+ break;
+ if ( ((max_v_div_b) < res) ||
+ (( (max_v_div_b) == res) &&
+ ( (max_v_mod_b) < (uint_fast64_t) digit) ) )
+ return 0;
+
+ res *= base;
+ res += (unsigned int) digit;
+ i++;
+ }
+
+ if (i)
+ {
+ if (8 == val_size)
+ *(uint_fast64_t *) out_val = res;
+ else if (4 == val_size)
+ *(uint_fast32_t *) out_val = (uint_fast32_t) res;
+ else
+ return 0;
+ }
+ return i;
+}
+
+
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+MHD_INTERNAL size_t
+mhd_uint32_to_strx (uint_fast32_t val,
+ char *buf,
+ size_t buf_size)
+{
+ size_t o_pos = 0; /**< position of the output character */
+ int digit_pos = 8; /** zero-based, digit position in @a 'val' */
+ int digit;
+
+ /* Skip leading zeros */
+ do
+ {
+ digit_pos--;
+ digit = (int) (val >> 28);
+ val <<= 4;
+ } while ((0 == digit) && (0 != digit_pos));
+
+ while (o_pos < buf_size)
+ {
+ buf[o_pos++] =
+ (char) ((digit <= 9) ?
+ ('0' + (char) digit) :
+ ('A' + (char) digit - 10));
+ if (0 == digit_pos)
+ return o_pos;
+ digit_pos--;
+ digit = (int) (val >> 28);
+ val <<= 4;
+ }
+ return 0; /* The buffer is too small */
+}
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+MHD_INTERNAL size_t
+mhd_uint16_to_str (uint_least16_t val,
+ char *buf,
+ size_t buf_size)
+{
+ char *chr; /**< pointer to the current printed digit */
+ /* The biggest printable number is 65535 */
+ uint_least16_t divisor = UINT16_C (10000);
+ int digit;
+
+ chr = buf;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+
+ /* Do not print leading zeros */
+ while ((0 == digit) && (1 < divisor))
+ {
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+
+ while (0 != buf_size)
+ {
+ *chr = (char) ((char) digit + '0');
+ chr++;
+ buf_size--;
+ if (1 == divisor)
+ return (size_t) (chr - buf);
+ val = (uint_least16_t) (val % divisor);
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+ return 0; /* The buffer is too small */
+}
+
+
+#endif /* !MHD_FAVOR_SMALL_CODE */
+
+
+MHD_INTERNAL size_t
+mhd_uint64_to_str (uint_fast64_t val,
+ char *buf,
+ size_t buf_size)
+{
+ char *chr; /**< pointer to the current printed digit */
+ /* The biggest printable number is 18446744073709551615 */
+ uint_fast64_t divisor = (uint_fast64_t) 10000000000000000000U;
+ int digit;
+
+ chr = buf;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+
+ /* Do not print leading zeros */
+ while ((0 == digit) && (1 < divisor))
+ {
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+
+ while (0 != buf_size)
+ {
+ *chr = (char) ((char) digit + '0');
+ chr++;
+ buf_size--;
+ if (1 == divisor)
+ return (size_t) (chr - buf);
+ val %= divisor;
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+ return 0; /* The buffer is too small */
+}
+
+
+MHD_INTERNAL size_t
+mhd_uint8_to_str_pad (uint8_t val,
+ uint8_t min_digits,
+ char *buf,
+ size_t buf_size)
+{
+ size_t pos; /**< the position of the current printed digit */
+ int digit;
+ mhd_assert (3 >= min_digits);
+ if (0 == buf_size)
+ return 0;
+
+ pos = 0;
+ digit = val / 100;
+ if (0 == digit)
+ {
+ if (3 <= min_digits)
+ buf[pos++] = '0';
+ }
+ else
+ {
+ buf[pos++] = (char) ('0' + (char) digit);
+ val %= 100;
+ min_digits = 2;
+ }
+
+ if (buf_size <= pos)
+ return 0;
+ digit = val / 10;
+ if (0 == digit)
+ {
+ if (2 <= min_digits)
+ buf[pos++] = '0';
+ }
+ else
+ {
+ buf[pos++] = (char) ('0' + (char) digit);
+ val %= 10;
+ }
+
+ if (buf_size <= pos)
+ return 0;
+ buf[pos++] = (char) ('0' + (char) val);
+ return pos;
+}
+
+
+MHD_INTERNAL size_t
+mhd_bin_to_hex (const void *restrict bin,
+ size_t size,
+ char *restrict hex)
+{
+ size_t i;
+
+ for (i = 0; i < size; ++i)
+ {
+ uint8_t j;
+ const uint8_t b = ((const uint8_t *) bin)[i];
+ j = b >> 4;
+ hex[i * 2] = (char) ((j < 10) ? (j + '0') : (j - 10 + 'a'));
+ j = b & 0x0f;
+ hex[i * 2 + 1] = (char) ((j < 10) ? (j + '0') : (j - 10 + 'a'));
+ }
+ return i * 2;
+}
+
+
+MHD_INTERNAL size_t
+mhd_bin_to_hex_z (const void *restrict bin,
+ size_t size,
+ char *restrict hex)
+{
+ size_t res;
+
+ res = mhd_bin_to_hex (bin, size, hex);
+ hex[res] = 0;
+
+ return res;
+}
+
+
+MHD_INTERNAL size_t
+mhd_hex_to_bin (const char *restrict hex,
+ size_t len,
+ void *restrict bin)
+{
+ size_t r;
+ size_t w;
+
+ if (0 == len)
+ return 0;
+ r = 0;
+ w = 0;
+ if (0 != len % 2)
+ {
+ /* Assume the first byte is encoded with single digit */
+ const int l = toxdigitvalue (hex[r++]);
+ if (0 > l)
+ return 0;
+ ((uint8_t *) bin)[w++] = (uint8_t) ((unsigned int) l);
+ }
+ while (r < len)
+ {
+ const int h = toxdigitvalue (hex[r++]);
+ const int l = toxdigitvalue (hex[r++]);
+ if ((0 > h) || (0 > l))
+ return 0;
+ ((uint8_t *) bin)[w++] = (uint8_t) ( ((uint8_t) (((uint8_t)
+ ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)) );
+ }
+ mhd_assert (len == r);
+ mhd_assert ((len + 1) / 2 == w);
+ return w;
+}
+
+
+MHD_INTERNAL size_t
+mhd_str_pct_decode_strict_n (const char *pct_encoded,
+ size_t pct_encoded_len,
+ char *decoded,
+ size_t buf_size)
+{
+#ifdef MHD_FAVOR_SMALL_CODE
+ bool broken;
+ size_t res;
+
+ res = mhd_str_pct_decode_lenient_n (pct_encoded, pct_encoded_len, decoded,
+ buf_size, &broken);
+ if (broken)
+ return 0;
+ return res;
+#else /* ! MHD_FAVOR_SMALL_CODE */
+ size_t r;
+ size_t w;
+ r = 0;
+ w = 0;
+
+ if (buf_size >= pct_encoded_len)
+ {
+ while (r < pct_encoded_len)
+ {
+ const char chr = pct_encoded[r];
+ if ('%' == chr)
+ {
+ if (2 > pct_encoded_len - r)
+ return 0;
+ else
+ {
+ const int h = toxdigitvalue (pct_encoded[++r]);
+ const int l = toxdigitvalue (pct_encoded[++r]);
+ unsigned char out;
+ if ((0 > h) || (0 > l))
+ return 0;
+ out =
+ (unsigned char) (((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)));
+ decoded[w] = (char) out;
+ }
+ }
+ else
+ decoded[w] = chr;
+ ++r;
+ ++w;
+ }
+ return w;
+ }
+
+ while (r < pct_encoded_len)
+ {
+ const char chr = pct_encoded[r];
+ if (w >= buf_size)
+ return 0;
+ if ('%' == chr)
+ {
+ if (2 > pct_encoded_len - r)
+ return 0;
+ else
+ {
+ const int h = toxdigitvalue (pct_encoded[++r]);
+ const int l = toxdigitvalue (pct_encoded[++r]);
+ unsigned char out;
+ if ((0 > h) || (0 > l))
+ return 0;
+ out =
+ (unsigned char) (((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)));
+ decoded[w] = (char) out;
+ }
+ }
+ else
+ decoded[w] = chr;
+ ++r;
+ ++w;
+ }
+ return w;
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+}
+
+
+MHD_INTERNAL size_t
+mhd_str_pct_decode_lenient_n (const char *pct_encoded,
+ size_t pct_encoded_len,
+ char *decoded,
+ size_t buf_size,
+ bool *broken_encoding)
+{
+ size_t r;
+ size_t w;
+ r = 0;
+ w = 0;
+ if (NULL != broken_encoding)
+ *broken_encoding = false;
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (buf_size >= pct_encoded_len)
+ {
+ while (r < pct_encoded_len)
+ {
+ const char chr = pct_encoded[r];
+ if ('%' == chr)
+ {
+ if (2 > pct_encoded_len - r)
+ {
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ decoded[w] = chr; /* Copy "as is" */
+ }
+ else
+ {
+ const int h = toxdigitvalue (pct_encoded[++r]);
+ const int l = toxdigitvalue (pct_encoded[++r]);
+ unsigned char out;
+ if ((0 > h) || (0 > l))
+ {
+ r -= 2;
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ decoded[w] = chr; /* Copy "as is" */
+ }
+ else
+ {
+ out =
+ (unsigned char) (((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)));
+ decoded[w] = (char) out;
+ }
+ }
+ }
+ else
+ decoded[w] = chr;
+ ++r;
+ ++w;
+ }
+ return w;
+ }
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ while (r < pct_encoded_len)
+ {
+ const char chr = pct_encoded[r];
+ if (w >= buf_size)
+ return 0;
+ if ('%' == chr)
+ {
+ if (2 > pct_encoded_len - r)
+ {
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ decoded[w] = chr; /* Copy "as is" */
+ }
+ else
+ {
+ const int h = toxdigitvalue (pct_encoded[++r]);
+ const int l = toxdigitvalue (pct_encoded[++r]);
+ if ((0 > h) || (0 > l))
+ {
+ r -= 2;
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ decoded[w] = chr; /* Copy "as is" */
+ }
+ else
+ {
+ unsigned char out;
+ out =
+ (unsigned char) (((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)));
+ decoded[w] = (char) out;
+ }
+ }
+ }
+ else
+ decoded[w] = chr;
+ ++r;
+ ++w;
+ }
+ return w;
+}
+
+
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_strict (char *str)
+{
+#ifdef MHD_FAVOR_SMALL_CODE
+ size_t res;
+ bool broken;
+
+ res = mhd_str_pct_decode_in_place_lenient (str, &broken);
+ if (broken)
+ {
+ res = 0;
+ str[0] = 0;
+ }
+ return res;
+#else /* ! MHD_FAVOR_SMALL_CODE */
+ size_t r;
+ size_t w;
+ r = 0;
+ w = 0;
+
+ while (0 != str[r])
+ {
+ const char chr = str[r++];
+ if ('%' == chr)
+ {
+ const char d1 = str[r++];
+ if (0 == d1)
+ return 0;
+ else
+ {
+ const char d2 = str[r++];
+ if (0 == d2)
+ return 0;
+ else
+ {
+ const int h = toxdigitvalue (d1);
+ const int l = toxdigitvalue (d2);
+ unsigned char out;
+ if ((0 > h) || (0 > l))
+ return 0;
+ out =
+ (unsigned char) (((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)));
+ str[w++] = (char) out;
+ }
+ }
+ }
+ else
+ str[w++] = chr;
+ }
+ str[w] = 0;
+ return w;
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+}
+
+
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_lenient (char *str,
+ bool *broken_encoding)
+{
+#ifdef MHD_FAVOR_SMALL_CODE
+ size_t len;
+ size_t res;
+
+ len = strlen (str);
+ res = mhd_str_pct_decode_lenient_n (str, len, str, len, broken_encoding);
+ str[res] = 0;
+
+ return res;
+#else /* ! MHD_FAVOR_SMALL_CODE */
+ size_t r;
+ size_t w;
+ if (NULL != broken_encoding)
+ *broken_encoding = false;
+ r = 0;
+ w = 0;
+ while (0 != str[r])
+ {
+ const char chr = str[r++];
+ if ('%' == chr)
+ {
+ const char d1 = str[r++];
+ if (0 == d1)
+ {
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ str[w++] = chr; /* Copy "as is" */
+ str[w] = 0;
+ return w;
+ }
+ else
+ {
+ const char d2 = str[r++];
+ if (0 == d2)
+ {
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ str[w++] = chr; /* Copy "as is" */
+ str[w++] = d1; /* Copy "as is" */
+ str[w] = 0;
+ return w;
+ }
+ else
+ {
+ const int h = toxdigitvalue (d1);
+ const int l = toxdigitvalue (d2);
+ unsigned char out;
+ if ((0 > h) || (0 > l))
+ {
+ if (NULL != broken_encoding)
+ *broken_encoding = true;
+ str[w++] = chr; /* Copy "as is" */
+ str[w++] = d1;
+ str[w++] = d2;
+ continue;
+ }
+ out =
+ (unsigned char) (((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
+ | ((uint8_t) ((unsigned int) l)));
+ str[w++] = (char) out;
+ continue;
+ }
+ }
+ }
+ str[w++] = chr;
+ }
+ str[w] = 0;
+ return w;
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+}
+
+
+#ifdef DAUTH_SUPPORT
+MHD_INTERNAL bool
+mhd_str_equal_quoted_bin_n (const char *quoted,
+ size_t quoted_len,
+ const char *unquoted,
+ size_t unquoted_len)
+{
+ size_t i;
+ size_t j;
+ if (unquoted_len < quoted_len / 2)
+ return false;
+
+ j = 0;
+ for (i = 0; quoted_len > i && unquoted_len > j; ++i, ++j)
+ {
+ if ('\\' == quoted[i])
+ {
+ i++; /* Advance to the next character */
+ if (quoted_len == i)
+ return false; /* No character after escaping backslash */
+ }
+ if (quoted[i] != unquoted[j])
+ return false; /* Different characters */
+ }
+ if ((quoted_len != i) || (unquoted_len != j))
+ return false; /* The strings have different length */
+
+ return true;
+}
+
+
+MHD_INTERNAL bool
+mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
+ size_t quoted_len,
+ const char *unquoted,
+ size_t unquoted_len)
+{
+ size_t i;
+ size_t j;
+ if (unquoted_len < quoted_len / 2)
+ return false;
+
+ j = 0;
+ for (i = 0; quoted_len > i && unquoted_len > j; ++i, ++j)
+ {
+ if ('\\' == quoted[i])
+ {
+ i++; /* Advance to the next character */
+ if (quoted_len == i)
+ return false; /* No character after escaping backslash */
+ }
+ if (! charsequalcaseless (quoted[i], unquoted[j]))
+ return false; /* Different characters */
+ }
+ if ((quoted_len != i) || (unquoted_len != j))
+ return false; /* The strings have different length */
+
+ return true;
+}
+
+
+MHD_INTERNAL size_t
+mhd_str_unquote (const char *quoted,
+ size_t quoted_len,
+ char *result)
+{
+ size_t r;
+ size_t w;
+
+ r = 0;
+ w = 0;
+
+ while (quoted_len > r)
+ {
+ if ('\\' == quoted[r])
+ {
+ ++r;
+ if (quoted_len == r)
+ return 0; /* Last backslash is not followed by char to unescape */
+ }
+ result[w++] = quoted[r++];
+ }
+ return w;
+}
+
+
+#endif /* DAUTH_SUPPORT */
+
+#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
+
+MHD_INTERNAL size_t
+mhd_str_quote (const char *unquoted,
+ size_t unquoted_len,
+ char *result,
+ size_t buf_size)
+{
+ size_t r;
+ size_t w;
+
+ r = 0;
+ w = 0;
+
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (unquoted_len * 2 <= buf_size)
+ {
+ /* Fast loop: the output will fit the buffer with any input string content */
+ while (unquoted_len > r)
+ {
+ const char chr = unquoted[r++];
+ if (('\\' == chr) || ('\"' == chr))
+ result[w++] = '\\'; /* Escape current char */
+ result[w++] = chr;
+ }
+ }
+ else
+ {
+ if (unquoted_len > buf_size)
+ return 0; /* Quick fail: the output buffer is too small */
+#else /* MHD_FAVOR_SMALL_CODE */
+ if (1)
+ {
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+ while (unquoted_len > r)
+ {
+ if (buf_size <= w)
+ return 0; /* The output buffer is too small */
+ else
+ {
+ const char chr = unquoted[r++];
+ if (('\\' == chr) || ('\"' == chr))
+ {
+ result[w++] = '\\'; /* Escape current char */
+ if (buf_size <= w)
+ return 0; /* The output buffer is too small */
+ }
+ result[w++] = chr;
+ }
+ }
+ }
+
+ mhd_assert (w >= r);
+ mhd_assert (w <= r * 2);
+ return w;
+}
+
+
+#endif /* DAUTH_SUPPORT || BAUTH_SUPPORT */
+
+#ifdef BAUTH_SUPPORT
+
+/*
+ * MHD_BASE64_FUNC_VERSION
+ * 1 = smallest,
+ * 2 = medium,
+ * 3 = fastest
+ */
+#ifndef MHD_BASE64_FUNC_VERSION
+#ifdef MHD_FAVOR_SMALL_CODE
+#define MHD_BASE64_FUNC_VERSION 1
+#else /* ! MHD_FAVOR_SMALL_CODE */
+#define MHD_BASE64_FUNC_VERSION 3
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+#endif /* ! MHD_BASE64_FUNC_VERSION */
+
+#if MHD_BASE64_FUNC_VERSION < 1 || MHD_BASE64_FUNC_VERSION > 3
+#error Wrong MHD_BASE64_FUNC_VERSION value
+#endif /* MHD_BASE64_FUNC_VERSION < 1 || MHD_BASE64_FUNC_VERSION > 3 */
+
+#if MHD_BASE64_FUNC_VERSION == 3
+#define mhd_base64_map_type int
+#else /* MHD_BASE64_FUNC_VERSION < 3 */
+#define mhd_base64_map_type int8_t
+#endif /* MHD_BASE64_FUNC_VERSION < 3 */
+
+#if MHD_BASE64_FUNC_VERSION == 1
+static mhd_base64_map_type
+base64_char_to_value_ (uint8_t c)
+{
+ if ('Z' >= c)
+ {
+ if ('A' <= c)
+ return (mhd_base64_map_type) ((c - 'A') + 0);
+ if ('0' <= c)
+ {
+ if ('9' >= c)
+ return (mhd_base64_map_type) ((c - '0') + 52);
+ if ('=' == c)
+ return -2;
+ return -1;
+ }
+ if ('+' == c)
+ return 62;
+ if ('/' == c)
+ return 63;
+ return -1;
+ }
+ if (('z' >= c) && ('a' <= c))
+ return (mhd_base64_map_type) ((c - 'a') + 26);
+ return -1;
+}
+
+
+#endif /* MHD_BASE64_FUNC_VERSION == 1 */
+
+
+MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
+
+
+MHD_INTERNAL size_t
+mhd_base64_to_bin_n (const char *base64,
+ size_t base64_len,
+ void *bin,
+ size_t bin_size)
+{
+#if MHD_BASE64_FUNC_VERSION >= 2
+ static const mhd_base64_map_type map[] = {
+ /* -1 = invalid char, -2 = padding
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /*
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ BS, HT, LF, VT, FF, CR, SO, SI, */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /*
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /*
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ CAN, EM, SUB, ESC, FS, GS, RS, US, */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /*
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ ' ', '!', '"', '#', '$', '%', '&', '\'', */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /*
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ '(', ')', '*', '+', ',', '-', '.', '/', */
+ -1, -1, -1, 62, -1, -1, -1, 63,
+ /*
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ '0', '1', '2', '3', '4', '5', '6', '7', */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ /*
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ '8', '9', ':', ';', '<', '=', '>', '?', */
+ 60, 61, -1, -1, -1, -2, -1, -1,
+ /*
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */
+ -1, 0, 1, 2, 3, 4, 5, 6,
+ /*
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ /*
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ /*
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */
+ 23, 24, 25, -1, -1, -1, -1, -1,
+ /*
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */
+ -1, 26, 27, 28, 29, 30, 31, 32,
+ /*
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */
+ 33, 34, 35, 36, 37, 38, 39, 40,
+ /*
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ /*
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 'x', 'y', 'z', '{', '|', '}', '~', DEL, */
+ 49, 50, 51, -1, -1, -1, -1, -1
+
+#if MHD_BASE64_FUNC_VERSION == 3
+ ,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80..8F */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90..9F */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0..AF */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0..BF */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0..CF */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0..DF */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0..EF */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0..FF */
+#endif /* ! MHD_BASE64_FUNC_VERSION == 3 */
+ };
+#define base64_char_to_value_(c) map[(c)]
+#endif /* MHD_BASE64_FUNC_VERSION >= 2 */
+ const uint8_t *const in = (const uint8_t *) base64;
+ uint8_t *const out = (uint8_t *) bin;
+ size_t i;
+ size_t j;
+ if (0 == base64_len)
+ return 0; /* Nothing to decode */
+ if (0 != base64_len % 4)
+ return 0; /* Wrong input length */
+ if (base64_len / 4 * 3 - 2 > bin_size)
+ return 0;
+
+ j = 0;
+ for (i = 0; i < (base64_len - 4); i += 4)
+ {
+#if MHD_BASE64_FUNC_VERSION == 2
+ if (0 != (0x80 & (in[i] | in[i + 1] | in[i + 2] | in[i + 3])))
+ return 0;
+#endif /* MHD_BASE64_FUNC_VERSION == 2 */
+ if (1)
+ {
+ const mhd_base64_map_type v1 = base64_char_to_value_ (in[i + 0]);
+ const mhd_base64_map_type v2 = base64_char_to_value_ (in[i + 1]);
+ const mhd_base64_map_type v3 = base64_char_to_value_ (in[i + 2]);
+ const mhd_base64_map_type v4 = base64_char_to_value_ (in[i + 3]);
+ if ((0 > v1) || (0 > v2) || (0 > v3) || (0 > v4))
+ return 0;
+ out[j + 0] = (uint8_t) (((uint8_t) (((uint8_t) v1) << 2))
+ | ((uint8_t) (((uint8_t) v2) >> 4)));
+ out[j + 1] = (uint8_t) (((uint8_t) (((uint8_t) v2) << 4))
+ | ((uint8_t) (((uint8_t) v3) >> 2)));
+ out[j + 2] = (uint8_t) (((uint8_t) (((uint8_t) v3) << 6))
+ | ((uint8_t) v4));
+ }
+ j += 3;
+ }
+#if MHD_BASE64_FUNC_VERSION == 2
+ if (0 != (0x80 & (in[i] | in[i + 1] | in[i + 2] | in[i + 3])))
+ return 0;
+#endif /* MHD_BASE64_FUNC_VERSION == 2 */
+ if (1)
+ { /* The last four chars block */
+ const mhd_base64_map_type v1 = base64_char_to_value_ (in[i + 0]);
+ const mhd_base64_map_type v2 = base64_char_to_value_ (in[i + 1]);
+ const mhd_base64_map_type v3 = base64_char_to_value_ (in[i + 2]);
+ const mhd_base64_map_type v4 = base64_char_to_value_ (in[i + 3]);
+ if ((0 > v1) || (0 > v2))
+ return 0; /* Invalid char or padding at first two positions */
+ mhd_assert (j < bin_size);
+ out[j++] = (uint8_t) (((uint8_t) (((uint8_t) v1) << 2))
+ | ((uint8_t) (((uint8_t) v2) >> 4)));
+ if (0 > v3)
+ { /* Third char is either padding or invalid */
+ if ((-2 != v3) || (-2 != v4))
+ return 0; /* Both two last chars must be padding */
+ if (0 != (uint8_t) (((uint8_t) v2) << 4))
+ return 0; /* Wrong last char */
+ return j;
+ }
+ if (j >= bin_size)
+ return 0; /* Not enough space */
+ out[j++] = (uint8_t) (((uint8_t) (((uint8_t) v2) << 4))
+ | ((uint8_t) (((uint8_t) v3) >> 2)));
+ if (0 > v4)
+ { /* Fourth char is either padding or invalid */
+ if (-2 != v4)
+ return 0; /* The char must be padding */
+ if (0 != (uint8_t) (((uint8_t) v3) << 6))
+ return 0; /* Wrong last char */
+ return j;
+ }
+ if (j >= bin_size)
+ return 0; /* Not enough space */
+ out[j++] = (uint8_t) (((uint8_t) (((uint8_t) v3) << 6))
+ | ((uint8_t) v4));
+ }
+ return j;
+#if MHD_BASE64_FUNC_VERSION >= 2
+#undef base64_char_to_value_
+#endif /* MHD_BASE64_FUNC_VERSION >= 2 */
+}
+
+
+MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
+
+
+#undef mhd_base64_map_type
+
+#endif /* BAUTH_SUPPORT */
diff --git a/src/mhd2/mhd_str.h b/src/mhd2/mhd_str.h
@@ -0,0 +1,769 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2015-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.h
+ * @brief Header for string manipulating helpers
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STR_H
+#define MHD_STR_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+#include "mhd_str_macros.h"
+#ifdef MHD_FAVOR_SMALL_CODE
+# include "mhd_limits.h"
+#endif
+/*
+ * Block of functions/macros that use US-ASCII charset as required by HTTP
+ * standards. Not affected by current locale settings.
+ */
+
+#ifndef MHD_FAVOR_SMALL_CODE
+/**
+ * Check two strings for equality, ignoring case of US-ASCII letters.
+ *
+ * @param str1 first string to compare
+ * @param str2 second string to compare
+ * @return 'true' if two strings are equal, 'false' otherwise.
+ */
+MHD_INTERNAL bool
+mhd_str_equal_caseless (const char *str1,
+ const char *str2);
+
+#else /* MHD_FAVOR_SMALL_CODE */
+/* Reuse mhd_str_equal_caseless_n() to reduce size */
+#define mhd_str_equal_caseless(s1,s2) \
+ mhd_str_equal_caseless_n ((s1),(s2), SIZE_MAX)
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+/**
+ * Check two string for equality, ignoring case of US-ASCII letters and
+ * checking not more than @a maxlen characters.
+ * Compares up to first terminating null character, but not more than
+ * first @a maxlen characters.
+ * @param str1 first string to compare
+ * @param str2 second string to compare
+ * @param maxlen maximum number of characters to compare
+ * @return 'true' if two strings are equal, 'false' otherwise.
+ */
+MHD_INTERNAL bool
+mhd_str_equal_caseless_n (const char *const str1,
+ const char *const str2,
+ size_t maxlen);
+
+
+/**
+ * Check two string for equality, ignoring case of US-ASCII letters and
+ * checking not more than @a len bytes.
+ * Compares not more first than @a len bytes, including binary zero characters.
+ * Comparison stops at first unmatched byte.
+ * @param str1 first string to compare
+ * @param str2 second string to compare
+ * @param len number of characters to compare
+ * @return 'true' if two strings are equal, 'false' otherwise.
+ */
+MHD_INTERNAL bool
+mhd_str_equal_caseless_bin_n (const char *const str1,
+ const char *const str2,
+ size_t len);
+
+
+/**
+ * Check whether string is equal statically allocated another string,
+ * ignoring case of US-ASCII letters and checking not more than @a len bytes.
+ *
+ * If strings have different sizes (lengths) then macro returns boolean false
+ * without checking the content.
+ *
+ * Compares not more first than @a len bytes, including binary zero characters.
+ * Comparison stops at first unmatched byte.
+ * @param a the statically allocated string to compare
+ * @param s the string to compare
+ * @param l the number of characters in the @a s string
+ * @return 'true' if two strings are equal, 'false' otherwise.
+ */
+#define mhd_str_equal_caseless_n_st(a,s,l) \
+ ((mhd_SSTR_LEN (a) == (l)) \
+ && mhd_str_equal_caseless_bin_n (a,s,l))
+
+/**
+ * Check whether @a str has case-insensitive @a token.
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * Match succeed if substring between start, end (of string) or comma
+ * contains only case-insensitive token and optional spaces and tabs.
+ * @warning token must not contain null-characters except optional
+ * terminating null-character.
+ * @param str the string to check
+ * @param token the token to find
+ * @param token_len length of token, not including optional terminating
+ * null-character.
+ * @return non-zero if two strings are equal, zero otherwise.
+ */
+MHD_INTERNAL bool
+mhd_str_has_token_caseless (const char *restrict str,
+ const char *const token,
+ size_t token_len);
+
+/**
+ * Check whether @a str has case-insensitive static @a tkn.
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * Match succeed if substring between start, end of string or comma
+ * contains only case-insensitive token and optional spaces and tabs.
+ * @warning tkn must be static string
+ * @param str the string to check
+ * @param tkn the static string of token to find
+ * @return non-zero if two strings are equal, zero otherwise.
+ */
+#define mhd_str_has_s_token_caseless(str,tkn) \
+ mhd_str_has_token_caseless ((str),(tkn),mhd_SSTR_LEN (tkn))
+
+
+/**
+ * Remove case-insensitive @a token from the @a str and put result
+ * to the output @a buf.
+ *
+ * Tokens in @a str could be surrounded by spaces and tabs and delimited by
+ * comma. The token match succeed if substring between start, end (of string)
+ * or comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The output string is normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma.
+ *
+ * @param str the string to process
+ * @param str_len the length of the @a str, not including optional
+ * terminating null-character.
+ * @param token the token to find
+ * @param token_len the length of @a token, not including optional
+ * terminating null-character.
+ * @param[out] buf the output buffer, not null-terminated.
+ * @param[in,out] buf_size pointer to the size variable, at input it
+ * is the size of allocated buffer, at output
+ * it is the size of the resulting string (can
+ * be up to 50% larger than input) or negative value
+ * if there is not enough space for the result
+ * @return 'true' if token has been removed,
+ * 'false' otherwise.
+ */
+MHD_INTERNAL bool
+mhd_str_remove_token_caseless (const char *restrict str,
+ size_t str_len,
+ const char *const restrict token,
+ const size_t token_len,
+ char *restrict buf,
+ ssize_t *restrict buf_size);
+
+
+/**
+ * Perform in-place case-insensitive removal of @a tokens from the @a str.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of the string), or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The input string must be normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma. The string is updated in-place.
+ *
+ * Behavior is undefined is the input string in not normalised.
+ *
+ * @param[in,out] str the string to update
+ * @param[in,out] str_len the length of the @a str, not including optional
+ * terminating null-character, not null-terminated
+ * @param tokens the token to find
+ * @param tokens_len the length of @a tokens, not including optional
+ * terminating null-character.
+ * @return 'true' if any token has been removed,
+ * 'false' otherwise.
+ */
+MHD_INTERNAL bool
+mhd_str_remove_tokens_caseless (char *restrict str,
+ size_t *restrict str_len,
+ const char *const restrict tkns,
+ const size_t tokens_len);
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+/* Use individual function for each case to improve speed */
+
+/**
+ * Convert decimal US-ASCII digits in string to number in uint_fast64_t.
+ * Conversion stopped at first non-digit character.
+ *
+ * @param str string to convert
+ * @param[out] out_val pointer to uint_fast64_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast64_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_str_to_uint64 (const char *restrict str,
+ uint_fast64_t *restrict out_val);
+
+/**
+ * Convert not more then @a maxlen decimal US-ASCII digits in string to
+ * number in uint_fast64_t.
+ * Conversion stopped at first non-digit character or after @a maxlen
+ * digits.
+ *
+ * @param str string to convert
+ * @param maxlen maximum number of characters to process
+ * @param[out] out_val pointer to uint_fast64_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast64_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_str_to_uint64_n (const char *restrict str,
+ size_t maxlen,
+ uint_fast64_t *restrict out_val);
+
+
+/**
+ * Convert hexadecimal US-ASCII digits in string to number in uint_fast32_t.
+ * Conversion stopped at first non-digit character.
+ *
+ * @param str string to convert
+ * @param[out] out_val pointer to uint_fast32_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast32_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_strx_to_uint32 (const char *restrict str,
+ uint_fast32_t *restrict out_val);
+
+
+/**
+ * Convert not more then @a maxlen hexadecimal US-ASCII digits in string
+ * to number in uint_fast32_t.
+ * Conversion stopped at first non-digit character or after @a maxlen
+ * digits.
+ *
+ * @param str string to convert
+ * @param maxlen maximum number of characters to process
+ * @param[out] out_val pointer to uint_fast32_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast32_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_strx_to_uint32_n (const char *restrict str,
+ size_t maxlen,
+ uint_fast32_t *restrict out_val);
+
+
+/**
+ * Convert hexadecimal US-ASCII digits in string to number in uint_fast64_t.
+ * Conversion stopped at first non-digit character.
+ *
+ * @param str string to convert
+ * @param[out] out_val pointer to uint_fast64_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast64_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_strx_to_uint64 (const char *restrict str,
+ uint_fast64_t *restrict out_val);
+
+
+/**
+ * Convert not more then @a maxlen hexadecimal US-ASCII digits in string
+ * to number in uint_fast64_t.
+ * Conversion stopped at first non-digit character or after @a maxlen
+ * digits.
+ *
+ * @param str string to convert
+ * @param maxlen maximum number of characters to process
+ * @param[out] out_val pointer to uint_fast64_t to store result of conversion
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then possible to store in uint_fast64_t or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_strx_to_uint64_n (const char *restrict str,
+ size_t maxlen,
+ uint_fast64_t *restrict out_val);
+
+#else /* MHD_FAVOR_SMALL_CODE */
+/* Use one universal function and macros to reduce size */
+
+/**
+ * Generic function for converting not more then @a maxlen
+ * hexadecimal or decimal US-ASCII digits in string to number.
+ * Conversion stopped at first non-digit character or after @a maxlen
+ * digits.
+ * To be used only within macro.
+ *
+ * @param str the string to convert
+ * @param maxlen the maximum number of characters to process
+ * @param out_val the pointer to variable to store result of conversion
+ * @param val_size the size of variable pointed by @a out_val, in bytes, 4 or 8
+ * @param max_val the maximum decoded number
+ * @param base the numeric base, 10 or 16
+ * @return non-zero number of characters processed on succeed,
+ * zero if no digit is found, resulting value is larger
+ * then @a max_val, @a val_size is not 4/8 or @a out_val is NULL
+ */
+MHD_INTERNAL size_t
+mhd_str_to_uvalue_n (const char *restrict str,
+ size_t maxlen,
+ void *restrict out_val,
+ size_t val_size,
+ uint_fast64_t max_val,
+ unsigned int base);
+
+#define mhd_str_to_uint64(s,ov) \
+ mhd_str_to_uvalue_n ((s),SIZE_MAX,(ov), \
+ sizeof(uint_fast64_t), \
+ UINT64_MAX,10)
+
+#define mhd_str_to_uint64_n(s,ml,ov) \
+ mhd_str_to_uvalue_n ((s),(ml),(ov), \
+ sizeof(uint_fast64_t), \
+ UINT64_MAX,10)
+
+#define mhd_strx_to_sizet(s,ov) \
+ mhd_str_to_uvalue_n ((s),SIZE_MAX,(ov), \
+ sizeof(size_t),SIZE_MAX, \
+ 16)
+
+#define mhd_strx_to_sizet_n(s,ml,ov) \
+ mhd_str_to_uvalue_n ((s),(ml),(ov), \
+ sizeof(size_t), \
+ SIZE_MAX,16)
+
+#define mhd_strx_to_uint32(s,ov) \
+ mhd_str_to_uvalue_n ((s),SIZE_MAX,(ov), \
+ sizeof(uint_fast32_t), \
+ UINT32_MAX,16)
+
+#define mhd_strx_to_uint32_n(s,ml,ov) \
+ mhd_str_to_uvalue_n ((s),(ml),(ov), \
+ sizeof(uint_fast32_t), \
+ UINT32_MAX,16)
+
+#define mhd_strx_to_uint64(s,ov) \
+ mhd_str_to_uvalue_n ((s),SIZE_MAX,(ov), \
+ sizeof(uint_fast64_t), \
+ UINT64_MAX,16)
+
+#define mhd_strx_to_uint64_n(s,ml,ov) \
+ mhd_str_to_uvalue_n ((s),(ml),(ov), \
+ sizeof(uint_fast64_t), \
+ UINT64_MAX,16)
+
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+/**
+ * Convert uint_fast32_t value to hexdecimal US-ASCII string.
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+MHD_INTERNAL size_t
+mhd_uint32_to_strx (uint_fast32_t val,
+ char *buf,
+ size_t buf_size);
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+/**
+ * Convert uint_least16_t value to decimal US-ASCII string.
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+MHD_INTERNAL size_t
+mhd_uint16_to_str (uint_least16_t val,
+ char *buf,
+ size_t buf_size);
+
+#else /* MHD_FAVOR_SMALL_CODE */
+#define mhd_uint16_to_str(v,b,s) mhd_uint64_to_str (v,b,s)
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+/**
+ * Convert uint_fast64_t value to decimal US-ASCII string.
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+MHD_INTERNAL size_t
+mhd_uint64_to_str (uint_fast64_t val,
+ char *buf,
+ size_t buf_size);
+
+
+/**
+ * Convert uint_least16_t value to decimal US-ASCII string padded with
+ * zeros on the left side.
+ *
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param min_digits the minimal number of digits to print,
+ * output padded with zeros on the left side,
+ * 'zero' value is interpreted as 'one',
+ * valid values are 3, 2, 1, 0
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+MHD_INTERNAL size_t
+mhd_uint8_to_str_pad (uint8_t val,
+ uint8_t min_digits,
+ char *buf,
+ size_t buf_size);
+
+
+/**
+ * Convert @a size bytes from input binary data to lower case
+ * hexadecimal digits.
+ * Result is NOT zero-terminated
+ * @param bin the pointer to the binary data to convert
+ * @param size the size in bytes of the binary data to convert
+ * @param[out] hex the output buffer, should be at least 2 * @a size
+ * @return The number of characters written to the output buffer.
+ */
+MHD_INTERNAL size_t
+mhd_bin_to_hex (const void *restrict bin,
+ size_t size,
+ char *restrict hex);
+
+/**
+ * Convert @a size bytes from input binary data to lower case
+ * hexadecimal digits, zero-terminate the result.
+ * @param bin the pointer to the binary data to convert
+ * @param size the size in bytes of the binary data to convert
+ * @param[out] hex the output buffer, should be at least 2 * @a size + 1
+ * @return The number of characters written to the output buffer,
+ * not including terminating zero.
+ */
+MHD_INTERNAL size_t
+mhd_bin_to_hex_z (const void *restrict bin,
+ size_t size,
+ char *restrict hex);
+
+/**
+ * Convert hexadecimal digits to binary data.
+ *
+ * The input decoded byte-by-byte (each byte is two hexadecimal digits).
+ * If length is an odd number, extra leading zero is assumed.
+ *
+ * @param hex the input string with hexadecimal digits
+ * @param len the length of the input string
+ * @param[out] bin the output buffer, must be at least len/2 bytes long (or
+ * len/2 + 1 if @a len is not even number)
+ * @return the number of bytes written to the output buffer,
+ * zero if found any character which is not hexadecimal digits
+ */
+MHD_INTERNAL size_t
+mhd_hex_to_bin (const char *restrict hex,
+ size_t len,
+ void *restrict bin);
+
+/**
+ * Decode string with percent-encoded characters as defined by
+ * RFC 3986 #section-2.1.
+ *
+ * This function decode string by converting percent-encoded characters to
+ * their decoded versions and copying all other characters without extra
+ * processing.
+ *
+ * @param pct_encoded the input string to be decoded
+ * @param pct_encoded_len the length of the @a pct_encoded
+ * @param[out] decoded the output buffer, NOT zero-terminated, can point
+ * to the same buffer as @a pct_encoded
+ * @param buf_size the size of the output buffer
+ * @return the number of characters written to the output buffer or
+ * zero if any percent-encoded characters is broken ('%' followed
+ * by less than two hexadecimal digits) or output buffer is too
+ * small to hold the result
+ */
+MHD_INTERNAL size_t
+mhd_str_pct_decode_strict_n (const char *pct_encoded,
+ size_t pct_encoded_len,
+ char *decoded,
+ size_t buf_size);
+
+/**
+ * Decode string with percent-encoded characters as defined by
+ * RFC 3986 #section-2.1.
+ *
+ * This function decode string by converting percent-encoded characters to
+ * their decoded versions and copying all other characters without extra
+ * processing.
+ *
+ * Any invalid percent-encoding sequences ('%' symbol not followed by two
+ * valid hexadecimal digits) are copied to the output string without decoding.
+ *
+ * @param pct_encoded the input string to be decoded
+ * @param pct_encoded_len the length of the @a pct_encoded
+ * @param[out] decoded the output buffer, NOT zero-terminated, can point
+ * to the same buffer as @a pct_encoded
+ * @param buf_size the size of the output buffer
+ * @param[out] broken_encoding will be set to true if any '%' symbol is not
+ * followed by two valid hexadecimal digits,
+ * optional, can be NULL
+ * @return the number of characters written to the output buffer or
+ * zero if output buffer is too small to hold the result
+ */
+MHD_INTERNAL size_t
+mhd_str_pct_decode_lenient_n (const char *pct_encoded,
+ size_t pct_encoded_len,
+ char *decoded,
+ size_t buf_size,
+ bool *broken_encoding);
+
+
+/**
+ * Decode string in-place with percent-encoded characters as defined by
+ * RFC 3986 #section-2.1.
+ *
+ * This function decode string by converting percent-encoded characters to
+ * their decoded versions and copying back all other characters without extra
+ * processing.
+ *
+ * @param[in,out] str the string to be updated in-place, must be zero-terminated
+ * on input, the output is zero-terminated; the string is
+ * truncated to zero length if broken encoding is found
+ * @return the number of character in decoded string
+ */
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_strict (char *str);
+
+
+/**
+ * Decode string in-place with percent-encoded characters as defined by
+ * RFC 3986 #section-2.1.
+ *
+ * This function decode string by converting percent-encoded characters to
+ * their decoded versions and copying back all other characters without extra
+ * processing.
+ *
+ * Any invalid percent-encoding sequences ('%' symbol not followed by two
+ * valid hexadecimal digits) are copied to the output string without decoding.
+ *
+ * @param[in,out] str the string to be updated in-place, must be zero-terminated
+ * on input, the output is zero-terminated
+ * @param[out] broken_encoding will be set to true if any '%' symbol is not
+ * followed by two valid hexadecimal digits,
+ * optional, can be NULL
+ * @return the number of character in decoded string
+ */
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_lenient (char *str,
+ bool *broken_encoding);
+
+#ifdef DAUTH_SUPPORT
+/**
+ * Check two strings for equality, "unquoting" the first string from quoted
+ * form as specified by RFC7230#section-3.2.6 and RFC7694#quoted.strings.
+ *
+ * Null-termination for input strings is not required, binary zeros compared
+ * like other characters.
+ *
+ * @param quoted the quoted string to compare, must NOT include leading and
+ * closing DQUOTE chars, does not need to be zero-terminated
+ * @param quoted_len the length in chars of the @a quoted string
+ * @param unquoted the unquoted string to compare, does not need to be
+ * zero-terminated
+ * @param unquoted_len the length in chars of the @a unquoted string
+ * @return zero if quoted form is broken (no character after the last escaping
+ * backslash), zero if strings are not equal after unquoting of the
+ * first string,
+ * non-zero if two strings are equal after unquoting of the
+ * first string.
+ */
+MHD_INTERNAL bool
+mhd_str_equal_quoted_bin_n (const char *quoted,
+ size_t quoted_len,
+ const char *unquoted,
+ size_t unquoted_len);
+
+/**
+ * Check whether the string after "unquoting" equals static string.
+ *
+ * Null-termination for input string is not required, binary zeros compared
+ * like other characters.
+ *
+ * @param q the quoted string to compare, must NOT include leading and
+ * closing DQUOTE chars, does not need to be zero-terminated
+ * @param l the length in chars of the @a q string
+ * @param u the unquoted static string to compare
+ * @return zero if quoted form is broken (no character after the last escaping
+ * backslash), zero if strings are not equal after unquoting of the
+ * first string,
+ * non-zero if two strings are equal after unquoting of the
+ * first string.
+ */
+#define mhd_str_equal_quoted_s_bin_n(q,l,u) \
+ mhd_str_equal_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
+
+/**
+ * Check two strings for equality, "unquoting" the first string from quoted
+ * form as specified by RFC7230#section-3.2.6 and RFC7694#quoted.strings and
+ * ignoring case of US-ASCII letters.
+ *
+ * Null-termination for input strings is not required, binary zeros compared
+ * like other characters.
+ *
+ * @param quoted the quoted string to compare, must NOT include leading and
+ * closing DQUOTE chars, does not need to be zero-terminated
+ * @param quoted_len the length in chars of the @a quoted string
+ * @param unquoted the unquoted string to compare, does not need to be
+ * zero-terminated
+ * @param unquoted_len the length in chars of the @a unquoted string
+ * @return zero if quoted form is broken (no character after the last escaping
+ * backslash), zero if strings are not equal after unquoting of the
+ * first string,
+ * non-zero if two strings are caseless equal after unquoting of the
+ * first string.
+ */
+MHD_INTERNAL bool
+mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
+ size_t quoted_len,
+ const char *unquoted,
+ size_t unquoted_len);
+
+/**
+ * Check whether the string after "unquoting" equals static string, ignoring
+ * case of US-ASCII letters.
+ *
+ * Null-termination for input string is not required, binary zeros compared
+ * like other characters.
+ *
+ * @param q the quoted string to compare, must NOT include leading and
+ * closing DQUOTE chars, does not need to be zero-terminated
+ * @param l the length in chars of the @a q string
+ * @param u the unquoted static string to compare
+ * @return zero if quoted form is broken (no character after the last escaping
+ * backslash), zero if strings are not equal after unquoting of the
+ * first string,
+ * non-zero if two strings are caseless equal after unquoting of the
+ * first string.
+ */
+#define mhd_str_equal_caseless_quoted_s_bin_n(q,l,u) \
+ mhd_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
+
+/**
+ * Convert string from quoted to unquoted form as specified by
+ * RFC7230#section-3.2.6 and RFC7694#quoted.strings.
+ *
+ * @param quoted the quoted string, must NOT include leading and closing
+ * DQUOTE chars, does not need to be zero-terminated
+ * @param quoted_len the length in chars of the @a quoted string
+ * @param[out] result the pointer to the buffer to put the result, must
+ * be at least @a size character long. May be modified even
+ * if @a quoted is invalid sequence. The result is NOT
+ * zero-terminated.
+ * @return The number of characters written to the output buffer,
+ * zero if last backslash is not followed by any character (or
+ * @a quoted_len is zero).
+ */
+MHD_INTERNAL size_t
+mhd_str_unquote (const char *quoted,
+ size_t quoted_len,
+ char *result);
+
+#endif /* DAUTH_SUPPORT */
+
+#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
+
+/**
+ * Convert string from unquoted to quoted form as specified by
+ * RFC7230#section-3.2.6 and RFC7694#quoted.strings.
+ *
+ * @param unquoted the unquoted string, does not need to be zero-terminated
+ * @param unquoted_len the length in chars of the @a unquoted string
+ * @param[out] result the pointer to the buffer to put the result. May be
+ * modified even if function failed due to insufficient
+ * space. The result is NOT zero-terminated and does not
+ * have opening and closing DQUOTE chars.
+ * @param buf_size the size of the allocated memory for @a result
+ * @return The number of copied characters, can be up to two times more than
+ * @a unquoted_len, zero if @a unquoted_len is zero or if quoted
+ * string is larger than @a buf_size.
+ */
+MHD_INTERNAL size_t
+mhd_str_quote (const char *unquoted,
+ size_t unquoted_len,
+ char *result,
+ size_t buf_size);
+
+#endif /* DAUTH_SUPPORT || BAUTH_SUPPORT */
+
+#ifdef BAUTH_SUPPORT
+
+/**
+ * Returns the maximum possible size of the Base64 decoded data.
+ * The real recoded size could be up to two bytes smaller.
+ * @param enc_size the size of encoded data, in characters
+ * @return the maximum possible size of the decoded data, in bytes, if
+ * @a enc_size is valid (properly padded),
+ * undefined value smaller then @a enc_size if @a enc_size is not valid
+ */
+#define mhd_base64_max_dec_size(enc_size) (((enc_size) / 4) * 3)
+
+/**
+ * Convert Base64 encoded string to binary data.
+ * @param base64 the input string with Base64 encoded data, could be NOT zero
+ * terminated
+ * @param base64_len the number of characters to decode in @a base64 string,
+ * valid number must be a multiple of four
+ * @param[out] bin the pointer to the output buffer, the buffer may be altered
+ * even if decoding failed
+ * @param bin_size the size of the @a bin buffer in bytes, if the size is
+ * at least @a base64_len / 4 * 3 then result will always
+ * fit, regardless of the amount of the padding characters
+ * @return 0 if @a base64_len is zero, or input string has wrong data (not
+ * valid Base64 sequence), or @a bin_size is too small;
+ * non-zero number of bytes written to the @a bin, the number must be
+ * (base64_len / 4 * 3 - 2), (base64_len / 4 * 3 - 1) or
+ * (base64_len / 4 * 3), depending on the number of padding characters.
+ */
+MHD_INTERNAL size_t
+mhd_base64_to_bin_n (const char *base64,
+ size_t base64_len,
+ void *bin,
+ size_t bin_size);
+
+#endif /* BAUTH_SUPPORT */
+
+#endif /* MHD_STR_H */
diff --git a/src/mhd2/mhd_str_macros.h b/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 */
diff --git a/src/mhd2/mhd_str_types.h b/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 */
diff --git a/src/mhd2/mhd_threads.c b/src/mhd2/mhd_threads.c
@@ -0,0 +1,420 @@
+/*
+ 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 microhttpd/mhd_threads.c
+ * @brief Implementation for thread functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_threads.h"
+#include "sys_null_macro.h"
+#ifdef MHD_USE_W32_THREADS
+# include <process.h>
+#endif
+#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 "sys_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)
+
+#else /* MHD_USE_THREAD_NAME_ */
+
+# 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 || \
+ HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */
+
+# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
+ defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \
+ || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
+
+/**
+ * Set thread name
+ *
+ * @param thread_id ID of thread
+ * @param thread_name name to set
+ * @return true on success, false otherwise
+ */
+static bool
+mhd_set_thread_name (const mhd_thread_ID_native thread_id,
+ const char *thread_name)
+{
+ if (NULL == thread_name)
+ return false;
+
+# 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 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 0 == pthread_setname_np (thread_id, thread_name, 0);
+# endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */
+}
+
+
+# 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__ */
+/* 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)
+
+/**
+ * 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 */
+
+# 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 true on success, false otherwise
+ */
+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)
+ struct thread_info_struct
+ {
+ DWORD type; /* Must be 0x1000. */
+ LPCSTR name; /* Pointer to name (in user address space). */
+ DWORD ID; /* Thread ID (-1 = caller thread). */
+ DWORD flags; /* Reserved for future use, must be zero. */
+ } thread_info;
+#pragma pack(pop)
+
+ if (NULL == thread_name)
+ return false;
+
+ thread_info.type = 0x1000;
+ thread_info.name = thread_name;
+ thread_info.ID = thread_id;
+ thread_info.flags = 0;
+
+ __try
+ { /* This exception is intercepted by debugger */
+ RaiseException (VC_SETNAME_EXC,
+ 0,
+ sizeof (thread_info) / sizeof(ULONG_PTR),
+ (ULONG_PTR *) &thread_info);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {}
+
+ return true;
+}
+
+
+/**
+ * Set current thread name
+ * @param n name to set
+ * @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 */
+
+#endif /* MHD_USE_THREAD_NAME_ */
+
+
+/**
+ * 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)
+{
+#if defined(MHD_USE_POSIX_THREADS)
+ int res;
+# 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 */
+ pthread_t new_tid;
+ pthread_t *const new_tid_ptr = &new_tid;
+# endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */
+
+ mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id));
+
+ if (0 != stack_size)
+ {
+ pthread_attr_t attr;
+ res = pthread_attr_init (&attr);
+ if (0 == res)
+ {
+ res = pthread_attr_setstacksize (&attr,
+ stack_size);
+ if (0 == res)
+ res = pthread_create (new_tid_ptr,
+ &attr,
+ start_routine,
+ arg);
+ pthread_attr_destroy (&attr);
+ }
+ }
+ else
+ res = pthread_create (new_tid_ptr,
+ NULL,
+ start_routine,
+ arg);
+
+ if (0 != res)
+ {
+ errno = res;
+ mhd_thread_handle_ID_set_invalid (handle_id);
+ }
+# 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 */
+
+ return 0 == res;
+#elif defined(MHD_USE_W32_THREADS)
+ uintptr_t thr_handle;
+ unsigned int stack_size_w32;
+# if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT
+
+ mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id));
+
+ stack_size_w32 = (unsigned int) stack_size;
+ if (stack_size != stack_size_w32)
+ {
+ errno = EINVAL;
+ return false;
+ }
+#endif /* SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT */
+ 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
+{
+ /**
+ * Real thread start routine
+ */
+ mhd_THREAD_START_ROUTINE start_routine;
+
+ /**
+ * Argument for thread start routine
+ */
+ void *arg;
+
+ /**
+ * Name for thread
+ */
+ const char *name;
+};
+
+
+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;
+ void *arg;
+ mhd_THREAD_START_ROUTINE thr_func;
+
+ if (NULL == data)
+ return (mhd_THRD_RTRN_TYPE) 0;
+
+ mhd_set_cur_thread_name (param->name);
+
+ arg = param->arg;
+ thr_func = param->start_routine;
+ free (data);
+
+ return thr_func (arg);
+}
+
+
+# endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
+
+
+/**
+ * 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 (with errno set)
+ */
+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)
+ int res;
+ pthread_attr_t attr;
+# 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 */
+ pthread_t new_tid;
+ pthread_t *const new_tid_ptr = &new_tid;
+# 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)
+ /* 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).
+ * MHD doesn't use '%' in thread names, so both forms are used in same way.
+ */
+ res = pthread_attr_setname_np (&attr,
+ thread_name,
+ 0);
+# elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
+ res = pthread_attr_setname_np (&attr,
+ thread_name);
+# else
+#error No pthread_attr_setname_np() function.
+# endif
+ if ((res == 0) && (0 != stack_size) )
+ res = pthread_attr_setstacksize (&attr,
+ stack_size);
+ if (0 == res)
+ res = pthread_create (new_tid_ptr,
+ &attr,
+ start_routine,
+ arg);
+ pthread_attr_destroy (&attr);
+ }
+ if (0 != res)
+ {
+ errno = res;
+ mhd_thread_handle_ID_set_invalid (handle_id);
+ }
+# 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 */
+
+ return 0 == res;
+# else /* ! MHD_USE_THREAD_ATTR_SETNAME */
+ struct mhd_named_helper_param *param;
+
+ if (NULL == thread_name)
+ {
+ errno = EINVAL;
+ return false;
+ }
+
+ param = malloc (sizeof (struct mhd_named_helper_param));
+ if (NULL == param)
+ return false;
+
+ param->start_routine = start_routine;
+ param->arg = arg;
+ param->name = thread_name;
+
+ /* 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))
+ {
+ int err_num;
+
+ err_num = errno;
+ free (param);
+ errno = err_num;
+ return false;
+ }
+
+ return true;
+# endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
+}
+
+
+#endif /* MHD_USE_THREAD_NAME_ */
diff --git a/src/mhd2/mhd_threads.h b/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
+ */
+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);
+
+#endif /* MHD_USE_THREAD_NAME_ */
+
+#endif /* ! MHD_THREADS_H */
diff --git a/src/mhd2/mhd_tristate.h b/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 */
diff --git a/src/mhd2/request_funcs.c b/src/mhd2/request_funcs.c
@@ -0,0 +1,71 @@
+/*
+ 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/request_funcs.c
+ * @brief The definition of the request internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "mhd_request.h"
+#include "mhd_connection.h"
+#include "request_funcs.h"
+#include "stream_funcs.h"
+#include "mhd_dlinked_list.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_add_field_nullable (struct MHD_Connection *restrict c,
+ enum MHD_ValueKind kind,
+ const struct MHD_String *restrict name,
+ const struct MHD_StringNullable *restrict value)
+{
+ struct mhd_RequestField *f;
+
+ f = (struct mhd_RequestField *)
+ mhd_stream_alloc_memory (c, sizeof(struct mhd_RequestField));
+ if (NULL == f)
+ return false;
+
+ f->field.nv.name = *name;
+ f->field.nv.value = *value;
+ f->field.kind = kind;
+ mhd_DLINKEDL_INIT_LINKS (f, fields);
+
+ mhd_DLINKEDL_INS_LAST (&(c->rq),f,fields);
+
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_add_field (struct MHD_Connection *restrict c,
+ enum MHD_ValueKind kind,
+ const struct MHD_String *restrict name,
+ const struct MHD_String *restrict value)
+{
+ struct MHD_StringNullable value2;
+
+ value2.len = value->len;
+ value2.cstr = value->cstr;
+
+ return mhd_stream_add_field_nullable (c, kind, name, &value2);
+}
diff --git a/src/mhd2/request_funcs.h b/src/mhd2/request_funcs.h
@@ -0,0 +1,78 @@
+/*
+ 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/request_funcs.h
+ * @brief The declaration of the request internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_REQUEST_FUNCS_H
+#define MHD_REQUEST_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+#include "mhd_str_types.h"
+#include "mhd_public_api.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Add field to the request.
+ * The memory allocated in the request memory pool
+ *
+ * @param c the connection to use
+ * @param kind the kind of the field to add
+ * @param name the name of the field to add, the string is not copied,
+ * only copied the pointer value
+ * @param value the value of the field to add, the string is not copied,
+ * only copied the pointer value
+ * @return true if succeed,
+ * false if memory cannot be allocated
+ */
+MHD_INTERNAL bool
+mhd_stream_add_field (struct MHD_Connection *restrict c,
+ enum MHD_ValueKind kind,
+ const struct MHD_String *restrict name,
+ const struct MHD_String *restrict value)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Add field to the request.
+ * The memory allocated in the request memory pool
+ * The value can have NULL string ("no value").
+ *
+ * @param c the connection to use
+ * @param kind the kind of the field to add
+ * @param name the name of the field to add, the string is not copied,
+ * only copied the pointer value
+ * @param value the value of the field to add, the string is not copied,
+ * only copied the pointer value
+ * @return true if succeed,
+ * false if memory cannot be allocated
+ */
+MHD_INTERNAL bool
+mhd_stream_add_field_nullable (struct MHD_Connection *restrict c,
+ enum MHD_ValueKind kind,
+ const struct MHD_String *restrict name,
+ const struct MHD_StringNullable *restrict value)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_REQUEST_FUNCS_H */
diff --git a/src/mhd2/request_get_value.c b/src/mhd2/request_get_value.c
@@ -0,0 +1,109 @@
+/*
+ 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/request_get_value.c
+ * @brief The implementation of MHD_request_get_value*() functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "request_get_value.h"
+#include "sys_base_types.h"
+#include <string.h>
+
+#include "mhd_request.h"
+
+#include "mhd_connection.h"
+
+#include "mhd_dlinked_list.h"
+#include "mhd_assert.h"
+#include "mhd_str.h"
+
+#include "mhd_public_api.h"
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4) const struct MHD_StringNullable *
+mhd_request_get_value_n (struct MHD_Request *restrict request,
+ enum MHD_ValueKind kind,
+ size_t key_len,
+ const char *restrict key)
+{
+ struct mhd_RequestField *f;
+
+ mhd_assert (strlen (key) == key_len);
+
+ for (f = mhd_DLINKEDL_GET_FIRST (request, fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ {
+ if ((key_len == f->field.nv.name.len) &&
+ (kind == f->field.kind) &&
+ (0 == memcmp (key, f->field.nv.name.cstr, key_len)))
+ return &(f->field.nv.value);
+ }
+ return NULL;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) const struct MHD_StringNullable *
+MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_ValueKind kind,
+ const char *MHD_RESTRICT key)
+{
+ size_t len;
+ len = strlen (key);
+ return mhd_request_get_value_n (request, kind, len, key);
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_CSTR_ (5) bool
+mhd_stream_has_header_token (const struct MHD_Connection *restrict c,
+ size_t header_len,
+ const char *restrict header,
+ size_t token_len,
+ const char *restrict token)
+{
+ struct mhd_RequestField *f;
+
+ mhd_assert (MHD_CONNECTION_START_REPLY >= c->state);
+
+ for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields);
+ NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ {
+ if ((MHD_VK_HEADER == f->field.kind) &&
+ (header_len == f->field.nv.name.len) &&
+ (mhd_str_equal_caseless_bin_n (header,
+ f->field.nv.name.cstr,
+ header_len)) &&
+ (mhd_str_has_token_caseless (f->field.nv.value.cstr,
+ token,
+ token_len)))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/mhd2/request_get_value.h b/src/mhd2/request_get_value.h
@@ -0,0 +1,116 @@
+/*
+ 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/request_get_value.h
+ * @brief The declaration of internal mhd_request_get_value* functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_REQUEST_GET_VALUE_H
+#define MHD_REQUEST_GET_VALUE_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+#include "mhd_str_macros.h"
+
+#include "mhd_public_api.h"
+
+/**
+ * Get specified field value from request
+ * If multiple values match the kind, return any one of them.
+ *
+ * The returned pointer is valid until the response is queued.
+ * If the data is needed beyond this point, it should be copied.
+ *
+ * @param request request to get values from
+ * @param kind what kind of value are we looking for
+ * @param key_len the length of the @a key string
+ * @param key the header to look for, empty to lookup 'trailing' value
+ * without a key
+ * @return NULL if no such item was found
+ * @ingroup request
+ */
+MHD_INTERNAL const struct MHD_StringNullable *
+mhd_request_get_value_n (struct MHD_Request *restrict request,
+ enum MHD_ValueKind kind,
+ size_t key_len,
+ const char *restrict key)
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
+
+/**
+ * Get specified field value from request
+ * If multiple values match the kind, return any one of them.
+ *
+ * The returned pointer is valid until the response is queued.
+ * If the data is needed beyond this point, it should be copied.
+ *
+ * @param request request to get values from
+ * @param kind what kind of value are we looking for
+ * @param key the header to look for, empty to lookup 'trailing' value
+ * without a key; must be a static string or array
+ * @return NULL if no such item was found
+ * @ingroup request
+ */
+#define mhd_request_get_value_st(r,k,str) \
+ mhd_request_get_value_n ((r),(k),mhd_SSTR_LEN (str),(str))
+
+#endif /* ! MHD_REQUEST_GET_VALUE_H */
+
+
+/**
+ * Check whether the request header contains particular token.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * Case-insensitive match used for header names and tokens.
+ * @param c the connection to check values
+ * @param header_len the length of header, not including optional
+ * terminating null-character
+ * @param header the header name
+ * @param token_len the length of token, not including optional
+ * terminating null-character.
+ * @param token the token to find
+ * @return true if the token is found in the specified header,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_stream_has_header_token (const struct MHD_Connection *restrict c,
+ size_t header_len,
+ const char *restrict header,
+ size_t token_len,
+ const char *restrict token)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5);
+
+/**
+ * Check whether the request header contains particular token.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * Case-insensitive match used for header names and tokens.
+ * @param c the connection to check values
+ * @param hdr the statically allocated header name string
+ * @param token the statically allocated string of token to find
+ * @return true if the token is found in the specified header,
+ * false otherwise
+ */
+#define mhd_stream_has_header_token_st(c,hdr,tkn) \
+ mhd_stream_has_header_token ((c), mhd_SSTR_LEN (hdr), (hdr), \
+ mhd_SSTR_LEN (tkn), (tkn))
diff --git a/src/mhd2/respond_with_error.c b/src/mhd2/respond_with_error.c
@@ -0,0 +1,123 @@
+/*
+ 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/respond_with_error.c
+ * @brief The implementation of error response functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "respond_with_error.h"
+
+#include "sys_base_types.h"
+#include "sys_null_macro.h"
+#include "mhd_str_macros.h"
+
+#include "sys_malloc.h"
+
+#include "mhd_connection.h"
+
+#include "mhd_mempool.h"
+
+#include "response_from.h"
+#include "daemon_logger.h"
+#include "response_destroy.h"
+#include "stream_funcs.h"
+#include "daemon_funcs.h"
+
+#include "mhd_public_api.h"
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) void
+respond_with_error_len (struct MHD_Connection *c,
+ unsigned int http_code,
+ size_t msg_len,
+ const char *msg,
+ size_t add_hdr_line_len,
+ char *add_hdr_line)
+{
+ struct MHD_Response *err_res;
+
+ mhd_assert (! c->stop_with_error); /* Do not send error twice */
+ mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state);
+
+ /* Discard most of the request data */
+
+ if (NULL != c->rq.cntn.lbuf.buf)
+ mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
+ c->rq.cntn.lbuf.buf = NULL;
+
+ c->write_buffer = NULL;
+ c->write_buffer_size = 0;
+ c->write_buffer_send_offset = 0;
+ c->write_buffer_append_offset = 0;
+
+ mhd_DLINKEDL_INIT_LIST (&(c->rq), fields);
+ c->rq.version = NULL;
+ c->rq.method = NULL;
+ c->rq.url = NULL;
+ c->continue_message_write_offset = 0;
+ if (0 != c->read_buffer_size)
+ {
+ mhd_pool_deallocate (c->pool,
+ c->read_buffer,
+ c->read_buffer_size);
+ c->read_buffer = NULL;
+ c->read_buffer_size = 0;
+ c->read_buffer_offset = 0;
+ }
+
+ c->stop_with_error = true;
+ c->discard_request = true;
+ if ((MHD_HTTP_STATUS_CONTENT_TOO_LARGE == http_code) ||
+ (MHD_HTTP_STATUS_URI_TOO_LONG == http_code) ||
+ (MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE == http_code))
+ c->rq.too_large = true;
+
+ mhd_LOG_PRINT (c->daemon,
+ MHD_SC_REQ_PROCCESSING_ERR_REPLY,
+ mhd_LOG_FMT ("Error processing request. Sending %u " \
+ "error reply: %s"),
+ (unsigned int) http_code, msg);
+
+ if (NULL != c->rp.response)
+ {
+ mhd_response_dec_use_count (c->rp.response);
+ c->rp.response = NULL;
+ }
+ err_res = mhd_response_special_for_error (http_code,
+ msg_len,
+ msg,
+ add_hdr_line_len,
+ add_hdr_line);
+ if (NULL == err_res)
+ {
+ if (NULL != add_hdr_line)
+ free (add_hdr_line);
+ mhd_STREAM_ABORT (c, \
+ mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE, \
+ "No memory to create error response.");
+ return;
+ }
+ c->rp.response = err_res;
+ c->state = MHD_CONNECTION_START_REPLY;
+}
diff --git a/src/mhd2/respond_with_error.h b/src/mhd2/respond_with_error.h
@@ -0,0 +1,95 @@
+/*
+ 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/respond_with_error.h
+ * @brief The declaration of error response functions and helper macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPOND_WITH_ERROR_H
+#define MHD_RESPOND_WITH_ERROR_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_null_macro.h"
+#include "mhd_str_macros.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Respond with provided error response.
+ * Current request will be aborted, stream will be closed after sending
+ * error response.
+ * @param c the connection to use
+ * @param http_code the reply HTTP status code
+ * @param msg_len the length of the @a msg
+ * @param msg the reply content, could be NULL
+ * @param add_hdr_line_len the length the @a add_hdr_line
+ * @param add_hdr_line the additional special header line, could be NULL,
+ * if not NULL it will be deallocated by free().
+ *
+ */
+MHD_INTERNAL void
+respond_with_error_len (struct MHD_Connection *c,
+ unsigned int http_code,
+ size_t msg_len,
+ const char *msg,
+ size_t add_hdr_line_len,
+ char *add_hdr_line)
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6);
+
+#ifdef HAVE_HTTP_AUTO_MESSAGES_BODIES
+/**
+ * Transmit static string as error response
+ */
+# define mhd_RESPOND_WITH_ERROR_STATIC(c, code, msg) \
+ respond_with_error_len ((c), (code), \
+ mhd_SSTR_LEN (msg), (msg), \
+ 0, NULL)
+
+/**
+ * Transmit static string as error response and add specified header
+ */
+# define mhd_RESPOND_WITH_ERROR_HEADER(c,code,m,hdrl_l,hdrl) \
+ respond_with_error_len ((c), (code), \
+ mhd_SSTR_LEN (m), (m), \
+ (hdrl_l), (hdrl))
+
+#else
+/**
+ * Transmit static string as error response
+ */
+# define mhd_RESPOND_WITH_ERROR_STATIC(c, code, msg) \
+ respond_with_error_len ((c), (code), \
+ 0, NULL, \
+ 0, NULL)
+
+/**
+ * Transmit static string as error response and add specified header
+ */
+# define mhd_RESPOND_WITH_ERROR_HEADER(c,code,m,hdrl_l,hdrl) \
+ respond_with_error_len ((c), (code), \
+ 0, NULL, \
+ (hdrl_l), (hdrl))
+#endif
+
+#endif /* ! MHD_RESPOND_WITH_ERROR_H */
diff --git a/src/mhd2/response_add_header.c b/src/mhd2/response_add_header.c
@@ -0,0 +1,165 @@
+/*
+ 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)
+ * @author Christian Grothoff
+ */
+
+#include "mhd_sys_options.h"
+
+#include "response_add_header.h"
+#include "mhd_response.h"
+#include "mhd_locks.h"
+
+#include <string.h>
+#include "sys_malloc.h"
+
+#include "mhd_public_api.h"
+
+
+static
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_IN_SIZE_ (3,2)
+MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_CSTR_ (5) MHD_FN_PAR_IN_SIZE_ (5,4) bool
+response_add_header_no_check (
+ struct MHD_Response *response,
+ size_t name_len,
+ const char name[MHD_FN_PAR_DYN_ARR_SIZE_ (name_len)],
+ size_t value_len,
+ const char value[MHD_FN_PAR_DYN_ARR_SIZE_ (value_len)])
+{
+ char *buf;
+ struct mhd_ResponseHeader *new_hdr;
+
+ new_hdr = (struct mhd_ResponseHeader *)
+ malloc (sizeof(struct mhd_ResponseHeader) + name_len
+ + value_len + 2);
+ if (NULL == new_hdr)
+ return false;
+
+ buf = ((char *) new_hdr) + sizeof(struct mhd_ResponseHeader);
+ memcpy (buf, name, name_len);
+ buf[name_len] = 0;
+ new_hdr->name.cstr = buf;
+ new_hdr->name.len = name_len;
+ buf += name_len + 1;
+ memcpy (buf, value, value_len);
+ buf[value_len] = 0;
+ new_hdr->value.cstr = buf;
+ new_hdr->value.len = value_len;
+
+ mhd_DLINKEDL_INIT_LINKS (new_hdr, headers);
+ mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
+ return true;
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_remove_all_headers (struct MHD_Response *restrict r)
+{
+ struct mhd_ResponseHeader *hdr;
+
+ for (hdr = mhd_DLINKEDL_GET_LAST (r, headers); NULL != hdr;
+ hdr = mhd_DLINKEDL_GET_LAST (r, headers))
+ {
+ mhd_DLINKEDL_DEL (r, hdr, headers);
+ free (hdr);
+ }
+}
+
+
+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 (value);
+
+ 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;
+
+ if (! response_add_header_no_check (response, name_len, name,
+ value_len, value))
+ return MHD_SC_RESPONSE_HEADER_MALLOC_FAILED;
+
+ 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_unlock;
+ enum MHD_StatusCode res;
+
+ if (response->frozen)
+ return MHD_SC_TOO_LATE;
+
+ if (response->reuse.reusable)
+ {
+ need_unlock = true;
+ if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
+ return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
+ mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter)));
+ }
+ else
+ need_unlock = false;
+
+ // TODO: add special processing for "Date", "Connection", "Content-Length", "Transfer-Encoding"
+
+ res = response_add_header_int (response, name, value);
+
+ if (need_unlock)
+ 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;
+}
diff --git a/src/mhd2/response_add_header.h b/src/mhd2/response_add_header.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/response_add_header.h
+ * @brief The declarations of the response header manipulation internal
+ * functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_ADD_HEADER_H
+#define MHD_RESPONSE_ADD_HEADER_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Remove all response headers
+ * @param r the response to use
+ */
+MHD_INTERNAL void
+mhd_response_remove_all_headers (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
+
+#endif /* ! MHD_RESPONSE_ADD_HEADER_H */
diff --git a/src/mhd2/response_destroy.c b/src/mhd2/response_destroy.c
@@ -0,0 +1,94 @@
+/*
+ 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_destroy.c
+ * @brief The declarations of internal functions for response deletion
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "response_destroy.h"
+#include "mhd_response.h"
+
+#include "mhd_assert.h"
+#include "mhd_atomic_counter.h"
+
+#include "sys_malloc.h"
+
+#include "mhd_public_api.h"
+
+#include "response_add_header.h"
+#include "response_funcs.h"
+#include "response_from.h"
+
+/**
+ * Perform full response de-initialisation, with cleaning-up / freeing
+ * all content data and headers.
+ * The response settings (if any) must be already freed.
+ * @param r the response to free
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+response_full_detinit (struct MHD_Response *restrict r)
+{
+ mhd_response_remove_all_headers (r);
+ if (NULL != r->special_resp.spec_hdr)
+ free (r->special_resp.spec_hdr);
+ if (r->reuse.reusable)
+ mhd_response_deinit_reusable (r);
+ mhd_response_deinit_content_data (r);
+ free (r);
+}
+
+
+MHD_INTERNAL void
+mhd_response_dec_use_count (struct MHD_Response *restrict r)
+{
+ mhd_assert (r->frozen);
+
+ if (r->reuse.reusable)
+ {
+ if (0 != mhd_atomic_counter_dec_get (&(r->reuse.counter)))
+ return; /* The response is still used somewhere */
+ }
+
+ response_full_detinit (r);
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) void
+MHD_response_destroy (struct MHD_Response *response)
+{
+ if (! response->frozen)
+ {
+ /* This response has been never used for actions */
+ mhd_assert (NULL != response->settings);
+ free (response->settings);
+#ifndef NDEBUG
+ /* Decrement counter to avoid triggering assert in deinit function */
+ mhd_assert (0 == mhd_atomic_counter_dec_get (&(response->reuse.counter)));
+#endif
+ response_full_detinit (response);
+ return;
+ }
+
+ mhd_response_dec_use_count (response);
+}
diff --git a/src/mhd2/response_destroy.h b/src/mhd2/response_destroy.h
@@ -0,0 +1,43 @@
+/*
+ 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_destroy.h
+ * @brief The declarations of internal functions for response deletion
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_DESTROY_H
+#define MHD_RESPONSE_DESTROY_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * 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_DESTROY_H */
diff --git a/src/mhd2/response_from.c b/src/mhd2/response_from.c
@@ -0,0 +1,405 @@
+/*
+ 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 <string.h>
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "compat_calloc.h"
+#include "sys_malloc.h"
+#include "sys_file_fd.h"
+
+#include "mhd_public_api.h"
+
+#include "mhd_locks.h"
+#include "mhd_response.h"
+#include "response_options.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_limits.h"
+
+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;
+
+ if ((100 > sc) || (999 < sc))
+ return NULL;
+
+ 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);
+ r->free.cb = NULL;
+ r->free.cls = NULL;
+ r->special_resp.spec_hdr = NULL;
+
+ 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->cntn_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 */
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_deinit_content_data (struct MHD_Response *restrict r)
+{
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype);
+ if (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype)
+ free (r->cntn.iovec.iov);
+ else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
+ close (r->cntn.file.fd);
+ /* For #mhd_RESPONSE_CONTENT_DATA_BUFFER clean-up performed by callback
+ for both modes: internal copy and external cleanup */
+ if (NULL != r->free.cb)
+ r->free.cb (r->free.cls);
+}
+
+
+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_dtype = mhd_RESPONSE_CONTENT_DATA_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_dtype = mhd_RESPONSE_CONTENT_DATA_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_dtype = mhd_RESPONSE_CONTENT_DATA_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;
+
+ /* 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_dtype = mhd_RESPONSE_CONTENT_DATA_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_elmn_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_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC;
+ res->cntn.iovec.iov = iov_copy;
+ res->cntn.iovec.cnt = i_cp;
+ return res; /* Success exit point */
+ }
+
+ /* Below is a cleanup path */
+ 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;
+ if (offset == MHD_SIZE_UNKNOWN)
+ return NULL;
+ if (size != MHD_SIZE_UNKNOWN)
+ {
+ if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU))
+ return NULL;
+ }
+ res = response_create_basic (sc, size, NULL, NULL);
+ if (NULL != res)
+ {
+ res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
+ res->cntn.file.fd = fd;
+ res->cntn.file.offset = offset;
+#ifdef MHD_USE_SENDFILE
+ res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN);
+#endif
+ 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_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
+ res->cntn.file.fd = fd;
+ res->cntn.file.offset = 0; /* Not necessary */
+#ifdef MHD_USE_SENDFILE
+ res->cntn.file.use_sf = false; /* Not necessary */
+#endif
+ res->cntn.file.is_pipe = true;
+ }
+ return res;
+}
+
+
+/**
+ * Create special internal response for sending error reply
+ * @param sc the HTTP status code
+ * @param cntn_len the length of the @a cntn
+ * @param cntn the content of the response, could be NULL
+ * @param spec_hdr_len the length of the @a spec_hdr
+ * @param spec_hdr the special header line, without last CRLF,
+ * if not NULL it will be deallocated by free().
+ * @return
+ */
+MHD_INTERNAL
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response *
+mhd_response_special_for_error (unsigned int sc,
+ size_t cntn_len,
+ const char *cntn,
+ size_t spec_hdr_len,
+ char *spec_hdr)
+{
+ struct MHD_Response *restrict res;
+
+ mhd_assert (100 <= sc);
+ mhd_assert (600 > sc);
+ mhd_assert ((NULL != cntn) || (0 == cntn_len));
+ mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len));
+
+ res = mhd_calloc (1, sizeof(struct MHD_Response));
+ if (NULL == res)
+ return NULL;
+
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+ mhd_DLINKEDL_INIT_LIST (res, headers);
+ res->free.cb = NULL;
+ res->free.cls = NULL;
+ res->special_resp.spec_hdr = NULL;
+#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
+ res->sc = (enum MHD_HTTP_StatusCode) sc;
+ res->cntn_size = cntn_len;
+ res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
+ res->cntn.buf = (const unsigned char *) ((0 != cntn_len) ? cntn : "");
+ res->cfg.close_forced = true;
+ res->cfg.int_err_resp = true;
+ res->special_resp.spec_hdr_len = spec_hdr_len;
+ res->special_resp.spec_hdr = spec_hdr;
+ res->frozen = true;
+
+ return res;
+}
diff --git a/src/mhd2/response_from.h b/src/mhd2/response_from.h
@@ -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/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"
+#include "sys_base_types.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Deinit / free / cleanup content data of the response
+ * @param r the response to use
+ */
+MHD_INTERNAL void
+mhd_response_deinit_content_data (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
+MHD_INTERNAL struct MHD_Response *
+mhd_response_special_for_error (unsigned int sc,
+ size_t cntn_len,
+ const char *cntn,
+ size_t spec_hdr_len,
+ char *spec_hdr)
+MHD_FN_PAR_CSTR_(3) MHD_FN_PAR_CSTR_(5);
+
+
+#endif /* ! MHD_RESPONSE_FROM_H */
diff --git a/src/mhd2/response_funcs.c b/src/mhd2/response_funcs.c
@@ -0,0 +1,137 @@
+/*
+ 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_malloc.h"
+
+#include "sys_null_macro.h"
+#include "mhd_response.h"
+#include "response_funcs.h"
+#include "mhd_locks.h"
+#include "response_options.h"
+
+
+#include "mhd_atomic_counter.h"
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) bool
+response_make_reusable (struct MHD_Response *restrict r)
+{
+ mhd_assert (! r->reuse.reusable);
+ mhd_assert (! r->frozen);
+ mhd_assert (NULL != r->settings);
+
+ if (mhd_mutex_init (&(r->reuse.settings_lock)))
+ {
+ if (mhd_atomic_counter_init (&(r->reuse.counter), 1))
+ {
+ r->reuse.reusable = true;
+ return true;
+ }
+ (void) mhd_mutex_destroy (&(r->reuse.settings_lock));
+ }
+ return false;
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_deinit_reusable (struct MHD_Response *restrict r)
+{
+ mhd_assert (r->reuse.reusable);
+ mhd_assert (0 == mhd_atomic_counter_get (&(r->reuse.counter)));
+
+ mhd_atomic_counter_deinit (&(r->reuse.counter));
+ mhd_mutex_destroy_chk (&(r->reuse.settings_lock));
+}
+
+
+static void
+response_set_properties (struct MHD_Response *restrict r)
+{
+ struct ResponseOptions *restrict const s = r->settings;
+ mhd_assert (NULL != s);
+
+ r->cfg.head_only = s->head_only_response;
+ if (s->http_1_0_compatible_strict)
+ {
+ r->cfg.close_forced = true;
+ r->cfg.chunked = false;
+ r->cfg.mode_1_0 = s->http_1_0_server;
+ }
+ else if (s->http_1_0_server)
+ {
+ r->cfg.close_forced = s->conn_close || (MHD_SIZE_UNKNOWN == r->cntn_size);
+ r->cfg.chunked = false;
+ r->cfg.mode_1_0 = true;
+ }
+ else
+ {
+ r->cfg.close_forced = s->conn_close;
+ r->cfg.chunked = s->chunked_enc || (MHD_SIZE_UNKNOWN == r->cntn_size);
+ r->cfg.mode_1_0 = false;
+ }
+
+ r->cfg.cnt_len_by_app = s->insanity_header_content_length; // TODO: set only if "content-lengh" header is used
+
+ // TODO: calculate size of the headers and the "Connection:" header
+
+ r->frozen = true;
+
+ r->settings = NULL;
+ free (s);
+}
+
+
+/**
+ * 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_unlock;
+ if (response->frozen)
+ return;
+
+ if (response->reuse.reusable)
+ {
+ need_unlock = true;
+ mhd_mutex_lock_chk (&(response->reuse.settings_lock));
+ mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter)));
+ }
+ else
+ need_unlock = false;
+
+ if (! response->frozen)/* Re-check under the lock */
+ response_set_properties (response);
+
+ if (need_unlock)
+ mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
+}
diff --git a/src/mhd2/response_funcs.h b/src/mhd2/response_funcs.h
@@ -0,0 +1,64 @@
+/*
+ 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"
+#include "sys_bool_type.h"
+
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Make response re-usable, initialise all required data
+ * @param r the response to make re-usable
+ * @return 'true' if succeed, 'false' if failed
+ */
+MHD_INTERNAL bool
+response_make_reusable (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
+
+/**
+ * De-initialise re-usability data
+ * @param r the response to de-initialise re-usability data
+ */
+MHD_INTERNAL void
+mhd_response_deinit_reusable (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
+
+/**
+ * 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 r);
+
+
+#endif /* ! MHD_RESPONSE_FUNCS_H */
diff --git a/src/mhd2/response_options.h b/src/mhd2/response_options.h
@@ -0,0 +1,69 @@
+/* This is generated code, it is still under LGPLv2.1+.
+ Do not edit directly! */
+/* *INDENT-OFF* */
+/**
+ * @file response_options.h
+ * @author response-options-generator.c
+ */
+
+#ifndef MHD_RESPONSE_OPTIONS_H
+#define MHD_RESPONSE_OPTIONS_H 1
+
+#include "mhd_sys_options.h"
+#include "mhd_public_api.h"
+
+struct ResponseOptions
+{
+ /**
+ * Value for #MHD_R_O_REUSABLE.
+ */
+ enum MHD_Bool reusable;
+
+
+ /**
+ * Value for #MHD_R_O_HEAD_ONLY_RESPONSE.
+ */
+ enum MHD_Bool head_only_response;
+
+
+ /**
+ * Value for #MHD_R_O_CHUNKED_ENC.
+ */
+ enum MHD_Bool chunked_enc;
+
+
+ /**
+ * Value for #MHD_R_O_CONN_CLOSE.
+ */
+ enum MHD_Bool conn_close;
+
+
+ /**
+ * Value for #MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT.
+ */
+ enum MHD_Bool http_1_0_compatible_strict;
+
+
+ /**
+ * Value for #MHD_R_O_HTTP_1_0_SERVER.
+ */
+ enum MHD_Bool http_1_0_server;
+
+
+ /**
+ * Value for #MHD_R_O_INSANITY_HEADER_CONTENT_LENGTH.
+ */
+ enum MHD_Bool insanity_header_content_length;
+
+
+ /**
+ * Value for #MHD_R_O_TERMINATION_CALLBACK.
+ * the function to call,
+ * NULL to not use the callback
+ */
+ struct MHD_ResponeOptionValueTermCB termination_callback;
+
+
+};
+
+#endif /* ! MHD_RESPONSE_OPTIONS_H 1 */
diff --git a/src/mhd2/response_set_options.c b/src/mhd2/response_set_options.c
@@ -0,0 +1,94 @@
+/* This is generated code, it is still under LGPLv2.1+.
+ Do not edit directly! */
+/* *INDENT-OFF* */
+/**
+ * @file response_set_options.c
+ * @author response-options-generator.c
+ */
+
+#include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "sys_malloc.h"
+#include <string.h>
+#include "mhd_response.h"
+#include "response_options.h"
+#include "mhd_public_api.h"
+#include "mhd_locks.h"
+#include "mhd_assert.h"
+#include "response_funcs.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)
+{
+ struct ResponseOptions *restrict settings = response->settings;
+ enum MHD_StatusCode res = MHD_SC_OK;
+ size_t i;
+ bool need_unlock = false;
+
+ if (response->frozen)
+ return MHD_SC_TOO_LATE;
+ if (response->reuse.reusable)
+ {
+ need_unlock = true;
+ if (! mhd_mutex_lock(&response->reuse.settings_lock))
+ return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
+ mhd_assert (1 == mhd_atomic_counter_get(&response->reuse.counter));
+ if (! response->frozen) /* Firm re-check under the lock */
+ {
+ mhd_mutex_unlock_chk(&response->reuse.settings_lock);
+ return MHD_SC_TOO_LATE;
+ }
+ }
+
+ for (i = 0; i < options_max_num; i++)
+ {
+ const struct MHD_ResponseOptionAndValue *const option
+ = options + i;
+ switch (option->opt)
+ {
+ case MHD_R_O_END:
+ i = options_max_num - 1;
+ break;
+ case MHD_R_O_REUSABLE:
+ settings->reusable = option->val.reusable;
+ continue;
+ case MHD_R_O_HEAD_ONLY_RESPONSE:
+ settings->head_only_response = option->val.head_only_response;
+ continue;
+ case MHD_R_O_CHUNKED_ENC:
+ settings->chunked_enc = option->val.chunked_enc;
+ continue;
+ case MHD_R_O_CONN_CLOSE:
+ settings->conn_close = option->val.conn_close;
+ continue;
+ case MHD_R_O_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:
+ settings->http_1_0_server = option->val.http_1_0_server;
+ continue;
+ case MHD_R_O_INSANITY_HEADER_CONTENT_LENGTH:
+ settings->insanity_header_content_length = option->val.insanity_header_content_length;
+ continue;
+ case MHD_R_O_TERMINATION_CALLBACK:
+ 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: /* for -Wswitch-default -Wswitch-enum */
+ res = MHD_SC_OPTION_UNKNOWN;
+ i = options_max_num - 1;
+ break;
+ }
+ }
+
+ if (need_unlock)
+ mhd_mutex_unlock_chk(&response->reuse.settings_lock);
+
+ return res;
+}
diff --git a/src/mhd2/response_set_options.h b/src/mhd2/response_set_options.h
@@ -0,0 +1,35 @@
+/*
+ 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_set_options.h
+ * @brief The declarations of the internal response functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_SET_OPTIONS_H
+#define MHD_RESPONSE_SET_OPTIONS_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+
+#endif /* ! MHD_RESPONSE_SET_OPTIONS_H */
diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c
@@ -0,0 +1,899 @@
+/*
+ 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/stream_funcs.c
+ * @brief The definition of the stream internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "stream_funcs.h"
+
+#include <string.h>
+#ifdef MHD_USE_EPOLL
+# include <sys/epoll.h>
+#endif
+#include "sys_malloc.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+#include "mhd_response.h"
+#include "mhd_assert.h"
+#include "mhd_mempool.h"
+#include "mhd_str.h"
+#include "mhd_str_macros.h"
+
+#include "mhd_sockets_funcs.h"
+
+#include "request_get_value.h"
+#include "response_destroy.h"
+#include "mhd_mono_clock.h"
+#include "daemon_logger.h"
+#include "daemon_funcs.h"
+#include "conn_mark_ready.h"
+#include "stream_process_reply.h"
+
+#include "mhd_public_api.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void *
+mhd_stream_alloc_memory (struct MHD_Connection *restrict c,
+ size_t size)
+{
+ struct mhd_MemoryPool *const restrict pool = c->pool; /* a short alias */
+ size_t need_to_be_freed = 0; /**< The required amount of additional free memory */
+ void *res;
+
+ res = mhd_pool_try_alloc (pool,
+ size,
+ &need_to_be_freed);
+ if (NULL != res)
+ return res;
+
+ if (mhd_pool_is_resizable_inplace (pool,
+ c->write_buffer,
+ c->write_buffer_size))
+ {
+ if (c->write_buffer_size - c->write_buffer_append_offset >=
+ need_to_be_freed)
+ {
+ char *buf;
+ const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
+ buf = mhd_pool_reallocate (pool,
+ c->write_buffer,
+ c->write_buffer_size,
+ new_buf_size);
+ mhd_assert (c->write_buffer == buf);
+ mhd_assert (c->write_buffer_append_offset <= new_buf_size);
+ mhd_assert (c->write_buffer_send_offset <= new_buf_size);
+ c->write_buffer_size = new_buf_size;
+ c->write_buffer = buf;
+ }
+ else
+ return NULL;
+ }
+ else if (mhd_pool_is_resizable_inplace (pool,
+ c->read_buffer,
+ c->read_buffer_size))
+ {
+ if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
+ {
+ char *buf;
+ const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
+ buf = mhd_pool_reallocate (pool,
+ c->read_buffer,
+ c->read_buffer_size,
+ new_buf_size);
+ mhd_assert (c->read_buffer == buf);
+ mhd_assert (c->read_buffer_offset <= new_buf_size);
+ c->read_buffer_size = new_buf_size;
+ c->read_buffer = buf;
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+ res = mhd_pool_allocate (pool, size, true);
+ mhd_assert (NULL != res); /* It has been checked that pool has enough space */
+ return res;
+}
+
+
+/**
+ * Shrink stream read buffer to the zero size of free space in the buffer
+ * @param c the connection whose read buffer is being manipulated
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_shrink_read_buffer (struct MHD_Connection *restrict c)
+{
+ void *new_buf;
+
+ if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
+ {
+ mhd_assert (0 == c->read_buffer_size);
+ mhd_assert (0 == c->read_buffer_offset);
+ return;
+ }
+
+ mhd_assert (c->read_buffer_offset <= c->read_buffer_size);
+ if (0 == c->read_buffer_offset)
+ {
+ mhd_pool_deallocate (c->pool, c->read_buffer, c->read_buffer_size);
+ c->read_buffer = NULL;
+ c->read_buffer_size = 0;
+ }
+ else
+ {
+ mhd_assert (mhd_pool_is_resizable_inplace (c->pool, c->read_buffer, \
+ c->read_buffer_size));
+ new_buf = mhd_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size,
+ c->read_buffer_offset);
+ mhd_assert (c->read_buffer == new_buf);
+ c->read_buffer = new_buf;
+ c->read_buffer_size = c->read_buffer_offset;
+ }
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ size_t
+mhd_stream_maximize_write_buffer (struct MHD_Connection *restrict c)
+{
+ struct mhd_MemoryPool *const restrict pool = c->pool;
+ void *new_buf;
+ size_t new_size;
+ size_t free_size;
+
+ mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
+ mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
+ mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);
+
+ free_size = mhd_pool_get_free (pool);
+ if (0 != free_size)
+ {
+ new_size = c->write_buffer_size + free_size;
+ /* This function must not move the buffer position.
+ * mhd_pool_reallocate () may return the new position only if buffer was
+ * allocated 'from_end' or is not the last allocation,
+ * which should not happen. */
+ mhd_assert ((NULL == c->write_buffer) || \
+ mhd_pool_is_resizable_inplace (pool, c->write_buffer, \
+ c->write_buffer_size));
+ new_buf = mhd_pool_reallocate (pool,
+ c->write_buffer,
+ c->write_buffer_size,
+ new_size);
+ mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
+ c->write_buffer = new_buf;
+ c->write_buffer_size = new_size;
+ if (c->write_buffer_send_offset == c->write_buffer_append_offset)
+ {
+ /* All data have been sent, reset offsets to zero. */
+ c->write_buffer_send_offset = 0;
+ c->write_buffer_append_offset = 0;
+ }
+ }
+
+ return c->write_buffer_size - c->write_buffer_append_offset;
+}
+
+
+#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
+/**
+ * A reasonable headers size (excluding request line) that should be sufficient
+ * for most requests.
+ * If incoming data buffer free space is not enough to process the complete
+ * header (the request line and all headers) and the headers size is larger than
+ * this size then the status code 431 "Request Header Fields Too Large" is
+ * returned to the client.
+ * The larger headers are processed by MHD if enough space is available.
+ */
+# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
+#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
+
+#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
+/**
+ * A reasonable request target (the request URI) size that should be sufficient
+ * for most requests.
+ * If incoming data buffer free space is not enough to process the complete
+ * header (the request line and all headers) and the request target size is
+ * larger than this size then the status code 414 "URI Too Long" is
+ * returned to the client.
+ * The larger request targets are processed by MHD if enough space is available.
+ * The value chosen according to RFC 9112 Section 3, paragraph 5
+ */
+# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
+#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
+/**
+ * A reasonable headers size (excluding request line) that should be sufficient
+ * for basic simple requests.
+ * When no space left in the receiving buffer try to avoid replying with
+ * the status code 431 "Request Header Fields Too Large" if headers size
+ * is smaller then this value.
+ */
+# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
+#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
+/**
+ * A reasonable request target (the request URI) size that should be sufficient
+ * for basic simple requests.
+ * When no space left in the receiving buffer try to avoid replying with
+ * the status code 414 "URI Too Long" if the request target size is smaller then
+ * this value.
+ */
+# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
+#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
+/**
+ * A reasonable request method string size that should be sufficient
+ * for basic simple requests.
+ * When no space left in the receiving buffer try to avoid replying with
+ * the status code 501 "Not Implemented" if the request method size is
+ * smaller then this value.
+ */
+# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
+#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
+/**
+ * A reasonable minimal chunk line length.
+ * When no space left in the receiving buffer reply with 413 "Content Too Large"
+ * if the chunk line length is larger than this value.
+ */
+# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
+#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_SIZE_ (4,3) unsigned int
+mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
+ enum MHD_ProcRecvDataStage stage,
+ size_t add_element_size,
+ const char *restrict add_element)
+{
+ size_t method_size;
+ size_t uri_size;
+ size_t opt_headers_size;
+ size_t host_field_line_size;
+
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state);
+ mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
+ mhd_assert ((0 == add_element_size) || (NULL != add_element));
+
+ c->rq.too_large = true;
+
+ if (MHD_CONNECTION_HEADERS_RECEIVED > c->state)
+ {
+ mhd_assert (NULL != c->rq.field_lines.start);
+ opt_headers_size =
+ (size_t) ((c->read_buffer + c->read_buffer_offset)
+ - c->rq.field_lines.start);
+ }
+ else
+ opt_headers_size = c->rq.field_lines.size;
+
+ /* The read buffer is fully used by the request line, the field lines
+ (headers) and internal information.
+ The return status code works as a suggestion for the client to reduce
+ one of the request elements. */
+
+ if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
+ (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
+ {
+ /* Request could be re-tried easily with smaller chunk sizes */
+ return MHD_HTTP_STATUS_CONTENT_TOO_LARGE;
+ }
+
+ host_field_line_size = 0;
+ /* The "Host:" field line is mandatory.
+ The total size of the field lines (headers) cannot be smaller than
+ the size of the "Host:" field line. */
+ if ((MHD_PROC_RECV_HEADERS == stage)
+ && (0 != add_element_size))
+ {
+ static const size_t header_host_key_len =
+ mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
+ const bool is_host_header =
+ (header_host_key_len + 1 <= add_element_size)
+ && ( (0 == add_element[header_host_key_len])
+ || (':' == add_element[header_host_key_len]) )
+ && mhd_str_equal_caseless_bin_n (MHD_HTTP_HEADER_HOST,
+ add_element,
+ header_host_key_len);
+ if (is_host_header)
+ {
+ const bool is_parsed = ! (
+ (MHD_CONNECTION_HEADERS_RECEIVED > c->state) &&
+ (add_element_size == c->read_buffer_offset) &&
+ (c->read_buffer == add_element) );
+ size_t actual_element_size;
+
+ mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
+ /* The actual size should be larger due to CRLF or LF chars,
+ however the exact termination sequence is not known here and
+ as perfect precision is not required, to simplify the code
+ assume the minimal length. */
+ if (is_parsed)
+ actual_element_size = add_element_size + 1; /* "1" for LF */
+ else
+ actual_element_size = add_element_size;
+
+ host_field_line_size = actual_element_size;
+ mhd_assert (opt_headers_size >= actual_element_size);
+ opt_headers_size -= actual_element_size;
+ }
+ }
+ if (0 == host_field_line_size)
+ {
+ static const size_t host_field_name_len =
+ mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
+ const struct MHD_StringNullable *host_value;
+ host_value = mhd_request_get_value_n (&(c->rq),
+ MHD_VK_HEADER,
+ host_field_name_len,
+ MHD_HTTP_HEADER_HOST);
+ if (NULL != host_value)
+ {
+ /* Calculate the minimal size of the field line: no space between
+ colon and the field value, line terminated by LR */
+ host_field_line_size =
+ host_field_name_len + host_value->len + 2; /* "2" for ':' and LF */
+
+ /* The "Host:" field could be added by application */
+ if (opt_headers_size >= host_field_line_size)
+ {
+ opt_headers_size -= host_field_line_size;
+ /* Take into account typical space after colon and CR at the end of the line */
+ if (opt_headers_size >= 2)
+ opt_headers_size -= 2;
+ }
+ else
+ host_field_line_size = 0; /* No "Host:" field line set by the client */
+ }
+ }
+
+ uri_size = c->rq.req_target_len;
+ if (mhd_HTTP_METHOD_OTHER != c->rq.http_mthd)
+ method_size = 0; /* Do not recommend shorter request method */
+ else
+ {
+ mhd_assert (NULL != c->rq.method);
+ method_size = strlen (c->rq.method);
+ }
+
+ if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
+ {
+ /* Typically the easiest way to reduce request header size is
+ a removal of some optional headers. */
+ if (opt_headers_size > (uri_size / 8))
+ {
+ if ((opt_headers_size / 2) > method_size)
+ return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+ else
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+ }
+ else
+ { /* Request target is MUCH larger than headers */
+ if ((uri_size / 16) > method_size)
+ return MHD_HTTP_STATUS_URI_TOO_LONG;
+ else
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+ }
+ }
+ if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
+ {
+ /* If request target size if larger than maximum reasonable size
+ recommend client to reduce the request target size (length). */
+ if ((uri_size / 16) > method_size)
+ return MHD_HTTP_STATUS_URI_TOO_LONG; /* Request target is MUCH larger than headers */
+ else
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+ }
+
+ /* The read buffer is too small to handle reasonably large requests */
+
+ if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
+ {
+ /* Recommend application to retry with minimal headers */
+ if ((opt_headers_size * 4) > uri_size)
+ {
+ if (opt_headers_size > method_size)
+ return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+ else
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+ }
+ else
+ { /* Request target is significantly larger than headers */
+ if (uri_size > method_size * 4)
+ return MHD_HTTP_STATUS_URI_TOO_LONG;
+ else
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+ }
+ }
+ if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
+ {
+ /* Recommend application to retry with a shorter request target */
+ if (uri_size > method_size * 4)
+ return MHD_HTTP_STATUS_URI_TOO_LONG;
+ else
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+ }
+
+ if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
+ {
+ /* The request target (URI) and headers are (reasonably) very small.
+ Some non-standard long request method is used. */
+ /* The last resort response as it means "the method is not supported
+ by the server for any URI". */
+ return MHD_HTTP_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* The almost impossible situation: all elements are small, but cannot
+ fit the buffer. The application set the buffer size to
+ critically low value? */
+
+ if ((1 < opt_headers_size) || (1 < uri_size))
+ {
+ if (opt_headers_size >= uri_size)
+ return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+ else
+ return MHD_HTTP_STATUS_URI_TOO_LONG;
+ }
+
+ /* Nothing to reduce in the request.
+ Reply with some status. */
+ if (0 != host_field_line_size)
+ return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+
+ return MHD_HTTP_STATUS_URI_TOO_LONG;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_switch_from_recv_to_send (struct MHD_Connection *c)
+{
+ /* Read buffer is not needed for this request, shrink it.*/
+ mhd_stream_shrink_read_buffer (c);
+}
+
+
+/**
+ * Finish request serving.
+ * The stream will be re-used or closed.
+ *
+ * @param c the connection to use.
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
+ bool reuse)
+{
+ struct MHD_Daemon *const restrict d = c->daemon;
+
+ if (! reuse)
+ {
+ mhd_assert (! c->stop_with_error || (NULL == c->rp.response) || \
+ (c->rp.response->cfg.int_err_resp));
+ /* Next function will destroy response, notify client,
+ * destroy memory pool and set connection state to "CLOSED" */
+ mhd_conn_pre_close (c,
+ c->stop_with_error ?
+ mhd_CONN_CLOSE_ERR_REPLY_SENT :
+ mhd_CONN_CLOSE_HTTP_COMPLETED,
+ NULL);
+ }
+ else
+ {
+ /* Reset connection to process the next request */
+ size_t new_read_buf_size;
+ mhd_assert (! c->stop_with_error);
+ mhd_assert (! c->discard_request);
+ mhd_assert (NULL == c->rq.cntn.lbuf.buf);
+
+#if 0 // TODO: notification callback
+ if ( (NULL != d->notify_completed) &&
+ (c->rq.app_aware) )
+ d->notify_completed (d->notify_completed_cls,
+ c,
+ &c->rq.app_context,
+ MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ c->rq.app_aware = false;
+#endif
+
+ mhd_stream_call_dcc_cleanup_if_needed (c);
+ if (NULL != c->rp.resp_iov.iov)
+ {
+ free (c->rp.resp_iov.iov);
+ c->rp.resp_iov.iov = NULL;
+ }
+
+ if (NULL != c->rp.response)
+ mhd_response_dec_use_count (c->rp.response);
+ c->rp.response = NULL;
+
+ c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
+ c->state = MHD_CONNECTION_INIT;
+ c->event_loop_info =
+ (0 == c->read_buffer_offset) ?
+ MHD_EVENT_LOOP_INFO_READ : MHD_EVENT_LOOP_INFO_PROCESS;
+
+ memset (&c->rq, 0, sizeof(c->rq));
+
+ /* iov (if any) will be deallocated by mhd_pool_reset */
+ memset (&c->rp, 0, sizeof(c->rp));
+
+ // TODO: set all rq and tp pointers to NULL manually. Do the same in other places.
+
+ 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;
+
+ /* Reset the read buffer to the starting size,
+ preserving the bytes we have already read. */
+ new_read_buf_size = d->conns.cfg.mem_pool_size / 2;
+ if (c->read_buffer_offset > new_read_buf_size)
+ new_read_buf_size = c->read_buffer_offset;
+
+ c->read_buffer
+ = mhd_pool_reset (c->pool,
+ c->read_buffer,
+ c->read_buffer_offset,
+ new_read_buf_size);
+ c->read_buffer_size = new_read_buf_size;
+ }
+ c->rq.app_context = NULL;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_check_timedout (struct MHD_Connection *restrict c)
+{
+ const uint_fast64_t timeout = c->connection_timeout_ms;
+ uint_fast64_t now;
+ uint_fast64_t since_actv;
+
+ mhd_assert (! c->suspended);
+
+ if (0 == timeout)
+ return false;
+
+ now = MHD_monotonic_msec_counter (); // TODO: Get and use timer value one time only per round
+ since_actv = now - c->last_activity;
+ /* Keep the next lines in sync with #connection_get_wait() to avoid
+ * undesired side-effects like busy-waiting. */
+ if (timeout < since_actv)
+ {
+ const uint_fast64_t jump_back = c->last_activity - now;
+ if (jump_back < since_actv)
+ {
+ /* Very unlikely that it is more than quarter-million years pause.
+ * More likely that system clock jumps back. */
+ if (4000 >= jump_back)
+ {
+ c->last_activity = now; /* Avoid repetitive messages.
+ Warn: the order of connections sorted
+ by timeout is not updated. */
+ mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_CORRECTED, \
+ mhd_LOG_FMT ("Detected system clock %u " \
+ "milliseconds jump back."),
+ (unsigned int) jump_back);
+ return false;
+ }
+ mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_LARGE, \
+ mhd_LOG_FMT ("Detected too large system clock %" \
+ PRIuFAST64 " milliseconds jump back"),
+ jump_back);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Update last activity mark to the current time..
+ * @param c the connection to update
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
+{
+ struct MHD_Daemon *const restrict d = c->daemon;
+#if defined(MHD_USE_THREADS)
+ mhd_assert (! mhd_D_HAS_WORKERS (d));
+#endif /* MHD_USE_THREADS */
+
+ mhd_assert (! c->suspended);
+
+ if (0 == c->connection_timeout_ms)
+ return; /* Skip update of activity for connections
+ without timeout timer. */
+
+ c->last_activity = MHD_monotonic_msec_counter (); // TODO: Get and use time value one time per round
+ if (mhd_D_HAS_THR_PER_CONN (d))
+ return; /* each connection has personal timeout */
+
+ if (c->connection_timeout_ms != d->conns.cfg.timeout)
+ return; /* custom timeout, no need to move it in "normal" DLL */
+
+ /* move connection to head of timeout list (by remove + add operation) */
+ mhd_DLINKEDL_DEL_D (&(d->conns.def_timeout), c, by_timeout);
+ mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout);
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3) void
+mhd_conn_pre_close (struct MHD_Connection *restrict c,
+ enum mhd_ConnCloseReason reason,
+ const char *log_msg)
+{
+ bool close_hard;
+ bool use_local_lingering;
+ enum MHD_RequestTerminationCode term_code;
+ enum MHD_StatusCode sc;
+
+ sc = MHD_SC_INTERNAL_ERROR;
+ switch (reason)
+ {
+ case mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ sc = MHD_SC_REQ_MALFORMED;
+ break;
+ case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
+ break;
+ case mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE;
+ break;
+ case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY:
+ close_hard = true;
+ term_code = (! c->stop_with_error || c->rq.too_large) ?
+ MHD_REQUEST_TERMINATED_NO_RESOURCES :
+ MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE;
+ break;
+ case mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE:
+ close_hard = true;
+ term_code = c->rq.too_large ?
+ MHD_REQUEST_TERMINATED_NO_RESOURCES :
+ MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ sc = MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE;
+ break;
+ case mhd_CONN_CLOSE_APP_ERROR:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_BY_APP_ERROR;
+ sc = MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED;
+ break;
+ case mhd_CONN_CLOSE_APP_ABORTED:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_BY_APP_ABORT;
+ sc = MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION;
+ break;
+ case mhd_CONN_CLOSE_INT_ERROR:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
+ break;
+ case mhd_CONN_CLOSE_SOCKET_ERR:
+ close_hard = true;
+ switch (c->sk_discnt_err)
+ {
+ case mhd_SOCKET_ERR_NOMEM:
+ term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
+ break;
+ case mhd_SOCKET_ERR_REMT_DISCONN:
+ close_hard = false;
+ term_code = (MHD_CONNECTION_INIT == c->state) ?
+ MHD_REQUEST_TERMINATED_COMPLETED_OK /* Not used */ :
+ MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ break;
+ case mhd_SOCKET_ERR_CONNRESET:
+ term_code = MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ break;
+ case mhd_SOCKET_ERR_CONN_BROKEN:
+ case mhd_SOCKET_ERR_NOTCONN:
+ case mhd_SOCKET_ERR_TLS:
+ case mhd_SOCKET_ERR_PIPE:
+ case mhd_SOCKET_ERR_NOT_CHECKED:
+ case mhd_SOCKET_ERR_BADF:
+ case mhd_SOCKET_ERR_INVAL:
+ case mhd_SOCKET_ERR_OPNOTSUPP:
+ case mhd_SOCKET_ERR_NOTSOCK:
+ case mhd_SOCKET_ERR_OTHER:
+ case mhd_SOCKET_ERR_INTERNAL:
+ case mhd_SOCKET_ERR_NO_ERROR:
+ term_code = MHD_REQUEST_TERMINATED_CONNECTION_ERROR;
+ break;
+ case mhd_SOCKET_ERR_AGAIN:
+ case mhd_SOCKET_ERR_INTR:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ }
+ break;
+ case mhd_CONN_CLOSE_DAEMON_SHUTDOWN:
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN;
+ break;
+
+ case mhd_CONN_CLOSE_TIMEDOUT:
+ if (MHD_CONNECTION_INIT == c->state)
+ {
+ close_hard = false;
+ term_code = MHD_REQUEST_TERMINATED_COMPLETED_OK; /* Not used */
+ break;
+ }
+ close_hard = true;
+ term_code = MHD_REQUEST_TERMINATED_TIMEOUT_REACHED;
+ break;
+
+ case mhd_CONN_CLOSE_ERR_REPLY_SENT:
+ close_hard = false;
+ term_code = c->rq.too_large ?
+ MHD_REQUEST_TERMINATED_NO_RESOURCES :
+ MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ break;
+ case mhd_CONN_CLOSE_HTTP_COMPLETED:
+ close_hard = false;
+ term_code = MHD_REQUEST_TERMINATED_COMPLETED_OK;
+ break;
+
+ default:
+ mhd_assert (0 && "Unreachable code");
+ MHD_UNREACHABLE_;
+ term_code = MHD_REQUEST_TERMINATED_COMPLETED_OK;
+ close_hard = false;
+ }
+
+ mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc));
+
+ use_local_lingering = false;
+ /* Make changes on the socket early to let the kernel and the remote
+ * to process the changes in parallel. */
+ if (close_hard)
+ {
+ /* Use abortive closing, send RST to remote to indicate a problem */
+ (void) mhd_socket_set_hard_close (c->socket_fd);
+ c->state = MHD_CONNECTION_CLOSED;
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ }
+ else
+ {
+ if (mhd_socket_shut_wr (c->socket_fd) && (! c->sk_rmt_shut_wr))
+ {
+ (void) 0; // TODO: start local lingering phase
+ c->state = MHD_CONNECTION_CLOSED; // TODO: start local lingering phase
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase
+ // use_local_lingering = true;
+ }
+ else
+ { /* No need / not possible to linger */
+ c->state = MHD_CONNECTION_CLOSED;
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ }
+ }
+
+#ifdef HAVE_LOG_FUNCTIONALITY
+ if (NULL != log_msg)
+ {
+ mhd_LOG_MSG (c->daemon, sc, log_msg);
+ }
+#else /* ! HAVE_LOG_FUNCTIONALITY */
+ (void) log_msg;
+#endif /* ! HAVE_LOG_FUNCTIONALITY */
+
+ mhd_stream_call_dcc_cleanup_if_needed (c);
+ if (NULL != c->rp.resp_iov.iov)
+ {
+ free (c->rp.resp_iov.iov);
+ c->rp.resp_iov.iov = NULL;
+ }
+
+#if 0 // TODO: notification callback
+ mhd_assert ((MHD_CONNECTION_INIT != c->state) || (! c->rq.app_aware));
+ if ( (NULL != d->notify_completed) &&
+ (c->rq.app_aware) )
+ d->notify_completed (d->notify_completed_cls,
+ c,
+ &c->rq.app_context,
+ MHD_REQUEST_TERMINATED_COMPLETED_OK);
+#else
+ (void) term_code;
+#endif
+ c->rq.app_aware = false;
+
+ if (! mhd_D_HAS_THR_PER_CONN (c->daemon))
+ {
+ if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout)
+ mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \
+ c, by_timeout);
+ else
+ mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \
+ c, by_timeout);
+ }
+
+#ifndef NDEBUG
+ c->dbg.pre_closed = true;
+#endif
+
+ if (! use_local_lingering)
+ mhd_conn_pre_clean (c);
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) void
+mhd_conn_pre_clean (struct MHD_Connection *restrict c)
+{
+ // TODO: support suspended connections
+
+ mhd_conn_mark_unready (c, c->daemon);
+
+ if (NULL != c->rq.cntn.lbuf.buf)
+ mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
+ c->rq.cntn.lbuf.buf = NULL;
+ if (NULL != c->rp.response)
+ mhd_response_dec_use_count (c->rp.response);
+ c->rp.response = NULL;
+
+ mhd_assert (NULL != c->pool);
+ c->read_buffer_offset = 0;
+ c->read_buffer_size = 0;
+ c->read_buffer = NULL;
+ c->write_buffer_send_offset = 0;
+ c->write_buffer_append_offset = 0;
+ c->write_buffer_size = 0;
+ c->write_buffer = NULL;
+ // TODO: call in the thread where it was allocated for thread-per-connection
+ mhd_pool_destroy (c->pool);
+ c->pool = NULL;
+
+#ifdef MHD_USE_EPOLL
+ if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type)
+ {
+ struct epoll_event event;
+
+ event.events = 0;
+ event.data.ptr = NULL;
+ if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd,
+ EPOLL_CTL_DEL,
+ c->socket_fd,
+ &event))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_EPOLL_CTL_REMOVE_FAILED,
+ "Failed to remove connection socket from epoll.");
+ }
+ }
+#endif /* MHD_USE_EPOLL */
+
+#ifndef NDEBUG
+ c->dbg.pre_cleaned = true;
+#endif
+}
diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h
@@ -0,0 +1,306 @@
+/*
+ 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/stream_funcs.h
+ * @brief The declaration of the stream internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STREAM_FUNCS_H
+#define MHD_STREAM_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+
+
+struct MHD_Connection; /* forward declaration */
+
+
+/**
+ * The stage of input data processing.
+ * Used for out-of-memory (in the pool) handling.
+ */
+enum MHD_FIXED_ENUM_ MHD_ProcRecvDataStage
+{
+ MHD_PROC_RECV_INIT, /**< No data HTTP request data have been processed yet */
+ MHD_PROC_RECV_METHOD, /**< Processing/receiving the request HTTP method */
+ MHD_PROC_RECV_URI, /**< Processing/receiving the request URI */
+ MHD_PROC_RECV_HTTPVER, /**< Processing/receiving the request HTTP version string */
+ MHD_PROC_RECV_HEADERS, /**< Processing/receiving the request HTTP headers */
+ MHD_PROC_RECV_COOKIE, /**< Processing the received request cookie header */
+ MHD_PROC_RECV_BODY_NORMAL, /**< Processing/receiving the request non-chunked body */
+ MHD_PROC_RECV_BODY_CHUNKED,/**< Processing/receiving the request chunked body */
+ MHD_PROC_RECV_FOOTERS /**< Processing/receiving the request footers */
+};
+
+/**
+ * Allocate memory from connection's memory pool.
+ * If memory pool doesn't have enough free memory but read or write buffer
+ * have some unused memory, the size of the buffer will be reduced as needed.
+ * @param connection the connection to use
+ * @param size the size of allocated memory area
+ * @return pointer to allocated memory region in the pool or
+ * NULL if no memory is available
+ */
+MHD_INTERNAL void *
+mhd_stream_alloc_memory (struct MHD_Connection *restrict connection,
+ size_t size)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Shrink stream read buffer to the zero size of free space in the buffer
+ * @param c the connection whose read buffer is being manipulated
+ */
+MHD_INTERNAL void
+mhd_stream_shrink_read_buffer (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Allocate the maximum available amount of memory from MemoryPool
+ * for write buffer.
+ * @param c the connection whose write buffer is being manipulated
+ * @return the size of the free space in the write buffer
+ */
+MHD_INTERNAL size_t
+mhd_stream_maximize_write_buffer (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Select the HTTP error status code for "out of receive buffer space" error.
+ * @param c the connection to process
+ * @param stage the current stage of request receiving
+ * @param add_element_size the size of the @a add_element;
+ * zero if @a add_element is NULL
+ * @param add_element the optional pointer to the element failed to be processed
+ * or added, the meaning of the element depends on
+ * the @a stage. Could be not zero-terminated and can
+ * contain binary zeros. Can be NULL.
+ * @return the HTTP error code to use in the error reply
+ */
+MHD_INTERNAL unsigned int
+mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
+ enum MHD_ProcRecvDataStage stage,
+ size_t add_element_size,
+ const char *restrict add_element)
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_SIZE_ (4,3);
+
+/**
+ * Switch connection from recv mode to send mode.
+ *
+ * Current request header or body will not be read anymore,
+ * response must be assigned to connection.
+ * @param c the connection to prepare for sending.
+ */
+MHD_INTERNAL void
+mhd_stream_switch_from_recv_to_send (struct MHD_Connection *c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Finish request serving.
+ * The stream will be re-used or closed.
+ *
+ * @param c the connection to use.
+ */
+MHD_INTERNAL void
+mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
+ bool reuse)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Update last activity mark to the current time..
+ * @param c the connection to update
+ */
+MHD_INTERNAL void
+mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Update last activity mark to the current time..
+ * @param c the connection to update
+ * @return 'true' if connection has not been timed out,
+ * 'false' otherwise
+ */
+MHD_INTERNAL bool
+mhd_stream_check_timedout (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * The reason to close the connection
+ */
+enum mhd_ConnCloseReason
+{
+ /* Hard problem while receiving */
+ /**
+ * Client sent data that cannot be interpreted as HTTP data
+ */
+ mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN
+ ,
+ /**
+ * No space in the connection pool memory for receiving or processing
+ * the request
+ */
+ mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST
+ ,
+ /**
+ * The client shut down send before complete request sent
+ */
+ mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY
+ ,
+
+ /* Hard problem while sending */
+
+ /**
+ * No space in the connection pool memory for the reply
+ */
+ mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY
+ ,
+ /**
+ * No memory to create error response
+ */
+ mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE
+ ,
+ /**
+ * Application behaves incorrectly
+ */
+ mhd_CONN_CLOSE_APP_ERROR
+ ,
+ /**
+ * Application requested about of the stream
+ */
+ mhd_CONN_CLOSE_APP_ABORTED
+ ,
+
+ /* Hard problem while receiving or sending */
+ /**
+ * MHD internal error.
+ * Should never appear.
+ */
+ mhd_CONN_CLOSE_INT_ERROR
+ ,
+ /**
+ * The TCP or TLS connection is broken or aborted due to error on socket
+ * or TLS
+ */
+ mhd_CONN_CLOSE_SOCKET_ERR
+ ,
+ /**
+ * The daemon is being shut down, all connection must be closed
+ */
+ mhd_CONN_CLOSE_DAEMON_SHUTDOWN
+ ,
+
+ /* Could be hard or soft error depending on connection state */
+ /**
+ * Timeout detected when receiving request
+ */
+ mhd_CONN_CLOSE_TIMEDOUT
+ ,
+
+ /* Soft problem */
+ /**
+ * The connection must be closed after error response as the client
+ * violates HTTP specification
+ */
+ mhd_CONN_CLOSE_ERR_REPLY_SENT
+ ,
+
+ /* Graceful closing */
+ /**
+ * Close connection after graceful completion of HTTP communication
+ */
+ mhd_CONN_CLOSE_HTTP_COMPLETED
+
+};
+
+
+/**
+ * Prepare connection for closing.
+ * @param c the connection for pre-closing
+ * @param reason the reason for closing
+ * @param log_msg the message for the log
+ */
+MHD_INTERNAL void
+mhd_conn_pre_close (struct MHD_Connection *restrict c,
+ enum mhd_ConnCloseReason reason,
+ const char *log_msg)
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
+
+/**
+ * Abort the stream and log message
+ */
+#ifdef HAVE_LOG_FUNCTIONALITY
+# define mhd_STREAM_ABORT(c,r,m) (mhd_conn_pre_close ((c),(r),(m)))
+#else /* ! HAVE_LOG_FUNCTIONALITY */
+# define mhd_STREAM_ABORT(c,r,m) (mhd_conn_pre_close ((c),(r),NULL))
+#endif /* ! HAVE_LOG_FUNCTIONALITY */
+
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "aborted by application"
+ * @param c the connection for pre-closing
+ */
+#define mhd_conn_pre_close_app_abort(c) \
+ mhd_conn_pre_close ((c), mhd_CONN_CLOSE_APP_ABORTED, NULL)
+
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "socket error"
+ * @param c the connection for pre-closing
+ */
+#define mhd_conn_pre_close_skt_err(c) \
+ mhd_conn_pre_close ((c), mhd_CONN_CLOSE_SOCKET_ERR, NULL)
+
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "request finished"
+ * @param c the connection for pre-closing
+ */
+#define mhd_conn_pre_close_req_finished(c) \
+ mhd_conn_pre_close ((c), mhd_CONN_CLOSE_HTTP_COMPLETED, NULL)
+
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "timed out".
+ * @param c the connection for pre-closing
+ */
+#define mhd_conn_pre_close_timedout(c) \
+ mhd_conn_pre_close ((c), mhd_CONN_CLOSE_TIMEDOUT, NULL)
+
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "daemon shutdown".
+ * @param c the connection for pre-closing
+ */
+#define mhd_conn_pre_close_d_shutdown(c) \
+ mhd_conn_pre_close ((c), mhd_CONN_CLOSE_DAEMON_SHUTDOWN, NULL)
+
+/**
+ * Perform initial connection cleanup.
+ * The connection must be prepared for closing.
+ * @param c the connection for pre-closing
+ */
+MHD_INTERNAL void
+mhd_conn_pre_clean (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ (1);
+
+#endif /* ! MHD_STREAM_FUNCS_H */
diff --git a/src/mhd2/stream_process_reply.c b/src/mhd2/stream_process_reply.c
@@ -0,0 +1,1314 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2014-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/stream_process_reply.h
+ * @brief The implementation of internal functions for forming and sending
+ * replies for requests
+ * @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 "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include <string.h>
+#ifdef HAVE_TIME_H
+# include <time.h>
+#endif
+
+#include "mhd_daemon.h"
+#include "mhd_response.h"
+#include "mhd_reply.h"
+#include "mhd_connection.h"
+
+#include "daemon_logger.h"
+#include "mhd_assert.h"
+
+#include "mhd_str.h"
+#include "http_status_str.h"
+#include "stream_process_reply.h"
+#include "stream_funcs.h"
+#include "request_get_value.h"
+
+#include "mhd_public_api.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_call_dcc_cleanup_if_needed (struct MHD_Connection *restrict c)
+{
+ if (mhd_DCC_ACTION_CONTINUE != c->rp.app_act.act)
+ return;
+ if (NULL == c->rp.app_act.data.cntnue.iov_data)
+ return;
+
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_CALLBACK == \
+ c->rp.response->cntn_dtype);
+
+ if (NULL != c->rp.app_act.data.cntnue.iov_data->iov_fcb)
+ {
+ c->rp.app_act.data.cntnue.iov_data->iov_fcb (
+ c->rp.app_act.data.cntnue.iov_data->iov_fcb_cls);
+ }
+
+ c->rp.app_act.data.cntnue.iov_data = NULL;
+}
+
+
+/**
+ * This enum type describes requirements for reply body and reply bode-specific
+ * headers (namely Content-Length, Transfer-Encoding).
+ */
+enum replyBodyUse
+{
+ /**
+ * No reply body allowed.
+ * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are
+ * not allowed as well.
+ */
+ RP_BODY_NONE = 0,
+
+ /**
+ * Do not send reply body.
+ * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are
+ * allowed, but optional.
+ */
+ RP_BODY_HEADERS_ONLY = 1,
+
+ /**
+ * Send reply body and
+ * reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked'.
+ * Reply body headers are required.
+ */
+ RP_BODY_SEND = 2
+};
+
+
+/**
+ * Is it allowed to reuse the connection?
+ * The TCP stream can be reused for the next requests if the connection
+ * is HTTP 1.1 and the "Connection" header either does not exist or
+ * is not set to "close", or if the connection is HTTP 1.0 and the
+ * "Connection" header is explicitly set to "keep-alive".
+ * If no HTTP version is specified (or if it is not 1.0 or 1.1), the connection
+ * is definitively closed. If the "Connection" header is not exactly "close"
+ * or "keep-alive", connection is reused if is it HTTP/1.1.
+ * If response has HTTP/1.0 flag or has "Connection: close" header
+ * then connection must be closed.
+ * If full request has not been read then connection must be closed
+ * as well as more client data may be sent.
+ *
+ * @param c the connection to check for re-use
+ * @return mhd_CONN_KEEPALIVE_POSSIBLE if (based on the request and
+ * the response) a connection could be reused,
+ * MHD_CONN_MUST_CLOSE if connection must be closed after sending
+ * complete reply,
+ * mhd_CONN_MUST_UPGRADE if connection must be upgraded.
+ */
+static MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnReuse
+get_conn_reuse (struct MHD_Connection *c)
+{
+ const struct MHD_Response *const restrict rp = c->rp.response;
+
+ mhd_assert (NULL != rp);
+ if (mhd_CONN_MUST_CLOSE == c->conn_reuse)
+ return mhd_CONN_MUST_CLOSE;
+
+ mhd_assert ( (! c->stop_with_error) || (c->discard_request));
+ if ((c->sk_rmt_shut_wr) || (c->discard_request))
+ return mhd_CONN_MUST_CLOSE;
+
+ if (rp->cfg.close_forced)
+ return mhd_CONN_MUST_CLOSE;
+
+ mhd_assert ((MHD_SIZE_UNKNOWN != rp->cntn_size) || \
+ (! rp->cfg.mode_1_0));
+
+ if (! MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver))
+ return mhd_CONN_MUST_CLOSE;
+
+ if (rp->cfg.mode_1_0 &&
+ ! mhd_stream_has_header_token_st (c,
+ MHD_HTTP_HEADER_CONNECTION,
+ "keep-alive"))
+ return mhd_CONN_MUST_CLOSE;
+
+#if 0 // def UPGRADE_SUPPORT // TODO: Implement upgrade support
+ /* TODO: Move below the next check when MHD stops closing connections
+ * when response is queued in first callback */
+ if (NULL != r->upgrade_handler)
+ {
+ /* No "close" token is enforced by 'add_response_header_connection()' */
+ mhd_assert (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
+ /* Valid HTTP version is enforced by 'MHD_queue_response()' */
+ mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (c->rq.http_ver));
+ mhd_assert (! c->stop_with_error);
+ return mhd_CONN_MUST_UPGRADE;
+ }
+#endif /* UPGRADE_SUPPORT */
+
+ return mhd_CONN_KEEPALIVE_POSSIBLE;
+}
+
+
+/**
+ * Check whether reply body must be used.
+ *
+ * If reply body is needed, it could be zero-sized.
+ *
+ * @param c the connection to check
+ * @param rcode the response code
+ * @return enum value indicating whether response body can be used and
+ * whether response body length headers are allowed or required.
+ * @sa is_reply_body_header_needed()
+ */
+static enum replyBodyUse
+is_reply_body_needed (struct MHD_Connection *restrict c,
+ uint_fast16_t rcode)
+{
+ mhd_assert (100 <= rcode);
+ mhd_assert (999 >= rcode);
+
+ if (199 >= rcode)
+ return RP_BODY_NONE;
+
+ if (MHD_HTTP_STATUS_NO_CONTENT == rcode)
+ return RP_BODY_NONE;
+
+#if 0
+ /* This check is not needed as upgrade handler is used only with code 101 */
+#ifdef UPGRADE_SUPPORT
+ if (NULL != rp.response->upgrade_handler)
+ return RP_BODY_NONE;
+#endif /* UPGRADE_SUPPORT */
+#endif
+
+#if 0
+ /* CONNECT is not supported by MHD */
+ /* Successful responses for connect requests are filtered by
+ * MHD_queue_response() */
+ if ( (mhd_HTTP_METHOD_CONNECT == c->rq.http_mthd) &&
+ (2 == rcode / 100) )
+ return false; /* Actually pass-through CONNECT is not supported by MHD */
+#endif
+
+ /* Reply body headers could be used.
+ * Check whether reply body itself must be used. */
+
+ if (mhd_HTTP_METHOD_HEAD == c->rq.http_mthd)
+ return RP_BODY_HEADERS_ONLY;
+
+ if (MHD_HTTP_STATUS_NOT_MODIFIED == rcode)
+ return RP_BODY_HEADERS_ONLY;
+
+ /* Reply body must be sent.
+ * The body may have zero length, but body size must be indicated by
+ * headers ('Content-Length:' or 'Transfer-Encoding: chunked'). */
+ return RP_BODY_SEND;
+}
+
+
+/**
+ * Setup connection reply properties.
+ *
+ * Reply properties include presence of reply body, transfer-encoding
+ * type and other.
+ *
+ * @param connection to connection to process
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+setup_reply_properties (struct MHD_Connection *restrict c)
+{
+ struct MHD_Response *const restrict r = c->rp.response; /**< a short alias */
+ enum replyBodyUse use_rp_body;
+ bool use_chunked;
+ bool end_by_closing;
+
+ mhd_assert (NULL != r);
+
+ /* ** Adjust reply properties ** */
+
+ c->conn_reuse = get_conn_reuse (c);
+ use_rp_body = is_reply_body_needed (c, r->sc);
+ c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
+ c->rp.props.use_reply_body_headers = (use_rp_body >= RP_BODY_HEADERS_ONLY);
+
+#if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
+ mhd_assert ( (NULL == r->upgrade_handler) ||
+ (RP_BODY_NONE == use_rp_body) );
+#endif /* UPGRADE_SUPPORT */
+
+ use_chunked = false;
+ end_by_closing = false;
+ if (c->rp.props.use_reply_body_headers)
+ {
+ if (r->cfg.chunked)
+ {
+ mhd_assert (! r->cfg.mode_1_0);
+ use_chunked = (MHD_HTTP_VERSION_1_1 == c->rq.http_ver);
+ }
+ if ((MHD_SIZE_UNKNOWN == r->cntn_size) &&
+ (! use_chunked) &&
+ (c->rp.props.send_reply_body))
+ {
+ /* End of the stream is indicated by closure */
+ end_by_closing = true;
+ }
+ }
+
+ if (end_by_closing)
+ {
+ mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
+ /* End of the stream is indicated by closure */
+ c->conn_reuse = mhd_CONN_MUST_CLOSE;
+ }
+
+ c->rp.props.chunked = use_chunked;
+ c->rp.props.end_by_closing = end_by_closing;
+
+ if ((! c->rp.props.send_reply_body) || (0 == r->cntn_size))
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+ else if (c->rp.props.chunked)
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+ else
+ {
+ switch (r->cntn_dtype)
+ {
+ case mhd_RESPONSE_CONTENT_DATA_BUFFER:
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_RESP_BUF;
+ break;
+ case mhd_RESPONSE_CONTENT_DATA_IOVEC:
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_IOV;
+ break;
+ case mhd_RESPONSE_CONTENT_DATA_FILE:
+#if 0 // TODO: TLS support
+ if (use_tls)
+ {
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+ break;
+ }
+#endif
+#ifdef MHD_USE_SENDFILE
+ if (r->cntn.file.use_sf)
+ {
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_FILE;
+ break;
+ }
+#endif
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+ break;
+ case mhd_RESPONSE_CONTENT_DATA_CALLBACK:
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+ break;
+ case mhd_RESPONSE_CONTENT_DATA_INVALID:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+ break;
+ }
+ }
+
+#ifdef _DEBUG
+ c->rp.props.set = true;
+#endif /* _DEBUG */
+}
+
+
+/**
+ * Check whether queued response is suitable for @a connection.
+ * @param connection to connection to check
+ */
+static void
+check_connection_reply (struct MHD_Connection *restrict c)
+{
+ struct MHD_Response *const restrict r = c->rp.response; /**< a short alias */
+
+ mhd_assert (c->rp.props.set);
+
+ if ( (! c->rp.props.use_reply_body_headers) &&
+ (0 != r->cntn_size) )
+ {
+ mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_NOT_EMPTY_RESPONSE,
+ mhd_LOG_FMT ("This reply with response code %u " \
+ "cannot use reply content. Non-empty " \
+ "response content is ignored and not used."),
+ (unsigned) (c->rp.response->sc));
+ }
+ if ( (! c->rp.props.use_reply_body_headers) &&
+ (r->cfg.cnt_len_by_app) )
+ {
+ mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_CONTENT_LENGTH_NOT_ALLOWED,
+ mhd_LOG_FMT ("This reply with response code %u " \
+ "cannot use reply content. Application " \
+ "defined \"Content-Length\" header " \
+ "violates HTTP specification."),
+ (unsigned) (c->rp.response->sc));
+ }
+}
+
+
+/**
+ * Produce time stamp.
+ *
+ * Result is NOT null-terminated.
+ * Result is always 29 bytes long.
+ *
+ * @param[out] date where to write the time stamp, with
+ * at least 29 bytes of savailable space.
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (1) bool
+get_date_str (char *date)
+{
+ static const char *const days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+ static const char *const mons[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ static const size_t buf_len = 29;
+ struct tm now;
+ time_t t;
+ const char *src;
+#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
+ ! defined(HAVE_GMTIME_R)
+ struct tm *pNow;
+#endif
+
+ if ((time_t) -1 == time (&t))
+ return false;
+#if defined(HAVE_GMTIME_R)
+ if (NULL == gmtime_r (&t,
+ &now))
+ return false;
+#elif defined(HAVE_C11_GMTIME_S)
+ if (NULL == gmtime_s (&t,
+ &now))
+ return false;
+#elif defined(HAVE_W32_GMTIME_S)
+ if (0 != gmtime_s (&now,
+ &t))
+ return false;
+#else
+ pNow = gmtime (&t);
+ if (NULL == pNow)
+ return false;
+ now = *pNow;
+#endif
+
+ /* Day of the week */
+ src = days[now.tm_wday % 7];
+ date[0] = src[0];
+ date[1] = src[1];
+ date[2] = src[2];
+ date[3] = ',';
+ date[4] = ' ';
+ /* Day of the month */
+ if (2 != mhd_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
+ date + 5, buf_len - 5))
+ return false;
+ date[7] = ' ';
+ /* Month */
+ src = mons[now.tm_mon % 12];
+ date[8] = src[0];
+ date[9] = src[1];
+ date[10] = src[2];
+ date[11] = ' ';
+ /* Year */
+ if (4 != mhd_uint16_to_str ((uint_least16_t) (1900 + now.tm_year), date + 12,
+ buf_len - 12))
+ return false;
+ date[16] = ' ';
+ /* Time */
+ mhd_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
+ date[19] = ':';
+ mhd_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
+ date[22] = ':';
+ mhd_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
+ date[25] = ' ';
+ date[26] = 'G';
+ date[27] = 'M';
+ date[28] = 'T';
+
+ return true;
+}
+
+
+/**
+ * Produce HTTP DATE header.
+ * Result is always 37 bytes long (plus one terminating null).
+ *
+ * @param[out] header where to write the header, with
+ * at least 38 bytes available space.
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (1) bool
+get_date_header (char *header)
+{
+ header[0] = 'D';
+ header[1] = 'a';
+ header[2] = 't';
+ header[3] = 'e';
+ header[4] = ':';
+ header[5] = ' ';
+ if (! get_date_str (header + 6))
+ {
+ header[0] = 0;
+ return false;
+ }
+ header[35] = '\r';
+ header[36] = '\n';
+ header[37] = 0;
+ return true;
+}
+
+
+/**
+ * Append data to the buffer if enough space is available,
+ * update position.
+ * @param[out] buf the buffer to append data to
+ * @param[in,out] ppos the pointer to position in the @a buffer
+ * @param buf_size the size of the @a buffer
+ * @param append the data to append
+ * @param append_size the size of the @a append
+ * @return true if data has been added and position has been updated,
+ * false if not enough space is available
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+buffer_append (char *buf,
+ size_t *ppos,
+ size_t buf_size,
+ const char *append,
+ size_t append_size)
+{
+ mhd_assert (NULL != buf); /* Mute static analyzer */
+ if (buf_size < *ppos + append_size)
+ return false;
+ memcpy (buf + *ppos, append, append_size);
+ *ppos += append_size;
+ return true;
+}
+
+
+/**
+ * Add user-defined headers from response object to
+ * the text buffer.
+ *
+ * @param buf the buffer to add headers to
+ * @param ppos the pointer to the position in the @a buf
+ * @param buf_size the size of the @a buf
+ * @param response the response
+ * @param filter_content_len skip "Content-Length" header if any
+ * @param add_close add "close" token to the
+ * "Connection:" header (if any), ignored if no "Connection:"
+ * header was added by user or if "close" token is already
+ * present in "Connection:" header
+ * @param add_keep_alive add "Keep-Alive" token to the
+ * "Connection:" header (if any)
+ * @return true if succeed,
+ * false if buffer is too small
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+add_user_headers (char *restrict buf,
+ size_t *restrict ppos,
+ size_t buf_size,
+ struct MHD_Response *restrict r,
+ bool filter_content_len,
+ bool add_close,
+ bool add_keep_alive)
+{
+ struct mhd_ResponseHeader *hdr; /**< Iterates through User-specified headers */
+ size_t el_size; /**< the size of current element to be added to the @a buf */
+
+ mhd_assert (! add_close || ! add_keep_alive);
+ mhd_assert (! add_keep_alive || ! add_close);
+
+ if (r->cfg.has_hdr_conn)
+ {
+ add_close = false; /* No such header */
+ add_keep_alive = false; /* No such header */
+ }
+
+ for (hdr = mhd_DLINKEDL_GET_FIRST (r, headers);
+ NULL != hdr;
+ hdr = mhd_DLINKEDL_GET_NEXT (hdr, headers))
+ {
+ size_t initial_pos = *ppos;
+
+ if (filter_content_len)
+ { /* Need to filter-out "Content-Length" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, \
+ hdr->name.cstr,
+ hdr->name.len))
+ {
+ /* Reset filter flag */
+ filter_content_len = false;
+ continue; /* Skip "Content-Length" header */
+ }
+ }
+
+ /* Add user header */
+ el_size = hdr->name.len + 2 + hdr->value.len + 2;
+ if (buf_size < *ppos + el_size)
+ return false;
+ memcpy (buf + *ppos, hdr->name.cstr, hdr->name.len);
+ (*ppos) += hdr->name.len;
+ buf[(*ppos)++] = ':';
+ buf[(*ppos)++] = ' ';
+ if (add_close || add_keep_alive)
+ {
+ /* "Connection:" header must be always the first one */
+ mhd_assert (mhd_str_equal_caseless_n (hdr->name.cstr, \
+ MHD_HTTP_HEADER_CONNECTION, \
+ hdr->name.len));
+
+ if (add_close)
+ {
+ el_size += mhd_SSTR_LEN ("close, ");
+ if (buf_size < initial_pos + el_size)
+ return false;
+ memcpy (buf + *ppos, "close, ",
+ mhd_SSTR_LEN ("close, "));
+ *ppos += mhd_SSTR_LEN ("close, ");
+ }
+ else
+ {
+ el_size += mhd_SSTR_LEN ("Keep-Alive, ");
+ if (buf_size < initial_pos + el_size)
+ return false;
+ memcpy (buf + *ppos, "Keep-Alive, ",
+ mhd_SSTR_LEN ("Keep-Alive, "));
+ *ppos += mhd_SSTR_LEN ("Keep-Alive, ");
+ }
+ add_close = false;
+ add_keep_alive = false;
+ }
+ if (0 != hdr->value.len)
+ memcpy (buf + *ppos, hdr->value.cstr, hdr->value.len);
+ *ppos += hdr->value.len;
+ buf[(*ppos)++] = '\r';
+ buf[(*ppos)++] = '\n';
+ mhd_assert (initial_pos + el_size == (*ppos));
+ }
+ return true;
+}
+
+
+/**
+ * Append static string to the buffer if enough space is available,
+ * update position.
+ * @param[out] buf the buffer to append data to
+ * @param[in,out] ppos the pointer to position in the @a buffer
+ * @param buf_size the size of the @a buffer
+ * @param str the static string to append
+ * @return true if data has been added and position has been updated,
+ * false if not enough space is available
+ */
+#define buffer_append_s(buf,ppos,buf_size,str) \
+ buffer_append (buf,ppos,buf_size,str, mhd_SSTR_LEN (str))
+
+/**
+ * Allocate the connection's write buffer and fill it with all of the
+ * headers from the response.
+ * Inner version of the function.
+ *
+ * @param c the connection to process
+ * @return 'true' if state has been update,
+ * 'false' if connection is going to be aborted
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+build_header_response_inn (struct MHD_Connection *restrict c)
+{
+ struct MHD_Response *const restrict r = c->rp.response;
+ char *restrict buf; /**< the output buffer */
+ size_t pos; /**< append offset in the @a buf */
+ size_t buf_size; /**< the size of the @a buf */
+ size_t el_size; /**< the size of current element to be added to the @a buf */
+ uint_fast16_t rcode; /**< the response code */
+ bool use_conn_close; /**< Use "Connection: close" header */
+ bool use_conn_k_alive; /**< Use "Connection: Keep-Alive" header */
+
+ mhd_assert (NULL != r);
+
+ /* ** Adjust response properties ** */
+ setup_reply_properties (c);
+
+ mhd_assert (c->rp.props.set);
+ mhd_assert ((mhd_CONN_MUST_CLOSE == c->conn_reuse) || \
+ (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse) || \
+ (mhd_CONN_MUST_UPGRADE == c->conn_reuse));
+#if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
+ mhd_assert ((NULL == r->upgrade_handler) || \
+ (mhd_CONN_MUST_UPGRADE == c->keepalive));
+#else /* ! UPGRADE_SUPPORT */
+ mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
+#endif /* ! UPGRADE_SUPPORT */
+ mhd_assert ((! c->rp.props.chunked) || c->rp.props.use_reply_body_headers);
+ mhd_assert ((! c->rp.props.send_reply_body) || \
+ c->rp.props.use_reply_body_headers);
+ mhd_assert ((! c->rp.props.end_by_closing) || \
+ (mhd_CONN_MUST_CLOSE == c->conn_reuse));
+#if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
+ mhd_assert (NULL == r->upgrade_handler || \
+ ! c->rp.props.use_reply_body_headers);
+#endif /* UPGRADE_SUPPORT */
+
+ check_connection_reply (c);
+
+ rcode = (uint_fast16_t) c->rp.response->sc;
+ if (mhd_CONN_MUST_CLOSE == c->conn_reuse)
+ {
+ /* The closure of connection must be always indicated by header
+ * to avoid hung connections */
+ use_conn_close = true;
+ use_conn_k_alive = false;
+ }
+ else if (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse)
+ {
+ mhd_assert (! r->cfg.mode_1_0);
+ use_conn_close = false;
+ /* Add "Connection: keep-alive" if request is HTTP/1.0 or
+ * if reply is HTTP/1.0
+ * For HTTP/1.1 add header only if explicitly requested by app
+ * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
+ if (r->cfg.mode_1_0 ||
+ (MHD_HTTP_VERSION_1_0 == c->rq.http_ver))
+ use_conn_k_alive = true;
+ else
+ use_conn_k_alive = false;
+ }
+ else
+ {
+ use_conn_close = false;
+ use_conn_k_alive = false;
+ }
+
+ /* ** Actually build the response header ** */
+
+ /* Get all space available */
+ mhd_stream_maximize_write_buffer (c);
+ buf = c->write_buffer;
+ pos = c->write_buffer_append_offset;
+ buf_size = c->write_buffer_size;
+ if (0 == buf_size)
+ return false;
+ mhd_assert (NULL != buf);
+
+ // TODO: use pre-calculated header size
+ /* * The status line * */
+
+ /* The HTTP version */
+ if (! c->rp.responseIcy)
+ { /* HTTP reply */
+ if (! r->cfg.mode_1_0)
+ { /* HTTP/1.1 reply */
+ /* Use HTTP/1.1 responses for HTTP/1.0 clients.
+ * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
+ if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1_STR))
+ return false;
+ }
+ else
+ { /* HTTP/1.0 reply */
+ if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0_STR))
+ return false;
+ }
+ }
+ else
+ { /* ICY reply */
+ if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
+ return false;
+ }
+
+ /* The response code */
+ if (buf_size < pos + 5) /* space + code + space */
+ return false;
+ buf[pos++] = ' ';
+ pos += mhd_uint16_to_str ((uint16_t) rcode, buf + pos,
+ buf_size - pos);
+ buf[pos++] = ' ';
+
+ /* The reason phrase */
+ if (1)
+ {
+ const struct MHD_String *stat_str;
+ stat_str = mhd_HTTP_status_code_to_string_int (rcode);
+ mhd_assert (0 != stat_str->len);
+ if (! buffer_append (buf, &pos, buf_size,
+ stat_str->cstr,
+ stat_str->len))
+ return false;
+ }
+ /* The linefeed */
+ if (buf_size < pos + 2)
+ return false;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+
+ /* * The headers * */
+
+ /* A special custom header */
+ if (0 != r->special_resp.spec_hdr_len)
+ {
+ mhd_assert (r->cfg.int_err_resp);
+ if (buf_size < pos + r->special_resp.spec_hdr_len + 2)
+ return false;
+ memcpy (buf + pos,
+ r->special_resp.spec_hdr,
+ r->special_resp.spec_hdr_len);
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+ }
+
+ /* Main automatic headers */
+
+ /* The "Date:" header */
+ if ( (! r->cfg.has_hdr_date) &&
+ (! c->daemon->req_cfg.suppress_date) )
+ {
+ /* Additional byte for unused zero-termination */
+ if (buf_size < pos + 38)
+ return false;
+ if (get_date_header (buf + pos))
+ pos += 37;
+ }
+ /* The "Connection:" header */
+ mhd_assert (! use_conn_close || ! use_conn_k_alive);
+ mhd_assert (! use_conn_k_alive || ! use_conn_close);
+ if (! r->cfg.has_hdr_conn)
+ {
+ if (use_conn_close)
+ {
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
+ return false;
+ }
+ else if (use_conn_k_alive)
+ {
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
+ return false;
+ }
+ }
+
+ /* User-defined headers */
+
+ if (! add_user_headers (buf, &pos, buf_size, r,
+ ! c->rp.props.use_reply_body_headers,
+ use_conn_close,
+ use_conn_k_alive))
+ return false;
+
+ /* Other automatic headers */
+
+ if (c->rp.props.use_reply_body_headers)
+ {
+ /* Body-specific headers */
+
+ if (c->rp.props.chunked)
+ { /* Chunked encoding is used */
+ mhd_assert (! c->rp.props.end_by_closing);
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \
+ "chunked\r\n"))
+ return false;
+ }
+ else /* Chunked encoding is not used */
+ {
+ if ((MHD_SIZE_UNKNOWN != r->cntn_size) &&
+ (! c->rp.props.end_by_closing) &&
+ (! r->cfg.chunked) &&
+ (! r->cfg.head_only))
+ { /* The size is known and can be indicated by the header */
+ if (! r->cfg.cnt_len_by_app)
+ { /* The response does not have app-defined "Content-Length" header */
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_CONTENT_LENGTH ": "))
+ return false;
+ el_size = mhd_uint64_to_str (r->cntn_size,
+ buf + pos,
+ buf_size - pos);
+ if (0 == el_size)
+ return false;
+ pos += el_size;
+
+ if (buf_size < pos + 2)
+ return false;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+ }
+ }
+ else
+ {
+ mhd_assert ((! c->rp.props.send_reply_body) || \
+ (mhd_CONN_MUST_CLOSE == c->conn_reuse));
+ (void) 0;
+ }
+ }
+ }
+
+ /* * Header termination * */
+ if (buf_size < pos + 2)
+ return false;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+
+ c->write_buffer_append_offset = pos;
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_build_header_response (struct MHD_Connection *restrict c)
+{
+ if (! build_header_response_inn (c))
+ {
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
+ "No memory in the pool for the reply headers.");
+ return false;
+ }
+ c->state = MHD_CONNECTION_HEADERS_SENDING;
+ return true;
+}
+
+
+/**
+ * Pre-process DCC action provided by application.
+ * 'abort' and 'suspend' actions are fully processed,
+ * 'continue' and 'finish' actions needs to be processed by the caller.
+ * @param c the stream to use
+ * @param act the action provided by application
+ * @return 'true' if action if 'continue' or 'finish' and need to be
+ * processed,
+ * 'false' if action is 'suspend' or 'abort' and is already processed.
+ */
+static MHD_FN_PAR_NONNULL_ (1) bool
+preprocess_dcc_action (struct MHD_Connection *restrict c,
+ const struct MHD_DynamicContentCreatorAction *act)
+{
+ /**
+ * The action created for the current request
+ */
+ struct MHD_DynamicContentCreatorAction *const a =
+ &(c->rp.app_act);
+
+ if (NULL != act)
+ {
+ if ((a != act) ||
+ ! mhd_DCC_ACTION_IS_VALID (c->rp.app_act.act) ||
+ ((MHD_SIZE_UNKNOWN != c->rp.response->cntn_size) &&
+ (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_ACTION_INVALID, \
+ "Provided Dynamic Content Creator action is not " \
+ "a correct action generated for the current request.");
+ act = NULL;
+ }
+ }
+ if (NULL == act)
+ a->act = mhd_DCC_ACTION_ABORT;
+
+ switch (a->act)
+ {
+ case mhd_DCC_ACTION_CONTINUE:
+ return true;
+ case mhd_DCC_ACTION_FINISH:
+ mhd_assert (MHD_SIZE_UNKNOWN == c->rp.response->cntn_size);
+ return true;
+ case mhd_DCC_ACTION_SUSPEND:
+ mhd_assert (0 && "Not implemented yet");
+ // TODO: Implement suspend;
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_INT_ERROR,
+ "Suspending connection is not implemented yet");
+ return false;
+ case mhd_DCC_ACTION_ABORT:
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_APP_ABORTED,
+ "Dynamic Content Creator requested abort " \
+ "of the request");
+ return false;
+ case mhd_DCC_ACTION_NO_ACTION:
+ default:
+ break;
+ }
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_INT_ERROR,
+ "Impossible code path");
+ return false;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
+{
+ struct MHD_Response *const restrict r = c->rp.response;
+
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size);
+
+ mhd_stream_call_dcc_cleanup_if_needed (c);
+
+ if (0 == r->cntn_size)
+ { /* 0-byte response is always ready */
+ c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+ return true;
+ }
+
+ mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc);
+ if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
+ {
+ (void) 0; /* Nothing to do, buffers are ready */
+ }
+ else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
+ {
+ if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype)
+ {
+ const struct MHD_DynamicContentCreatorAction *act;
+ const size_t size_to_fill =
+ c->write_buffer_size - c->write_buffer_append_offset;
+ size_t filled;
+
+ mhd_assert (c->write_buffer_append_offset < c->write_buffer_size);
+ mhd_assert (NULL == c->rp.app_act_ctx.connection);
+
+ c->rp.app_act_ctx.connection = c;
+ c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION;
+
+ act =
+ r->cntn.dyn.cb (r->cntn.dyn.cls,
+ &(c->rp.app_act_ctx),
+ c->rp.rsp_cntn_read_pos,
+ (void *)
+ (c->write_buffer + c->write_buffer_append_offset),
+ size_to_fill);
+ c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
+ if (! preprocess_dcc_action (c, act))
+ return false;
+ if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
+ {
+ mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
+ mhd_assert (c->rp.props.end_by_closing);
+
+ c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+
+ return true;
+ }
+ mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act);
+ // TODO: implement iov sending
+
+ filled = c->rp.app_act.data.cntnue.buf_data_size;
+ if (size_to_fill < filled)
+ {
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_APP_ERROR,
+ "Closing connection (application returned more data "
+ "than requested).");
+ return false;
+ }
+ c->rp.rsp_cntn_read_pos += filled;
+ c->write_buffer_append_offset += filled;
+ }
+ else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
+ {
+ // TODO: implement fallback
+ mhd_assert (0 && "Not implemented yet");
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+ c->rp.rsp_cntn_read_pos = r->cntn_size;
+ }
+ else
+ {
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+ c->rp.rsp_cntn_read_pos = r->cntn_size;
+ }
+
+ mhd_assert (0 && "Not implemented yet");
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+ c->rp.rsp_cntn_read_pos = r->cntn_size;
+ }
+ else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
+ {
+ size_t copy_size;
+
+ mhd_assert (NULL == c->rp.resp_iov.iov);
+ mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype);
+
+ copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec);
+ c->rp.resp_iov.iov = mhd_stream_alloc_memory (c,
+ copy_size);
+ if (NULL == c->rp.resp_iov.iov)
+ {
+ /* not enough memory */
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
+ "No memory in the pool for the response data.");
+ return false;
+ }
+ memcpy (c->rp.resp_iov.iov,
+ &(r->cntn.iovec.iov),
+ copy_size);
+ c->rp.resp_iov.cnt = r->cntn.iovec.cnt;
+ c->rp.resp_iov.sent = 0;
+ }
+#if defined(MHD_USE_SENDFILE)
+ else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
+ {
+ (void) 0; /* Nothing to do, file should be read directly */
+ }
+#endif /* MHD_USE_SENDFILE */
+ else
+ {
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+ c->rp.rsp_cntn_read_pos = r->cntn_size;
+ }
+
+ c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY;
+ return false;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
+{
+ size_t filled;
+ struct MHD_Response *const restrict r = c->rp.response;
+ static const size_t max_chunk = 0xFFFFFF;
+ char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
+ /* "FFFFFF" + "\r\n" */
+ static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
+ /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
+ static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
+ size_t chunk_hdr_len;
+ uint64_t left_to_send;
+ size_t size_to_fill;
+
+ mhd_assert (0 == c->write_buffer_append_offset);
+ mhd_assert (0 == c->write_buffer_send_offset);
+
+ mhd_stream_call_dcc_cleanup_if_needed (c);
+
+ /* The buffer must be reasonably large enough */
+ if (32 > c->write_buffer_size)
+ {
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
+ "No memory in the pool for the reply chunked content.");
+ }
+ mhd_assert (max_chunk_overhead < \
+ (c->write_buffer_size));
+
+ if (MHD_SIZE_UNKNOWN == r->cntn_size)
+ left_to_send = MHD_SIZE_UNKNOWN;
+ else
+ left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos;
+
+ mhd_assert (0 != left_to_send);
+ if (0 != left_to_send)
+ {
+ size_to_fill =
+ c->write_buffer_size - max_chunk_overhead;
+ /* Limit size for the callback to the max usable size */
+ if (max_chunk < size_to_fill)
+ size_to_fill = max_chunk;
+ if (left_to_send < size_to_fill)
+ size_to_fill = (size_t) left_to_send;
+ }
+ else
+ size_to_fill = 0;
+
+ if ((0 == left_to_send) &&
+ (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype))
+ {
+ c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
+ return true;
+ }
+ else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype)
+ {
+ mhd_assert (size_to_fill <= \
+ r->cntn_size - (size_t) c->rp.rsp_cntn_read_pos);
+ memcpy (c->write_buffer + max_chunk_hdr_len,
+ r->cntn.buf + (size_t) c->rp.rsp_cntn_read_pos,
+ size_to_fill);
+ filled = size_to_fill;
+ }
+ else if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype)
+ {
+ const struct MHD_DynamicContentCreatorAction *act;
+
+ mhd_assert (NULL == c->rp.app_act_ctx.connection);
+
+ c->rp.app_act_ctx.connection = c;
+ c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION;
+
+ act =
+ r->cntn.dyn.cb (r->cntn.dyn.cls,
+ &(c->rp.app_act_ctx),
+ c->rp.rsp_cntn_read_pos,
+ (void *)
+ (c->write_buffer + max_chunk_hdr_len),
+ size_to_fill);
+ c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
+ if (! preprocess_dcc_action (c, act))
+ return false;
+ if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
+ {
+ mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
+ c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
+
+ return true;
+ }
+ mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act);
+ // TODO: implement iov sending
+
+ filled = c->rp.app_act.data.cntnue.buf_data_size;
+ if (size_to_fill < filled)
+ {
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_APP_ERROR,
+ "Closing connection (application returned more data "
+ "than requested).");
+ return false;
+ }
+ c->rp.rsp_cntn_read_pos += filled;
+ c->write_buffer_append_offset += filled;
+ }
+ else
+ {
+ mhd_assert (0 && "Not implemented yet");
+ filled = 0;
+ }
+
+ chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t) filled,
+ chunk_hdr,
+ sizeof(chunk_hdr));
+ mhd_assert (chunk_hdr_len != 0);
+ mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
+ c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2);
+ memcpy (c->write_buffer + c->write_buffer_send_offset,
+ chunk_hdr,
+ chunk_hdr_len);
+ c->write_buffer[max_chunk_hdr_len - 2] = '\r';
+ c->write_buffer[max_chunk_hdr_len - 1] = '\n';
+ c->write_buffer[max_chunk_hdr_len + filled] = '\r';
+ c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n';
+ c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2;
+ if (0 != filled)
+ c->rp.rsp_cntn_read_pos += filled;
+ else
+ c->rp.rsp_cntn_read_pos = r->cntn_size;
+
+ c->state = MHD_CONNECTION_CHUNKED_BODY_READY;
+
+ return false;
+}
+
+
+/**
+ * Allocate the connection's write buffer (if necessary) and fill it
+ * with response footers.
+ * Inner version.
+ *
+ * @param c the connection
+ * @return 'true' if footers formed successfully,
+ * 'false' if not enough buffer
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+prep_chunked_footer_inn (struct MHD_Connection *restrict c)
+{
+ char *buf; /**< the buffer to write footers to */
+ size_t buf_size; /**< the size of the @a buf */
+ size_t used_size; /**< the used size of the @a buf */
+ // struct MHD_HTTP_Res_Header *pos;
+
+ mhd_assert (c->rp.props.chunked);
+ mhd_assert (MHD_CONNECTION_CHUNKED_BODY_SENT == c->state);
+ mhd_assert (NULL != c->rp.response);
+
+ buf_size = mhd_stream_maximize_write_buffer (c);
+ /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
+ if (buf_size < 5)
+ return false;
+ mhd_assert (NULL != c->write_buffer);
+ buf = c->write_buffer + c->write_buffer_append_offset;
+ mhd_assert (NULL != buf);
+ used_size = 0;
+ buf[used_size++] = '0';
+ buf[used_size++] = '\r';
+ buf[used_size++] = '\n';
+
+#if 0 // TODO: use dynamic/connection's footers
+ for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
+ {
+ if (MHD_FOOTER_KIND == pos->kind)
+ {
+ size_t new_used_size; /* resulting size with this header */
+ /* '4' is colon, space, linefeeds */
+ new_used_size = used_size + pos->header_size + pos->value_size + 4;
+ if (new_used_size > buf_size)
+ return MHD_NO;
+ memcpy (buf + used_size, pos->header, pos->header_size);
+ used_size += pos->header_size;
+ buf[used_size++] = ':';
+ buf[used_size++] = ' ';
+ memcpy (buf + used_size, pos->value, pos->value_size);
+ used_size += pos->value_size;
+ buf[used_size++] = '\r';
+ buf[used_size++] = '\n';
+ mhd_assert (used_size == new_used_size);
+ }
+ }
+#endif
+
+ if (used_size + 2 > buf_size)
+ return false;
+ buf[used_size++] = '\r';
+ buf[used_size++] = '\n';
+
+ c->write_buffer_append_offset += used_size;
+ mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size);
+
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
+{
+ if (! prep_chunked_footer_inn (c))
+ {
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
+ "No memory in the pool for the reply chunked footer.");
+ return;
+ }
+ c->state = MHD_CONNECTION_FOOTERS_SENDING;
+}
diff --git a/src/mhd2/stream_process_reply.h b/src/mhd2/stream_process_reply.h
@@ -0,0 +1,96 @@
+/*
+ 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/stream_process_reply.h
+ * @brief The declarations of internal functions for forming and sending
+ * replies for requests
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STREAM_PROCESS_REPLY_H
+#define MHD_STREAM_PROCESS_REPLY_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declaration */
+
+
+/**
+ * Check whether Dynamic Content Creator cleanup callback is set and
+ * call it, if needed.
+ * Un-set cleanup callback after calling.
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_stream_call_dcc_cleanup_if_needed (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Allocate the connection's write buffer and fill it with all of the
+ * headers from the response.
+ * Required headers are added here.
+ *
+ * @param c the connection to process
+ * @return 'true' if state has been update,
+ * 'false' if connection is going to be aborted
+ */
+MHD_INTERNAL bool
+mhd_stream_build_header_response (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Prepare the unchunked response content of this connection for sending.
+ *
+ * @param c the connection
+ * @return 'true' if connection new state could be processed now,
+ * 'false' if no new state processing is needed.
+ */
+MHD_INTERNAL bool
+mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Prepare the chunked response content of this connection for sending.
+ *
+ * @return 'true' if connection new state could be processed now,
+ * 'false' if no new state processing is needed.
+ */
+MHD_INTERNAL bool
+mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Allocate the connection's write buffer (if necessary) and fill it
+ * with response footers.
+ *
+ * @param c the connection
+ */
+MHD_INTERNAL void
+mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_STREAM_PROCESS_REPLY_H */
diff --git a/src/mhd2/stream_process_request.c b/src/mhd2/stream_process_request.c
@@ -0,0 +1,3840 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2014-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/stream_process_request.c
+ * @brief The implementation of internal functions for requests parsing
+ * and processing
+ * @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 "stream_process_request.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "sys_malloc.h"
+
+#include "mhd_str_types.h"
+#include "mhd_str_macros.h"
+#include "mhd_str.h"
+
+#include <string.h>
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "daemon_logger.h"
+#include "mhd_assert.h"
+#include "mhd_panic.h"
+
+#include "mhd_mempool.h"
+
+#include "request_funcs.h"
+#include "request_get_value.h"
+#include "respond_with_error.h"
+#include "stream_funcs.h"
+#include "daemon_funcs.h"
+
+#include "mhd_public_api.h"
+
+
+/**
+ * Response text used when the request (http header) is
+ * malformed.
+ */
+#define ERR_RSP_REQUEST_MALFORMED \
+ "<html><head><title>Request malformed</title></head>" \
+ "<body>HTTP request is syntactically incorrect.</body></html>"
+
+/**
+ * Response text used when the request HTTP version is too old.
+ */
+#define ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD \
+ "<html>" \
+ "<head><title>Requested HTTP version is not supported</title></head>" \
+ "<body>Requested HTTP version is too old and not " \
+ "supported.</body></html>"
+/**
+ * Response text used when the request HTTP version is not supported.
+ */
+#define ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED \
+ "<html>" \
+ "<head><title>Requested HTTP version is not supported</title></head>" \
+ "<body>Requested HTTP version is not supported.</body></html>"
+
+/**
+ * Response text used when the request HTTP header has bare CR character
+ * without LF character (and CR is not allowed to be treated as whitespace).
+ */
+#define ERR_RSP_BARE_CR_IN_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Request HTTP header has bare CR character without " \
+ "following LF character.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request HTTP footer has bare CR character
+ * without LF character (and CR is not allowed to be treated as whitespace).
+ */
+#define ERR_RSP_BARE_CR_IN_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Request HTTP footer has bare CR character without " \
+ "following LF character.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request HTTP header has bare LF character
+ * without CR character.
+ */
+#define ERR_RSP_BARE_LF_IN_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Request HTTP header has bare LF character without " \
+ "preceding CR character.</body>" \
+ "</html>"
+/**
+ * Response text used when the request HTTP footer has bare LF character
+ * without CR character.
+ */
+#define ERR_RSP_BARE_LF_IN_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Request HTTP footer has bare LF character without " \
+ "preceding CR character.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request line has more then two whitespaces.
+ */
+#define ERR_RSP_RQ_LINE_TOO_MANY_WSP \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>The request line has more then two whitespaces.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request line has invalid characters in URI.
+ */
+#define ERR_RSP_RQ_TARGET_INVALID_CHAR \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request has invalid characters in " \
+ "the request-target.</body>" \
+ "</html>"
+
+/**
+ * Response text used when line folding is used in request headers.
+ */
+#define ERR_RSP_OBS_FOLD \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Obsolete line folding is used in HTTP request header.</body>" \
+ "</html>"
+
+/**
+ * Response text used when line folding is used in request footers.
+ */
+#define ERR_RSP_OBS_FOLD_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Obsolete line folding is used in HTTP request footer.</body>" \
+ "</html>"
+
+/**
+ * Response text used when request header has no colon character.
+ */
+#define ERR_RSP_HEADER_WITHOUT_COLON \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request header line has no colon character.</body>" \
+ "</html>"
+
+/**
+ * Response text used when request footer has no colon character.
+ */
+#define ERR_RSP_FOOTER_WITHOUT_COLON \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request footer line has no colon character.</body>" \
+ "</html>"
+/**
+ * Response text used when the request has whitespace at the start
+ * of the first header line.
+ */
+#define ERR_RSP_WSP_BEFORE_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request has whitespace between the request line and " \
+ "the first header.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request has whitespace at the start
+ * of the first footer line.
+ */
+#define ERR_RSP_WSP_BEFORE_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>First HTTP footer line has whitespace at the first " \
+ "position.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the whitespace found before colon (inside header
+ * name or between header name and colon).
+ */
+#define ERR_RSP_WSP_IN_HEADER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request has whitespace before the first colon " \
+ "in header line.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the whitespace found before colon (inside header
+ * name or between header name and colon).
+ */
+#define ERR_RSP_WSP_IN_FOOTER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request has whitespace before the first colon " \
+ "in footer line.</body>" \
+ "</html>"
+/**
+ * Response text used when request header has invalid character.
+ */
+#define ERR_RSP_INVALID_CHR_IN_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request has invalid character in header.</body>" \
+ "</html>"
+
+/**
+ * Response text used when request header has invalid character.
+ */
+#define ERR_RSP_INVALID_CHR_IN_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request has invalid character in footer.</body>" \
+ "</html>"
+
+/**
+ * Response text used when request header has zero-length header (filed) name.
+ */
+#define ERR_RSP_EMPTY_HEADER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request header has empty header name.</body>" \
+ "</html>"
+
+/**
+ * Response text used when request header has zero-length header (filed) name.
+ */
+#define ERR_RSP_EMPTY_FOOTER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>HTTP request footer has empty footer name.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request header is too big to be processed.
+ */
+#define ERR_RSP_REQUEST_HEADER_TOO_BIG \
+ "<html>" \
+ "<head><title>Request too big</title></head>" \
+ "<body><p>The total size of the request headers, which includes the " \
+ "request target and the request field lines, exceeds the memory " \
+ "constraints of this web server.</p>" \
+ "<p>The request could be re-tried with shorter field lines, a shorter " \
+ "request target or a shorter request method token.</p></body>" \
+ "</html>"
+
+/**
+ * Response text used when the request header is too big to be processed.
+ */
+#define ERR_RSP_REQUEST_FOOTER_TOO_BIG \
+ "<html>" \
+ "<head><title>Request too big</title></head>" \
+ "<body><p>The total size of the request headers, which includes the " \
+ "request target, the request field lines and the chunked trailer " \
+ "section exceeds the memory constraints of this web server.</p>" \
+ "<p>The request could be re-tried with a shorter chunked trailer " \
+ "section, shorter field lines, a shorter request target or " \
+ "a shorter request method token.</p></body>" \
+ "</html>"
+
+/**
+ * Response text used when the request (http header) is too big to
+ * be processed.
+ */
+#define ERR_RSP_MSG_REQUEST_TOO_BIG \
+ "<html>" \
+ "<head><title>Request too big</title></head>" \
+ "<body>Request HTTP header is too big for the memory constraints " \
+ "of this webserver.</body>" \
+ "</html>"
+/**
+ * Response text used when the request chunk size line with chunk extension
+ * cannot fit the buffer.
+ */
+#define ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG \
+ "<html>" \
+ "<head><title>Request too big</title></head>" \
+ "<body><p>The total size of the request target, the request field lines " \
+ "and the chunk size line exceeds the memory constraints of this web " \
+ "server.</p>" \
+ "<p>The request could be re-tried without chunk extensions, with a smaller " \
+ "chunk size, shorter field lines, a shorter request target or a shorter " \
+ "request method token.</p></body>" \
+ "</html>"
+
+/**
+ * Response text used when the request chunk size line without chunk extension
+ * cannot fit the buffer.
+ */
+#define ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG \
+ "<html>" \
+ "<head><title>Request too big</title></head>" \
+ "<body><p>The total size of the request target, the request field lines " \
+ "and the chunk size line exceeds the memory constraints of this web " \
+ "server.</p>" \
+ "<p>The request could be re-tried with a smaller " \
+ "chunk size, shorter field lines, a shorter request target or a shorter " \
+ "request method token.</p></body>" \
+ "</html>"
+
+/**
+ * Response text used when the request (http header) does not
+ * contain a "Host:" header and still claims to be HTTP 1.1.
+ */
+#define ERR_RSP_REQUEST_LACKS_HOST \
+ "<html>" \
+ "<head><title>"Host:" header required</title></head>" \
+ "<body>HTTP/1.1 request without <b>"Host:"</b>.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request has more than one "Host:" header.
+ */
+#define ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS \
+ "<html>" \
+ "<head>" \
+ "<title>Several "Host:" headers used</title></head>" \
+ "<body>" \
+ "Request with more than one <b>"Host:"</b> header.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request has unsupported "Transfer-Encoding:".
+ */
+#define ERR_RSP_UNSUPPORTED_TR_ENCODING \
+ "<html>" \
+ "<head><title>Unsupported Transfer-Encoding</title></head>" \
+ "<body>The Transfer-Encoding used in request is not supported.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request has unsupported both headers:
+ * "Transfer-Encoding:" and "Content-Length:"
+ */
+#define ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING \
+ "<html>" \
+ "<head><title>Malformed request</title></head>" \
+ "<body>Wrong combination of the request headers: both Transfer-Encoding " \
+ "and Content-Length headers are used at the same time.</body>" \
+ "</html>"
+
+/**
+ * Response text used when the request HTTP content is too large.
+ */
+#define ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE \
+ "<html><head><title>Request content too large</title></head>" \
+ "<body>HTTP request has too large value for " \
+ "<b>Content-Length</b> header.</body></html>"
+
+/**
+ * Response text used when the request HTTP chunked encoding is
+ * malformed.
+ */
+#define ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED \
+ "<html><head><title>Request malformed</title></head>" \
+ "<body>HTTP request has wrong value for " \
+ "<b>Content-Length</b> header.</body></html>"
+
+/**
+ * Response text used when the request has more than one "Content-Length:"
+ * header.
+ */
+#define ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL \
+ "<html><head><title>Request malformed</title></head>" \
+ "<body>HTTP request has several " \
+ "<b>Content-Length</b> headers.</body></html>"
+
+/**
+ * Response text used when the request HTTP chunked encoding is
+ * malformed.
+ */
+#define ERR_RSP_REQUEST_CHUNKED_MALFORMED \
+ "<html><head><title>Request malformed</title></head>" \
+ "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
+
+/**
+ * Response text used when the request HTTP chunk is too large.
+ */
+#define ERR_RSP_REQUEST_CHUNK_TOO_LARGE \
+ "<html><head><title>Request content too large</title></head>" \
+ "<body>The chunk size used in HTTP chunked encoded " \
+ "request is too large.</body></html>"
+
+
+/**
+ * The reasonable length of the upload chunk "header" (the size specifier
+ * with optional chunk extension).
+ * MHD tries to keep the space in the read buffer large enough to read
+ * the chunk "header" in one step.
+ * The real "header" could be much larger, it will be handled correctly
+ * anyway, however it may require several rounds of buffer grow.
+ */
+#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
+
+/**
+ * Get whether bare LF in HTTP header and other protocol elements
+ * should be treated as the line termination depending on the configured
+ * strictness level.
+ * RFC 9112, section 2.2
+ */
+#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
+
+/**
+ * The valid length of any HTTP version string
+ */
+#define HTTP_VER_LEN (mhd_SSTR_LEN (MHD_HTTP_VERSION_1_1_STR))
+
+
+/**
+ * Detect standard HTTP request method
+ *
+ * @param connection the connection
+ * @param method the pointer to HTTP request method string
+ * @param len the length of @a method in bytes
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2) void
+parse_http_std_method (struct MHD_Connection *restrict connection,
+ size_t len,
+ const char *restrict method)
+{
+ const char *const m = method; /**< short alias */
+ mhd_assert (NULL != m);
+ mhd_assert (0 != len);
+
+ if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_GET, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_GET;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_HEAD, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_HEAD;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_POST, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_POST;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_PUT, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_PUT;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_DELETE, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_DELETE;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_CONNECT, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_CONNECT;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_OPTIONS, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_OPTIONS;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_TRACE, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_TRACE;
+ else if ((mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_ASTERISK) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_STR_ASTERISK, len)))
+ connection->rq.http_mthd = mhd_HTTP_METHOD_ASTERISK;
+ else
+ connection->rq.http_mthd = mhd_HTTP_METHOD_OTHER;
+}
+
+
+/**
+ * Detect HTTP version, send error response if version is not supported
+ *
+ * @param connection the connection
+ * @param http_string the pointer to HTTP version string
+ * @param len the length of @a http_string in bytes
+ * @return true if HTTP version is correct and supported,
+ * false if HTTP version is not correct or unsupported.
+ */
+static bool
+parse_http_version (struct MHD_Connection *restrict connection,
+ const char *restrict http_string,
+ size_t len)
+{
+ const char *const h = http_string; /**< short alias */
+ mhd_assert (NULL != http_string);
+
+ /* String must start with 'HTTP/d.d', case-sensetive match.
+ * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
+ if ((HTTP_VER_LEN != len) ||
+ ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
+ ('/' != h[4])
+ || ('.' != h[6]))
+ {
+ connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+ mhd_RESPOND_WITH_ERROR_STATIC (connection,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_MALFORMED);
+ return false;
+ }
+ if (1 == h[5] - '0')
+ {
+ /* HTTP/1.x */
+ if (1 == h[7] - '0')
+ {
+ connection->rq.http_ver = MHD_HTTP_VERSION_1_1;
+ return true;
+ }
+ else if (0 == h[7] - '0')
+ {
+ connection->rq.http_ver = MHD_HTTP_VERSION_1_0;
+ return true;
+ }
+ else
+ connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+
+ }
+ else if (0 == h[5] - '0')
+ {
+ /* Too old major version */
+ connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+ mhd_RESPOND_WITH_ERROR_STATIC (connection,
+ MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+ ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD);
+ return false;
+ }
+ else if ((2 == h[5] - '0') && ('0' == h[7] - '0'))
+ connection->rq.http_ver = MHD_HTTP_VERSION_2;
+ else
+ connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+
+ mhd_RESPOND_WITH_ERROR_STATIC (connection,
+ MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+ ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED);
+ return false;
+}
+
+
+#ifndef MHD_MAX_EMPTY_LINES_SKIP
+/**
+ * The maximum number of ignored empty line before the request line
+ * at default "strictness" level.
+ */
+# define MHD_MAX_EMPTY_LINES_SKIP 1024
+#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
+
+
+/**
+ * Find and parse the request line.
+ * @param c the connection to process
+ * @return true if request line completely processed (or unrecoverable error
+ * found) and state is changed,
+ * false if not enough data yet in the receive buffer
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+get_request_line_inner (struct MHD_Connection *restrict c)
+{
+ size_t p; /**< The current processing position */
+ const int discp_lvl = c->daemon->req_cfg.strictnees;
+ /* Allow to skip one or more empty lines before the request line.
+ RFC 9112, section 2.2 */
+ const bool skip_empty_lines = (1 >= discp_lvl);
+ /* Allow to skip more then one empty line before the request line.
+ RFC 9112, section 2.2 */
+ const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
+ /* Allow to skip unlimited number of empty lines before the request line.
+ RFC 9112, section 2.2 */
+ const bool skip_unlimited_empty_lines =
+ (skip_empty_lines && (-3 >= discp_lvl));
+ /* Treat bare LF as the end of the line.
+ RFC 9112, section 2.2 */
+ const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+ /* Treat tab as whitespace delimiter.
+ RFC 9112, section 3 */
+ const bool tab_as_wsp = (0 >= discp_lvl);
+ /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
+ RFC 9112, section 3 */
+ const bool other_wsp_as_wsp = (-1 >= discp_lvl);
+ /* Treat continuous whitespace block as a single space.
+ RFC 9112, section 3 */
+ const bool wsp_blocks = (-1 >= discp_lvl);
+ /* Parse whitespace in URI, special parsing of the request line.
+ RFC 9112, section 3.2 */
+ const bool wsp_in_uri = (0 >= discp_lvl);
+ /* Keep whitespace in URI, give app URI with whitespace instead of
+ automatic redirect to fixed URI.
+ Violates RFC 9112, section 3.2 */
+ const bool wsp_in_uri_keep = (-2 >= discp_lvl);
+ /* Keep bare CR character as is.
+ Violates RFC 9112, section 2.2 */
+ const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
+ /* Treat bare CR as space; replace it with space before processing.
+ RFC 9112, section 2.2 */
+ const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
+
+ mhd_assert (MHD_CONNECTION_INIT == c->state || \
+ MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (NULL == c->rq.method || \
+ MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
+ MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
+ 0 != c->rq.hdrs.rq_line.proc_pos);
+
+ if (0 == c->read_buffer_offset)
+ {
+ mhd_assert (MHD_CONNECTION_INIT == c->state);
+ return false; /* No data to process */
+ }
+ p = c->rq.hdrs.rq_line.proc_pos;
+ mhd_assert (p <= c->read_buffer_offset);
+
+ /* Skip empty lines, if any (and if allowed) */
+ /* See RFC 9112, section 2.2 */
+ if ((0 == p)
+ && (skip_empty_lines))
+ {
+ /* Skip empty lines before the request line.
+ See RFC 9112, section 2.2 */
+ bool is_empty_line;
+ mhd_assert (MHD_CONNECTION_INIT == c->state);
+ mhd_assert (NULL == c->rq.method);
+ mhd_assert (NULL == c->rq.url);
+ mhd_assert (0 == c->rq.url_len);
+ mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert (0 == c->rq.req_target_len);
+ mhd_assert (NULL == c->rq.version);
+ do
+ {
+ is_empty_line = false;
+ if ('\r' == c->read_buffer[0])
+ {
+ if (1 == c->read_buffer_offset)
+ return false; /* Not enough data yet */
+ if ('\n' == c->read_buffer[1])
+ {
+ is_empty_line = true;
+ c->read_buffer += 2;
+ c->read_buffer_size -= 2;
+ c->read_buffer_offset -= 2;
+ c->rq.hdrs.rq_line.skipped_empty_lines++;
+ }
+ }
+ else if (('\n' == c->read_buffer[0]) &&
+ (bare_lf_as_crlf))
+ {
+ is_empty_line = true;
+ c->read_buffer += 1;
+ c->read_buffer_size -= 1;
+ c->read_buffer_offset -= 1;
+ c->rq.hdrs.rq_line.skipped_empty_lines++;
+ }
+ if (is_empty_line)
+ {
+ if ((! skip_unlimited_empty_lines) &&
+ (((unsigned int) ((skip_several_empty_lines) ?
+ MHD_MAX_EMPTY_LINES_SKIP : 1)) <
+ c->rq.hdrs.rq_line.skipped_empty_lines))
+ {
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "Too many meaningless extra empty lines " \
+ "received before the request.");
+ return true; /* Process connection closure */
+ }
+ if (0 == c->read_buffer_offset)
+ return false; /* No more data to process */
+ }
+ } while (is_empty_line);
+ }
+ /* All empty lines are skipped */
+
+ c->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
+ /* Read and parse the request line */
+ mhd_assert (1 <= c->read_buffer_offset);
+
+ while (p < c->read_buffer_offset)
+ {
+ char *const restrict read_buffer = c->read_buffer;
+ const char chr = read_buffer[p];
+ bool end_of_line;
+ /*
+ The processing logic is different depending on the configured strictness:
+
+ When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
+ processed BEFORE processing of the current character.
+ When whitespace BLOCKS are ALLOWED, the end of the whitespace is
+ processed AFTER processing of the current character.
+
+ When space char in the URI is ALLOWED, the delimiter between the URI and
+ the HTTP version string is processed only at the END of the line.
+ When space in the URI is NOT ALLOWED, the delimiter between the URI and
+ the HTTP version string is processed as soon as the FIRST whitespace is
+ found after URI start.
+ */
+
+ end_of_line = false;
+
+ mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
+ (c->rq.hdrs.rq_line.last_ws_end > \
+ c->rq.hdrs.rq_line.last_ws_start));
+ mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
+ (0 != c->rq.hdrs.rq_line.last_ws_end));
+
+ /* Check for the end of the line */
+ if ('\r' == chr)
+ {
+ if (p + 1 == c->read_buffer_offset)
+ {
+ c->rq.hdrs.rq_line.proc_pos = p;
+ return false; /* Not enough data yet */
+ }
+ else if ('\n' == read_buffer[p + 1])
+ end_of_line = true;
+ else
+ {
+ /* Bare CR alone */
+ /* Must be rejected or replaced with space char.
+ See RFC 9112, section 2.2 */
+ if (bare_cr_as_sp)
+ {
+ read_buffer[p] = ' ';
+ c->rq.num_cr_sp_replaced++;
+ continue; /* Re-start processing of the current character */
+ }
+ else if (! bare_cr_keep)
+ {
+ /* A quick simple check whether this line looks like an HTTP request */
+ if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+ (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_BARE_CR_IN_HEADER);
+ }
+ else
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "Bare CR characters are not allowed " \
+ "in the request line.");
+
+ return true; /* Error in the request */
+ }
+ }
+ }
+ else if ('\n' == chr)
+ {
+ /* Bare LF may be recognised as a line delimiter.
+ See RFC 9112, section 2.2 */
+ if (bare_lf_as_crlf)
+ end_of_line = true;
+ else
+ {
+ /* While RFC does not enforce error for bare LF character,
+ if this char is not treated as a line delimiter, it should be
+ rejected to avoid any security weakness due to request smuggling. */
+ /* A quick simple check whether this line looks like an HTTP request */
+ if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+ (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_BARE_LF_IN_HEADER);
+ }
+ else
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "Bare LF characters are not allowed " \
+ "in the request line.");
+ return true; /* Error in the request */
+ }
+ }
+
+ if (end_of_line)
+ {
+ /* Handle the end of the request line */
+
+ if (NULL != c->rq.method)
+ {
+ if (wsp_in_uri)
+ {
+ /* The end of the URI and the start of the HTTP version string
+ should be determined now. */
+ mhd_assert (NULL == c->rq.version);
+ mhd_assert (0 == c->rq.req_target_len);
+ if (0 != c->rq.hdrs.rq_line.last_ws_end)
+ {
+ /* Determine the end and the length of the URI */
+ if (NULL != c->rq.hdrs.rq_line.rq_tgt)
+ {
+ read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
+ c->rq.req_target_len =
+ c->rq.hdrs.rq_line.last_ws_start
+ - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer);
+ }
+ else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
+ c->rq.hdrs.rq_line.last_ws_end) &&
+ (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
+ {
+ /* Found only HTTP method and HTTP version and more than one
+ whitespace between them. Assume zero-length URI. */
+ mhd_assert (wsp_blocks);
+ c->rq.hdrs.rq_line.last_ws_start++;
+ read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
+ c->rq.hdrs.rq_line.rq_tgt =
+ read_buffer + c->rq.hdrs.rq_line.last_ws_start;
+ c->rq.req_target_len = 0;
+ c->rq.hdrs.rq_line.num_ws_in_uri = 0;
+ c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+ }
+ /* Determine the start of the HTTP version string */
+ if (NULL != c->rq.hdrs.rq_line.rq_tgt)
+ {
+ c->rq.version = read_buffer + c->rq.hdrs.rq_line.last_ws_end;
+ }
+ }
+ }
+ else
+ {
+ /* The end of the URI and the start of the HTTP version string
+ should be already known. */
+ if ((NULL == c->rq.version)
+ && (NULL != c->rq.hdrs.rq_line.rq_tgt)
+ && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
+ - read_buffer))
+ && (0 != read_buffer[(size_t)
+ (c->rq.hdrs.rq_line.rq_tgt
+ - read_buffer) - 1]))
+ {
+ /* Found only HTTP method and HTTP version and more than one
+ whitespace between them. Assume zero-length URI. */
+ size_t uri_pos;
+ mhd_assert (wsp_blocks);
+ mhd_assert (0 == c->rq.req_target_len);
+ uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer) - 1;
+ mhd_assert (uri_pos < p);
+ c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
+ read_buffer[uri_pos] = 0; /* Zero terminate the URI */
+ c->rq.hdrs.rq_line.rq_tgt = read_buffer + uri_pos;
+ c->rq.req_target_len = 0;
+ c->rq.hdrs.rq_line.num_ws_in_uri = 0;
+ c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+ }
+ }
+
+ if (NULL != c->rq.version)
+ {
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ if (! parse_http_version (c, c->rq.version,
+ p
+ - (size_t) (c->rq.version
+ - read_buffer)))
+ {
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
+ return true; /* Unsupported / broken HTTP version */
+ }
+ read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
+ if ('\r' == chr)
+ {
+ p++; /* Consume CR */
+ mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
+ }
+ p++; /* Consume LF */
+ c->read_buffer += p;
+ c->read_buffer_size -= p;
+ c->read_buffer_offset -= p;
+ mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
+ c->rq.req_target_len);
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (0 != c->rq.req_target_len));
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
+ - c->rq.hdrs.rq_line.rq_tgt) < \
+ c->rq.req_target_len));
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
+ c->rq.hdrs.rq_line.rq_tgt));
+ return true; /* The request line is successfully parsed */
+ }
+ }
+ /* Error in the request line */
+
+ /* A quick simple check whether this line looks like an HTTP request */
+ if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+ (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_MALFORMED);
+ }
+ else
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "The request line is malformed.");
+
+ return true;
+ }
+
+ /* Process possible end of the previously found whitespace delimiter */
+ if ((! wsp_blocks) &&
+ (p == c->rq.hdrs.rq_line.last_ws_end) &&
+ (0 != c->rq.hdrs.rq_line.last_ws_end))
+ {
+ /* Previous character was a whitespace char and whitespace blocks
+ are not allowed. */
+ /* The current position is the next character after
+ a whitespace delimiter */
+ if (NULL == c->rq.hdrs.rq_line.rq_tgt)
+ {
+ /* The current position is the start of the URI */
+ mhd_assert (0 == c->rq.req_target_len);
+ mhd_assert (NULL == c->rq.version);
+ c->rq.hdrs.rq_line.rq_tgt = read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ else
+ {
+ /* It was a whitespace after the start of the URI */
+ if (! wsp_in_uri)
+ {
+ mhd_assert ((0 != c->rq.req_target_len) || \
+ (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p));
+ mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
+ c->rq.version = read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ }
+ }
+
+ /* Process the current character.
+ Is it not the end of the line. */
+ if ((' ' == chr)
+ || (('\t' == chr) && (tab_as_wsp))
+ || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
+ {
+ /* A whitespace character */
+ if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
+ (p != c->rq.hdrs.rq_line.last_ws_end) ||
+ (! wsp_blocks))
+ {
+ /* Found first whitespace char of the new whitespace block */
+ if (NULL == c->rq.method)
+ {
+ /* Found the end of the HTTP method string */
+ mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start);
+ mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end);
+ mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert (0 == c->rq.req_target_len);
+ mhd_assert (NULL == c->rq.version);
+ if (0 == p)
+ {
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "The request line starts with a whitespace.");
+ return true; /* Error in the request */
+ }
+ read_buffer[p] = 0; /* Zero-terminate the request method string */
+ c->rq.method = read_buffer;
+ parse_http_std_method (c, p, c->rq.method);
+ }
+ else
+ {
+ /* A whitespace after the start of the URI */
+ if (! wsp_in_uri)
+ {
+ /* Whitespace in URI is not allowed to be parsed */
+ if (NULL == c->rq.version)
+ {
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ /* This is a delimiter between URI and HTTP version string */
+ read_buffer[p] = 0; /* Zero-terminate request URI string */
+ mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
+ - read_buffer)) <= p);
+ c->rq.req_target_len =
+ p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer);
+ }
+ else
+ {
+ /* This is a delimiter AFTER version string */
+
+ /* A quick simple check whether this line looks like an HTTP request */
+ if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+ (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_RQ_LINE_TOO_MANY_WSP);
+ }
+ else
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "The request line has more than "
+ "two whitespaces.");
+ return true; /* Error in the request */
+ }
+ }
+ else
+ {
+ /* Whitespace in URI is allowed to be parsed */
+ if (0 != c->rq.hdrs.rq_line.last_ws_end)
+ {
+ /* The whitespace after the start of the URI has been found already */
+ c->rq.hdrs.rq_line.num_ws_in_uri +=
+ c->rq.hdrs.rq_line.last_ws_end
+ - c->rq.hdrs.rq_line.last_ws_start;
+ }
+ }
+ }
+ c->rq.hdrs.rq_line.last_ws_start = p;
+ c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
+ }
+ else
+ {
+ /* Continuation of the whitespace block */
+ mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end);
+ mhd_assert (0 != p);
+ c->rq.hdrs.rq_line.last_ws_end = p + 1;
+ }
+ }
+ else
+ {
+ /* Non-whitespace char, not the end of the line */
+ mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
+ (c->rq.hdrs.rq_line.last_ws_end == p) || \
+ wsp_in_uri);
+
+ if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
+ (0 != c->rq.hdrs.rq_line.last_ws_end) &&
+ (wsp_blocks))
+ {
+ /* The end of the whitespace block */
+ if (NULL == c->rq.hdrs.rq_line.rq_tgt)
+ {
+ /* This is the first character of the URI */
+ mhd_assert (0 == c->rq.req_target_len);
+ mhd_assert (NULL == c->rq.version);
+ c->rq.hdrs.rq_line.rq_tgt = read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ else
+ {
+ if (! wsp_in_uri)
+ {
+ /* This is the first character of the HTTP version */
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert ((0 != c->rq.req_target_len) || \
+ (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p));
+ mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
+ c->rq.version = read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ }
+ }
+
+ /* Handle other special characters */
+ if ('?' == chr)
+ {
+ if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
+ (NULL != c->rq.hdrs.rq_line.rq_tgt))
+ {
+ c->rq.hdrs.rq_line.rq_tgt_qmark = read_buffer + p;
+ }
+ }
+ else if ((0xb == chr) || (0xc == chr))
+ {
+ /* VT or LF characters */
+ mhd_assert (! other_wsp_as_wsp);
+ if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
+ (NULL == c->rq.version) &&
+ (wsp_in_uri))
+ {
+ c->rq.hdrs.rq_line.num_ws_in_uri++;
+ }
+ else
+ {
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "Invalid character is in the request line.");
+ return true; /* Error in the request */
+ }
+ }
+ else if (0 == chr)
+ {
+ /* NUL character */
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
+ "The NUL character is in the request line.");
+ return true; /* Error in the request */
+ }
+ }
+
+ p++;
+ }
+
+ c->rq.hdrs.rq_line.proc_pos = p;
+ return false; /* Not enough data yet */
+}
+
+
+/**
+ * Callback for iterating over GET parameters
+ * @param cls the iterator metadata
+ * @param name the name of the parameter
+ * @param value the value of the parameter
+ * @return bool to continue iterations,
+ * false to stop the iteration
+ */
+static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool
+request_add_get_arg (void *restrict cls,
+ const struct MHD_String *restrict name,
+ const struct MHD_StringNullable *restrict value)
+{
+ struct MHD_Connection *c = (struct MHD_Connection *) cls;
+
+ return mhd_stream_add_field_nullable (c, MHD_VK_GET_ARGUMENT, name, value);
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_INOUT_ (2) bool
+// TODO: detect and report errors
+mhd_parse_get_args (size_t args_len,
+ char *restrict args,
+ mhd_GetArgumentInter cb,
+ void *restrict cls)
+{
+ size_t i;
+
+ mhd_assert (args_len < (size_t) (args_len + 1)); /* Does not work when args_len == SIZE_MAX */
+
+ for (i = 0; i < args_len; ++i) /* Looking for names of the parameters */
+ {
+ size_t name_start;
+ size_t name_len;
+ size_t value_start;
+ size_t value_len;
+ struct MHD_String name;
+ struct MHD_StringNullable value;
+
+ /* Found start of the name */
+
+ value_start = 0;
+ for (name_start = i; i < args_len; ++i) /* Processing parameter */
+ {
+ if ('+' == args[i])
+ args[i] = ' ';
+ else if ('=' == args[i])
+ {
+ /* Found start of the value */
+ args[i] = 0; /* zero-terminate the name */
+ for (value_start = ++i; i < args_len; ++i) /* Processing parameter value */
+ {
+ if ('+' == args[i])
+ args[i] = ' ';
+ else if ('&' == args[i]) /* delimiter for the next parameter */
+ break; /* Next parameter */
+ }
+ break; /* End of the current parameter */
+ }
+ else if ('&' == args[i])
+ break; /* End of the name of the parameter without a value */
+ }
+ if (i < args_len) /* Zero-terminate if not terminated */
+ args[i] = 0;
+ mhd_assert (0 == args[i]);
+
+ /* Store found parameter */
+
+ if (0 != value_start) /* Value cannot start at zero position */
+ { /* Name with value */
+ mhd_assert (name_start + 2 <= value_start);
+ name_len = value_start - name_start - 1;
+
+ value_len =
+ mhd_str_pct_decode_lenient_n (args + value_start, i - value_start,
+ args + value_start, i - value_start,
+ NULL); // TODO: add support for broken encoding detection
+ value.cstr = args + value_start;
+ value.len = value_len;
+ }
+ else
+ { /* Name without value */
+ name_len = i - name_start;
+
+ value.cstr = NULL;
+ value.len = 0;
+ }
+ name_len = mhd_str_pct_decode_lenient_n (args + name_start, name_len,
+ args + name_start, name_len,
+ NULL); // TODO: add support for broken encoding detection
+ name.cstr = args + name_start;
+ name.len = name_len;
+ if (! cb (cls, &name, &value))
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Process request-target string, form URI and URI parameters
+ * @param c the connection to process
+ * @return true if request-target successfully processed,
+ * false if error encountered
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_request_target (struct MHD_Connection *c)
+{
+ size_t params_len;
+
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (NULL == c->rq.url);
+ mhd_assert (0 == c->rq.url_len);
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (c->rq.hdrs.rq_line.rq_tgt <= c->rq.hdrs.rq_line.rq_tgt_qmark));
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (c->rq.req_target_len > \
+ (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
+ - c->rq.hdrs.rq_line.rq_tgt)));
+
+ /* Log callback before the request-target is modified/decoded */
+ if (NULL != c->daemon->req_cfg.uri_cb.cb)
+ {
+ struct MHD_String full_uri;
+ struct MHD_EarlyUriCbData req_data;
+ full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt;
+ full_uri.len = c->rq.req_target_len;
+ req_data.request = &(c->rq);
+ req_data.request_app_context = NULL;
+ c->rq.app_aware = true;
+ c->daemon->req_cfg.uri_cb.cb (c->daemon->req_cfg.uri_cb.cls,
+ &full_uri,
+ &req_data);
+ c->rq.app_context = req_data.request_app_context;
+ }
+
+ if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
+ {
+ params_len =
+ c->rq.req_target_len
+ - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
+
+ mhd_assert (1 <= params_len);
+
+ c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
+
+ // TODO: support detection of decoding errors
+ if (! mhd_parse_get_args (params_len - 1,
+ c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
+ &request_add_get_arg,
+ c))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM,
+ "Not enough memory in the pool to store GET parameter");
+
+ mhd_RESPOND_WITH_ERROR_STATIC (
+ c,
+ mhd_stream_get_no_space_err_status_code (c,
+ MHD_PROC_RECV_URI,
+ 0,
+ NULL),
+ ERR_RSP_MSG_REQUEST_TOO_BIG);
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING != c->state);
+ return false;
+
+ }
+ }
+ else
+ params_len = 0;
+
+ mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
+ c->rq.req_target_len - params_len);
+
+ /* Finally unescape URI itself */
+ // TODO: support detection of decoding errors
+ c->rq.url_len =
+ mhd_str_pct_decode_lenient_n (c->rq.hdrs.rq_line.rq_tgt,
+ c->rq.req_target_len - params_len,
+ c->rq.hdrs.rq_line.rq_tgt,
+ c->rq.req_target_len - params_len,
+ NULL);
+ c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
+
+ return true;
+}
+
+
+#ifndef MHD_MAX_FIXED_URI_LEN
+/**
+ * The maximum size of the fixed URI for automatic redirection
+ */
+#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
+#endif /* ! MHD_MAX_FIXED_URI_LEN */
+
+/**
+ * Send the automatic redirection to fixed URI when received URI with
+ * whitespaces.
+ * If URI is too large, close connection with error.
+ *
+ * @param c the connection to process
+ */
+static void
+send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
+{
+ static const char hdr_prefix[] = MHD_HTTP_HEADER_LOCATION ": ";
+ static const size_t hdr_prefix_len =
+ mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION ": ");
+ char *hdr_line;
+ char *b;
+ size_t fixed_uri_len;
+ size_t i;
+ size_t o;
+
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri);
+ mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
+ c->rq.req_target_len);
+ fixed_uri_len = c->rq.req_target_len
+ + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
+ if ( (fixed_uri_len + 200 > c->daemon->conns.cfg.mem_pool_size) ||
+ (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
+ (NULL == (hdr_line = malloc (fixed_uri_len + 1 + hdr_prefix_len))) )
+ {
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, \
+ "The request has whitespace character is " \
+ "in the URI and the URI is too large to " \
+ "send automatic redirect to fixed URI.");
+ return;
+ }
+ memcpy (hdr_line, hdr_prefix, hdr_prefix_len);
+ b = hdr_line + hdr_prefix_len;
+ i = 0;
+ o = 0;
+
+ do
+ {
+ const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
+
+ mhd_assert ('\r' != chr); /* Replaced during request line parsing */
+ mhd_assert ('\n' != chr); /* Rejected during request line parsing */
+ mhd_assert (0 != chr); /* Rejected during request line parsing */
+ switch (chr)
+ {
+ case ' ':
+ b[o++] = '%';
+ b[o++] = '2';
+ b[o++] = '0';
+ break;
+ case '\t':
+ b[o++] = '%';
+ b[o++] = '0';
+ b[o++] = '9';
+ break;
+ case 0x0B: /* VT (vertical tab) */
+ b[o++] = '%';
+ b[o++] = '0';
+ b[o++] = 'B';
+ break;
+ case 0x0C: /* FF (form feed) */
+ b[o++] = '%';
+ b[o++] = '0';
+ b[o++] = 'C';
+ break;
+ default:
+ b[o++] = chr;
+ break;
+ }
+ } while (i < c->rq.req_target_len);
+ mhd_assert (fixed_uri_len == o);
+ b[o] = 0; /* Zero-terminate the result */
+
+ mhd_RESPOND_WITH_ERROR_HEADER (c,
+ MHD_HTTP_STATUS_MOVED_PERMANENTLY,
+ ERR_RSP_RQ_TARGET_INVALID_CHAR,
+ o + hdr_prefix_len,
+ hdr_line);
+
+ return;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_get_request_line (struct MHD_Connection *restrict c)
+{
+ const int discp_lvl = c->daemon->req_cfg.strictnees;
+ /* Parse whitespace in URI, special parsing of the request line */
+ const bool wsp_in_uri = (0 >= discp_lvl);
+ /* Keep whitespace in URI, give app URI with whitespace instead of
+ automatic redirect to fixed URI */
+ const bool wsp_in_uri_keep = (-2 >= discp_lvl);
+
+ if (! get_request_line_inner (c))
+ {
+ /* End of the request line has not been found yet */
+ mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
+ if ((NULL != c->rq.version) &&
+ (HTTP_VER_LEN <
+ (c->rq.hdrs.rq_line.proc_pos
+ - (size_t) (c->rq.version - c->read_buffer))))
+ {
+ c->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_MALFORMED);
+ return true; /* Error in the request */
+ }
+ return false;
+ }
+ if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state)
+ return true; /* Error in the request */
+
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (NULL == c->rq.url);
+ mhd_assert (0 == c->rq.url_len);
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
+ {
+ if (! wsp_in_uri)
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_RQ_TARGET_INVALID_CHAR);
+ return true; /* Error in the request */
+ }
+ if (! wsp_in_uri_keep)
+ {
+ send_redirect_fixed_rq_target (c);
+ return true; /* Error in the request */
+ }
+ }
+ if (! process_request_target (c))
+ return true; /* Error in processing */
+
+ c->state = MHD_CONNECTION_REQ_LINE_RECEIVED;
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c)
+{
+ c->rq.field_lines.start = c->read_buffer;
+ mhd_stream_reset_rq_hdr_proc_state (c);
+ c->state = MHD_CONNECTION_REQ_HEADERS_RECEIVING;
+}
+
+
+/**
+ * Send error reply when receive buffer space exhausted while receiving or
+ * storing the request headers
+ * @param c the connection to handle
+ * @param add_header the optional pointer to the current header string being
+ * processed or the header failed to be added.
+ * Could be not zero-terminated and can contain binary zeros.
+ * Can be NULL.
+ * @param add_header_size the size of the @a add_header
+ */
+MHD_static_inline_
+MHD_FN_PAR_NONNULL_ (1) void
+handle_req_headers_no_space (struct MHD_Connection *restrict c,
+ const char *restrict add_header,
+ size_t add_header_size)
+{
+ unsigned int err_code;
+
+ err_code = mhd_stream_get_no_space_err_status_code (c,
+ MHD_PROC_RECV_HEADERS,
+ add_header_size,
+ add_header);
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ err_code,
+ ERR_RSP_REQUEST_HEADER_TOO_BIG);
+}
+
+
+/**
+ * Send error reply when receive buffer space exhausted while receiving or
+ * storing the request footers (for chunked requests).
+ * @param c the connection to handle
+ * @param add_footer the optional pointer to the current footer string being
+ * processed or the footer failed to be added.
+ * Could be not zero-terminated and can contain binary zeros.
+ * Can be NULL.
+ * @param add_footer_size the size of the @a add_footer
+ */
+MHD_static_inline_
+MHD_FN_PAR_NONNULL_ (1) void
+handle_req_footers_no_space (struct MHD_Connection *restrict c,
+ const char *restrict add_footer,
+ size_t add_footer_size)
+{
+ (void) add_footer; (void) add_footer_size; /* Unused */
+ mhd_assert (c->rq.have_chunked_upload);
+
+ /* Footers should be optional */
+ mhd_RESPOND_WITH_ERROR_STATIC (
+ c,
+ MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE,
+ ERR_RSP_REQUEST_FOOTER_TOO_BIG);
+}
+
+
+/**
+ * Results of header line reading
+ */
+enum MHD_FIXED_ENUM_ mhd_HdrLineReadRes
+{
+ /**
+ * Not enough data yet
+ */
+ MHD_HDR_LINE_READING_NEED_MORE_DATA = 0,
+ /**
+ * New header line has been read
+ */
+ MHD_HDR_LINE_READING_GOT_HEADER,
+ /**
+ * Error in header data, error response has been queued
+ */
+ MHD_HDR_LINE_READING_DATA_ERROR,
+ /**
+ * Found the end of the request header (end of field lines)
+ */
+ MHD_HDR_LINE_READING_GOT_END_OF_HEADER
+};
+
+
+/**
+ * Find the end of the request header line and make basic header parsing.
+ * Handle errors and header folding.
+ * @param c the connection to process
+ * @param process_footers if true then footers are processed,
+ * if false then headers are processed
+ * @param[out] hdr_name the name of the parsed header (field)
+ * @param[out] hdr_name the value of the parsed header (field)
+ * @return mhd_HdrLineReadRes value
+ */
+static enum mhd_HdrLineReadRes
+get_req_header (struct MHD_Connection *restrict c,
+ bool process_footers,
+ struct MHD_String *restrict hdr_name,
+ struct MHD_String *restrict hdr_value)
+{
+ const int discp_lvl = c->daemon->req_cfg.strictnees;
+ /* Treat bare LF as the end of the line.
+ RFC 9112, section 2.2-3
+ Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
+ Bare LF is processed as end of the line or rejected as broken request. */
+ const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+ /* Keep bare CR character as is.
+ Violates RFC 9112, section 2.2-4 */
+ const bool bare_cr_keep = (-3 >= discp_lvl);
+ /* Treat bare CR as space; replace it with space before processing.
+ RFC 9112, section 2.2-4 */
+ const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
+ /* Treat NUL as space; replace it with space before processing.
+ RFC 9110, section 5.5-5 */
+ const bool nul_as_sp = (-1 >= discp_lvl);
+ /* Allow folded header lines.
+ RFC 9112, section 5.2-4 */
+ const bool allow_folded = (0 >= discp_lvl);
+ /* Do not reject headers with the whitespace at the start of the first line.
+ When allowed, the first line with whitespace character at the first
+ position is ignored (as well as all possible line foldings of the first
+ line).
+ RFC 9112, section 2.2-8 */
+ const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
+ /* Allow whitespace in header (field) name.
+ Violates RFC 9110, section 5.1-2 */
+ const bool allow_wsp_in_name = (-2 >= discp_lvl);
+ /* Allow zero-length header (field) name.
+ Violates RFC 9110, section 5.1-2 */
+ const bool allow_empty_name = (-2 >= discp_lvl);
+ /* Allow whitespace before colon.
+ Violates RFC 9112, section 5.1-2 */
+ const bool allow_wsp_before_colon = (-3 >= discp_lvl);
+ /* Do not abort the request when header line has no colon, just skip such
+ bad lines.
+ RFC 9112, section 5-1 */
+ const bool allow_line_without_colon = (-2 >= discp_lvl);
+
+ size_t p; /**< The position of the currently processed character */
+
+ (void) process_footers; /* Unused parameter in non-debug and no messages */
+
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+
+ p = c->rq.hdrs.hdr.proc_pos;
+
+ mhd_assert (p <= c->read_buffer_offset);
+ while (p < c->read_buffer_offset)
+ {
+ char *const restrict read_buffer = c->read_buffer;
+ const char chr = read_buffer[p];
+ bool end_of_line;
+
+ mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
+ (c->rq.hdrs.hdr.name_len < p));
+ mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
+ mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
+ (c->rq.hdrs.hdr.name_end_found));
+ mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
+ (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
+ mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
+ (0 != c->rq.hdrs.hdr.name_len));
+ mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
+ (0 == c->rq.hdrs.hdr.name_len) || \
+ (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
+ mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
+ (0 == c->rq.hdrs.hdr.value_start) || \
+ (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
+
+ /* Check for the end of the line */
+ if ('\r' == chr)
+ {
+ if (0 != p)
+ {
+ /* Line is not empty, need to check for possible line folding */
+ if (p + 2 >= c->read_buffer_offset)
+ break; /* Not enough data yet to check for folded line */
+ }
+ else
+ {
+ /* Line is empty, no need to check for possible line folding */
+ if (p + 2 > c->read_buffer_offset)
+ break; /* Not enough data yet to check for the end of the line */
+ }
+ if ('\n' == read_buffer[p + 1])
+ end_of_line = true;
+ else
+ {
+ /* Bare CR alone */
+ /* Must be rejected or replaced with space char.
+ See RFC 9112, section 2.2-4 */
+ if (bare_cr_as_sp)
+ {
+ read_buffer[p] = ' ';
+ c->rq.num_cr_sp_replaced++;
+ continue; /* Re-start processing of the current character */
+ }
+ else if (! bare_cr_keep)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_BARE_CR_IN_HEADER);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_BARE_CR_IN_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ end_of_line = false;
+ }
+ }
+ else if ('\n' == chr)
+ {
+ /* Bare LF may be recognised as a line delimiter.
+ See RFC 9112, section 2.2-3 */
+ if (bare_lf_as_crlf)
+ {
+ if (0 != p)
+ {
+ /* Line is not empty, need to check for possible line folding */
+ if (p + 1 >= c->read_buffer_offset)
+ break; /* Not enough data yet to check for folded line */
+ }
+ end_of_line = true;
+ }
+ else
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_BARE_LF_IN_HEADER);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_BARE_LF_IN_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ }
+ else
+ end_of_line = false;
+
+ if (end_of_line)
+ {
+ /* Handle the end of the line */
+ /**
+ * The full length of the line, including CRLF (or bare LF).
+ */
+ const size_t line_len = p + (('\r' == chr) ? 2 : 1);
+ char next_line_char;
+ mhd_assert (line_len <= c->read_buffer_offset);
+
+ if (0 == p)
+ {
+ /* Zero-length header line. This is the end of the request header
+ section.
+ RFC 9112, Section 2.1-1 */
+ mhd_assert (! c->rq.hdrs.hdr.starts_with_ws);
+ mhd_assert (! c->rq.hdrs.hdr.name_end_found);
+ mhd_assert (0 == c->rq.hdrs.hdr.name_len);
+ mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
+ mhd_assert (0 == c->rq.hdrs.hdr.value_start);
+ /* Consume the line with CRLF (or bare LF) */
+ c->read_buffer += line_len;
+ c->read_buffer_offset -= line_len;
+ c->read_buffer_size -= line_len;
+ return MHD_HDR_LINE_READING_GOT_END_OF_HEADER;
+ }
+
+ mhd_assert (line_len < c->read_buffer_offset);
+ mhd_assert (0 != line_len);
+ mhd_assert ('\n' == read_buffer[line_len - 1]);
+ next_line_char = read_buffer[line_len];
+ if ((' ' == next_line_char) ||
+ ('\t' == next_line_char))
+ {
+ /* Folded line */
+ if (! allow_folded)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_OBS_FOLD);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_OBS_FOLD_FOOTER);
+
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ /* Replace CRLF (or bare LF) character(s) with space characters.
+ See RFC 9112, Section 5.2-4 */
+ read_buffer[p] = ' ';
+ if ('\r' == chr)
+ read_buffer[p + 1] = ' ';
+ continue; /* Re-start processing of the current character */
+ }
+ else
+ {
+ /* It is not a folded line, it's the real end of the non-empty line */
+ bool skip_line = false;
+ mhd_assert (0 != p);
+ if (c->rq.hdrs.hdr.starts_with_ws)
+ {
+ /* This is the first line and it starts with whitespace. This line
+ must be discarded completely.
+ See RFC 9112, Section 2.2-8 */
+ mhd_assert (allow_wsp_at_start);
+
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED,
+ "Whitespace-prefixed first header line " \
+ "has been skipped.");
+ skip_line = true;
+ }
+ else if (! c->rq.hdrs.hdr.name_end_found)
+ {
+ if (! allow_line_without_colon)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_HEADER_WITHOUT_COLON);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_FOOTER_WITHOUT_COLON);
+
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ /* Skip broken line completely */
+ c->rq.skipped_broken_lines++;
+ skip_line = true;
+ }
+ if (skip_line)
+ {
+ /* Skip the entire line */
+ c->read_buffer += line_len;
+ c->read_buffer_offset -= line_len;
+ c->read_buffer_size -= line_len;
+ p = 0;
+ /* Reset processing state */
+ memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
+ /* Start processing of the next line */
+ continue;
+ }
+ else
+ {
+ /* This line should be valid header line */
+ size_t value_len;
+ mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
+
+ hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */
+ hdr_name->len = c->rq.hdrs.hdr.name_len;
+ mhd_assert (0 == hdr_name->cstr[hdr_name->len]);
+
+ if (0 == c->rq.hdrs.hdr.value_start)
+ {
+ c->rq.hdrs.hdr.value_start = p;
+ read_buffer[p] = 0;
+ value_len = 0;
+ }
+ else if (0 != c->rq.hdrs.hdr.ws_start)
+ {
+ mhd_assert (p > c->rq.hdrs.hdr.ws_start);
+ mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start);
+ read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
+ value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
+ }
+ else
+ {
+ mhd_assert (p > c->rq.hdrs.hdr.ws_start);
+ read_buffer[p] = 0;
+ value_len = p - c->rq.hdrs.hdr.value_start;
+ }
+ hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start;
+ hdr_value->len = value_len;
+ mhd_assert (0 == hdr_value->cstr[hdr_value->len]);
+ /* Consume the entire line */
+ c->read_buffer += line_len;
+ c->read_buffer_offset -= line_len;
+ c->read_buffer_size -= line_len;
+ return MHD_HDR_LINE_READING_GOT_HEADER;
+ }
+ }
+ }
+ else if ((' ' == chr) || ('\t' == chr))
+ {
+ if (0 == p)
+ {
+ if (! allow_wsp_at_start)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_BEFORE_HEADER);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_BEFORE_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->rq.hdrs.hdr.starts_with_ws = true;
+ }
+ else if ((! c->rq.hdrs.hdr.name_end_found) &&
+ (! c->rq.hdrs.hdr.starts_with_ws))
+ {
+ /* Whitespace in header name / between header name and colon */
+ if (allow_wsp_in_name || allow_wsp_before_colon)
+ {
+ if (0 == c->rq.hdrs.hdr.ws_start)
+ c->rq.hdrs.hdr.ws_start = p;
+ }
+ else
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_IN_HEADER_NAME);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_IN_FOOTER_NAME);
+
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ }
+ else
+ {
+ /* Whitespace before/inside/after header (field) value */
+ if (0 == c->rq.hdrs.hdr.ws_start)
+ c->rq.hdrs.hdr.ws_start = p;
+ }
+ }
+ else if (0 == chr)
+ {
+ if (! nul_as_sp)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_INVALID_CHR_IN_HEADER);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_INVALID_CHR_IN_FOOTER);
+
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ read_buffer[p] = ' ';
+ continue; /* Re-start processing of the current character */
+ }
+ else
+ {
+ /* Not a whitespace, not the end of the header line */
+ mhd_assert ('\r' != chr);
+ mhd_assert ('\n' != chr);
+ mhd_assert ('\0' != chr);
+ if ((! c->rq.hdrs.hdr.name_end_found) &&
+ (! c->rq.hdrs.hdr.starts_with_ws))
+ {
+ /* Processing the header (field) name */
+ if (':' == chr)
+ {
+ if (0 == c->rq.hdrs.hdr.ws_start)
+ c->rq.hdrs.hdr.name_len = p;
+ else
+ {
+ mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
+ if (! allow_wsp_before_colon)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_IN_HEADER_NAME);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_IN_FOOTER_NAME);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start;
+#ifndef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ }
+ if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_EMPTY_HEADER_NAME);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_EMPTY_FOOTER_NAME);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->rq.hdrs.hdr.name_end_found = true;
+ read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
+ }
+ else
+ {
+ if (0 != c->rq.hdrs.hdr.ws_start)
+ {
+ /* End of the whitespace in header (field) name */
+ mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
+ if (! allow_wsp_in_name)
+ {
+ if (! process_footers)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_IN_HEADER_NAME);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_WSP_IN_FOOTER_NAME);
+
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+#ifndef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ }
+ }
+ }
+ else
+ {
+ /* Processing the header (field) value */
+ if (0 == c->rq.hdrs.hdr.value_start)
+ c->rq.hdrs.hdr.value_start = p;
+#ifndef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ }
+#ifdef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* MHD_FAVOR_SMALL_CODE */
+ }
+ p++;
+ }
+ c->rq.hdrs.hdr.proc_pos = p;
+ return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
+}
+
+
+/**
+ * Reset request header processing state.
+ *
+ * This function resets the processing state before processing the next header
+ * (or footer) line.
+ * @param c the connection to process
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_reset_rq_hdr_proc_state (struct MHD_Connection *c)
+{
+ memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
+ bool process_footers)
+{
+ do
+ {
+ struct MHD_String hdr_name;
+ struct MHD_String hdr_value;
+ enum mhd_HdrLineReadRes res;
+
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+
+#ifndef NDEBUG
+ hdr_name.cstr = NULL;
+ hdr_value.cstr = NULL;
+#endif /* ! NDEBUG */
+ res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
+ if (MHD_HDR_LINE_READING_GOT_HEADER == res)
+ {
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+ mhd_assert (NULL != hdr_name.cstr);
+ mhd_assert (NULL != hdr_value.cstr);
+ /* Values must be zero-terminated and must not have binary zeros */
+ mhd_assert (strlen (hdr_name.cstr) == hdr_name.len);
+ mhd_assert (strlen (hdr_value.cstr) == hdr_value.len);
+ /* Values must not have whitespaces at the start or at the end */
+ mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' '));
+ mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t'));
+ mhd_assert ((hdr_name.len == 0) || \
+ (hdr_name.cstr[hdr_name.len - 1] != ' '));
+ mhd_assert ((hdr_name.len == 0) || \
+ (hdr_name.cstr[hdr_name.len - 1] != '\t'));
+ mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' '));
+ mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t'));
+ mhd_assert ((hdr_value.len == 0) || \
+ (hdr_value.cstr[hdr_value.len - 1] != ' '));
+ mhd_assert ((hdr_value.len == 0) || \
+ (hdr_value.cstr[hdr_value.len - 1] != '\t'));
+
+ if (! mhd_stream_add_field (c,
+ process_footers ?
+ MHD_VK_FOOTER : MHD_VK_HEADER,
+ &hdr_name,
+ &hdr_value))
+ {
+ size_t add_element_size;
+
+ mhd_assert (hdr_name.cstr < hdr_value.cstr);
+
+ if (! process_footers)
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+ "Failed to allocate memory in the connection memory " \
+ "pool to store header.");
+ else
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+ "Failed to allocate memory in the connection memory " \
+ "pool to store footer.");
+
+ add_element_size = hdr_value.len
+ + (size_t) (hdr_value.cstr - hdr_name.cstr);
+
+ if (! process_footers)
+ handle_req_headers_no_space (c, hdr_name.cstr, add_element_size);
+ else
+ handle_req_footers_no_space (c, hdr_name.cstr, add_element_size);
+
+ mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
+ return true;
+ }
+ /* Reset processing state */
+ mhd_stream_reset_rq_hdr_proc_state (c);
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+ /* Read the next header (field) line */
+ continue;
+ }
+ else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res)
+ {
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+ return false;
+ }
+ else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
+ {
+ mhd_assert ((process_footers ? \
+ MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) < c->state);
+ mhd_assert (c->stop_with_error);
+ mhd_assert (c->discard_request);
+ return true;
+ }
+ mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res);
+ break;
+ } while (1);
+
+ if (1 == c->rq.num_cr_sp_replaced)
+ {
+ if (! process_footers)
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+ "One bare CR character has been replaced with space " \
+ "in the request line or in the request headers.");
+ else
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \
+ "One bare CR character has been replaced with space " \
+ "in the request footers.");
+ }
+ else if (0 != c->rq.num_cr_sp_replaced)
+ {
+ if (! process_footers)
+ mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+ mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
+ "been replaced with spaces in the request " \
+ "line and/or in the request headers."), \
+ (uint_fast64_t) c->rq.num_cr_sp_replaced);
+ else
+ mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+ mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
+ "been replaced with spaces in the request " \
+ "footers."), \
+ (uint_fast64_t) c->rq.num_cr_sp_replaced);
+
+
+ }
+ if (1 == c->rq.skipped_broken_lines)
+ {
+ if (! process_footers)
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \
+ "One header line without colon has been skipped.");
+ else
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \
+ "One footer line without colon has been skipped.");
+ }
+ else if (0 != c->rq.skipped_broken_lines)
+ {
+ if (! process_footers)
+ mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+ mhd_LOG_FMT ("%" PRIu64 " header lines without colons "
+ "have been skipped."),
+ (uint_fast64_t) c->rq.skipped_broken_lines);
+ else
+ mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+ mhd_LOG_FMT ("%" PRIu64 " footer lines without colons "
+ "have been skipped."),
+ (uint_fast64_t) c->rq.skipped_broken_lines);
+ }
+
+ mhd_assert (c->rq.method < c->read_buffer);
+ if (! process_footers)
+ {
+ c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
+ mhd_assert (NULL != c->rq.field_lines.start);
+ c->rq.field_lines.size =
+ (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
+ if ('\r' == *(c->read_buffer - 2))
+ c->rq.field_lines.size--;
+ c->state = MHD_CONNECTION_HEADERS_RECEIVED;
+
+ if (mhd_BUF_INC_SIZE > c->read_buffer_size)
+ {
+ /* Try to re-use some of the last bytes of the request header */
+ /* Do this only if space in the read buffer is limited AND
+ amount of read ahead data is small. */
+ /**
+ * The position of the terminating NUL after the last character of
+ * the last header element.
+ */
+ const char *last_elmnt_end;
+ size_t shift_back_size;
+ struct mhd_RequestField *header;
+ header = mhd_DLINKEDL_GET_LAST (&(c->rq), fields);
+ if (NULL != header)
+ last_elmnt_end =
+ header->field.nv.value.cstr + header->field.nv.value.len;
+ else
+ last_elmnt_end = c->rq.version + HTTP_VER_LEN;
+ mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
+ shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
+ if (0 != c->read_buffer_offset)
+ memmove (c->read_buffer - shift_back_size,
+ c->read_buffer,
+ c->read_buffer_offset);
+ c->read_buffer -= shift_back_size;
+ c->read_buffer_size += shift_back_size;
+ }
+ }
+ else
+ c->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+
+ return true;
+}
+
+
+#ifdef COOKIE_SUPPORT
+
+/**
+ * Cookie parsing result
+ */
+enum _MHD_ParseCookie
+{
+ MHD_PARSE_COOKIE_OK_LAX = 2 /**< Cookies parsed, but workarounds used */
+ ,
+ MHD_PARSE_COOKIE_OK = 1 /**< Success or no cookies in headers */
+ ,
+ MHD_PARSE_COOKIE_NO_MEMORY = 0 /**< Not enough memory in the pool */
+ ,
+ MHD_PARSE_COOKIE_MALFORMED = -1 /**< Invalid cookie header */
+};
+
+
+/**
+ * Parse the cookies string (see RFC 6265).
+ *
+ * Try to parse the cookies string even if it is not strictly formed
+ * as specified by RFC 6265.
+ *
+ * @param str the string to parse, without leading whitespaces
+ * @param str_len the size of the @a str, not including mandatory
+ * zero-termination
+ * @param connection the connection to add parsed cookies
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_INOUT_SIZE_ (2,1) enum _MHD_ParseCookie
+parse_cookies_string (const size_t str_len,
+ char *restrict str,
+ struct MHD_Connection *restrict connection)
+{
+ size_t i;
+ bool non_strict;
+ /* Skip extra whitespaces and empty cookies */
+ const bool allow_wsp_empty = (0 >= connection->daemon->req_cfg.strictnees);
+ /* Allow whitespaces around '=' character */
+ const bool wsp_around_eq = (-3 >= connection->daemon->req_cfg.strictnees);
+ /* Allow whitespaces in quoted cookie value */
+ const bool wsp_in_quoted = (-2 >= connection->daemon->req_cfg.strictnees);
+ /* Allow tab as space after semicolon between cookies */
+ const bool tab_as_sp = (0 >= connection->daemon->req_cfg.strictnees);
+ /* Allow no space after semicolon between cookies */
+ const bool allow_no_space = (0 >= connection->daemon->req_cfg.strictnees);
+
+ non_strict = false;
+ i = 0;
+ while (i < str_len)
+ {
+ size_t name_start;
+ size_t name_len;
+ size_t value_start;
+ size_t value_len;
+ bool val_quoted;
+ /* Skip any whitespaces and empty cookies */
+ while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
+ {
+ if (! allow_wsp_empty)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ i++;
+ if (i == str_len)
+ return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
+ }
+ /* 'i' must point to the first char of cookie-name */
+ name_start = i;
+ /* Find the end of the cookie-name */
+ do
+ {
+ const char l = str[i];
+ if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
+ (';' == l) || (0 == l))
+ break;
+ } while (str_len > ++i);
+ name_len = i - name_start;
+ /* Skip any whitespaces */
+ while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+ {
+ if (! wsp_around_eq)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ i++;
+ }
+ if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
+ return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
+ /* 'i' must point to the '=' char */
+ mhd_assert ('=' == str[i]);
+ i++;
+ /* Skip any whitespaces */
+ while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+ {
+ if (! wsp_around_eq)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ i++;
+ }
+ /* 'i' must point to the first char of cookie-value */
+ if (str_len == i)
+ {
+ value_start = 0;
+ value_len = 0;
+#ifndef NDEBUG
+ val_quoted = false; /* This assignment used in assert */
+#endif
+ }
+ else
+ {
+ bool valid_cookie;
+ val_quoted = ('"' == str[i]);
+ if (val_quoted)
+ i++;
+ value_start = i;
+ /* Find the end of the cookie-value */
+ while (str_len > i)
+ {
+ const char l = str[i];
+ if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
+ ('\\' == l) || (0 == l))
+ break;
+ if ((' ' == l) || ('\t' == l))
+ {
+ if (! val_quoted)
+ break;
+ if (! wsp_in_quoted)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ }
+ i++;
+ }
+ value_len = i - value_start;
+ if (val_quoted)
+ {
+ if ((str_len == i) || ('"' != str[i]))
+ return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
+ i++;
+ }
+ /* Skip any whitespaces */
+ if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
+ {
+ do
+ {
+ i++;
+ } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
+ /* Whitespace at the end? */
+ if (str_len > i)
+ {
+ if (! allow_wsp_empty)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ }
+ }
+ if (str_len == i)
+ valid_cookie = true;
+ else if (';' == str[i])
+ valid_cookie = true;
+ else
+ valid_cookie = false;
+
+ if (! valid_cookie)
+ return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
+ }
+ mhd_assert (0 != name_len);
+ str[name_start + name_len] = 0; /* Zero-terminate the name */
+ if (0 != value_len)
+ {
+ struct MHD_String name;
+ struct MHD_String value;
+ mhd_assert (value_start + value_len <= str_len);
+ name.cstr = str + name_start;
+ name.len = name_len;
+ str[value_start + value_len] = 0; /* Zero-terminate the value */
+ value.cstr = str + value_start;
+ value.len = value_len;
+ if (! mhd_stream_add_field (connection,
+ MHD_VK_COOKIE,
+ &name,
+ &value))
+ return MHD_PARSE_COOKIE_NO_MEMORY;
+ }
+ else
+ {
+ struct MHD_String name;
+ struct MHD_String value;
+ name.cstr = str + name_start;
+ name.len = name_len;
+ value.cstr = "";
+ value.len = 0;
+ if (! mhd_stream_add_field (connection,
+ MHD_VK_COOKIE,
+ &name,
+ &value))
+ return MHD_PARSE_COOKIE_NO_MEMORY;
+ }
+ if (str_len > i)
+ {
+ mhd_assert (0 == str[i] || ';' == str[i]);
+ mhd_assert (! val_quoted || ';' == str[i]);
+ mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
+ i++;
+ if (str_len == i)
+ { /* No next cookie after semicolon */
+ if (! allow_wsp_empty)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ }
+ else if (' ' != str[i])
+ {/* No space after semicolon */
+ if (('\t' == str[i]) && tab_as_sp)
+ i++;
+ else if (! allow_no_space)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ }
+ else
+ {
+ i++;
+ if (str_len == i)
+ {
+ if (! allow_wsp_empty)
+ return MHD_PARSE_COOKIE_MALFORMED;
+ non_strict = true;
+ }
+ }
+ }
+ }
+ return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
+}
+
+
+/**
+ * Parse the cookie header (see RFC 6265).
+ *
+ * @param connection connection to parse header of
+ * @param cookie_val the value of the "Cookie:" header
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
+ */
+static enum _MHD_ParseCookie
+parse_cookie_header (struct MHD_Connection *restrict connection,
+ struct MHD_StringNullable *restrict cookie_val)
+{
+ char *cpy;
+ size_t i;
+ enum _MHD_ParseCookie parse_res;
+ struct mhd_RequestField *const saved_tail =
+ connection->rq.fields.last; // FIXME: a better way?
+ const bool allow_partially_correct_cookie =
+ (1 >= connection->daemon->req_cfg.strictnees);
+
+ if (NULL == cookie_val)
+ return MHD_PARSE_COOKIE_OK;
+ if (0 == cookie_val->len)
+ return MHD_PARSE_COOKIE_OK;
+
+ cpy = mhd_stream_alloc_memory (connection,
+ cookie_val->len + 1);
+ if (NULL == cpy)
+ parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
+ else
+ {
+ memcpy (cpy,
+ cookie_val->cstr,
+ cookie_val->len + 1);
+ mhd_assert (0 == cpy[cookie_val->len]);
+
+ /* Must not have initial whitespaces */
+ mhd_assert (' ' != cpy[0]);
+ mhd_assert ('\t' != cpy[0]);
+
+ i = 0;
+ parse_res = parse_cookies_string (cookie_val->len - i, cpy + i, connection);
+ }
+
+ switch (parse_res)
+ {
+ case MHD_PARSE_COOKIE_OK:
+ break;
+ case MHD_PARSE_COOKIE_OK_LAX:
+ if (saved_tail != connection->rq.fields.last)
+ mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \
+ "The Cookie header has been parsed, but it is not "
+ "fully compliant with specifications.");
+ break;
+ case MHD_PARSE_COOKIE_MALFORMED:
+ if (saved_tail != connection->rq.fields.last) // FIXME: a better way?
+ {
+ if (! allow_partially_correct_cookie)
+ {
+ /* Remove extracted values from partially broken cookie */
+ /* Memory remains allocated until the end of the request processing */
+ connection->rq.fields.last = saved_tail; // FIXME: a better way?
+ saved_tail->fields.next = NULL; // FIXME: a better way?
+ mhd_LOG_MSG ( \
+ connection->daemon, MHD_SC_REQ_COOKIE_IGNORED_NOT_COMPLIANT, \
+ "The Cookie header is ignored as it contains malformed data.");
+ }
+ else
+ mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \
+ "The Cookie header has been only partially parsed " \
+ "as it contains malformed data.");
+ }
+ else
+ mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID,
+ "The Cookie header has malformed data.");
+ break;
+ case MHD_PARSE_COOKIE_NO_MEMORY:
+ mhd_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE,
+ "Not enough memory in the connection pool to "
+ "parse client cookies!\n");
+ break;
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ }
+
+ return parse_res;
+}
+
+
+/**
+ * Send error reply when receive buffer space exhausted while receiving or
+ * storing the request headers
+ * @param c the connection to handle
+ * @param add_header the optional pointer to the current header string being
+ * processed or the header failed to be added.
+ * Could be not zero-terminated and can contain binary zeros.
+ * Can be NULL.
+ * @param add_header_size the size of the @a add_header
+ */
+MHD_static_inline_ void
+handle_req_cookie_no_space (struct MHD_Connection *restrict c)
+{
+ unsigned int err_code;
+
+ err_code = mhd_stream_get_no_space_err_status_code (c,
+ MHD_PROC_RECV_COOKIE,
+ 0,
+ NULL);
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ err_code,
+ ERR_RSP_REQUEST_HEADER_TOO_BIG);
+}
+
+
+#endif /* COOKIE_SUPPORT */
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
+{
+ bool has_host;
+ bool has_trenc;
+ bool has_cntnlen;
+ bool has_keepalive;
+ struct mhd_RequestField *f;
+
+ /* The presence of the request body is indicated by "Content-Length:" or
+ "Transfer-Encoding:" request headers.
+ Unless one of these two headers is used, the request has no request body.
+ See RFC9112, Section 6, paragraph 4. */
+ c->rq.have_chunked_upload = false;
+ c->rq.cntn.cntn_size = 0;
+
+ has_host = false;
+ has_trenc = false;
+ has_cntnlen = false;
+ has_keepalive = true;
+
+ for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields);
+ NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ {
+ if (MHD_VK_HEADER != f->field.kind)
+ continue;
+
+ /* "Host:" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_HOST,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ if ((has_host)
+ && (-3 < c->daemon->req_cfg.strictnees))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \
+ "Received request with more than one 'Host' header.");
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS);
+ return;
+ }
+ has_host = true;
+ continue;
+ }
+
+#ifdef COOKIE_SUPPORT
+ /* "Cookie:" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ if (MHD_PARSE_COOKIE_NO_MEMORY ==
+ parse_cookie_header (c,
+ &(f->field.nv.value)))
+ {
+ handle_req_cookie_no_space (c);
+ return;
+ }
+ continue;
+ }
+#endif /* COOKIE_SUPPORT */
+
+ /* "Content-Length:" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ size_t num_digits;
+ uint_fast64_t cntn_size;
+
+ num_digits = mhd_str_to_uint64_n (f->field.nv.value.cstr,
+ f->field.nv.value.len,
+ &cntn_size);
+ if (((0 == num_digits) &&
+ (0 != f->field.nv.value.len) &&
+ ('9' >= f->field.nv.value.cstr[0])
+ && ('0' <= f->field.nv.value.cstr[0]))
+ || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \
+ "Too large value of 'Content-Length' header. " \
+ "Closing connection.");
+ mhd_RESPOND_WITH_ERROR_STATIC (c, \
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE, \
+ ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE);
+ return;
+ }
+ else if ((f->field.nv.value.len != num_digits) ||
+ (0 == num_digits))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \
+ "Failed to parse 'Content-Length' header. " \
+ "Closing connection.");
+ mhd_RESPOND_WITH_ERROR_STATIC (c, \
+ MHD_HTTP_STATUS_BAD_REQUEST, \
+ ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED);
+ return;
+ }
+
+ if (has_cntnlen)
+ {
+ bool send_err;
+ send_err = false;
+ if (c->rq.cntn.cntn_size == cntn_size)
+ {
+ if (0 < c->daemon->req_cfg.strictnees)
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \
+ "Received request with more than one " \
+ "'Content-Length' header with the same value.");
+ send_err = true;
+ }
+ }
+ else
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT, \
+ "Received request with more than one " \
+ "'Content-Length' header with conflicting values.");
+ send_err = true;
+ }
+
+ if (send_err)
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC ( \
+ c, \
+ MHD_HTTP_STATUS_BAD_REQUEST, \
+ ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL);
+ return;
+ }
+ }
+ mhd_assert ((0 == c->rq.cntn.cntn_size) || \
+ (c->rq.cntn.cntn_size == cntn_size));
+ c->rq.cntn.cntn_size = cntn_size;
+ has_cntnlen = true;
+ continue;
+ }
+
+ /* "Connection:" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string
+ "close",
+ mhd_SSTR_LEN ("close")))
+ {
+ mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
+ c->conn_reuse = mhd_CONN_MUST_CLOSE;
+ }
+ else if ((MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
+ && (mhd_CONN_MUST_CLOSE != c->conn_reuse))
+ {
+ if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string
+ "keep-alive",
+ mhd_SSTR_LEN ("keep-alive")))
+ has_keepalive = true;
+ }
+
+ continue;
+ }
+
+ /* "Transfer-Encoding:" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ if (mhd_str_equal_caseless_n_st ("chunked",
+ f->field.nv.value.cstr,
+ f->field.nv.value.len))
+ {
+ c->rq.have_chunked_upload = true;
+ c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
+ }
+ else
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_CHUNKED_ENCODING_UNSUPPORTED, \
+ "The 'Transfer-Encoding' used in request is " \
+ "unsupported or invalid.");
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_UNSUPPORTED_TR_ENCODING);
+ return;
+ }
+ has_trenc = true;
+ continue;
+ }
+ }
+
+ if (has_trenc && has_cntnlen)
+ {
+ if (0 < c->daemon->req_cfg.strictnees)
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC ( \
+ c, \
+ MHD_HTTP_STATUS_BAD_REQUEST, \
+ ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING);
+ return;
+ }
+ /* Must close connection after reply to prevent potential attack */
+ c->conn_reuse = mhd_CONN_MUST_CLOSE;
+ c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
+ mhd_assert (c->rq.have_chunked_upload);
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \
+ "The 'Content-Length' request header is ignored " \
+ "as chunked 'Transfer-Encoding' is used " \
+ "for this request.");
+ }
+
+ if (MHD_HTTP_VERSION_1_1 <= c->rq.http_ver)
+ {
+ if ((! has_host) &&
+ (-3 < c->daemon->req_cfg.strictnees))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \
+ "Received HTTP/1.1 request without 'Host' header.");
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_LACKS_HOST);
+ return;
+ }
+ }
+ else
+ {
+ if (! has_keepalive)
+ c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Do not re-use HTTP/1.0 connection by default */
+ if (has_trenc)
+ c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */
+ }
+
+ c->state = MHD_CONNECTION_HEADERS_PROCESSED;
+ return;
+}
+
+
+/**
+ * Is "100 CONTINUE" needed to be sent for current request?
+ *
+ * @param c the connection to check
+ * @return false 100 CONTINUE is not needed,
+ * true otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+need_100_continue (struct MHD_Connection *restrict c)
+{
+ const struct MHD_StringNullable *hvalue;
+
+ mhd_assert (MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver));
+ mhd_assert (MHD_CONNECTION_BODY_RECEIVING > c->state);
+
+ if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
+ return false;
+
+ if (0 != c->read_buffer_offset)
+ return false; /* Part of the content has been received already */
+
+ hvalue = mhd_request_get_value_st (&(c->rq),
+ MHD_VK_HEADER,
+ MHD_HTTP_HEADER_EXPECT);
+ if (NULL == hvalue)
+ return false;
+
+ if (mhd_str_equal_caseless_n_st ("100-continue", \
+ hvalue->cstr, hvalue->len))
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Check whether special buffer is required to handle the upload content and
+ * try to allocate if necessary.
+ * Respond with error to the client if buffer cannot be allocated
+ * @param c the connection to
+ * @return true if succeed,
+ * false if error response is set
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+check_and_alloc_buf_for_upload_processing (struct MHD_Connection *restrict c)
+{
+ mhd_assert ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) || \
+ (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act));
+
+ if (c->rq.have_chunked_upload)
+ return true; /* The size is unknown, buffers will be dynamically allocated
+ and re-allocated */
+ mhd_assert (c->read_buffer_size > c->read_buffer_offset);
+#if 0 // TODO: support processing full response in the connection buffer
+ if ((c->read_buffer_size - c->read_buffer_offset) >=
+ c->rq.cntn.cntn_size)
+ return true; /* No additional buffer needed */
+#endif
+
+ if ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) &&
+ (NULL == c->rq.app_act.head_act.data.upload.full.cb))
+ return true; /* data will be processed only incrementally */
+
+ if (mhd_ACTION_UPLOAD != c->rq.app_act.head_act.act)
+ {
+ // TODO: add check for intermental-only POST processing */
+ mhd_assert (0 && "Not implemented yet");
+ return false;
+ }
+
+ if ((c->rq.cntn.cntn_size >
+ c->rq.app_act.head_act.data.upload.large_buffer_size) ||
+ ! mhd_daemon_get_lbuf (c->daemon, c->rq.cntn.cntn_size,
+ &(c->rq.cntn.lbuf)))
+ {
+ if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
+ {
+ c->rq.app_act.head_act.data.upload.full.cb = NULL;
+ return true; /* Data can be processed incrementally */
+ }
+
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+ ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE);
+ return false;
+ }
+
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
+{
+ struct MHD_Daemon *restrict d = c->daemon;
+ struct MHD_String path;
+ const struct MHD_Action *a;
+
+ mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd);
+ mhd_assert (NULL == c->rp.response);
+
+ if (mhd_ACTION_NO_ACTION != c->rq.app_act.head_act.act)
+ MHD_PANIC ("MHD_Action has been set already");
+
+ path.cstr = c->rq.url;
+ path.len = c->rq.url_len;
+
+ c->rq.app_aware = true;
+ a = d->req_cfg.cb (d->req_cfg.cb_cls,
+ &(c->rq),
+ &path,
+ (enum MHD_HTTP_Method) c->rq.http_mthd,
+ c->rq.cntn.cntn_size);
+
+ if ((NULL != a)
+ && (((&(c->rq.app_act.head_act) != a))
+ || ! mhd_ACTION_IS_VALID (c->rq.app_act.head_act.act)))
+ {
+ mhd_LOG_MSG (d, MHD_SC_ACTION_INVALID, \
+ "Provided action is not a correct action generated " \
+ "for the current request.");
+ a = NULL;
+ }
+ if (NULL == a)
+ c->rq.app_act.head_act.act = mhd_ACTION_ABORT;
+
+ switch (c->rq.app_act.head_act.act)
+ {
+ case mhd_ACTION_RESPONSE:
+ c->rp.response = c->rq.app_act.head_act.data.response;
+ c->state = MHD_CONNECTION_REQ_RECV_FINISHED;
+ return true;
+ case mhd_ACTION_UPLOAD:
+ if (0 != c->rq.cntn.cntn_size)
+ {
+ if (! check_and_alloc_buf_for_upload_processing (c))
+ return true;
+ if (need_100_continue (c))
+ {
+ c->state = MHD_CONNECTION_CONTINUE_SENDING;
+ return true;
+ }
+ c->state = MHD_CONNECTION_BODY_RECEIVING;
+ return (0 != c->read_buffer_offset);
+ }
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return true;
+ case mhd_ACTION_POST_PROCESS:
+ mhd_assert (0 && "Not implemented yet");
+ return true;
+ case mhd_ACTION_SUSPEND:
+ c->suspended = true;
+ return false;
+ case mhd_ACTION_ABORT:
+ mhd_conn_pre_close_app_abort (c);
+ return false;
+ case mhd_ACTION_NO_ACTION:
+ default:
+ mhd_assert (0 && "Impossible value");
+ break;
+ }
+ MHD_UNREACHABLE_;
+ return false;
+}
+
+
+/**
+ * React on provided action for upload
+ * @param c the stream to use
+ * @param act the action provided by application
+ * @param final set to 'true' if this is final upload callback
+ * @return true if connection state has been changed,
+ * false otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1) bool
+process_upload_action (struct MHD_Connection *restrict c,
+ const struct MHD_UploadAction *act,
+ bool final)
+{
+ if (NULL != act)
+ {
+ if ((&(c->rq.app_act.upl_act) != act) ||
+ ! mhd_UPLOAD_ACTION_IS_VALID (c->rq.app_act.upl_act.act) ||
+ (final &&
+ (mhd_UPLOAD_ACTION_CONTINUE == c->rq.app_act.upl_act.act)))
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \
+ "Provided action is not a correct action generated " \
+ "for the current request.");
+ act = NULL;
+ }
+ }
+ if (NULL == act)
+ c->rq.app_act.upl_act.act = mhd_UPLOAD_ACTION_ABORT;
+
+ switch (c->rq.app_act.upl_act.act)
+ {
+ case mhd_UPLOAD_ACTION_RESPONSE:
+ c->rp.response = c->rq.app_act.upl_act.data.response;
+ c->state = MHD_CONNECTION_REQ_RECV_FINISHED;
+ return true;
+ case mhd_UPLOAD_ACTION_CONTINUE:
+ memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act));
+ return false;
+ case mhd_UPLOAD_ACTION_SUSPEND:
+ c->suspended = true;
+ return false;
+ case mhd_UPLOAD_ACTION_ABORT:
+ mhd_conn_pre_close_app_abort (c);
+ return false;
+ case mhd_UPLOAD_ACTION_NO_ACTION:
+ default:
+ mhd_assert (0 && "Impossible value");
+ break;
+ }
+ MHD_UNREACHABLE_;
+ return false;
+}
+
+
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_request_chunked_body (struct MHD_Connection *restrict c)
+{
+ struct MHD_Daemon *restrict d = c->daemon;
+ size_t available;
+ bool has_more_data;
+ char *restrict buffer_head;
+ const int discp_lvl = d->req_cfg.strictnees;
+ /* Treat bare LF as the end of the line.
+ RFC 9112, section 2.2-3
+ Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
+ Bare LF is processed as end of the line or rejected as broken request. */
+ const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+ /* Allow "Bad WhiteSpace" in chunk extension.
+ RFC 9112, Section 7.1.1, Paragraph 2 */
+ const bool allow_bws = (2 < discp_lvl);
+ bool state_updated;
+
+ mhd_assert (NULL == c->rp.response);
+ mhd_assert (c->rq.have_chunked_upload);
+ mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
+
+ buffer_head = c->read_buffer;
+ available = c->read_buffer_offset;
+ state_updated = false;
+ do
+ {
+ size_t cntn_data_ready;
+ bool need_inc_proc;
+
+ has_more_data = false;
+
+ if ( (c->rq.current_chunk_offset ==
+ c->rq.current_chunk_size) &&
+ (0 != c->rq.current_chunk_size) )
+ {
+ size_t i;
+ mhd_assert (0 != available);
+ /* skip new line at the *end* of a chunk */
+ i = 0;
+ if ( (2 <= available) &&
+ ('\r' == buffer_head[0]) &&
+ ('\n' == buffer_head[1]) )
+ i += 2; /* skip CRLF */
+ else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
+ i++; /* skip bare LF */
+ else if (2 > available)
+ break; /* need more upload data */
+ if (0 == i)
+ {
+ /* malformed encoding */
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_CHUNKED_MALFORMED);
+ return true;
+ }
+ available -= i;
+ buffer_head += i;
+ c->rq.current_chunk_offset = 0;
+ c->rq.current_chunk_size = 0;
+ if (0 == available)
+ break;
+ }
+ if (0 != c->rq.current_chunk_size)
+ {
+ uint_fast64_t cur_chunk_left;
+ mhd_assert (c->rq.current_chunk_offset < \
+ c->rq.current_chunk_size);
+ /* we are in the middle of a chunk, give
+ as much as possible to the client (without
+ crossing chunk boundaries) */
+ cur_chunk_left
+ = c->rq.current_chunk_size
+ - c->rq.current_chunk_offset;
+ if (cur_chunk_left > available)
+ cntn_data_ready = available;
+ else
+ { /* cur_chunk_left <= (size_t)available */
+ cntn_data_ready = (size_t) cur_chunk_left;
+ if (available > cntn_data_ready)
+ has_more_data = true;
+ }
+ }
+ else
+ { /* Need the parse the chunk size line */
+ /** The number of found digits in the chunk size number */
+ size_t num_dig;
+ uint_fast64_t chunk_size;
+ bool broken;
+ bool overflow;
+
+ mhd_assert (0 != available);
+
+ overflow = false;
+ chunk_size = 0; /* Mute possible compiler warning.
+ The real value will be set later. */
+
+ num_dig = mhd_strx_to_uint64_n (buffer_head,
+ available,
+ &chunk_size);
+ mhd_assert (num_dig <= available);
+ if (num_dig == available)
+ continue; /* Need line delimiter */
+
+ broken = (0 == num_dig);
+ if (broken)
+ /* Check whether result is invalid due to uint64_t overflow */
+ overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) ||
+ (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) ||
+ (('a' <= buffer_head[0]) && ('f' >= buffer_head[0])));
+ else
+ {
+ /**
+ * The length of the string with the number of the chunk size,
+ * including chunk extension
+ */
+ size_t chunk_size_line_len;
+
+ chunk_size_line_len = 0;
+ if ((';' == buffer_head[num_dig]) ||
+ (allow_bws &&
+ ((' ' == buffer_head[num_dig]) ||
+ ('\t' == buffer_head[num_dig]))))
+ { /* Chunk extension */
+ size_t i;
+
+ /* Skip bad whitespaces (if any) */
+ for (i = num_dig; i < available; ++i)
+ {
+ if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
+ break;
+ }
+ if (i == available)
+ break; /* need more data */
+ if (';' == buffer_head[i])
+ {
+ for (++i; i < available; ++i)
+ {
+ if ('\n' == buffer_head[i])
+ break;
+ }
+ if (i == available)
+ break; /* need more data */
+ mhd_assert (i > num_dig);
+ mhd_assert (1 <= i);
+ /* Found LF position */
+ if (bare_lf_as_crlf)
+ chunk_size_line_len = i; /* Don't care about CR before LF */
+ else if ('\r' == buffer_head[i - 1])
+ chunk_size_line_len = i;
+ }
+ else
+ { /* No ';' after "bad whitespace" */
+ mhd_assert (allow_bws);
+ mhd_assert (0 == chunk_size_line_len);
+ }
+ }
+ else
+ {
+ mhd_assert (available >= num_dig);
+ if ((2 <= (available - num_dig)) &&
+ ('\r' == buffer_head[num_dig]) &&
+ ('\n' == buffer_head[num_dig + 1]))
+ chunk_size_line_len = num_dig + 2;
+ else if (bare_lf_as_crlf &&
+ ('\n' == buffer_head[num_dig]))
+ chunk_size_line_len = num_dig + 1;
+ else if (2 > (available - num_dig))
+ break; /* need more data */
+ }
+
+ if (0 != chunk_size_line_len)
+ { /* Valid termination of the chunk size line */
+ mhd_assert (chunk_size_line_len <= available);
+ /* Start reading payload data of the chunk */
+ c->rq.current_chunk_offset = 0;
+ c->rq.current_chunk_size = chunk_size;
+
+ available -= chunk_size_line_len;
+ buffer_head += chunk_size_line_len;
+
+ if (0 == chunk_size)
+ { /* The final (termination) chunk */
+ c->rq.cntn.cntn_size = c->rq.cntn.recv_size;
+ c->state = MHD_CONNECTION_BODY_RECEIVED;
+ state_updated = true;
+ break;
+ }
+ if (available > 0)
+ has_more_data = true;
+ continue;
+ }
+ /* Invalid chunk size line */
+ }
+
+ if (! overflow)
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ ERR_RSP_REQUEST_CHUNKED_MALFORMED);
+ else
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+ ERR_RSP_REQUEST_CHUNK_TOO_LARGE);
+ return true;
+ }
+ mhd_assert (c->rq.app_aware);
+
+ if (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act)
+ {
+ mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
+ return false;
+ }
+
+ if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
+ {
+ need_inc_proc = false;
+
+ mhd_assert (0 == c->rq.cntn.proc_size);
+ if ((uint_fast64_t) c->rq.cntn.lbuf.size <
+ c->rq.cntn.recv_size + cntn_data_ready)
+ {
+ size_t grow_size;
+
+ grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready
+ - c->rq.cntn.lbuf.size);
+ if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) <
+ cntn_data_ready) || (! mhd_daemon_grow_lbuf (d,
+ grow_size,
+ &(c->rq.cntn.lbuf))))
+ {
+ /* Failed to grow the buffer, no space to put the new data */
+ const struct MHD_UploadAction *act;
+ if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (
+ c,
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+ ERR_RSP_MSG_REQUEST_TOO_BIG);
+ return true;
+ }
+ c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */
+ /* Process previously buffered data */
+ mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ c->rq.cntn.recv_size,
+ c->rq.cntn.lbuf.buf);
+ c->rq.cntn.proc_size = c->rq.cntn.recv_size;
+ mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf));
+ if (process_upload_action (c, act, false))
+ return true;
+ need_inc_proc = true;
+ }
+ }
+ if (! need_inc_proc)
+ {
+ memcpy (c->rq.cntn.lbuf.buf + c->rq.cntn.recv_size,
+ buffer_head, cntn_data_ready);
+ c->rq.cntn.recv_size += cntn_data_ready;
+ }
+ }
+ else
+ need_inc_proc = true;
+
+ if (need_inc_proc)
+ {
+ const struct MHD_UploadAction *act;
+ mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
+
+ c->rq.cntn.recv_size += cntn_data_ready;
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ cntn_data_ready,
+ buffer_head);
+ c->rq.cntn.proc_size += cntn_data_ready;
+ state_updated = process_upload_action (c, act, false);
+ }
+
+ /* dh left "processed" bytes in buffer for next time... */
+ buffer_head += cntn_data_ready;
+ available -= cntn_data_ready;
+ mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
+ c->rq.current_chunk_offset += cntn_data_ready;
+ } while (has_more_data && ! state_updated);
+ /* TODO: optionally? zero out reused memory region */
+ if ( (available > 0) &&
+ (buffer_head != c->read_buffer) )
+ memmove (c->read_buffer,
+ buffer_head,
+ available);
+ else
+ mhd_assert ((0 == available) || \
+ (c->read_buffer_offset == available));
+ c->read_buffer_offset = available;
+
+ return state_updated;
+}
+
+
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_request_nonchunked_body (struct MHD_Connection *restrict c)
+{
+ size_t cntn_data_ready;
+ bool read_buf_reuse;
+ bool state_updated;
+
+ mhd_assert (NULL == c->rp.response);
+ mhd_assert (! c->rq.have_chunked_upload);
+ mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size);
+ mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size);
+ mhd_assert (c->rq.app_aware);
+
+ if ((c->rq.cntn.cntn_size - c->rq.cntn.recv_size) < c->read_buffer_offset)
+ cntn_data_ready = (size_t) (c->rq.cntn.cntn_size - c->rq.cntn.recv_size);
+ else
+ cntn_data_ready = c->read_buffer_offset;
+
+ if (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act)
+ {
+ mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
+ return false;
+ }
+
+ mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
+ read_buf_reuse = false;
+ state_updated = false;
+ if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
+ {
+ // TODO: implement processing in pool memory if buffer is large enough
+ mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <=
+ (uint_fast64_t) c->rq.cntn.lbuf.size);
+ memcpy (c->rq.cntn.lbuf.buf + c->rq.cntn.recv_size,
+ c->read_buffer, cntn_data_ready);
+ c->rq.cntn.recv_size += cntn_data_ready;
+ read_buf_reuse = true;
+ if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
+ {
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ state_updated = true;
+ }
+ }
+ else
+ {
+ const struct MHD_UploadAction *act;
+ mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
+
+ c->rq.cntn.recv_size += cntn_data_ready;
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ cntn_data_ready,
+ c->read_buffer);
+ c->rq.cntn.proc_size += cntn_data_ready;
+ read_buf_reuse = true;
+ state_updated = process_upload_action (c, act, false);
+ }
+
+ if (read_buf_reuse)
+ {
+ size_t data_left_size;
+ mhd_assert (c->read_buffer_offset >= cntn_data_ready);
+ data_left_size = c->read_buffer_offset - cntn_data_ready;
+ if (0 != data_left_size)
+ memmove (c->read_buffer, c->read_buffer + cntn_data_ready, data_left_size)
+ ;
+ c->read_buffer_offset = data_left_size;
+ }
+
+ return state_updated;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_process_request_body (struct MHD_Connection *restrict c)
+{
+ if (c->rq.have_chunked_upload)
+ return process_request_chunked_body (c);
+
+ return process_request_nonchunked_body (c);
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
+{
+ const struct MHD_UploadAction *act;
+ mhd_assert (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act || \
+ mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
+
+ if (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act)
+ {
+ mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
+ return false;
+ }
+
+ if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
+ {
+ mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size);
+ mhd_assert (0 == c->rq.cntn.proc_size);
+ mhd_assert (NULL != c->rq.cntn.lbuf.buf);
+ mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
+ // TODO: implement processing in pool memory if it is large enough
+ act = c->rq.app_act.head_act.data.upload.full.cb (
+ c->rq.app_act.head_act.data.upload.full.cls,
+ &(c->rq),
+ c->rq.cntn.recv_size,
+ c->rq.cntn.lbuf.buf);
+ c->rq.cntn.proc_size = c->rq.cntn.recv_size;
+ }
+ else
+ {
+ mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
+ mhd_assert (c->rq.cntn.cntn_size == c->rq.cntn.proc_size);
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ 0,
+ NULL);
+ }
+ return process_upload_action (c, act, true);
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
+{
+ if (NULL != c->rq.cntn.lbuf.buf)
+ mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
+ c->rq.cntn.lbuf.buf = NULL;
+ if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size)
+ c->discard_request = true;
+ mhd_assert (NULL != c->rp.response);
+ c->state = MHD_CONNECTION_START_REPLY;
+ return true;
+}
+
+
+/**
+ * Send error reply when receive buffer space exhausted while receiving
+ * the chunk size line.
+ * @param c the connection to handle
+ * @param add_header the optional pointer to the partially received
+ * the current chunk size line.
+ * Could be not zero-terminated and can contain binary zeros.
+ * Can be NULL.
+ * @param add_header_size the size of the @a add_header
+ */
+static void
+handle_req_chunk_size_line_no_space (struct MHD_Connection *c,
+ const char *chunk_size_line,
+ size_t chunk_size_line_size)
+{
+ unsigned int err_code;
+
+ if (NULL != chunk_size_line)
+ {
+ const char *semicol;
+ /* Check for chunk extension */
+ semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
+ if (NULL != semicol)
+ { /* Chunk extension present. It could be removed without any loss of the
+ details of the request. */
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+ ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG);
+ }
+ }
+ err_code = mhd_stream_get_no_space_err_status_code (c,
+ MHD_PROC_RECV_BODY_CHUNKED,
+ chunk_size_line_size,
+ chunk_size_line);
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ err_code,
+ ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG);
+}
+
+
+/**
+ * Handle situation with read buffer exhaustion.
+ * Must be called when no more space left in the read buffer, no more
+ * space left in the memory pool to grow the read buffer, but more data
+ * need to be received from the client.
+ * Could be called when the result of received data processing cannot be
+ * stored in the memory pool (like some header).
+ * @param c the connection to process
+ * @param stage the receive stage where the exhaustion happens.
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+handle_recv_no_space (struct MHD_Connection *c,
+ enum MHD_ProcRecvDataStage stage)
+{
+ mhd_assert (MHD_PROC_RECV_INIT <= stage);
+ mhd_assert (MHD_PROC_RECV_FOOTERS >= stage);
+ mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state);
+ mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
+ (MHD_CONNECTION_INIT == c->state));
+ mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
+ (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
+ mhd_assert ((MHD_PROC_RECV_URI != stage) || \
+ (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
+ mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
+ (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
+ mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
+ (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state));
+ mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
+ mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
+ (MHD_CONNECTION_BODY_RECEIVING == c->state));
+ mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
+ (MHD_CONNECTION_BODY_RECEIVING == c->state));
+ mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
+ (MHD_CONNECTION_FOOTERS_RECEIVING == c->state));
+ mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
+ (! c->rq.have_chunked_upload));
+ mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
+ (c->rq.have_chunked_upload));
+ switch (stage)
+ {
+ case MHD_PROC_RECV_INIT:
+ case MHD_PROC_RECV_METHOD:
+ /* Some data has been received, but it is not clear yet whether
+ * the received data is an valid HTTP request */
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
+ "No space left in the read buffer when " \
+ "receiving the initial part of " \
+ "the request line.");
+ return;
+ case MHD_PROC_RECV_URI:
+ case MHD_PROC_RECV_HTTPVER:
+ /* Some data has been received, but the request line is incomplete */
+ mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd);
+ mhd_assert (MHD_HTTP_VERSION_INVALID == c->rq.http_ver);
+ /* A quick simple check whether the incomplete line looks
+ * like an HTTP request */
+ if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+ (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_URI_TOO_LONG,
+ ERR_RSP_MSG_REQUEST_TOO_BIG);
+ return;
+ }
+ mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
+ "No space left in the read buffer when " \
+ "receiving the URI in " \
+ "the request line. " \
+ "The request uses non-standard HTTP request " \
+ "method token.");
+ return;
+ case MHD_PROC_RECV_HEADERS:
+ handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset);
+ return;
+ case MHD_PROC_RECV_BODY_NORMAL:
+ /* A header probably has been added to a suspended connection and
+ it took precisely all the space in the buffer.
+ Very low probability. */
+ mhd_assert (! c->rq.have_chunked_upload);
+ handle_req_headers_no_space (c, NULL, 0); // FIXME: check
+ return;
+ case MHD_PROC_RECV_BODY_CHUNKED:
+ mhd_assert (c->rq.have_chunked_upload);
+ if (c->rq.current_chunk_offset != c->rq.current_chunk_size)
+ { /* Receiving content of the chunk */
+ /* A header probably has been added to a suspended connection and
+ it took precisely all the space in the buffer.
+ Very low probability. */
+ handle_req_headers_no_space (c, NULL, 0); // FIXME: check
+ }
+ else
+ {
+ if (0 != c->rq.current_chunk_size)
+ { /* Waiting for chunk-closing CRLF */
+ /* Not really possible as some payload should be
+ processed and the space used by payload should be available. */
+ handle_req_headers_no_space (c, NULL, 0); // FIXME: check
+ }
+ else
+ { /* Reading the line with the chunk size */
+ handle_req_chunk_size_line_no_space (c,
+ c->read_buffer,
+ c->read_buffer_offset);
+ }
+ }
+ return;
+ case MHD_PROC_RECV_FOOTERS:
+ handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset);
+ return;
+ /* The next cases should not be possible */
+ case MHD_PROC_RECV_COOKIE:
+ default:
+ break;
+ }
+ mhd_assert (0 && "Should be unreachable");
+}
+
+
+/**
+ * Try growing the read buffer. We initially claim half the available
+ * buffer space for the read buffer (the other half being left for
+ * management data structures; the write buffer can in the end take
+ * virtually everything as the read buffer can be reduced to the
+ * minimum necessary at that point.
+ *
+ * @param connection the connection
+ * @param required set to 'true' if grow is required, i.e. connection
+ * will fail if no additional space is granted
+ * @return 'true' on success, 'false' on failure
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+try_grow_read_buffer (struct MHD_Connection *restrict connection,
+ bool required)
+{
+ size_t new_size;
+ size_t avail_size;
+ const size_t def_grow_size = 1536; // TODO: remove hardcoded increment
+ void *rb;
+
+ avail_size = mhd_pool_get_free (connection->pool);
+ if (0 == avail_size)
+ return false; /* No more space available */
+ if (0 == connection->read_buffer_size)
+ new_size = avail_size / 2; /* Use half of available buffer for reading */
+ else
+ {
+ size_t grow_size;
+
+ grow_size = avail_size / 8;
+ if (def_grow_size > grow_size)
+ { /* Shortage of space */
+ const size_t left_free =
+ connection->read_buffer_size - connection->read_buffer_offset;
+ mhd_assert (connection->read_buffer_size >= \
+ connection->read_buffer_offset);
+ if ((def_grow_size <= grow_size + left_free)
+ && (left_free < def_grow_size))
+ grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
+ else if (! required)
+ return false; /* Grow is not mandatory, leave some space in pool */
+ else
+ {
+ /* Shortage of space, but grow is mandatory */
+ const size_t small_inc =
+ ((mhd_BUF_INC_SIZE > def_grow_size) ?
+ def_grow_size : mhd_BUF_INC_SIZE) / 8;
+ if (small_inc < avail_size)
+ grow_size = small_inc;
+ else
+ grow_size = avail_size;
+ }
+ }
+ new_size = connection->read_buffer_size + grow_size;
+ }
+ /* Make sure that read buffer will not be moved */
+ if ((NULL != connection->read_buffer) &&
+ ! mhd_pool_is_resizable_inplace (connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size))
+ {
+ mhd_assert (0);
+ return false;
+ }
+ /* we can actually grow the buffer, do it! */
+ rb = mhd_pool_reallocate (connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size,
+ new_size);
+ if (NULL == rb)
+ {
+ /* This should NOT be possible: we just computed 'new_size' so that
+ it should fit. If it happens, somehow our read buffer is not in
+ the right position in the pool, say because someone called
+ mhd_pool_allocate() without 'from_end' set to 'true'? Anyway,
+ should be investigated! (Ideally provide all data from
+ *pool and connection->read_buffer and new_size for debugging). */
+ mhd_assert (0);
+ return false;
+ }
+ mhd_assert (connection->read_buffer == rb);
+ connection->read_buffer = rb;
+ mhd_assert (NULL != connection->read_buffer);
+ connection->read_buffer_size = new_size;
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
+{
+ /**
+ * The increase of read buffer size is desirable.
+ */
+ bool rbuff_grow_desired;
+ /**
+ * The increase of read buffer size is a hard requirement.
+ */
+ bool rbuff_grow_required;
+
+ mhd_assert (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info));
+ mhd_assert (! c->discard_request);
+
+ rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
+ if (rbuff_grow_required)
+ rbuff_grow_desired = true;
+ else
+ {
+ rbuff_grow_desired = (c->read_buffer_offset + 1536 > // TODO: remove handcoded buffer grow size
+ c->read_buffer_size);
+
+ if ((rbuff_grow_desired) &&
+ (MHD_CONNECTION_BODY_RECEIVING == c->state))
+ {
+ if (! c->rq.have_chunked_upload)
+ {
+ mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size);
+ /* Do not grow read buffer more than necessary to process the current
+ request. */
+ rbuff_grow_desired =
+ (c->rq.cntn.cntn_size - c->rq.cntn.recv_size > c->read_buffer_size); // FIXME
+ }
+ else
+ {
+ mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
+ if (0 == c->rq.current_chunk_size)
+ rbuff_grow_desired = /* Reading value of the next chunk size */
+ (MHD_CHUNK_HEADER_REASONABLE_LEN >
+ c->read_buffer_size);
+ else
+ {
+ const uint_fast64_t cur_chunk_left =
+ c->rq.current_chunk_size - c->rq.current_chunk_offset;
+ /* Do not grow read buffer more than necessary to process the current
+ chunk with terminating CRLF. */
+ mhd_assert (c->rq.current_chunk_offset <= c->rq.current_chunk_size);
+ rbuff_grow_desired =
+ ((cur_chunk_left + 2) > (uint_fast64_t) (c->read_buffer_size));
+ }
+ }
+ }
+ }
+
+ if (! rbuff_grow_desired)
+ return true; /* No need to increase the buffer */
+
+ if (try_grow_read_buffer (c, rbuff_grow_required))
+ return true; /* Buffer increase succeed */
+
+ if (! rbuff_grow_required)
+ return true; /* Can continue without buffer increase */
+
+ /* Failed to increase the read buffer size, but need to read the data
+ from the network.
+ No more space left in the buffer, no more space to increase the buffer. */
+
+ if (1)
+ {
+ enum MHD_ProcRecvDataStage stage;
+
+ switch (c->state)
+ {
+ case MHD_CONNECTION_INIT:
+ stage = MHD_PROC_RECV_INIT;
+ break;
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
+ if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd)
+ stage = MHD_PROC_RECV_METHOD;
+ else if (0 == c->rq.req_target_len)
+ stage = MHD_PROC_RECV_URI;
+ else
+ stage = MHD_PROC_RECV_HTTPVER;
+ break;
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
+ stage = MHD_PROC_RECV_HEADERS;
+ break;
+ case MHD_CONNECTION_BODY_RECEIVING:
+ stage = c->rq.have_chunked_upload ?
+ MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL;
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
+ stage = MHD_PROC_RECV_FOOTERS;
+ break;
+ case MHD_CONNECTION_REQ_LINE_RECEIVED:
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ case MHD_CONNECTION_BODY_RECEIVED:
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ case MHD_CONNECTION_REQ_RECV_FINISHED:
+ case MHD_CONNECTION_START_REPLY:
+ case MHD_CONNECTION_HEADERS_SENDING:
+ case MHD_CONNECTION_HEADERS_SENT:
+ case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
+ case MHD_CONNECTION_UNCHUNKED_BODY_READY:
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ case MHD_CONNECTION_CHUNKED_BODY_SENT:
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ case MHD_CONNECTION_FULL_REPLY_SENT:
+ case MHD_CONNECTION_CLOSED:
+#if 0 // def UPGRADE_SUPPORT // TODO: Upgrade support
+ case MHD_CONNECTION_UPGRADE:
+#endif
+ default:
+ mhd_assert (0);
+ MHD_UNREACHABLE_;
+ stage = MHD_PROC_RECV_BODY_NORMAL;
+ }
+
+ handle_recv_no_space (c, stage);
+ }
+ return false;
+}
diff --git a/src/mhd2/stream_process_request.h b/src/mhd2/stream_process_request.h
@@ -0,0 +1,200 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2014-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/stream_process_request.h
+ * @brief The declarations of internal functions for requests parsing
+ * and processing
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
+ * contributors.
+ */
+
+#ifndef MHD_STREAM_PROCESS_REQUEST_H
+#define MHD_STREAM_PROCESS_REQUEST_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_str_types.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Callback for iterating over GET parameters
+ * @param cls the iterator metadata
+ * @param name the name of the parameter
+ * @param value the value of the parameter
+ * @return bool to continue iterations,
+ * false to stop the iteration
+ */
+typedef bool
+(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3)
+ *mhd_GetArgumentInter)(void *restrict cls,
+ const struct MHD_String *restrict name,
+ const struct MHD_StringNullable *restrict value);
+
+/**
+ * Parse and unescape the arguments given by the client
+ * as part of the HTTP request URI.
+ *
+ * @param args_len the function to call on each key-value pair found
+ * @param[in,out] args argument URI string (after "?" in URI),
+ * clobbered in the process!
+ * @param cb function to call on each key-value pair found
+ * @param cls the iterator context
+ * @param[out] enc_broken the pointer to get
+ * @return false on failure
+ * true on success (parsing succeeded, @a cb always returned true)
+ */
+MHD_INTERNAL bool
+mhd_parse_get_args (size_t args_len,
+ char *restrict args,
+ mhd_GetArgumentInter cb,
+ void *restrict cls)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_INOUT_ (2);
+
+
+/**
+ * Find and parse the request line.
+ * @param c the connection to process
+ * @return true if request line completely processed (or unrecoverable error
+ * found) and state is changed,
+ * false if not enough data yet in the receive buffer
+ */
+MHD_INTERNAL bool
+mhd_stream_get_request_line (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Switch to request headers (field lines) processing state.
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Reset request header processing state.
+ *
+ * This function resets the processing state before processing the header
+ * (or footer) line.
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_stream_reset_rq_hdr_proc_state (struct MHD_Connection *c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Find the end of the request GET parameters.
+ * Advance to the next state when done, handle errors.
+ * @param c the connection to process
+ * @param process_footers if true then footers are processed,
+ * if false then headers are processed
+ * @return true if request headers reading finished (either successfully
+ * or with error),
+ * false if not enough data yet in the receive buffer
+ */
+MHD_INTERNAL bool
+mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
+ bool process_footers)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Parse the various request headers; figure out the size of the upload and
+ * make sure the headers follow the protocol.
+ * Advance to the appropriate state.
+ *
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Call application request handling callback, process action given by app.
+ * Advance to the next state when done, handle errors.
+ * @param c the connection to process
+ * @return true if advanced to the next state and the next state could
+ * be processes right now,
+ * false if connection is suspended or aborted or more data needed
+ * to process the next state
+ */
+MHD_INTERNAL bool
+mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Process non-chunked request body or upload chunking encoding.
+ * Call the upload handler of the application.
+ * Advance to the next state when done, handle errors.
+ *
+ * @param c the connection to process
+ * @return true if advanced to the next state,
+ * false if more data needed or connection is suspended or aborted
+ */
+MHD_INTERNAL bool
+mhd_stream_process_request_body (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Call application final upload callback, process action given by app.
+ * Advance to the next state, handle errors.
+ * @param c the connection to process
+ * @return true if advanced to the next state,
+ * false if connection is suspended or aborted
+ */
+MHD_INTERNAL bool
+mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Process finalisation of request receiving.
+ * Advance to the next state, handle errors.
+ * @param c the connection to process
+ * @return true if advanced to the next state,
+ * false if connection is suspended or aborted
+ */
+MHD_INTERNAL bool
+mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Check whether enough space is available in the read buffer for the next
+ * operation.
+ * Handles grow of the buffer if required and error conditions (when buffer
+ * grow is required but not possible).
+ * Must be called only when processing the event loop states and when
+ * reading is required for the next phase.
+ * @param c the connection to check
+ * @return true if connection handled successfully and enough buffer
+ * is available,
+ * false if not enough buffer is available and the loop's states
+ * must be processed again as connection is in the error state.
+ */
+MHD_INTERNAL bool
+mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_STREAM_PROCESS_REQUEST_H */
diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c
@@ -0,0 +1,503 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2014-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/stream_process_states.h
+ * @brief The definitions of internal functions for processing
+ * stream states
+ * @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 "sys_bool_type.h"
+
+#include "mhd_str_macros.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+#include "mhd_response.h"
+
+#include "stream_process_states.h"
+#include "stream_funcs.h"
+#include "stream_process_request.h"
+#include "stream_process_reply.h"
+
+#include "conn_mark_ready.h"
+
+/**
+ * Update current processing state: need to receive, need to send.
+ * Mark stream as ready or not ready for processing.
+ * Grow the receive buffer if neccesary, close stream if no buffer space left,
+ * but connection needs to receive.
+ * @param c the connection to update
+ * @return true if connection states updated successfully,
+ * false if connection has been prepared for closing
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+update_active_state (struct MHD_Connection *restrict c)
+{
+ /* Do not update states of suspended connection */
+ mhd_assert (! c->suspended);
+
+ if (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY))
+ {
+ mhd_assert (0 && "Should be handled earlier");
+ mhd_conn_pre_close_skt_err (c);
+ return false;
+ }
+
+#if 0 // def HTTPS_SUPPORT // TODO: implement TLS support
+ if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
+ { /* HTTPS connection. */
+ switch (connection->tls_state)
+ {
+ }
+ }
+#endif /* HTTPS_SUPPORT */
+ while (1)
+ {
+ switch (c->state)
+ {
+ case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ break;
+ case MHD_CONNECTION_REQ_LINE_RECEIVED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ break;
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+ break;
+ case MHD_CONNECTION_BODY_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ break;
+ case MHD_CONNECTION_BODY_RECEIVED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ mhd_assert (0 && "Should not be possible");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ break;
+ case MHD_CONNECTION_REQ_RECV_FINISHED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_START_REPLY:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_HEADERS_SENDING:
+ /* headers in buffer, keep writing */
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
+ mhd_assert (0 && "Should not be possible");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ break;
+ case MHD_CONNECTION_UNCHUNKED_BODY_READY:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ mhd_assert (0 && "Should not be possible");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_SENT:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+ break;
+ case MHD_CONNECTION_FULL_REPLY_SENT:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_CLOSED:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ return false; /* do nothing, not even reading */
+#if 0 // def UPGRADE_SUPPORT // TODO: Upgrade support
+ case MHD_CONNECTION_UPGRADE:
+ mhd_assert (0);
+ break;
+#endif /* UPGRADE_SUPPORT */
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ }
+
+ if (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
+ {
+ /* Check whether the space is available to receive data */
+ if (! mhd_stream_check_and_grow_read_buffer_space (c))
+ {
+ mhd_assert (c->discard_request);
+ continue;
+ }
+ }
+
+ /* Current MHD design assumes that data must be always processes when
+ * available. If it is not possible, connection must be suspended. */
+ mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
+
+ /* Sockets errors must be already handled */
+ mhd_assert (0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY));
+
+ if (0 !=
+ (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info)
+ & (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_WRITE)))
+ mhd_conn_mark_ready (c, c->daemon);
+ else
+ mhd_conn_mark_unready (c, c->daemon);
+
+ break; /* Everything was processed. */
+ }
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_conn_process_data (struct MHD_Connection *restrict c)
+{
+ struct MHD_Daemon *const d = c->daemon;
+ bool daemon_closing;
+
+ /* 'daemon' is not used if epoll is not available and asserts are disabled */
+ (void) d; /* Mute compiler warning */
+
+ if ((c->sk_rmt_shut_wr) && (MHD_CONNECTION_START_REPLY > c->state))
+ {
+ if (0 == c->read_buffer_offset)
+ { /* Read buffer is empty, connection state is actual */
+ mhd_conn_pre_close (c,
+ (MHD_CONNECTION_INIT == c->state) ?
+ mhd_CONN_CLOSE_HTTP_COMPLETED :
+ mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
+ NULL);
+ return false;
+ }
+ }
+
+ mhd_assert (c->resuming || ! c->suspended);
+ if (c->resuming)
+ {
+ // TODO: finish resuming, update activity mark
+ // TODO: move to special function
+ }
+
+ if ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) ||
+ (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)))
+ {
+ mhd_assert ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) || \
+ mhd_SOCKET_ERR_IS_HARD (c->sk_discnt_err));
+ if ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) ||
+ (mhd_SOCKET_ERR_NOT_CHECKED == c->sk_discnt_err))
+ c->sk_discnt_err = mhd_socket_error_get_from_socket (c->socket_fd);
+ mhd_conn_pre_close_skt_err (c);
+ return false;
+ }
+
+ daemon_closing = (mhd_DAEMON_STATE_STOPPING == d->state);
+#ifdef MHD_USE_THREADS
+ daemon_closing = daemon_closing || d->threading.stop_requested;
+#endif /* MHD_USE_THREADS */
+ if (daemon_closing)
+ {
+ mhd_conn_pre_close_d_shutdown (c);
+ return false;
+ }
+
+ while (true) // TODO: support suspend
+ {
+#ifdef HTTPS_SUPPORT
+ // TODO: support TLS, handshake
+#endif /* HTTPS_SUPPORT */
+ switch (c->state)
+ {
+ case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
+ if (mhd_stream_get_request_line (c))
+ {
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
+ mhd_assert ((MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver)) \
+ || (c->discard_request));
+ continue;
+ }
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING >= c->state);
+ break;
+ case MHD_CONNECTION_REQ_LINE_RECEIVED:
+ mhd_stream_switch_to_rq_headers_proc (c);
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED != c->state);
+ continue;
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
+ if (mhd_stream_get_request_headers (c, false))
+ {
+ mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < c->state);
+ mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == c->state) || \
+ (c->discard_request));
+ continue;
+ }
+ mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state);
+ break;
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ mhd_stream_parse_request_headers (c);
+ mhd_assert (c->state != MHD_CONNECTION_HEADERS_RECEIVED);
+ continue;
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ if (mhd_stream_call_app_request_cb (c))
+ {
+ mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED < c->state);
+ continue;
+ }
+ // TODO: add assert
+ break;
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ if (c->continue_message_write_offset ==
+ mhd_SSTR_LEN (mdh_HTTP_1_1_100_CONTINUE_REPLY))
+ {
+ c->state = MHD_CONNECTION_BODY_RECEIVING;
+ continue;
+ }
+ break;
+ case MHD_CONNECTION_BODY_RECEIVING:
+ mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size);
+ mhd_assert (! c->discard_request);
+ mhd_assert (NULL == c->rp.response);
+ if (0 == c->read_buffer_offset)
+ break; /* Need more data to process */
+
+ if (mhd_stream_process_request_body (c))
+ continue;
+ mhd_assert (! c->discard_request);
+ mhd_assert (NULL == c->rp.response);
+ break;
+ case MHD_CONNECTION_BODY_RECEIVED:
+ mhd_assert (! c->discard_request);
+ mhd_assert (NULL == c->rp.response);
+ mhd_assert (c->rq.have_chunked_upload);
+ /* Reset counter variables reused for footers */
+ c->rq.num_cr_sp_replaced = 0;
+ c->rq.skipped_broken_lines = 0;
+ mhd_stream_reset_rq_hdr_proc_state (c);
+ c->state = MHD_CONNECTION_FOOTERS_RECEIVING;
+ continue;
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
+ mhd_assert (c->rq.have_chunked_upload);
+ if (mhd_stream_get_request_headers (c, true))
+ {
+ mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < c->state);
+ mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == c->state) || \
+ (c->discard_request));
+ continue;
+ }
+ mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING == c->state);
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ mhd_assert (c->rq.have_chunked_upload);
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ continue;
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ if (mhd_stream_call_app_final_upload_cb (c))
+ {
+ mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING != c->state);
+ continue;
+ }
+ break;
+ case MHD_CONNECTION_REQ_RECV_FINISHED:
+ if (mhd_stream_process_req_recv_finished (c))
+ continue;
+ break;
+ // TODO: add stage for setup and full request buffers cleanup
+ case MHD_CONNECTION_START_REPLY:
+ mhd_assert (NULL != c->rp.response);
+ mhd_stream_switch_from_recv_to_send (c);
+ if (! mhd_stream_build_header_response (c))
+ break;
+ mhd_assert (MHD_CONNECTION_START_REPLY != c->state);
+ break;
+ case MHD_CONNECTION_HEADERS_SENDING:
+ /* no default action, wait for sending all the headers */
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+#if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
+ if (NULL != c->rp.response->upgrade_handler)
+ {
+ mhd_assert (0 && "Not implemented yet");
+ c->state = MHD_CONNECTION_UPGRADE;
+ /* This connection is "upgraded". Pass socket to application. */
+ if (MHD_NO ==
+ MHD_response_execute_upgrade_ (c->rp.response,
+ connection))
+ {
+ /* upgrade failed, fail hard */
+ CONNECTION_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ /* Response is not required anymore for this connection. */
+ if (1)
+ {
+ struct MHD_Response *const resp = c->rp.response;
+
+ c->rp.response = NULL;
+ MHD_destroy_response (resp);
+ }
+ continue;
+ }
+#endif /* UPGRADE_SUPPORT */
+
+ if (c->rp.props.send_reply_body)
+ {
+ if (c->rp.props.chunked)
+ c->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+ else
+ c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY;
+ }
+ else
+ c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+ continue;
+ case MHD_CONNECTION_UNCHUNKED_BODY_READY:
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (! c->rp.props.chunked);
+ /* nothing to do here, send the data */
+ break;
+ case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (! c->rp.props.chunked);
+ if (0 == c->rp.response->cntn_size)
+ { /* a shortcut */
+ c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+ continue;
+ }
+ if (mhd_stream_prep_unchunked_body (c))
+ continue;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (c->rp.props.chunked);
+ /* nothing to do here */
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (c->rp.props.chunked);
+ if ( (0 == c->rp.response->cntn_size) ||
+ (c->rp.rsp_cntn_read_pos ==
+ c->rp.response->cntn_size) )
+ {
+ c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
+ continue;
+ }
+ if (mhd_stream_prep_chunked_body (c))
+ continue;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_SENT:
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (c->rp.props.chunked);
+ mhd_assert (c->write_buffer_send_offset <= \
+ c->write_buffer_append_offset);
+ mhd_stream_call_dcc_cleanup_if_needed (c);
+ mhd_stream_prep_chunked_footer (c);
+ break;
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ mhd_assert (c->rp.props.send_reply_body);
+ mhd_assert (c->rp.props.chunked);
+ /* no default action */
+ break;
+ case MHD_CONNECTION_FULL_REPLY_SENT:
+ // FIXME: support MHD_HTTP_STATUS_PROCESSING ?
+ /* Reset connection after complete reply */
+ mhd_stream_finish_req_serving ( \
+ c,
+ mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse
+ && ! c->discard_request
+ && ! c->sk_rmt_shut_wr);
+ continue;
+ case MHD_CONNECTION_CLOSED:
+ break;
+#if 0 // def UPGRADE_SUPPORT
+ case MHD_CONNECTION_UPGRADE:
+ return MHD_YES; /* keep open */
+#endif /* UPGRADE_SUPPORT */
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ }
+ break;
+ }
+
+ if (MHD_CONNECTION_CLOSED == c->state)
+ return false;
+
+ if (c->suspended)
+ {
+ // TODO: process
+ mhd_assert (0 && "Not implemented yet");
+ return true;
+ }
+
+ if ((c->sk_rmt_shut_wr) && (MHD_CONNECTION_START_REPLY > c->state))
+ {
+ mhd_conn_pre_close (c,
+ (MHD_CONNECTION_INIT == c->state) ?
+ mhd_CONN_CLOSE_HTTP_COMPLETED :
+ mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
+ NULL);
+ return false;
+ }
+
+ if (mhd_stream_check_timedout (c)) // TODO: centralise timeout checks
+ {
+ mhd_conn_pre_close_timedout (c);
+ return false;
+ }
+ update_active_state (c);
+ /* MHD_connection_update_event_loop_info (c);*/
+
+ return true;
+}
diff --git a/src/mhd2/stream_process_states.h b/src/mhd2/stream_process_states.h
@@ -0,0 +1,48 @@
+/*
+ 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/stream_process_states.h
+ * @brief The declarations of internal functions for processing
+ * stream states
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STREAM_PROCESS_STATES_H
+#define MHD_STREAM_PROCESS_STATES_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Process states and the data for the connection
+ * For HTTP/1.1 connection is equal stream
+ * @param c the connection to process
+ * @return true if states and data has been successfully processed,
+ * false if connection needs to be closed
+ */
+MHD_INTERNAL bool
+mhd_conn_process_data (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_STREAM_PROCESS_STATES_H */
diff --git a/src/mhd2/sys_base_types.h b/src/mhd2/sys_base_types.h
@@ -0,0 +1,79 @@
+/*
+ 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_base_types.h
+ * @brief The header for basic system types and the NULL constant
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header should provide macros or typedefs for uint_fastXX_t, int_fastXX_t
+ * size_t, ssize_t and NULL.
+ */
+
+#ifndef MHD_SYS_BASE_TYPES_H
+#define MHD_SYS_BASE_TYPES_H 1
+
+#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 */
+#elif defined(HAVE_STDLIB_H)
+# include <stdlib.h> /* should provide size_t, NULL */
+#else
+# include <stdio.h> /* should provide size_t, NULL */
+#endif
+#ifdef HAVE_CRTDEFS_H
+# include <crtdefs.h> /* W32-specific header */
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#ifndef HAVE_SSIZE_T
+# if defined(HAVE_PTRDIFF_T)
+typedef ptrdiff_t ssize_t;
+# elif defined(HAVE_INTPTR_T)
+/* Not an ideal choice, the size of the largest allocation may be smaller
+ than the total size of the addressable memory. */
+typedef intptr_t ssize_t;
+# else
+# error Cannot find suitable 'ssize_t' replacement
+# endif
+# define HAVE_SSIZE_T 1
+# ifdef _WIN32
+# define _SSIZE_T_DEFINED
+# endif
+#endif /* ! HAVE_SSIZE_T */
+
+#ifndef PRIuFAST64
+# ifdef PRIu64
+# define PRIuFAST64 PRIu64
+# else
+# define PRIuFAST64 "llu"
+# endif
+#endif
+
+#endif /* ! MHD_SYS_BASE_TYPES_H */
diff --git a/src/mhd2/sys_bool_type.h b/src/mhd2/sys_bool_type.h
@@ -0,0 +1,38 @@
+/*
+ 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_bool_type.h
+ * @brief The header for the system 'bool' type
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header provides 'bool' type and 'true' and 'false' values.
+ */
+
+#ifndef MHD_BASE_BOOL_TYPE_H
+#define MHD_BASE_BOOL_TYPE_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+
+#endif /* ! MHD_BASE_BOOL_TYPE_H */
diff --git a/src/mhd2/sys_errno.h b/src/mhd2/sys_errno.h
@@ -0,0 +1,99 @@
+/*
+ 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 EBADF
+# define mhd_EBADF_OR_ZERO EBADF
+#else
+# define mhd_EBADF_OR_ZERO (0)
+#endif
+
+#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
+
+#ifdef ENOMEM
+# define mhd_ENOMEM_OR_ZERO ENOMEM
+#else
+# define mhd_ENOMEM_OR_ZERO (0)
+#endif
+
+#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 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
+
+#endif /* ! MHD_SYS_ERRNO_H */
diff --git a/src/mhd2/sys_file_fd.h b/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 */
diff --git a/src/mhd2/sys_ip_headers.h b/src/mhd2/sys_ip_headers.h
@@ -0,0 +1,115 @@
+/*
+ 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
+
+#ifdef IPPROTO_TCP
+# if defined(TCP_CORK)
+/**
+ * Value of TCP_CORK or TCP_NOPUSH
+ */
+# define mhd_TCP_CORK_NOPUSH TCP_CORK
+# elif defined(TCP_NOPUSH)
+/**
+ * Value of TCP_CORK or TCP_NOPUSH
+ */
+# define mhd_TCP_CORK_NOPUSH TCP_NOPUSH
+# endif /* TCP_NOPUSH */
+#endif /* IPPROTO_TCP */
+
+#ifdef mhd_TCP_CORK_NOPUSH
+# ifdef __linux__
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ */
+# define mhd_CORK_RESET_PUSH_DATA 1
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ * even if TCP_CORK/TCP_NOPUSH was in switched off state.
+ */
+# define mhd_CORK_RESET_PUSH_DATA_ALWAYS 1
+#endif /* __linux__ */
+#if (defined(__FreeBSD__) && \
+ ((__FreeBSD__ + 0) >= 5 || (__FreeBSD_version + 0) >= 450000)) || \
+ (defined(__FreeBSD_kernel_version) && \
+ (__FreeBSD_kernel_version + 0) >= 450000)
+/* FreeBSD pushes data to the network with reset of TCP_NOPUSH
+ * starting from version 4.5. */
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ */
+#define mhd_CORK_RESET_PUSH_DATA 1
+#endif /* __FreeBSD_version >= 450000 */
+#ifdef __OpenBSD__
+/* OpenBSD took implementation from FreeBSD */
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ */
+#define mhd_CORK_RESET_PUSH_DATA 1
+#endif /* __OpenBSD__ */
+#endif /* MHD_TCP_CORK_NOPUSH */
+
+#ifdef __linux__
+/**
+ * Indicate that set of TCP_NODELAY push data to the network
+ */
+# define mhd_NODELAY_SET_PUSH_DATA 1
+/**
+ * Indicate that set of TCP_NODELAY push data to the network even
+ * if TCP_DELAY was already set and regardless of TCP_CORK / TCP_NOPUSH state
+ */
+# define mhd_NODELAY_SET_PUSH_DATA_ALWAYS 1
+#endif /* __linux__ */
+
+
+#endif /* ! MHD_SYS_IP_HEADERS_H */
diff --git a/src/mhd2/sys_malloc.h b/src/mhd2/sys_malloc.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_malloc.h
+ * @brief The wrapper header for malloc() and free() system declarations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SYS_MALLOC_H
+#define MHD_SYS_MALLOC_H 1
+
+#include "mhd_sys_options.h"
+
+#if defined(HAVE_STDLIB_H)
+# include <stdlib.h>
+#elif defined(HAVE_MALLOC_H)
+# include <malloc.h>
+#else
+/* Try some set of headers, hoping the right header is included */
+# if defined(HAVE_UNISTD_H)
+# include <unistd.h>
+# endif
+# include <stdio.h>
+# include <string.h>
+#endif
+
+#endif /* ! MHD_SYS_MALLOC_H */
diff --git a/src/mhd2/sys_null_macro.h b/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 */
diff --git a/src/mhd2/sys_poll.h b/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 */
diff --git a/src/mhd2/sys_select.h b/src/mhd2/sys_select.h
@@ -0,0 +1,61 @@
+/*
+ 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_select.h
+ * @brief The header for the system 'select()' function and related data types
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SYS_SELECT_H
+#define MHD_SYS_SELECT_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef MHD_USE_SELECT
+# include "mhd_socket_type.h"
+# if 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
+# elif defined(MHD_WINSOCK_SOCKETS)
+# include <winsock2.h>
+# else
+#error Uknown sockets type
+# endif
+
+#endif /* MHD_USE_SELECT */
+
+#endif /* ! MHD_SYS_SELECT_H */
diff --git a/src/mhd2/sys_sendfile.h b/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 */
diff --git a/src/mhd2/sys_sockets_headers.h b/src/mhd2/sys_sockets_headers.h
@@ -0,0 +1,165 @@
+/*
+ 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/un.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
+
+#ifdef MSG_MORE
+# ifdef __linux__
+/* MSG_MORE signal kernel to buffer outbond data and works like
+ * TCP_CORK per call without actually setting TCP_CORK value.
+ * It's known to work on Linux. Add more OSes if they are compatible. */
+/**
+ * Indicate MSG_MORE is usable for buffered send().
+ */
+# define mhd_USE_MSG_MORE 1
+# endif /* __linux__ */
+#endif /* MSG_MORE */
+
+#ifdef mhd_USE_MSG_MORE
+# define mhd_MSG_MORE MSG_MORE
+#else
+# define mhd_MSG_MORE (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
+
+
+#if defined(MHD_socket_nosignal_) || \
+ (defined(SOL_SOCKET) && defined(SO_NOSIGPIPE))
+/**
+ * Indicate that SIGPIPE can be suppressed by MHD for normal send() by flags
+ * or socket options.
+ * If this macro is undefined, MHD cannot suppress SIGPIPE for socket functions
+ * so sendfile() or writev() calls are avoided in application threads.
+ */
+# define mhd_SEND_SPIPE_SUPPRESS_POSSIBLE 1
+#endif /* MHD_WINSOCK_SOCKETS || MHD_socket_nosignal_ || MSG_NOSIGNAL */
+
+
+#if ! defined(MHD_WINSOCK_SOCKETS)
+/**
+ * Indicate that suppression of SIGPIPE is required for some network
+ * system calls.
+ */
+# define mhd_SEND_SPIPE_SUPPRESS_NEEDED 1
+#endif
+
+
+#endif /* ! MHD_SYS_SOCKETS_HEADERS_H */
diff --git a/src/mhd2/sys_sockets_types.h b/src/mhd2/sys_sockets_types.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/sys_sockets_types.h
+ * @brief The header for system types related to sockets
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This header should provide declaration of 'struct sockaddr' and
+ * socklen_t (if it is present in headers).
+ */
+
+#ifndef MHD_SYS_SOCKETS_TYPES_H
+#define MHD_SYS_SOCKETS_TYPES_H 1
+
+#include "mhd_sys_options.h"
+
+#include "mhd_socket_type.h"
+
+#ifdef MHD_POSIX_SOCKETS
+# ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+# else
+# include <sys/types.h> /* bad fallback */
+# ifdef HAVE_SOCKLIB_H
+# include <sockLib.h>
+# endif
+# endif
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#endif
+
+#endif /* ! MHD_SYS_SOCKETS_TYPES_H */
diff --git a/src/mhd2/sys_thread_entry_type.h b/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 */
diff --git a/src/mhd2/w32_lib_res.rc.in b/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, @W32CRT@ run-time lib)\0"
+#endif
+ VALUE "InternalName", "libmicrohttpd2\0"
+#ifdef DLL_EXPORT
+ VALUE "OriginalFilename", "libmicrohttpd2-@MHD_W32_DLL_SUFF@.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
+
diff --git a/src/tests/.gitignore b/src/tests/.gitignore
@@ -0,0 +1,3 @@
+test_*[a-z0-9_][a-z0-9_][a-z0-9_]
+!*.c
+!*.h
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
@@ -0,0 +1,5 @@
+# This Makefile.am is in the public domain
+
+SUBDIRS = basic
+
+.NOTPARALLEL:
diff --git a/src/tests/basic/Makefile.am b/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 \
+ -I$(top_srcdir)/src/mhd2 \
+ -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_pool_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_pool_ipv4_SOURCES = $(basic_test_sources)
diff --git a/src/tests/basic/test_basic_checks.c b/src/tests/basic/test_basic_checks.c
@@ -0,0 +1,362 @@
+/*
+ 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_basic_checks.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;
+ 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;
+}