diff --git a/Makefile.am b/Makefile.am index 314916c1..c36d601b 100644 --- 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 diff --git a/configure.ac b/configure.ac index bb27aac8..b4a8df9f 100644 --- a/configure.ac +++ b/configure.ac @@ -23,10 +23,10 @@ # AC_PREREQ([2.64]) LT_PREREQ([2.4.0]) -AC_INIT([GNU libmicrohttpd],[1.0.1],[libmicrohttpd@gnu.org]) +AC_INIT([GNU libmicrohttpd],[1.99.0],[libmicrohttpd@gnu.org]) AC_CONFIG_AUX_DIR([build-aux]) MHD_AUX_DIR='build-aux' # Must be set to the same value as in the previous line -AC_CONFIG_HEADERS([MHD_config.h]) +AC_CONFIG_HEADERS([src/incl_priv/mhd_config.h]) AC_CONFIG_MACRO_DIR([m4]) m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl @@ -46,7 +46,7 @@ AC_SUBST([PACKAGE_VERSION_MAJOR]) AC_SUBST([PACKAGE_VERSION_MINOR]) AC_SUBST([PACKAGE_VERSION_SUBMINOR]) AC_SUBST([MHD_W32_DLL_SUFF]) -AC_CONFIG_FILES([src/microhttpd/microhttpd_dll_res.rc]) +AC_CONFIG_FILES([src/mhd2/w32_lib_res.rc]) MHD_LIB_CPPFLAGS="" MHD_LIB_CFLAGS="" @@ -485,7 +485,7 @@ AC_CHECK_HEADERS_ONCE([stdio.h string.h stdint.h stdarg.h errno.h limits.h fcntl # Check for basic optional headers AC_CHECK_HEADERS([stddef.h stdlib.h inttypes.h sys/types.h sys/stat.h unistd.h \ - sys/uio.h crtdefs.h malloc.h], [], [], [AC_INCLUDES_DEFAULT]) + sys/uio.h crtdefs.h malloc.h io.h], [], [], [AC_INCLUDES_DEFAULT]) # Check for clock-specific optional headers AC_CHECK_HEADERS([sys/time.h time.h], [], [], [AC_INCLUDES_DEFAULT]) @@ -1801,7 +1801,7 @@ AS_IF([test "x$USE_THREADS" = "xposix"], [AC_DEFINE([MHD_USE_W32_THREADS],[1],[define to use W32 threads])])]) AM_CONDITIONAL([USE_POSIX_THREADS], [test "x$USE_THREADS" = "xposix"]) AM_CONDITIONAL([USE_W32_THREADS], [test "x$USE_THREADS" = "xw32"]) -AM_CONDITIONAL([USE_THREADS], [test "x$USE_THREADS" != "xnone"]) +AM_CONDITIONAL([MHD_USE_THREADS], [test "x$USE_THREADS" != "xnone"]) AM_CONDITIONAL([DISABLE_THREADS], [test "x$USE_THREADS" = "xnone"]) AC_MSG_RESULT([$USE_THREADS]) @@ -2595,14 +2595,14 @@ AC_CACHE_CHECK([whether socket value is a signed type],[mhd_cv_socket_signed], /* Keep in sync with microhttpd.h */ #if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET) -typedef int MHD_socket; +typedef int MHD_Socket; #else /* defined(_WIN32) && ! defined(_SYS_TYPES_FD_SET) */ -typedef SOCKET MHD_socket; +typedef SOCKET MHD_Socket; #endif /* defined(_WIN32) && ! defined(_SYS_TYPES_FD_SET) */ int main(void) { - int test_arr[2 - 5*(!!(0 < ((MHD_socket)-1)))]; + int test_arr[2 - 5*(!!(0 < ((MHD_Socket)-1)))]; test_arr[1] = 0; return test_arr[1]; } @@ -2710,14 +2710,14 @@ AS_VAR_IF([mhd_cv_macro___func___avail], ["yes"], ) AC_CACHE_CHECK([[whether __builtin_bswap32() is available]], [[mhd_cv_func___builtin_bswap32_avail]], [dnl - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include]], [[uint32_t a = 1; uint32_t b = __builtin_bswap32(a); a = b; (void) a;]])], + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include]], [[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]], [[uint64_t a = 1; uint64_t b = __builtin_bswap64(a); a = b; (void) a;]])], + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include]], [[uint_fast64_t a = 1; uint_fast64_t b = __builtin_bswap64(a); a = b; (void) a;]])], [[mhd_cv_func___builtin_bswap64_avail="yes"]], [[mhd_cv_func___builtin_bswap64_avail="no"]]) ]) AS_IF([[test "x$mhd_cv_func___builtin_bswap64_avail" = "xyes"]], @@ -2795,70 +2795,163 @@ AM_CONDITIONAL([HEAVY_TESTS],[test "x$use_heavy_tests" = "xyes"]) AM_CONDITIONAL([VHEAVY_TESTS],[test "x$use_vheavy_tests" = "xyes"]) AM_CONDITIONAL([TESTS_STRESS_OS],[false]) -AC_ARG_ENABLE([[poll]], - [AS_HELP_STRING([[--enable-poll[=ARG]]], [enable poll support (yes, no, auto) [auto]])], - [enable_poll=${enableval}], - [enable_poll='auto'] - ) - -AS_IF([test "$enable_poll" != "no"], +AC_ARG_ENABLE([[select]], [ - AS_IF([test "$os_is_native_w32" != "yes"], + AS_HELP_STRING([[--enable-select[=ARG]]], [enable 'select()' support (yes, no, auto) [auto]]) + ],[],[enable_select='auto'] +) +AC_CACHE_CHECK([for select() function],[mhd_cv_func_select], + [ + AS_IF([test "x$os_is_native_w32" = "xyes"], + [mhd_cv_func_select="yes"], [ - AC_CHECK_HEADERS([poll.h], + AC_LINK_IFELSE( [ - MHD_CHECK_FUNC([poll], - [[ + AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SELECTLIB_H +#include +#endif + +int main(void) +{ + int res; + fd_set rfds; + fd_set wfds; + fd_set efds; + struct timeval tv_to; + + tv_to.tv_sec = 1; + tv_to.tv_usec = 0; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + FD_SET(1, &wfds); + + res = select(2, &rfds, &wfds, &efds, &tv_to); + if (res < 0) + return 3; + + FD_CLR(1, &efds); + if (FD_ISSET(1, &efds) + return 4; + + return 0; +} + ]] + ) + ], + [mhd_cv_func_select="yes"], + [mhd_cv_func_select="no"] + ) + ] + ) + ] +) +AS_VAR_IF([mhd_cv_func_select],["yes"], + [ + AC_DEFINE([[HAVE_SELECT]],[[1]],[Define to '1' if select() is supported on your platform]) + AS_IF([test "x$enable_select" != "xno"], + [ + enable_select="yes" + AC_DEFINE([[MHD_USE_SELECT]],[[1]],[Define to '1' to enable use of select() system call]) + ] + ) + ], + [ + AS_VAR_IF([enable_select],["yes"], + [AC_MSG_ERROR([Support for select() was explicitly requested but cannot be enabled on this platform.])] + ) + ] +) +AM_CONDITIONAL([MHD_USE_SELECT],[test "x${enable_select}" = "xyes"]) + +AC_ARG_ENABLE([[poll]], + [ + AS_HELP_STRING([[--enable-poll[=ARG]]], [enable 'poll()' support (yes, no, auto) [auto]]) + ],[],[enable_poll='auto'] +) +AS_IF([test "$os_is_native_w32" != "yes"], + [ + AC_CHECK_HEADERS([poll.h],[AC_INCLUDES_DEFAULT]) + MHD_CHECK_FUNC([poll], + [[ #include - ]], - [[ + ]], + [[ struct pollfd fds[2]; fds[0].fd = 0; fds[0].events = POLLIN; if (0 > poll(fds, 1, 0)) return 2; - ]], - [have_poll='yes'], [have_poll='no'] - ) - ], [], [AC_INCLUDES_DEFAULT] - ) - ], - [ - MHD_CHECK_FUNC([WSAPoll], - [[ + ]], + [have_poll='yes'],[have_poll='no'] + ) + ], + [ + MHD_CHECK_FUNC([WSAPoll], + [[ #include - ]], - [[ + ]], + [[ 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_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([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 +#endif +#ifdef HAVE_WINSOCK2_H +#include +#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_VAR_IF([enable_poll],["yes"], + [AC_MSG_ERROR([Support for poll() was explicitly requested but cannot be enabled on this platform.])] + ) + ] +) +AM_CONDITIONAL([MHD_USE_POLL],[test "x${enable_poll}" = "xyes"]) AC_ARG_ENABLE([[epoll]], - [AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]])], - [enable_epoll=${enableval}], - [enable_epoll='auto'] - ) - + [ + AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]]) + ],[],[enable_epoll='auto'] +) AS_IF([test "$enable_epoll" != "no"], [ AX_HAVE_EPOLL AS_IF([test "${ax_cv_have_epoll}" = "yes"], [ - AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support]) + AC_DEFINE([[HAVE_EPOLL]],[[1]],[Define to '1' if epoll is supported on your platform]) + AC_DEFINE([[MHD_USE_EPOLL]],[[1]],[Define to '1' to enable 'epoll' functionality]) enable_epoll='yes' ], [ @@ -2870,8 +2963,7 @@ AS_IF([test "$enable_epoll" != "no"], ) ] ) - -AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]]) +AM_CONDITIONAL([MHD_USE_EPOLL], [[test "x${enable_epoll}" = "xyes"]]) AS_IF([test "x$enable_epoll" = "xyes"], [ @@ -2964,8 +3056,8 @@ static struct timeval test_var; ) AC_DEFINE_UNQUOTED([SIZEOF_STRUCT_TIMEVAL_TV_SEC], [$mhd_cv_size_timeval_tv_sec], [The size of `tv_sec' member of `struct timeval', as computed by sizeof]) -AC_CHECK_SIZEOF([int64_t], [], [[#include ]]) -AC_CHECK_SIZEOF([uint64_t], [], [[#include ]]) +AC_CHECK_SIZEOF([int_fast64_t], [], [[#include ]]) +AC_CHECK_SIZEOF([uint_fast64_t], [], [[#include ]]) AC_CHECK_SIZEOF([int], [], [[#include ]]) AC_CHECK_SIZEOF([unsigned int], [], [[#include ]]) AC_CHECK_SIZEOF([unsigned long long], [], [[#include ]]) @@ -3384,7 +3476,7 @@ int main(void) [ use_itc='eventfd' enable_itc="$use_itc" - AC_DEFINE([[_MHD_ITC_EVENTFD]], [[1]], [Define to use eventFD for inter-thread communication]) + AC_DEFINE([[MHD_ITC_EVENTFD_]], [[1]], [Define to use eventFD for inter-thread communication]) ], [ AS_VAR_IF([[enable_itc]], [["eventfd"]], [AC_MSG_ERROR([[eventfd(2) is not usable, consider using other type of inter-thread communication]])]) @@ -3418,7 +3510,7 @@ AC_INCLUDES_DEFAULT AS_VAR_IF([[mhd_cv_pipe_usable]], [["yes"]], [ use_itc='pipe' enable_itc="$use_itc" - AC_DEFINE([[_MHD_ITC_PIPE]], [[1]], [Define to use pipe for inter-thread communication]) + AC_DEFINE([[MHD_ITC_PIPE_]], [[1]], [Define to use pipe for inter-thread communication]) MHD_CHECK_LINK_RUN([[whether pipe2(2) is usable]],[[mhd_cv_pipe2_usable]], [ # Cross-compiling @@ -3452,42 +3544,69 @@ AC_INCLUDES_DEFAULT ]) ]) -AS_IF([[test "x$enable_itc" = "xsocketpair" || test "x$enable_itc" = "xauto"]], [ - AS_VAR_IF([[os_is_native_w32]], [["yes"]], [[mhd_cv_socketpair_usable='yes']], [ - AC_CACHE_CHECK([[whether socketpair(3) is usable]], [[mhd_cv_socketpair_usable]], [ - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([ -AC_INCLUDES_DEFAULT +AS_IF([[test "x$enable_itc" = "xsocketpair" || test "x$enable_itc" = "xauto"]], + [ + AS_IF([test "x${os_is_native_w32}" = "xyes"], + [mhd_cv_socketpair_usable='yes'], + [test "x${mhd_cv_func_socketpair}" = "xyes"], + [ + AC_CACHE_CHECK([[whether socketpair(3) is usable]], [[mhd_cv_socketpair_usable]], + [ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM( + [[ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif - ], [[ - int arr[2]; - int res; -#if defined(AF_LOCAL) - res = socketpair(AF_LOCAL, SOCK_STREAM, 0, arr); -#elif defined(AF_UNIX) - res = socketpair(AF_UNIX, SOCK_STREAM, 0, arr); +#if defined(HAVE_UNISTD_H) +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif + ]], [[ + int arr[2]; + int res; +#if defined(AF_UNIX) + res = socketpair(AF_UNIX, SOCK_STREAM, 0, arr); +#elif defined(AF_LOCAL) + res = socketpair(AF_LOCAL, SOCK_STREAM, 0, arr); #else #error AF_LOCAL and AF_UNIX are both undefined - choke me now; + choke me now; #endif - if (res != 0) return 1 - ]]) - ], [[mhd_cv_socketpair_usable='yes']], [[mhd_cv_socketpair_usable='no']]) - ]) - ]) - AS_VAR_IF([[mhd_cv_socketpair_usable]], [["yes"]], [ - use_itc='socketpair' - enable_itc="$use_itc" - AC_DEFINE([[_MHD_ITC_SOCKETPAIR]], [[1]], [Define to use socketpair for inter-thread communication]) - ], [ - AS_VAR_IF([[enable_itc]], [["socketpair"]], [AC_MSG_ERROR([[socketpair(3) is not usable, consider using other type of inter-thread communication]])]) - ]) -]) + if (res != 0) return 1; + close (arr[1]); + close (arr[0]); + ]] + ) + ], + [[mhd_cv_socketpair_usable='yes']], + [[mhd_cv_socketpair_usable='no']] + ) + ] + ) + ], + [AS_UNSET([mhd_cv_socketpair_usable])] + ) + + AS_VAR_IF([[mhd_cv_socketpair_usable]], [["yes"]], + [ + use_itc='socketpair' + enable_itc="$use_itc" + AC_DEFINE([[MHD_ITC_SOCKETPAIR_]], [[1]], [Define to use socketpair for inter-thread communication]) + ], + [ + AS_VAR_IF([[enable_itc]], [["socketpair"]], [ + AC_MSG_ERROR([[socketpair(3) is not usable, consider using other type of inter-thread communication]])] + ) + ] + ) + ] +) AS_IF([[test -z "$use_itc"]], [AC_MSG_ERROR([[cannot find usable type of inter-thread communication]])]) @@ -3929,6 +4048,313 @@ int main(void) {return test_intern_func();} ] ) CFLAGS="${CFLAGS_ac} ${user_CFLAGS} ${errattr_CFLAGS}" +AC_CACHE_CHECK([whether $CC supports __attribute__ ((warn_unused_result))],[mhd_cv_cc_attr_warn_unused_res], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((warn_unused_result)) int +test_func(void) {return 0; } + +int main(void) { return test_func(); } + ]]) + ], + [mhd_cv_cc_attr_warn_unused_res="yes"],[mhd_cv_cc_attr_warn_unused_res="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_warn_unused_res],["yes"], + [ + AC_DEFINE([HAVE_ATTR_WARN_UNUSED_RES],[1], + [Define to '1' if your compiler supports __attribute__ ((warn_unused_result))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((returns_nonnull))],[mhd_cv_cc_attr_ret_nonnull], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((returns_nonnull)) int * +test_func(void) { + static int i = 0; + return &i; +} + +int main(void) { + return *(test_func()); +} + ]]) + ], + [mhd_cv_cc_attr_ret_nonnull="yes"],[mhd_cv_cc_attr_ret_nonnull="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_ret_nonnull],["yes"], + [ + AC_DEFINE([HAVE_ATTR_RET_NONNULL],[1], + [Define to '1' if your compiler supports __attribute__ ((returns_nonnull))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((nonnull))],[mhd_cv_cc_attr_nonnull], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((nonnull)) int +test_func(int *p) {return 0 == *p;} + +int main(void) { + int i = 0; + return test_func(&i) ? 0 : 5; +} + ]]) + ], + [mhd_cv_cc_attr_nonnull="yes"],[mhd_cv_cc_attr_nonnull="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_nonnull],["yes"], + [ + AC_DEFINE([HAVE_ATTR_NONNULL],[1], + [Define to '1' if your compiler supports __attribute__ ((nonnull))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((nonnull(NUM)))],[mhd_cv_cc_attr_nonnull_num], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((nonnull(2))) int +test_func(int *p1, int *p2) { + return (((int *)(void *)0) == p1) && (0 == *p2); +} + +int main(void) { + int i = 0; + return test_func( (int *)(void *)0, &i) ? 0 : 5; +} + ]]) + ], + [mhd_cv_cc_attr_nonnull_num="yes"],[mhd_cv_cc_attr_nonnull_num="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_nonnull_num],["yes"], + [ + AC_DEFINE([HAVE_ATTR_NONNULL_NUM],[1], + [Define to '1' if your compiler supports __attribute__ ((nonnull(NUM)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_only,NUM)))],[mhd_cv_cc_attr_access_read], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((access (read_only,1))) int +test_func(int *p) {return 0 == *p;} + +int main(void) { + int i = 0; + return test_func(&i) ? 0 : 5; +} + ]]) + ], + [mhd_cv_cc_attr_access_read="yes"],[mhd_cv_cc_attr_access_read="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_access_read],["yes"], + [ + AC_DEFINE([HAVE_ATTR_ACCESS_READ],[1], + [Define to '1' if your compiler supports __attribute__ ((access (read_only,NUM)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (write_only,NUM)))],[mhd_cv_cc_attr_access_write], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((access (write_only,1))) void +test_func(int *p) {*p = 0;} + +int main(void) { + int i = 1; + test_func(&i); + return i; +} + ]]) + ], + [mhd_cv_cc_attr_access_write="yes"],[mhd_cv_cc_attr_access_write="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_access_write],["yes"], + [ + AC_DEFINE([HAVE_ATTR_ACCESS_WRITE],[1], + [Define to '1' if your compiler supports __attribute__ ((access (write_only,NUM)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_write,NUM)))],[mhd_cv_cc_attr_access_read_write], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((access (read_write,1))) void +test_func(int *p) {*p = *p - 1;} + +int main(void) { + int i = 1; + test_func(&i); + return i; +} + ]]) + ], + [mhd_cv_cc_attr_access_read_write="yes"],[mhd_cv_cc_attr_access_read_write="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_access_read_write],["yes"], + [ + AC_DEFINE([HAVE_ATTR_ACCESS_READ_WRITE],[1], + [Define to '1' if your compiler supports __attribute__ ((access (read_write,NUM)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_only,NUM,NUM_SIZE)))],[mhd_cv_cc_attr_access_read_size], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((access (read_only,2,1))) int +test_func(unsigned int num, int *arr) {return 2 == arr[num - 1];} + +int main(void) { + int arr[4] = {5, 4, 3, 2}; + return test_func(4, arr) ? 0 : 5; +} + ]]) + ], + [mhd_cv_cc_attr_access_read_size="yes"],[mhd_cv_cc_attr_access_read_size="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_access_read_size],["yes"], + [ + AC_DEFINE([HAVE_ATTR_ACCESS_READ_SIZE],[1], + [Define to '1' if your compiler supports __attribute__ ((access (read_only,NUM,NUM_SIZE)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (write_only,NUM,NUM_SIZE)))],[mhd_cv_cc_attr_access_write_size], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((access (write_only,2,1))) void +test_func(unsigned int num, int *arr) {arr[num-1] = 0;} + +int main(void) { + int arr[4] = {5, 4, 3, 2}; + test_func(4, arr); + return arr[3]; +} + ]]) + ], + [mhd_cv_cc_attr_access_write_size="yes"],[mhd_cv_cc_attr_access_write_size="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_access_write_size],["yes"], + [ + AC_DEFINE([HAVE_ATTR_ACCESS_WRITE_SIZE],[1], + [Define to '1' if your compiler supports __attribute__ ((access (write_only,NUM,NUM_SIZE)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((access (read_write,NUM,NUM_SIZE)))],[mhd_cv_cc_attr_access_read_write_size], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((access (read_write,2,1))) void +test_func(unsigned int num, int *arr) {arr[num-1] = arr[1] - 4;} + +int main(void) { + int arr[4] = {5, 4, 3, 2}; + test_func(4, arr); + return arr[3]; +} + ]]) + ], + [mhd_cv_cc_attr_access_read_write_size="yes"],[mhd_cv_cc_attr_access_read_write_size="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_access_read_write_size],["yes"], + [ + AC_DEFINE([HAVE_ATTR_ACCESS_READ_WRITE_SIZE],[1], + [Define to '1' if your compiler supports __attribute__ ((access (read_write,NUM,NUM_SIZE)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((fd_arg_read (NUM)))],[mhd_cv_cc_attr_fd_arg_read], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#ifndef _CRT_NONSTDC_NO_WARNINGS +#define _CRT_NONSTDC_NO_WARNINGS 1 +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#ifdef HAVE_IO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +static __attribute__ ((fd_arg_read (1))) int +test_func(int fd) +{ + int data_in; + int read_size; + read_size = (int) read(fd, &data_in, sizeof(data_in)); + if (read_size < (int) sizeof(data_in)) return 4; + return data_in; +} + +int main(void) { + int fd = open("test.txt", O_RDONLY); + int res = test_func(fd); + close (fd); + return res; +} + ]]) + ], + [mhd_cv_cc_attr_fd_arg_read="yes"],[mhd_cv_cc_attr_fd_arg_read="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_fd_arg_read],["yes"], + [ + AC_DEFINE([HAVE_ATTR_FD_ARG_READ],[1], + [Define to '1' if your compiler supports __attribute__ ((fd_arg_read (NUM)))] + ) + ] +) +AC_CACHE_CHECK([whether $CC supports __attribute__ ((null_terminated_string_arg (NUM)))],[mhd_cv_cc_attr_null_term_str], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static __attribute__ ((null_terminated_string_arg (1))) int +test_func(const char *str) {return (0 == str[0]) ? 5 : 0;} + +int main(void) { + return test_func("test"); +} + ]]) + ], + [mhd_cv_cc_attr_null_term_str="yes"],[mhd_cv_cc_attr_null_term_str="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_attr_null_term_str],["yes"], + [ + AC_DEFINE([HAVE_ATTR_NULL_TERM_STR],[1], + [Define to '1' if your compiler supports __attribute__ ((null_terminated_string_arg (NUM)))] + ) + ] +) AC_CACHE_CHECK([whether $CC supports __attribute__((enum_extensibility (closed)))],[mhd_cv_cc_attr_enum_extns_closed], [ SAVE_ac_c_werror_flag="$ac_c_werror_flag" @@ -3992,6 +4418,52 @@ AS_VAR_IF([mhd_cv_cc_attr_flag_enum],["yes"], ) ] ) +AC_CACHE_CHECK([[whether $CC supports array[static N] with fixed N as a function parameter]],[mhd_cv_cc_func_param_arr_static_fixed], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static int +test_func(int arr[static 4]) { return arr[2] - 4; } + +int main(void) { + int arr[4] = {5, 4, 3, 2}; + return test_func(arr); +} + ]]) + ], + [mhd_cv_cc_func_param_arr_static_fixed="yes"],[mhd_cv_cc_func_param_arr_static_fixed="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_func_param_arr_static_fixed],["yes"], + [ + AC_DEFINE([HAVE_FUNC_PARAM_ARR_STATIC_FIXED],[1], + [[Define to '1' if your compiler supports 'array[static N]' with fixed N as function parameter]] + ) + ] +) +AC_CACHE_CHECK([[whether $CC supports array[static N] with variable N as a function parameter]],[mhd_cv_cc_func_param_arr_static_var], + [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +static int +test_func(int num, int arr[static num]) { return arr[num-2] - 4; } + +int main(void) { + int arr[4] = {5, 4, 3, 2}; + return test_func(4, arr); +} + ]]) + ], + [mhd_cv_cc_func_param_arr_static_var="yes"],[mhd_cv_cc_func_param_arr_static_var="no"] + ) + ] +) +AS_VAR_IF([mhd_cv_cc_func_param_arr_static_var],["yes"], + [ + AC_DEFINE([HAVE_FUNC_PARAM_ARR_STATIC_VAR],[1], + [[Define to '1' if your compiler supports 'array[static N]' with variable N as a function parameter]] + ) + ] +) CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" # libcurl (required for testing) @@ -4099,10 +4571,10 @@ AS_VAR_IF([[found_sendfile]], [["no"]], static void empty_func(void) { /* Check for declaration */ +#ifndef sendfile (void)sendfile; +#endif } -/* Declare again to check form match */ -ssize_t sendfile(int, int, off_t*, size_t); ]], [[ int fd1=0, fd2=2; @@ -4155,11 +4627,10 @@ AS_VAR_IF([[found_sendfile]], [["no"]], static void empty_func(void) { /* Check for declaration */ +#ifndef sendfile (void)sendfile; +#endif } -/* Declare again to check form match */ -int sendfile(int, int, off_t, size_t, - struct sf_hdtr*, off_t*, int); ]], [[ int fd1=0, fd2=1; @@ -4167,7 +4638,7 @@ int sendfile(int, int, off_t, size_t, size_t s = 5; off_t r1; int r2; - r2 = sendfile (fd1, fd2, o, s, (void*)0, &r1, 0); + r2 = sendfile (fd1, fd2, o, s, (struct sf_hdtr *)0, &r1, 0); if (r2) empty_func(); ]] @@ -4203,18 +4674,17 @@ AS_VAR_IF([[found_sendfile]], [["no"]], static void empty_func(void) { /* Check for declaration */ +#ifndef sendfile (void)sendfile; +#endif } -/* Declare again to check form match */ -int sendfile(int, int, off_t, off_t*, - struct sf_hdtr *, int); ]], [[ int fd=0, s=1; off_t o = 0; off_t l = 5; int r; - r = sendfile (fd, s, o, &l, (void*)0, 0); + r = sendfile (fd, s, o, &l, (struct sf_hdtr *)0, 0); if (r) empty_func(); ]] @@ -4233,7 +4703,7 @@ int sendfile(int, int, off_t, off_t*, AS_VAR_IF([[found_sendfile]], [["no"]], [ - AC_MSG_CHECKING([[for Solaris-style sendfile(3)]]) + AC_MSG_CHECKING([[for old Solaris-style sendfile(3)]]) SAVE_LIBS="$LIBS" LIBS="-lsendfile $LIBS" AC_LINK_IFELSE( @@ -4253,11 +4723,10 @@ AS_VAR_IF([[found_sendfile]], [["no"]], static void empty_func(void) { /* Check for declaration */ +#ifndef sendfile (void)sendfile; +#endif } -/* Declare again to check form match */ -ssize_t sendfile(int out_fd, int in_fd, - off_t *off, size_t len); ]], [[ int fd1=0, fd2=1; @@ -4271,7 +4740,8 @@ ssize_t sendfile(int out_fd, int in_fd, ) ], [ - AC_DEFINE([HAVE_SOLARIS_SENDFILE], [1], [Define to 1 if you have Solaris-style sendfile(3).]) + # Solaris uses the same format as GNU/Linux. Old versions need libsendfile. + AC_DEFINE([HAVE_LINUX_SENDFILE], [1], [Define to 1 if you have linux-style sendfile(2).]) found_sendfile="yes, Solaris-style" MHD_LIBDEPS="-lsendfile $MHD_LIBDEPS" MHD_LIBDEPS_PKGCFG="-lsendfile $MHD_LIBDEPS_PKGCFG" @@ -4291,6 +4761,9 @@ ssize_t sendfile(int out_fd, int in_fd, ]], [[ off64_t f_offset = (off64_t) 0; +#ifndef sendfile64 + (void)sendfile64; +#endif if (0 > sendfile64 (0, 1, &f_offset, 1)) return 3; ]] @@ -4820,7 +5293,7 @@ AS_VAR_IF([enable_dauth], ["yes"], #include ]], [[ - static int arr[((int) 2) - 4 * (int)(${enable_dauth_def_max_nc} != ((uint32_t)${enable_dauth_def_max_nc}))]; + static int arr[((int) 2) - 4 * (int)(${enable_dauth_def_max_nc} != ((uint_fast32_t)${enable_dauth_def_max_nc}))]; (void) arr; ]] ) @@ -5175,6 +5648,7 @@ AC_CACHE_CHECK([[for calloc()]], [[mhd_cv_have_func_calloc]], ) AS_VAR_IF([[mhd_cv_have_func_calloc]], [["yes"]], [AC_DEFINE([[HAVE_CALLOC]], [[1]], [Define to 1 if you have the usable `calloc' function.])]) +AM_CONDITIONAL([HAVE_SYS_CALLOC], [[test "x${mhd_cv_have_func_calloc}" = "xyes"]]) # Some systems have IPv6 disabled in kernel at run-time AS_IF([[test "x${mhd_cv_have_inet6}" = "xyes" && test "x${cross_compiling}" = "xno"]], @@ -5267,6 +5741,8 @@ int main(void) AS_VAR_IF([mhd_cv_ipv6_for_testing],["yes"], [AC_DEFINE([[USE_IPV6_TESTING]], [[1]], [Define to 1 if your kernel supports IPv6 and IPv6 is enabled and useful for testing.])] ) +AM_CONDITIONAL([USE_IPV6_TESTING], [[test "x$mhd_cv_ipv6_for_testing" = "xyes"]]) + AS_VAR_IF([enable_tools],["yes"], [ @@ -6713,9 +7189,11 @@ doc/doxygen/Makefile doc/examples/Makefile m4/Makefile src/Makefile +src/incl_priv/Makefile src/include/Makefile -src/lib/Makefile -src/microhttpd/Makefile +src/mhd2/Makefile +src/tests/Makefile +src/tests/basic/Makefile src/microhttpd_ws/Makefile src/examples/Makefile src/tools/Makefile @@ -6775,7 +7253,8 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary: Threading lib: ${USE_THREADS} Inter-thread comm: ${use_itc} Shutdown of listening socket triggers select: ${mhd_cv_host_shtdwn_trgr_select} - poll support: ${enable_poll=no} + select() support: ${enable_select} + poll() support: ${enable_poll=no} epoll support: ${enable_epoll=no} sendfile used: ${found_sendfile} HTTPS support: ${MSG_HTTPS} diff --git a/m4/mhd_shutdown_socket_trigger.m4 b/m4/mhd_shutdown_socket_trigger.m4 index 5f3bbb4f..9bdea98c 100644 --- 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 # endif - typedef int MHD_socket; + typedef int MHD_Socket; # define MHD_INVALID_SOCKET (-1) # define MHD_POSIX_SOCKETS 1 #else # include # include # include - 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 index 4673d0d8..ad20b157 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,25 +1,25 @@ # This Makefile.am is in the public domain -SUBDIRS = include microhttpd . +SUBDIRS = include incl_priv mhd2 tests . if RUN_LIBCURL_TESTS -SUBDIRS += testcurl +#SUBDIRS += testcurl if RUN_ZZUF_TESTS -SUBDIRS += testzzuf +#SUBDIRS += testzzuf endif endif # Finally (last!) also build experimental lib... if HAVE_EXPERIMENTAL -SUBDIRS += microhttpd_ws lib +SUBDIRS += microhttpd_ws endif if BUILD_EXAMPLES -SUBDIRS += examples +#SUBDIRS += examples endif if BUILD_TOOLS -SUBDIRS += tools +#SUBDIRS += tools endif EXTRA_DIST = \ diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index cdf509e5..ecd998dd 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am @@ -44,7 +44,7 @@ noinst_PROGRAMS += \ websocket_chatserver_example endif -if MHD_HAVE_EPOLL +if MHD_USE_EPOLL noinst_PROGRAMS += \ suspend_resume_epoll endif diff --git a/src/incl_priv/.gitignore b/src/incl_priv/.gitignore new file mode 100644 index 00000000..a6f4796f --- /dev/null +++ b/src/incl_priv/.gitignore @@ -0,0 +1,3 @@ +/stamp-h1 +/mhd_config.h.in +/mhd_config.h diff --git a/src/incl_priv/Makefile.am b/src/incl_priv/Makefile.am new file mode 100644 index 00000000..95d79dc3 --- /dev/null +++ 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/include/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h similarity index 62% rename from src/include/mhd_sys_options.h rename to src/incl_priv/mhd_sys_options.h index 7e1cc6cb..8685f198 100644 --- a/src/include/mhd_sys_options.h +++ b/src/incl_priv/mhd_sys_options.h @@ -34,7 +34,11 @@ #ifndef MHD_SYS_OPTIONS_H #define MHD_SYS_OPTIONS_H 1 -#include "MHD_config.h" +#ifndef HAVE_CONFIG_H +#error HAVE_CONFIG_H must be defind +#endif + +#include "mhd_config.h" /** * Macro to make it easy to mark text for translation. Note that @@ -89,6 +93,9 @@ # define MHD_VISIBILITY_INTERNAL /* empty */ #endif +/* To be used with internal non-static functions */ +#define MHD_INTERNAL MHD_VISIBILITY_INTERNAL + #ifdef HAVE_ATTR_PURE # define MHD_FN_PURE_ __attribute__((pure)) #else @@ -101,35 +108,209 @@ # define MHD_FN_CONST_ MHD_FN_PURE_ #endif -/* To be used with internal non-static functions */ -#define MHD_INTERNAL MHD_VISIBILITY_INTERNAL +#ifdef HAVE_ATTR_WARN_UNUSED_RES +# define MHD_FN_MUST_CHECK_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define MHD_FN_MUST_CHECK_RESULT_ /* empty */ +#endif -#ifdef HAVE_MACRO_VARIADIC -# define MHD_USE_VARARG_MACROS 1 +#ifdef HAVE_ATTR_RET_NONNULL +# define MHD_FN_RETURNS_NONNULL_ __attribute__ ((returns_nonnull)) +#else +# define MHD_FN_RETURNS_NONNULL_ /* empty */ +#endif + +#ifdef HAVE_ATTR_NONNULL_NUM +# define MHD_FN_PAR_NONNULL_(param_num) __attribute__ ((nonnull (param_num))) +#else +# define MHD_FN_PAR_NONNULL_(param_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_NONNULL +# define MHD_FN_PAR_NONNULL_ALL_ __attribute__ ((nonnull)) +#else +# define MHD_FN_PAR_NONNULL_ALL_ /* empty */ +#endif + +#ifdef HAVE_ATTR_ACCESS_READ +# define MHD_FN_PAR_IN_(param_num) \ + __attribute__ ((access (read_only,param_num))) +#else +# define MHD_FN_PAR_IN_(param_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_ACCESS_READ_SIZE +# define MHD_FN_PAR_IN_SIZE_(param_num,size_num) \ + __attribute__ ((access (read_only,param_num,size_num))) +#else +# define MHD_FN_PAR_IN_SIZE_(param_num,size_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_ACCESS_READ_WRITE +# define MHD_FN_PAR_OUT_(param_num) \ + __attribute__ ((access (write_only,param_num))) +#else +# define MHD_FN_PAR_OUT_(param_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_ACCESS_WRITE +# define MHD_FN_PAR_OUT_SIZE_(param_num,size_num) \ + __attribute__ ((access (write_only,param_num,size_num))) +#else +# define MHD_FN_PAR_OUT_SIZE_(param_num,size_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_ACCESS_READ_WRITE +# define MHD_FN_PAR_INOUT_(param_num) \ + __attribute__ ((access (read_write,param_num))) +#else +# define MHD_FN_PAR_INOUT_(param_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_ACCESS_READ_WRITE_SIZE +# define MHD_FN_PAR_INOUT_SIZE_(param_num,size_num) \ + __attribute__ ((access (read_write,param_num,size_num))) +#else +# define MHD_FN_PAR_INOUT_SIZE_(param_num,size_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_FD_ARG_READ +# define MHD_FN_PAR_FD_READ_(param_num) \ + __attribute__ ((fd_arg_read (param_num))) +#else +# define MHD_FN_PAR_FD_READ_(param_num) /* empty */ +#endif + +#ifdef HAVE_ATTR_NULL_TERM_STR +# define MHD_FN_PAR_CSTR_(param_num) \ + __attribute__ ((null_terminated_string_arg (param_num))) +#else +# define MHD_FN_PAR_CSTR_(param_num) /* empty */ +#endif + +#ifdef HAVE_FUNC_PARAM_ARR_STATIC_FIXED +# define MHD_FN_PAR_FIX_ARR_SIZE_(size) static size +#else +# define MHD_FN_PAR_FIX_ARR_SIZE_(size) size +#endif + +#ifdef HAVE_FUNC_PARAM_ARR_STATIC_VAR +# define MHD_FN_PAR_DYN_ARR_SIZE_(size) static size +#else +# define MHD_FN_PAR_DYN_ARR_SIZE_(size) 1 +#endif + +#ifdef HAVE_ATTR_ENUM_EXTNS_CLOSED +# define MHD_FIXED_ENUM_ __attribute__((enum_extensibility (closed))) +#else +# define MHD_FIXED_ENUM_ /* empty */ +#endif + +#ifdef HAVE_ATTR_FLAG_ENUM +# define MHD_FLAGS_ENUM_ __attribute__((flag_enum)) +#else +# define MHD_FLAGS_ENUM_ /* empty */ +#endif /* MHD_FLAGS_ENUM_ */ + +#define MHD_FIXED_FLAGS_ENUM_ MHD_FIXED_ENUM_ MHD_FLAGS_ENUM_ + + +#ifdef BUILDING_MHD_LIB +# define MHD_FIXED_ENUM_APP_SET_ /* empty */ /* handle unknown values set by the app */ +#else +# define MHD_FIXED_ENUM_APP_SET_ MHD_FIXED_ENUM_ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_ +#else +# define MHD_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FIXED_FLAGS_ENUM_APP_SET_ MHD_FLAGS_ENUM_ /* handle unknown values set by the app */ +#else +# define MHD_FIXED_FLAGS_ENUM_APP_SET_ MHD_FIXED_FLAGS_ENUM_ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FIXED_ENUM_MHD_SET_ MHD_FIXED_ENUM_ +#else +# define MHD_FIXED_ENUM_MHD_SET_ /* empty */ /* enum can be extended in next MHD versions */ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_ +#else +# define MHD_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FIXED_FLAGS_ENUM_MHD_SET_ MHD_FIXED_FLAGS_ENUM_ +#else +# define MHD_FIXED_FLAGS_ENUM_MHD_SET_ MHD_FLAGS_ENUM_ /* enum can be extended in next MHD versions */ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FIXED_ENUM_MHD_APP_SET_ /* empty */ /* handle unknown values set by the app */ +#else +# define MHD_FIXED_ENUM_MHD_APP_SET_ /* empty */ /* enum can be extended in next MHD versions */ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ +#else +# define MHD_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ +#endif + +#ifdef BUILDING_MHD_LIB +# define MHD_FIXED_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ /* handle unknown values set by the app */ +#else +# define MHD_FIXED_FLAGS_ENUM_MHD_APP_SET_ MHD_FLAGS_ENUM_ /* enum can be extended in next MHD versions */ +#endif + +/** + * Automatic string with the name of the current function + */ +#if defined(HAVE___FUNC__) +# define MHD_FUNC_ __func__ +# define MHD_HAVE_MHD_FUNC_ 1 +#elif defined(HAVE___FUNCTION__) +# define MHD_FUNC_ __FUNCTION__ +# define MHD_HAVE_MHD_FUNC_ 1 +#elif defined(HAVE___PRETTY_FUNCTION__) +# define MHD_FUNC_ __PRETTY_FUNCTION__ +# define MHD_HAVE_MHD_FUNC_ 1 +#else +# define MHD_FUNC_ "**name unavailable**" +# ifdef MHD_HAVE_MHD_FUNC_ +# undef MHD_HAVE_MHD_FUNC_ +# endif /* MHD_HAVE_MHD_FUNC_ */ #endif /* Some platforms (FreeBSD, Solaris, W32) allow to override default FD_SETSIZE by defining it before including headers. */ #ifdef FD_SETSIZE -/* FD_SETSIZE defined in command line or in MHD_config.h */ +/* FD_SETSIZE defined in command line or in mhd_config.h */ #elif defined(_WIN32) || defined(__CYGWIN__) /* Platform with WinSock and without overridden FD_SETSIZE */ -#define FD_SETSIZE 2048 /* Override default small value (64) */ +# ifdef _WIN64 +# define FD_SETSIZE 4096 /* Override default small value (64) */ +# else +# define FD_SETSIZE 1024 /* Override default small value (64) */ +# endif #else /* !FD_SETSIZE && !W32 */ /* System default value of FD_SETSIZE is used */ -#define _MHD_FD_SETSIZE_IS_DEFAULT 1 +# define MHD_FD_SETSIZE_IS_DEFAULT_ 1 #endif /* !FD_SETSIZE && !W32 */ #if defined(HAVE_LINUX_SENDFILE) || defined(HAVE_FREEBSD_SENDFILE) || \ - defined(HAVE_DARWIN_SENDFILE) || defined(HAVE_SOLARIS_SENDFILE) + defined(HAVE_DARWIN_SENDFILE) /* Have any supported sendfile() function. */ -#define _MHD_HAVE_SENDFILE -#endif /* HAVE_LINUX_SENDFILE || HAVE_FREEBSD_SENDFILE || - HAVE_DARWIN_SENDFILE || HAVE_SOLARIS_SENDFILE */ -#if defined(HAVE_LINUX_SENDFILE) || defined(HAVE_SOLARIS_SENDFILE) -#define MHD_LINUX_SOLARIS_SENDFILE 1 -#endif /* HAVE_LINUX_SENDFILE || HAVE_SOLARIS_SENDFILE */ +# define MHD_USE_SENDFILE 1 +#endif /* HAVE_LINUX_SENDFILE || HAVE_FREEBSD_SENDFILE + || HAVE_DARWIN_SENDFILE */ #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) # ifndef MHD_USE_THREADS @@ -137,6 +318,20 @@ # endif #endif /* MHD_USE_POSIX_THREADS || MHD_USE_W32_THREADS */ +/** + * Macro to drop 'const' qualifier from pointer. + * Try to avoid compiler warning. + * To be used *only* to deal with broken external APIs, which require non-const + * pointer to unmodifiable data. + * Must not be used to transform pointers for internal MHD needs. + */ +#ifdef HAVE_UINTPTR_T +# define mhd_DROP_CONST(ptr) ((void *) ((uintptr_t) ((const void *) (ptr)))) +#else +# define mhd_DROP_CONST(ptr) ((void *) ((const void *) (ptr))) +#endif + + #if defined(OS390) #define _OPEN_THREADS #define _OPEN_SYS_SOCK_IPV6 @@ -162,6 +357,12 @@ # endif /* !WIN32_LEAN_AND_MEAN */ #endif /* _WIN32 && ! __CYGWIN__ */ +#if defined(__MINGW32__) +# ifdef __USE_MINGW_ANSI_STDIO +# define __USE_MINGW_ANSI_STDIO 0 /* Force use native printf, the code is well-adapted */ +# endif +#endif + #if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS) #define RESTRICT __restrict__ #endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */ @@ -177,6 +378,14 @@ #define __STDC_WANT_LIB_EXT1__ 1 #endif /* HAVE_C11_GMTIME_S */ +#if ! defined(_DEBUG) && ! defined(NDEBUG) +# ifndef DEBUG /* Used by some toolchains */ +# define NDEBUG 1 /* Use NDEBUG by default */ +# else /* DEBUG */ +# define _DEBUG 1 /* Non-standart macro */ +# endif /* DEBUG */ +#endif /* !_DEBUG && !NDEBUG */ + #if defined(MHD_FAVOR_FAST_CODE) && defined(MHD_FAVOR_SMALL_CODE) #error \ MHD_FAVOR_FAST_CODE and MHD_FAVOR_SMALL_CODE are both defined. Cannot favor speed and size at the same time. @@ -236,25 +445,6 @@ __pragma(runtime_checks("c", restore)) #endif /* _MSC_FULL_VER */ -/** - * Automatic string with the name of the current function - */ -#if defined(HAVE___FUNC__) -#define MHD_FUNC_ __func__ -#define MHD_HAVE_MHD_FUNC_ 1 -#elif defined(HAVE___FUNCTION__) -#define MHD_FUNC_ __FUNCTION__ -#define MHD_HAVE_MHD_FUNC_ 1 -#elif defined(HAVE___PRETTY_FUNCTION__) -#define MHD_FUNC_ __PRETTY_FUNCTION__ -#define MHD_HAVE_MHD_FUNC_ 1 -#else -#define MHD_FUNC_ "**name unavailable**" -#ifdef MHD_HAVE_MHD_FUNC_ -#undef MHD_HAVE_MHD_FUNC_ -#endif /* MHD_HAVE_MHD_FUNC_ */ -#endif - /* Un-define some HAVE_DECL_* macro if they equal zero. This should allow safely use #ifdef in the code. Define HAS_DECL_* macros only if matching HAVE_DECL_* macro @@ -339,4 +529,13 @@ # define MHD_DAUTH_DEF_MAX_NC_ 1000 #endif /* ! MHD_DAUTH_DEF_MAX_NC_ */ +/* Eclipse parse compatibility */ +#ifdef __CDT_PARSER__ +# undef MHD_NORETURN_ +# define MHD_NORETURN_ __attribute__((__noreturn__)) +#endif + +/* Avoid interference with third-party headers */ +#undef HAVE_CONFIG_H + #endif /* MHD_SYS_OPTIONS_H */ diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 266b0f85..da1a627b 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -1,25 +1,25 @@ # This Makefile.am is in the public domain SUBDIRS = . -microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h: d_options.rec options-generator - rm -f microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h - ./options-generator daemon > microhttpd2_generated_daemon_options.h - chmod -w $@ -microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h: r_options.rec options-generator - rm -f microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h - ./options-generator response > microhttpd2_generated_response_options.h - chmod -w $@ +#microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h: d_options.rec options-generator +# rm -f microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h +# ./options-generator daemon > microhttpd2_generated_daemon_options.h +# chmod -w $@ +#microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h: r_options.rec options-generator +# rm -f microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h +# ./options-generator response > microhttpd2_generated_response_options.h +# chmod -w $@ -microhttpd2.h: microhttpd2_preamble.h.in microhttpd2_inline_daemon_documentation.h.in microhttpd2_inline_response_documentation.h.in microhttpd2_main.h.in microhttpd2_postamble.h.in - rm -f $@ - cat $^ >$@ - chmod -w $@ +#microhttpd2.h: microhttpd2_preamble.h.in microhttpd2_inline_daemon_documentation.h.in microhttpd2_inline_response_documentation.h.in microhttpd2_main.h.in microhttpd2_postamble.h.in +# rm -f $@ +# cat $^ >$@ +# chmod -w $@ -noinst_PROGRAMS = \ - options-generator +#noinst_PROGRAMS = \ +# options-generator -options_generator_SOURCES = \ - options-generator.c +#options_generator_SOURCES = \ +# options-generator.c mhd2includedir = $(includedir)/mhd2 diff --git a/src/include/mhd_future.h b/src/include/mhd_future.h index e2d2221c..c3af2498 100644 --- a/src/include/mhd_future.h +++ b/src/include/mhd_future.h @@ -111,9 +111,6 @@ #ifndef MHD_FN_RETURNS_NONNULL_ # define MHD_FN_RETURNS_NONNULL_ /* empty */ #endif /* ! MHD_FN_RETURNS_NONNULL_ */ -#ifndef MHD_FN_WARN_UNUSED_RESULT_ -# define MHD_FN_WARN_UNUSED_RESULT_ /* empty */ -#endif /* ! MHD_FN_WARN_UNUSED_RESULT_ */ /** diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index e1ba16d8..bc7caa45 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h @@ -171,6 +171,7 @@ enum MHD_Bool MHD_YES = 1 }; +#ifndef MHD_STRINGS_DEFINED /** * String with length data. @@ -209,20 +210,24 @@ struct MHD_StringNullable const char *cstr; }; +#define MHD_STRINGS_DEFINED 1 +#endif /* ! MHD_STRINGS_DEFINED */ /** - * Constant used to indicate unknown size (use when - * creating a response). + * Constant used to indicate unknown size (use when creating a response). + * Any possible larger sizes are interpreted as the same value. */ #ifdef UINT64_MAX # define MHD_SIZE_UNKNOWN UINT64_MAX #else -# define MHD_SIZE_UNKNOWN MHD_STATIC_CAST_ (uint_fast64_t,0xffffffffffffffffU) +# define MHD_SIZE_UNKNOWN \ + MHD_STATIC_CAST_ (uint_fast64_t,0xffffffffffffffffU) #endif /** * Constant used to indicate unlimited wait time. + * Any possible larger values are interpreted as the this value. */ #ifdef UINT64_MAX # define MHD_WAIT_INDEFINITELY UINT64_MAX @@ -292,6 +297,15 @@ struct MHD_Request; */ struct MHD_Action; + +/** + * @brief Actions are returned by the application when processing client upload + * to drive the request handling of MHD. + * + * @defgroup action Request actions + */ +struct MHD_UploadAction; + /** * @defgroup general Primary MHD functions and data */ @@ -331,11 +345,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_OK = 0 , - /** - * We were asked to return a timeout, but, there is no timeout. - */ - MHD_SC_NO_TIMEOUT = 1 - , /* 10000-level status codes indicate intermediate results of some kind. */ @@ -365,6 +374,17 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_ACCEPT_FAILED_EAGAIN = 10004 , + /** + * Accepted socket is unknown type (probably non-IP). + */ + MHD_SC_ACCEPTED_UNKNOWN_TYPE = 10040 + , + /** + * The sockaddr for the accepted socket does not fit the buffer. + * (Strange) + */ + MHD_SC_ACCEPTED_SOCKADDR_TOO_LARGE = 10041 + , /* 20000-level status codes indicate success of some kind. */ @@ -391,71 +411,75 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_LIMIT_CONNECTIONS_REACHED = 30000 , - /** - * We failed to allocate memory for poll() syscall. - * (May be transient.) - */ - MHD_SC_POLL_MALLOC_FAILURE = 30001 - , /** * The operation failed because the respective * daemon is already too deep inside of the shutdown * activity. */ - MHD_SC_DAEMON_ALREADY_SHUTDOWN = 30002 + MHD_SC_DAEMON_ALREADY_SHUTDOWN = 30020 , /** - * We failed to start a thread. + * Failed to start new thread because of system limits. */ - MHD_SC_THREAD_LAUNCH_FAILURE = 30003 + MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED = 30030 + , + /** + * Failed to start a thread. + */ + MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE = 30031 , /** * The operation failed because we either have no * listen socket or were already quiesced. */ - MHD_SC_DAEMON_ALREADY_QUIESCED = 30004 + MHD_SC_DAEMON_ALREADY_QUIESCED = 30040 , /** * The operation failed because client disconnected * faster than we could accept(). */ - MHD_SC_ACCEPT_FAST_DISCONNECT = 30005 + MHD_SC_ACCEPT_FAST_DISCONNECT = 30040 , /** * Operating resource limits hit on accept(). */ - MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED = 30006 + MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED = 30060 , /** * Connection was refused by accept policy callback. */ - MHD_SC_ACCEPT_POLICY_REJECTED = 30007 + MHD_SC_ACCEPT_POLICY_REJECTED = 30070 + , + /** + * Failed to allocate memory for the daemon resources. + */ + MHD_SC_DAEMON_MALLOC_FAILURE = 30081 , /** * We failed to allocate memory for the connection. * (May be transient.) */ - MHD_SC_CONNECTION_MALLOC_FAILURE = 30008 + MHD_SC_CONNECTION_MALLOC_FAILURE = 30082 , /** * We failed to allocate memory for the connection's memory pool. * (May be transient.) */ - MHD_SC_POOL_MALLOC_FAILURE = 30009 + MHD_SC_POOL_MALLOC_FAILURE = 30083 , /** * We failed to forward data from a Web socket to the * application to the remote side due to the socket * being closed prematurely. (May be transient.) */ - MHD_SC_UPGRADE_FORWARD_INCOMPLETE = 30010 + MHD_SC_UPGRADE_FORWARD_INCOMPLETE = 30100 , /** * We failed to allocate memory for generating the response from our * memory pool. Likely the request header was too large to leave * enough room. */ - MHD_SC_CONNECTION_POOL_MALLOC_FAILURE = 30011 + MHD_SC_CONNECTION_POOL_MALLOC_FAILURE = 30130 , /* 40000-level errors are caused by the HTTP client @@ -532,276 +556,402 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_ITC_INITIALIZATION_FAILED = 50005 , /** - * File descriptor for ITC channel too large. + * File descriptor for ITC cannot be used because the FD number is higher + * than the limit set by FD_SETSIZE (if internal polling with select is used) + * or by application. */ - MHD_SC_ITC_DESCRIPTOR_TOO_LARGE = 50006 + MHD_SC_ITC_FD_OUTSIDE_OF_SET_RANGE = 50006 , /** * The specified value for the NC length is way too large * for this platform (integer overflow on `size_t`). */ - MHD_SC_DIGEST_AUTH_NC_LENGTH_TOO_BIG = 50007 + MHD_SC_DIGEST_AUTH_NC_LENGTH_TOO_BIG = 50010 , /** * We failed to allocate memory for the specified nonce * counter array. The option was not set. */ - MHD_SC_DIGEST_AUTH_NC_ALLOCATION_FAILURE = 50008 + MHD_SC_DIGEST_AUTH_NC_ALLOCATION_FAILURE = 50011 , /** * This build of the library does not support * digest authentication. */ - MHD_SC_DIGEST_AUTH_NOT_SUPPORTED_BY_BUILD = 50009 + MHD_SC_DIGEST_AUTH_NOT_SUPPORTED_BY_BUILD = 50012 , /** * IPv6 requested but not supported by this build. + * @sa #MHD_SC_AF_NOT_SUPPORTED_BY_BUILD */ - MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD = 50010 + MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD = 50020 , /** - * We failed to open the listen socket. Maybe the build - * supports IPv6, but your kernel does not? + * Specified address/protocol family is not supported by this build. + * @sa MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD */ - MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET = 50011 + MHD_SC_AF_NOT_SUPPORTED_BY_BUILD = 50021 , /** - * Specified address family is not supported by this build. + * The requested address/protocol family is rejected by the OS. + * @sa #MHD_SC_AF_NOT_SUPPORTED_BY_BUILD */ - MHD_SC_AF_NOT_SUPPORTED_BY_BUILD = 50012 + MHD_SC_AF_NOT_AVAILABLE = 500022 + , + /** + * We failed to open the listen socket. + */ + MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET = 50040 + , + /** + * Failed to enable listen port reuse. + */ + MHD_SC_LISTEN_PORT_REUSE_ENABLE_FAILED = 50041 + , + /** + * Failed to enable listen port reuse. + */ + MHD_SC_LISTEN_PORT_REUSE_ENABLE_NOT_SUPPORTED = 50042 , /** * Failed to enable listen address reuse. */ - MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED = 50013 + MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED = 50043 , /** * Enabling listen address reuse is not supported by this platform. */ - MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED = 50014 + MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED = 50044 , /** - * Failed to disable listen address reuse. + * Failed to enable exclusive use of listen address. */ - MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED = 50015 + MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED = 50045 , /** - * Disabling listen address reuse is not supported by this platform. + * Dual stack configuration is not possible for provided sockaddr. */ - MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED = 50016 + MHD_SC_LISTEN_DUAL_STACK_NOT_SUITABLE = 50046 , /** - * We failed to explicitly enable or disable dual stack for - * the IPv6 listen socket. The socket will be used in whatever - * the default is the OS gives us. + * Failed to enable or disable dual stack for the IPv6 listen socket. + * The OS default dual-stack setting is different from what is requested. */ - MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED = 50017 + MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED = 50047 + , + /** + * Failed to enable or disable dual stack for the IPv6 listen socket. + * The socket will be used in whatever the default is the OS uses. + */ + MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_UNKNOWN = 50048 , /** * On this platform, MHD does not support explicitly configuring - * dual stack behavior. + * dual stack behaviour. */ - MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED = 50018 + MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED = 50049 , /** * Failed to enable TCP FAST OPEN option. */ - MHD_SC_FAST_OPEN_FAILURE = 50020 + MHD_SC_LISTEN_FAST_OPEN_FAILURE = 50050 , /** - * Failed to start listening on listen socket. + * TCP FAST OPEN is not supported by the platform or by this MHD build. */ - MHD_SC_LISTEN_FAILURE = 50021 - , - /** - * Failed to obtain our listen port via introspection. - */ - MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE = 50022 - , - /** - * Failed to obtain our listen port via introspection - * due to unsupported address family being used. - */ - MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF = 50023 + MHD_SC_FAST_OPEN_NOT_SUPPORTED = 50051 , /** * We failed to set the listen socket to non-blocking. */ - MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE = 50024 + MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE = 50052 , /** - * Listen socket value is too large (for use with select()). + * Failed to configure listen socket to be non-inheritable. */ - MHD_SC_LISTEN_SOCKET_TOO_LARGE = 50025 + MHD_SC_LISTEN_SOCKET_NOINHERIT_FAILED = 50053 , /** - * We failed to allocate memory for the thread pool. + * Listen socket FD cannot be used because the FD number is higher than + * the limit set by FD_SETSIZE (if internal polling with select is used) or + * by application. */ - MHD_SC_THREAD_POOL_MALLOC_FAILURE = 50026 + MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE = 50054 , /** - * We failed to allocate mutex for thread pool worker. + * We failed to bind the listen socket. */ - MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE = 50027 + MHD_SC_LISTEN_SOCKET_BIND_FAILED = 50055 , /** - * There was an attempt to upgrade a connection on - * a daemon where upgrades are disallowed. + * Failed to start listening on listen socket. */ - MHD_SC_UPGRADE_ON_DAEMON_WITH_UPGRADE_DISALLOWED = 50028 + MHD_SC_LISTEN_FAILURE = 50056 , /** - * Failed to signal via ITC channel. + * Failed to detect the port number on the listening socket */ - MHD_SC_ITC_USE_FAILED = 50029 - , - /** - * We failed to initialize the main thread for listening. - */ - MHD_SC_THREAD_MAIN_LAUNCH_FAILURE = 50030 - , - /** - * We failed to initialize the threads for the worker pool. - */ - MHD_SC_THREAD_POOL_LAUNCH_FAILURE = 50031 - , - /** - * We failed to add a socket to the epoll() set. - */ - MHD_SC_EPOLL_CTL_ADD_FAILED = 50032 + MHD_SC_LISTEN_PORT_DETECT_FAILURE = 50057 , /** * We failed to create control socket for the epoll(). */ - MHD_SC_EPOLL_CTL_CREATE_FAILED = 50034 + MHD_SC_EPOLL_CTL_CREATE_FAILED = 50060 , /** * We failed to configure control socket for the epoll() * to be non-inheritable. */ - MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED = 50035 + MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED = 50061 , /** - * We failed to build the FD set because a socket was - * outside of the permitted range. + * The epoll() control FD cannot be used because the FD number is higher + * than the limit set by application. */ - MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE = 50036 + MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE = 50062 , /** - * This daemon was not configured with options that - * would allow us to build an FD set for select(). + * Failed to allocate memory for daemon's fd_sets */ - MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_FDSET = 50037 + MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE = 50063 + , + /** + * Failed to allocate memory for poll() structures + */ + MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE = 50063 + , + /** + * Failed to allocate memory for epoll data + */ + MHD_SC_EPOLL_EVENTS_MEMORY_ALLOCATE_FAILURE = 50064 + , + /** + * Failed to add daemon's FDs (ITC and/or listening) to the epoll monitoring + */ + MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE = 50065 + , + /** + * The select() syscall is not available on this platform or in this MHD + * build. + */ + MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE = 50070 + , + /** + * The poll() syscall is not available on this platform or in this MHD + * build. + */ + MHD_SC_POLL_SYSCALL_NOT_AVAILABLE = 50071 + , + /** + * The epoll syscalls are not available on this platform or in this MHD + * build. + */ + MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE = 50072 + , + /** + * Failed to obtain our listen port via introspection. + * FIXME: remove? + */ + MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE = 50080 + , + /** + * Failed to obtain our listen port via introspection + * due to unsupported address family being used. + */ + MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF = 50081 + , + /** + * Failed to allocate memory for the thread pool. + */ + MHD_SC_THREAD_POOL_MALLOC_FAILURE = 50090 + , + /** + * We failed to allocate mutex for thread pool worker. + */ + MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE = 50093 + , + /** + * Failed to start the main daemon thread. + */ + MHD_SC_THREAD_MAIN_LAUNCH_FAILURE = 50095 + , + /** + * Failed to start the daemon thread for listening. + */ + MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE = 50096 + , + /** + * Failed to start the worker thread for the thread pool. + */ + MHD_SC_THREAD_WORKER_LAUNCH_FAILURE = 50097 + , + /** + * There was an attempt to upgrade a connection on + * a daemon where upgrades are disallowed. + */ + MHD_SC_UPGRADE_ON_DAEMON_WITH_UPGRADE_DISALLOWED = 50100 + , + /** + * Failed to signal via ITC channel. + */ + MHD_SC_ITC_USE_FAILED = 500101 + , + /** + * Failed to check for the signal on the ITC channel. + */ + MHD_SC_ITC_CHECK_FAILED = 500102 + , + /** + * System reported error conditions on the ITC FD.. + */ + MHD_SC_ITC_STATUS_ERROR = 500104 + , + /** + * We failed to add a socket to the epoll() set. + */ + MHD_SC_EPOLL_CTL_ADD_FAILED = 500110 + , + /** + * Socket FD cannot be used because the FD number is higher than the limit set + * by FD_SETSIZE (if internal polling with select is used) or by application. + */ + MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE = 500111 + , + /** + * The daemon cannot be started with the specified settings as no space + * left for the connections sockets within limits set by FD_SETSIZE. + * Consider use another sockets polling syscall (only select() has such + * limitations) + */ + MHD_SC_SYS_FD_SETSIZE_TOO_STRICT = 50112 , /** * This daemon was not configured with options that * would allow us to obtain a meaningful timeout. */ - MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_TIMEOUT = 50038 + MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_TIMEOUT = 50113 , /** * This daemon was not configured with options that * would allow us to run with select() data. */ - MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_SELECT = 50039 + MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_SELECT = 50114 , /** * This daemon was not configured to run with an * external event loop. */ - MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_EXTERNAL = 50040 - , - /** - * Encountered an unexpected event loop style - * (should never happen). - */ - MHD_SC_CONFIGURATION_UNEXPECTED_ELS = 50041 + MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_EXTERNAL = 50115 , /** * Encountered an unexpected error from select() * (should never happen). */ - MHD_SC_UNEXPECTED_SELECT_ERROR = 50042 + MHD_SC_UNEXPECTED_SELECT_ERROR = 50116 , /** * poll() is not supported. */ - MHD_SC_POLL_NOT_SUPPORTED = 50043 + MHD_SC_POLL_NOT_SUPPORTED = 50120 , /** - * Encountered an unexpected error from poll() - * (should never happen). + * Encountered a (potentially) recoverable error from poll(). */ - MHD_SC_UNEXPECTED_POLL_ERROR = 50044 + MHD_SC_POLL_SOFT_ERROR = 50121 + , + /** + * Encountered an unrecoverable error from poll(). + */ + MHD_SC_POLL_HARD_ERROR = 50122 + , + /** + * System reported error conditions on the listening socket. + */ + MHD_SC_LISTEN_STATUS_ERROR = 500104 , /** * We failed to configure accepted socket - * to not use a signal pipe. + * to not use a SIGPIPE. */ - MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED = 50045 - , - /** - * Encountered an unexpected error from epoll_wait() - * (should never happen). - */ - MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR = 50046 - , - /** - * epoll file descriptor is invalid (strange) - */ - MHD_SC_EPOLL_FD_INVALID = 50047 + MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED = 50140 , /** * We failed to configure accepted socket * to be non-inheritable. */ - MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED = 50048 + MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED = 50141 , /** * We failed to configure accepted socket * to be non-blocking. */ - MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED = 50049 + MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED = 50142 , /** - * accept() returned non-transient error. + * The accepted socket FD value is too large. */ - MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY = 50050 + MHD_SC_ACCEPT_OUTSIDE_OF_SET_RANGE = 50143 + , + /** + * accept() returned unexpected error. + */ + MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY = 50144 , /** * Operating resource limits hit on accept() while * zero connections are active. Oopsie. */ - MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY = 50051 + MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY = 50145 + , + /** + * The daemon sockets polling mode requires non-blocking sockets. + */ + MHD_SC_NONBLOCKING_REQUIRED = 50146 + , + /** + * Encountered an unexpected error from epoll_wait() + * (should never happen). + */ + MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR = 50150 + , + /** + * epoll file descriptor is invalid (strange) + */ + MHD_SC_EPOLL_FD_INVALID = 50151 + , + /** + * Unexpected socket error (strange) + */ + MHD_SC_UNEXPECTED_SOCKET_ERROR = 50152 , /** * Failed to add IP address to per-IP counter for * some reason. */ - MHD_SC_IP_COUNTER_FAILURE = 50052 + MHD_SC_IP_COUNTER_FAILURE = 50160 , /** * Application violated our API by calling shutdown * while having an upgrade connection still open. */ - MHD_SC_SHUTDOWN_WITH_OPEN_UPGRADED_CONNECTION = 50053 + MHD_SC_SHUTDOWN_WITH_OPEN_UPGRADED_CONNECTION = 50180 , /** * Due to an unexpected internal error with the * state machine, we closed the connection. */ - MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED = 50054 + MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED = 50200 , /** * Failed to allocate memory in connection's pool * to parse the cookie header. */ - MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE = 50055 + MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE = 50220 , /** * MHD failed to build the response header. */ - MHD_SC_FAILED_RESPONSE_HEADER_GENERATION = 50056 + MHD_SC_FAILED_RESPONSE_HEADER_GENERATION = 50230 , /** * The feature is not supported by this MHD build (either @@ -812,7 +962,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode * will be run on another kernel, computer or system * configuration. */ - MHD_SC_FEATURE_DISABLED = 500057 + MHD_SC_FEATURE_DISABLED = 50300 , /** * The feature is not supported by this platform, while @@ -821,7 +971,45 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode * running on another computer or with other system * configuration. */ - MHD_SC_FEATURE_NOT_AVAILABLE = 500058 + MHD_SC_FEATURE_NOT_AVAILABLE = 50320 + , + /** + * Failed to stop the thread + */ + MHD_SC_DAEMON_THREAD_STOP_ERROR = 50350 + , + /** + * Unexpected reasons for thread stop + */ + MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED = 50350 + , + /** + * Failed to acquire response mutex lock + */ + MHD_SC_RESPONSE_MUTEX_LOCK_FAILED = 50500 + , + /** + * Failed to initialise response mutex + */ + MHD_SC_RESPONSE_MUTEX_INIT_FAILED = 50501 + , + /** + * Unable to clear "reusable" flag. + * One this flag set, it cannot be removed for the response lifetime. + */ + MHD_SC_RESPONSE_CANNOT_CLEAR_REUSE = 50520 + , + /** + * Unable to allocate memory for the response header + */ + MHD_SC_RESPONSE_HEADER_MALLOC_FAILED = 50540 + , + /** + * Something wrong in the internal MHD logic. + * This error should be never returned if MHD works as expected. + * If this code is ever returned, please report to MHD maintainers. + */ + MHD_SC_INTERNAL_ERROR = 51000 , /* 60000-level errors are because the application @@ -829,9 +1017,9 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode /** * MHD does not support the requested combination of - * EPOLL with thread-per-connection mode. + * the sockets polling syscall and the work mode. */ - MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID = 60000 + MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID = 60000 , /** * MHD does not support quiescing if ITC was disabled @@ -839,11 +1027,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001 , - /** - * We failed to bind the listen socket. - */ - MHD_SC_LISTEN_SOCKET_BIND_FAILED = 60002 - , /** * The application requested an unsupported TLS backend to be used. */ @@ -892,7 +1075,48 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode * already set. */ MHD_SC_OPTIONS_CONFLICT = 60010 - + , + /** + * Attempted to set an option that not recognised by MHD. + */ + MHD_SC_OPTION_UNKNOWN = 60011 + , + /** + * Parameter specified unknown work mode. + */ + MHD_SC_CONFIGURATION_UNEXPECTED_WM = 60012 + , + /** + * Parameter specified unknown socket poll syscall. + */ + MHD_SC_CONFIGURATION_UNEXPECTED_SPS = 60013 + , + /** + * The size of the provided sockaddr does not match address family. + */ + MHD_SC_CONFIGURATION_WRONG_SA_SIZE = 60014 + , + /** + * The number set by #MHD_D_O_FD_NUMBER_LIMIT is too strict to run + * the daemon + */ + MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT = 60015 + , + /** + * The number set by #MHD_D_O_GLOBAL_CONNECTION_LIMIT is too for the daemon + * configuration + */ + MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016 + , + /** + * The response header name has forbidden characters + */ + MHD_SC_RESP_HEADER_NAME_INVALID = 60050 + , + /** + * The response header value has forbidden characters + */ + MHD_SC_RESP_HEADER_VALUE_INVALID = 60051 }; @@ -908,7 +1132,16 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_EXTERN_ const struct MHD_String * MHD_status_code_to_string (enum MHD_StatusCode code) -MHD_FN_PURE_; +MHD_FN_CONST_; + +/** + * Get the pointer to the C string for the MHD error code, never NULL. + */ +#define MHD_status_code_to_string_lazy(code) \ + (MHD_status_code_to_string ((code)) ? \ + ((MHD_status_code_to_string (code))->cstr) : ("[No code]") ) + +#ifndef MHD_HTTP_METHOD_DEFINED /** * @brief HTTP request methods @@ -931,13 +1164,13 @@ MHD_FN_PURE_; * #MHD_HTTP_METHOD_OTHER today, it may return a method-specific header in the * future! */ -enum MHD_FIXED_ENUM_ MHD_HTTP_Method +enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_Method { /** * Method did not match any of the methods given below. */ - MHD_HTTP_METHOD_OTHER = 0 + MHD_HTTP_METHOD_OTHER = 255 , /* Main HTTP methods. */ @@ -996,6 +1229,9 @@ enum MHD_FIXED_ENUM_ MHD_HTTP_Method MHD_HTTP_METHOD_ASTERISK = 9 }; +#define MHD_HTTP_METHOD_DEFINED 1 +#endif /* ! MHD_HTTP_METHOD_DEFINED */ + /** * Get text version of the method name. * @param method the method to get the text version @@ -1005,7 +1241,8 @@ enum MHD_FIXED_ENUM_ MHD_HTTP_Method */ MHD_EXTERN_ const struct MHD_String * MHD_http_method_to_string (enum MHD_HTTP_Method method) -MHD_FN_PURE_; +MHD_FN_CONST_; + /* Main HTTP methods. */ /* Safe. Idempotent. RFC9110, Section 9.3.1. */ @@ -1025,7 +1262,7 @@ MHD_FN_PURE_; /* Safe. Idempotent. RFC9110, Section 9.3.8. */ #define MHD_HTTP_METHOD_STR_TRACE "TRACE" /* Not safe. Not idempotent. RFC9110, Section 18.2. */ -#define MHD_HTTP_METHOD_STR_ASTERISK "*" +#define MHD_HTTP_METHOD_STR_ASTERISK "*" /* Additional HTTP methods. */ /* Not safe. Idempotent. RFC3744, Section 8.1. */ @@ -1093,6 +1330,7 @@ MHD_FN_PURE_; /** @} */ /* end of group methods */ +#ifndef MHD_HTTP_POSTENCODING_DEFINED /** * @brief Possible encodings for HTML forms submitted as HTTP POST requests @@ -1136,6 +1374,8 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_HTTP_PostEncoding /** @} */ /* end of group postenc */ +#define MHD_HTTP_POSTENCODING_DEFINED 1 +#endif /* ! MHD_HTTP_POSTENCODING_DEFINED */ /** * @brief Standard headers found in HTTP requests and responses. @@ -1689,7 +1929,7 @@ enum MHD_PredefinedHeader */ MHD_EXTERN_ const struct MHD_String * MHD_predef_header_to_string (enum MHD_PredefinedHeader stk) -MHD_FN_PURE_; +MHD_FN_CONST_; /** @} */ /* end of group headers */ @@ -1697,6 +1937,9 @@ MHD_FN_PURE_; * A client has requested the given url using the given method * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). + * If @a upload_size is not zero and response action is provided by this + * callback, then upload will be discarded and the stream (the connection for + * HTTP/1.1) will be closed after sending the response. * * @param cls argument given together with the function * pointer when the handler was registered with MHD @@ -2071,7 +2314,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_WorkMode * that gets triggered by any MHD event. * This FD can be watched as an aggregate indicator for all MHD events. * This mode is available only on selected platforms (currently - * GNU/Linux only), see #MHD_LIB_INFO_FIXED_HAS_AGGREGATE_FD. + * GNU/Linux and OpenIndiana only), see #MHD_LIB_INFO_FIXED_HAS_AGGREGATE_FD. * When the FD is triggered, #MHD_daemon_process_nonblocking() should * be called. * Use helper macro #MHD_D_OPTION_WM_EXTERNAL_SINGLE_FD_WATCH() to enable @@ -2436,20 +2679,38 @@ typedef void enum MHD_FIXED_ENUM_APP_SET_ MHD_DaemonOptionBindType { /** - * The listen socket bind to the networks address without sharing the address. + * The listen socket bind to the networks address with sharing the address. + * Several sockets can bind to the same address. + */ + MHD_D_OPTION_BIND_TYPE_SHARED = -1 + , + /** + * The listen socket bind to the networks address without sharing the address, + * except allowing binding to port/address which has TIME_WAIT state (the + * state after closing connection). + * On some platforms it may also allow to bind to specific address if other + * socket already bond to the same port of wildcard address (or bind to + * wildcard address when other socket already bond to specific address + * with the same port). + * Typically achieved by enabling 'SO_REUSEADDR' socket option. * Default. */ MHD_D_OPTION_BIND_TYPE_NOT_SHARED = 0 , /** - * The listen socket bind to the networks address with sharing the address. - * Several sockets can bind to the same address. + * The listen socket bind to the networks address without sharing the address. + * The daemon way fail to start when any sockets still in "TIME_WAIT" state + * on the same port, which effectively prevents quick restart of the daemon + * on the same port. + * On W32 systems it works like #MHD_D_OPTION_BIND_TYPE_NOT_SHARED due to + * the OS limitations. */ - MHD_D_OPTION_BIND_TYPE_SHARED = 1 + MHD_D_OPTION_BIND_TYPE_NOT_SHARED_STRICTER = 1 , /** * The list socket bind to the networks address in explicit exclusive mode. - * Ignored on platforms without support for the explicit exclusive socket use. + * Works as #MHD_D_OPTION_BIND_TYPE_NOT_SHARED_STRICTER on platforms without + * support for the explicit exclusive socket use. */ MHD_D_OPTION_BIND_TYPE_EXCLUSIVE = 2 }; @@ -2474,8 +2735,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TCPFastOpenType , /** * Require TCP_FASTOPEN. - * Also causes #MHD_daemon_start() to fail if setting - * the option fails later. + * Also causes #MHD_daemon_start() to fail if TCP_FASTOPEN cannot be enabled. */ MHD_FOM_REQUIRE = 1 }; @@ -2499,19 +2759,32 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_AddressFamily MHD_AF_AUTO = 1 , /** - * Use IPv4. + * Use IPv4 only. */ MHD_AF_INET4 = 2 , /** - * Use IPv6. + * Use IPv6 only. */ MHD_AF_INET6 = 3 , /** - * Use dual stack. + * Use dual stack (IPv4 and IPv6 on the same socket). */ MHD_AF_DUAL = 4 + , + /** + * Use dual stack (IPv4 and IPv6 on the same socket), + * fallback to pure IPv6 if dual stack is not possible. + */ + MHD_AF_DUAL_v4_OPTIONAL = 5 + , + /** + * Use dual stack (IPv4 and IPv6 on the same socket), + * fallback to pure IPv4 if dual stack is not possible. + */ + MHD_AF_DUAL_v6_OPTIONAL = 6 + }; @@ -2524,22 +2797,22 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_SockPollSyscall * Automatic selection of best-available method. This is also the * default. */ - MHD_ELS_AUTO = 0 + MHD_SPS_AUTO = 0 , /** * Use select(). */ - MHD_ELS_SELECT = 1 + MHD_SPS_SELECT = 1 , /** * Use poll(). */ - MHD_ELS_POLL = 2 + MHD_SPS_POLL = 2 , /** * Use epoll. */ - MHD_ELS_EPOLL = 3 + MHD_SPS_EPOLL = 3 }; @@ -3953,9 +4226,6 @@ MHD_FN_PAR_NONNULL_ (1); * other things required to run webserver. * Once all connections are processed, function returns. * - * The optional @a next_max_wait pointer returns the same value as - * if #MHD_DAEMON_INFO_DYNAMIC_MAX_TIME_TO_WAIT would requested immediately. - * * @param daemon the daemon to run * @return #MHD_SC_OK on success, otherwise * an error code @@ -4566,10 +4836,19 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_HTTP_StatusCode */ MHD_EXTERN_ const struct MHD_String * MHD_HTTP_status_code_to_string (enum MHD_HTTP_StatusCode code) -MHD_FN_PURE_; +MHD_FN_CONST_; + +/** + * Get the pointer to the C string for the HTTP response code, never NULL. + */ +#define MHD_HTTP_status_code_to_string_lazy(code) \ + (MHD_HTTP_status_code_to_string ((code)) ? \ + ((MHD_HTTP_status_code_to_string (code))->cstr) : ("[No status]") ) + /** @} */ /* end of group httpcode */ +#ifndef MHD_HTTP_PROTOCOL_VER_DEFINED /** * @brief HTTP protocol versions @@ -4588,20 +4867,23 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_ProtocolVersion , MHD_HTTP_VERSION_3_0 = 4 , - MHD_HTTP_VERSION_FUTURE = 99 + MHD_HTTP_VERSION_FUTURE = 255 }; +#define MHD_HTTP_PROTOCOL_VER_DEFINED 1 +#endif /* ! MHD_HTTP_PROTOCOL_VER_DEFINED */ + /** * Return the string representation of the requested HTTP version. * Note: this is suitable mainly for logging and similar proposes as * HTTP/2 (and later) is not used inside the HTTP protocol. * @param pv the protocol version * @return the string representation of the protocol version, - * NULL for invalid values + * NULL for unknown values */ MHD_EXTERN_ const struct MHD_String * MHD_protocol_version_to_string (enum MHD_HTTP_ProtocolVersion pv) -MHD_FN_PURE_; +MHD_FN_CONST_; /** * HTTP/1.0 identification string @@ -4691,22 +4973,15 @@ struct MHD_Response; * client. * * @param[in,out] request the request for which the action is generated - * @param suspend_microsec the maximum duration of suspension after which - * the request is automatically resumed, if not - * resumed earlier by #MHD_request_resume(), - * the precise resume moment is not guaranteed, it - * may happen later (but not earlier) depending - * on timer granularity and the system load; - * if set to #MHD_WAIT_INDEFINITELY (or higher) - * the request is not resumed automatically - * @return action to cause a request to be suspended. + * @return action to cause a request to be suspended, + * NULL if any action has been already created for the @a request * @ingroup action */ MHD_EXTERN_ const struct MHD_Action * -MHD_action_suspend (struct MHD_Request *request, - uint_fast64_t suspend_microsec) +MHD_action_suspend (struct MHD_Request *request) MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_; + /** * Converts a @a response to an action. If #MHD_R_O_REUSABLE * is not set, the reference to the @a response is consumed @@ -4722,14 +4997,28 @@ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_; * #MHD_action_close_connection() call * @return pointer to the action, the action must be consumed * otherwise response object may leak; - * NULL if failed (no memory), when failed - * the response object is consumed and need not - * to be "destroyed". + * NULL if failed (no memory) or if any action has been already + * created for the @a request; + * when failed the response object is consumed and need not + * to be "destroyed" * @ingroup action */ MHD_EXTERN_ const struct MHD_Action * MHD_action_from_response (struct MHD_Request *request, - struct MHD_Response *response); + struct MHD_Response *response) +MHD_FN_PAR_NONNULL_(1); + + +/** + * Action telling MHD to close the connection hard + * (kind-of breaking HTTP specification). + * + * @param req the request to make an action + * @return action operation, always NULL + * @ingroup action + */ +#define MHD_action_close_connection(req) \ + MHD_STATIC_CAST_ (const struct MHD_Action *, NULL) /** @@ -4996,6 +5285,7 @@ typedef const struct MHD_DynamicContentCreatorAction * * @param dyn_cont_cls extra argument to @a crc * @param dyn_cont_fc callback to call to free @a dyn_cont_cls resources * @return NULL on error (i.e. invalid arguments, out of memory) + * FIXME: Call free callback on error? * @ingroup response */ MHD_EXTERN_ struct MHD_Response * @@ -5020,6 +5310,7 @@ MHD_response_from_callback (enum MHD_HTTP_StatusCode sc, * to skip the free/cleanup callback * @param free_cb_cls the parameter for @a free_cb * @return NULL on error (i.e. invalid arguments, out of memory) + * FIXME: Call free callback on error? * @ingroup response */ MHD_EXTERN_ struct MHD_Response * @@ -5040,7 +5331,7 @@ MHD_FN_PAR_IN_SIZE_ (3,2); * @param sc status code to use for the response */ #define MHD_response_from_empty(sc) \ - MHD_response_from_buffer (sc, 0, NULL, NULL, NULL) + MHD_response_from_buffer (sc, 0, "", NULL, NULL) /** @@ -5053,6 +5344,7 @@ MHD_FN_PAR_IN_SIZE_ (3,2); * an internal copy will be made, there is no need to * keep this data after return from this function * @return NULL on error (i.e. invalid arguments, out of memory) + * FIXME: Call free callback on error? * @ingroup response */ MHD_EXTERN_ struct MHD_Response * @@ -5099,6 +5391,7 @@ struct MHD_IoVec * the response is destroyed. * @param free_cb_cls the argument passed to @a free_cb * @return NULL on error (i.e. invalid arguments, out of memory) + * FIXME: Call free callback on error? * @ingroup response */ MHD_EXTERN_ struct MHD_Response * @@ -5126,6 +5419,7 @@ MHD_response_from_iovec ( * sizes larger than 2 GiB may be not supported by OS or * MHD build; see #MHD_LIB_INFO_FIXED_HAS_LARGE_FILE * @return NULL on error (i.e. invalid arguments, out of memory) + * FIXME: Close FD on error? * @ingroup response */ MHD_EXTERN_ struct MHD_Response * @@ -5151,11 +5445,13 @@ MHD_FN_PAR_FD_READ_ (2); * data; will be closed when response is destroyed; * fd should be in 'blocking' mode * @return NULL on error (i.e. invalid arguments, out of memory) + * FIXME: Close pipe FD on error? * @ingroup response */ MHD_EXTERN_ struct MHD_Response * MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc, - int fd); + int fd) +MHD_FN_PAR_FD_READ_ (2); /** @@ -5218,14 +5514,62 @@ MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3); /** - * Action telling MHD to continue processing the upload. + * Suspend handling of network data for a given request. This can + * be used to dequeue a request from MHD's event loop for a while. * - * @param req the request to make an action - * @return action operation, never NULL + * Suspended requests continue to count against the total number of + * requests allowed (per daemon, as well as per IP, if such limits + * are set). Suspended requests will NOT time out; timeouts will + * restart when the request handling is resumed. While a + * request is suspended, MHD may not detect disconnects by the + * client. + * + * @param[in,out] request the request for which the action is generated + * @return action to cause a request to be suspended, + * NULL if any action has been already created for the @a request * @ingroup action */ -MHD_EXTERN_ const struct MHD_Action * -MHD_action_continue (struct MHD_Request *req); +MHD_EXTERN_ const struct MHD_UploadAction * +MHD_upload_action_suspend (struct MHD_Request *request) +MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_; + +/** + * Converts a @a response to an action. If #MHD_R_O_REUSABLE + * is not set, the reference to the @a response is consumed + * by the conversion. If #MHD_R_O_REUSABLE is #MHD_YES, + * then the @a response can be used again to create actions in + * the future. + * However, the @a response is frozen by this step and + * must no longer be modified (i.e. by setting headers). + * + * @param request the request to create the action for + * @param[in] response the response to convert, + * if NULL then this function is equivalent to + * #MHD_upload_action_close_connection() call + * @return pointer to the action, the action must be consumed + * otherwise response object may leak; + * NULL if failed (no memory) or if any action has been already + * created for the @a request; + * when failed the response object is consumed and need not + * to be "destroyed" + * @ingroup action + */ +MHD_EXTERN_ const struct MHD_UploadAction * +MHD_upload_action_from_response (struct MHD_Request *request, + struct MHD_Response *response) +MHD_FN_PAR_NONNULL_(1); + +/** + * Action telling MHD to continue processing the upload. + * + * @param request the request to make an action + * @return action operation, + * NULL if any action has been already created for the @a request + * @ingroup action + */ +MHD_EXTERN_ const struct MHD_UploadAction * +MHD_upload_action_continue (struct MHD_Request *request) +MHD_FN_RETURNS_NONNULL_; /** @@ -5236,9 +5580,10 @@ MHD_action_continue (struct MHD_Request *req); * @return action operation, always NULL * @ingroup action */ -#define MHD_action_close_connection(req) \ - MHD_STATIC_CAST_ (const struct MHD_Action *, NULL) +#define MHD_upload_action_close_connection(req) \ + MHD_STATIC_CAST_ (const struct MHD_UploadAction *, NULL) +#ifndef MHD_UPLOADCALLBACK_DEFINED /** * Function to process data uploaded by a client. @@ -5247,26 +5592,29 @@ MHD_action_continue (struct MHD_Request *req); * pointer when the handler was registered with MHD * @param request the request is being processed * @param content_data_size the size of the @a content_data, - * zero if all data have been processed + * zero when all data have been processed * @param[in] content_data the uploaded content data, * may be modified in the callback, * valid only until return from the callback, - * NULL is all data have been processed - * @return action specifying how to proceed, often - * #MHD_action_continue() if all is well, - * #MHD_action_suspend() to stop reading the upload until - * the request is resumed, - * MHD_action_close_connection to close the socket, or a response - * to discard the rest of the upload and return the data given + * NULL when all data have been processed + * @return action specifying how to proceed: + * #MHD_upload_action_continue() if all is well, + * #MHD_upload_action_suspend() to stop reading the upload until + * the request is resumed, + * #MHD_upload_action_close_connection() to close the socket, + * or a response to discard the rest of the upload and transmit + * the response * @ingroup action */ -typedef const struct MHD_Action * -(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_IN_SIZE_ (4,3) +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 */ /** * Create an action that handles an upload. @@ -5275,10 +5623,10 @@ typedef const struct MHD_Action * * then request is aborted without response. * * @param request the request to create action for - * @param upload_buffer_size how large should the upload buffer be. - * May allocate memory from the shared "large" - * memory pool if necessary and non-zero is given. - * Must be zero if @a uc_full is NULL. + * @param large_buffer_size how large should the upload buffer be. + * May allocate memory from the shared "large" + * memory pool if necessary and non-zero is given. + * Must be zero if @a uc_full is NULL. * @param uc_full the function to call when complete upload * is received (only if fit @a upload_buffer_size), * can be NULL if uc_inc is not NULL, @@ -5290,14 +5638,17 @@ typedef const struct MHD_Action * * @a uc_full is NULL, * can be NULL if uc_full is not NULL * @param uc_inc_cls closure for @a uc_inc - * @return NULL on error (out of memory. both @a uc_full and @a uc_inc are NULL) + * @return NULL on error (out of memory, invalid parameters) + * @return pointer to the action, + * NULL if failed (no memory) or if any action has been already + * created for the @a request. * @sa #MHD_D_OPTION_LARGE_POOL_SIZE() * @ingroup action */ MHD_EXTERN_ const struct MHD_Action * MHD_action_process_upload ( struct MHD_Request *request, - size_t upload_buffer_size, + size_t large_buffer_size, MHD_UploadCallback uc_full, void *uc_full_cls, MHD_UploadCallback uc_inc, @@ -5313,7 +5664,7 @@ MHD_FN_PAR_NONNULL_ (1); * @param uc the function to call when complete upload * is received (only if fit @a upload_buffer_size) * @param uc_cls closure for @a uc - * @return NULL on error (out of memory. both @a uc_full and @a uc_inc are NULL) + * @return NULL on error (out of memory. both @a uc is NULL) * @ingroup action */ #define MHD_action_process_upload_full(request,buff_size,uc,uc_cls) \ @@ -5325,12 +5676,13 @@ MHD_FN_PAR_NONNULL_ (1); * @param request the request to create action for * @param uc the function to incrementally process the upload data * @param uc_cls closure for @a uc - * @return NULL on error (out of memory. both @a uc_full and @a uc_inc are NULL) + * @return NULL on error (out of memory. both @a uc is NULL) * @ingroup action */ #define MHD_action_process_upload_inc(request,uc,uc_cls) \ MHD_action_process_upload (request, 0, NULL, NULL, uc, uc_cls) +#ifndef MHD_POST_DATA_READER_DEFINED /** * Iterator over key-value pairs where the value maybe made available @@ -5348,15 +5700,16 @@ MHD_FN_PAR_NONNULL_ (1); * NOT zero-terminated * @param off offset of data in the overall value * @param size number of bytes in @a data available - * @return action specifying how to proceed, often - * #MHD_action_continue() if all is well, - * #MHD_action_suspend() to stop reading the upload until - * the request is resumed, - * NULL to close the socket, or a response - * to discard the rest of the upload and return the data given + * @return action specifying how to proceed: + * #MHD_upload_action_continue() if all is well, + * #MHD_upload_action_suspend() to stop reading the upload until + * the request is resumed, + * #MHD_upload_action_close_connection() to close the socket, + * or a response to discard the rest of the upload and transmit + * the response * @ingroup action */ -typedef const struct MHD_Action * +typedef const struct MHD_UploadAction * (*MHD_PostDataReader) (void *cls, const struct MHD_String *name, const struct MHD_String *filename, @@ -5372,12 +5725,15 @@ typedef const struct MHD_Action * * of the postprocessor upload data. * @param req the request * @param cls the closure - * @return the action to proceed + * @return the action to proceed, #MHD_upload_action_continue() is not possible + * an */ -typedef const struct MHD_Action * +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 */ /** * Create an action to parse the POSTed body from the client. @@ -5386,8 +5742,8 @@ typedef const struct MHD_Action * * @param pp_buffer_size how much data should the post processor * buffer in memory. May allocate memory from * the shared "large" memory pool if necessary. - * @param pp_stream_limit values above which length should be - * given to @a iter for stream processing + * @param pp_stream_limit values above which length should be // FIXME: Remove? Duplicated with pp_buffer_size + * given to @a iter for stream processing // FIXME: iter?? * @param enc the data encoding to use, * set to #MHD_HTTP_POST_ENCODING_OTHER to detect automatically * @param reader function to call for "oversize" values in the stream, @@ -5399,13 +5755,16 @@ typedef const struct MHD_Action * * #MHD_request_get_values_cb(), #MHD_request_get_values_list() and * #MHD_request_get_post_data_cb(), #MHD_request_get_post_data_list() * @param done_cb_cls closure for @a done_cb + * @return pointer to the action, + * NULL if failed (no memory) or if any action has been already + * created for the @a request. * @sa #MHD_D_OPTION_LARGE_POOL_SIZE() * @ingroup action */ MHD_EXTERN_ struct MHD_Action * MHD_action_post_processor (struct MHD_Request *request, size_t pp_buffer_size, - size_t pp_stream_limit, + size_t pp_stream_limit, // FIXME: Remove? Duplicated with pp_buffer_size enum MHD_HTTP_PostEncoding enc, MHD_PostDataReader reader, void *reader_cls, @@ -8274,19 +8633,23 @@ MHD_FN_PURE_; * Callback for serious error condition. The default action is to print * an error message and `abort()`. * The callback should not return. + * Some parameters could be empty strings (the strings with zero-termination at + * zero position) if MHD built without log messages (only for embedded + * projects). * * @param cls user specified value - * @param file where the error occurred, could be NULL if MHD built without - * messages (only for embedded project) + * @param file where the error occurred, could be empty + * @param func the name of the function, where the error occurred, may be empty * @param line where the error occurred - * @param reason error detail, may be NULL + * @param message the error details, could be empty * @ingroup logging */ typedef void (*MHD_PanicCallback) (void *cls, const char *file, + const char *func, unsigned int line, - const char *reason); + const char *message); /** @@ -8313,4 +8676,7 @@ MHD_lib_set_panic_func (MHD_PanicCallback cb, MHD_lib_set_panic_func (MHD_STATIC_CAST_ (MHD_PanicCallback,NULL),NULL) MHD_C_DECLRATIONS_FINISH_HERE_ +MHD_EXTERN_ void +MHD_lib_global_fake (void); + #endif /* ! MICROHTTPD2_H */ diff --git a/src/include/microhttpd2_generated_response_options.h b/src/include/microhttpd2_generated_response_options.h index e1144f55..c7470d4f 100644 --- a/src/include/microhttpd2_generated_response_options.h +++ b/src/include/microhttpd2_generated_response_options.h @@ -46,6 +46,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption * Response still use HTTP/1.1 version in header, but always close the connection after sending the response and do not use chunked encoding for the response. * You can also set the #MHD_R_O_HTTP_1_0_SERVER flag to force HTTP/1.0 version in the response. * Responses are still compatible with HTTP/1.1. + * * This option can be used to communicate with some broken client, which does not implement HTTP/1.1 features, but advertises HTTP/1.1 support. */ MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT = 80 @@ -58,6 +59,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption * Chunked encoding will not be used for the response. * Due to backward compatibility, responses still can be used with HTTP/1.1 clients. * This option can be used to emulate HTTP/1.0 server (for response part only as chunked encoding in requests (if any) is processed by MHD). + * * With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections). */ MHD_R_O_HTTP_1_0_SERVER = 81 diff --git a/src/include/microhttpd2_portability.h b/src/include/microhttpd2_portability.h index 5d2ef3ff..48341def 100644 --- a/src/include/microhttpd2_portability.h +++ b/src/include/microhttpd2_portability.h @@ -319,11 +319,11 @@ typedef SOCKET MHD_Socket; * 'pragma push/pop' support was added in GCC 4.6.0 */ # define MHD_WARN_PUSH_ _Pragma("GCC diagnostic push") # define MHD_WARN_POP_ _Pragma("GCC diagnostic pop") -# define MHD_WARN_INGORE_(warn) \ +# define MHD_WARN_IGNORE_(warn) \ _Pragma(MHD_MACRO_STR_(GCC diagnostic ignored MHD_MACRO_STR__(warn))) # ifdef MHD_USE_VARARG_MACROS_EXT # define MHD_NOWARN_VARIADIC_MACROS_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wvariadic-macros) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wvariadic-macros) # define MHD_RESTORE_WARN_VARIADIC_MACROS_ MHD_WARN_POP_ # endif # ifdef MHD_USE_COMPOUND_LITERALS_EXT @@ -331,43 +331,43 @@ typedef SOCKET MHD_Socket; # define MHD_RESTORE_WARN_COMPOUND_LITERALS_ /* empty */ # endif # define MHD_NOWARN_UNUSED_FUNC_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wunused-function) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wunused-function) # define MHD_RESTORE_WARN_UNUSED_FUNC_ MHD_WARN_POP_ # elif MHD_CLANG_MINV (3,1) # define MHD_WARN_PUSH_ _Pragma("clang diagnostic push") # define MHD_WARN_POP_ _Pragma("clang diagnostic pop") -# define MHD_WARN_INGORE_(warn) \ +# define MHD_WARN_IGNORE_(warn) \ _Pragma(MHD_MACRO_STR_(clang diagnostic ignored MHD_MACRO_STR__(warn))) # ifdef MHD_USE_VARARG_MACROS_EXT # define MHD_NOWARN_VARIADIC_MACROS_ \ MHD_WARN_PUSH_ \ - MHD_WARN_INGORE_ (-Wvariadic-macros) \ - MHD_WARN_INGORE_ (-Wc++98-compat-pedantic) + MHD_WARN_IGNORE_ (-Wvariadic-macros) \ + MHD_WARN_IGNORE_ (-Wc++98-compat-pedantic) # define MHD_RESTORE_WARN_VARIADIC_MACROS_ MHD_WARN_POP_ # else /* ! MHD_USE_VARARG_MACROS_EXT */ # define MHD_NOWARN_VARIADIC_MACROS_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wc++98-compat-pedantic) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wc++98-compat-pedantic) # define MHD_RESTORE_WARN_VARIADIC_MACROS_ MHD_WARN_POP_ # endif # ifdef MHD_USE_CPP_INIT_LIST # define MHD_NOWARN_CPP_INIT_LIST_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wc++98-compat) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wc++98-compat) # define MHD_RESTORE_WARN_CPP_INIT_LIST_ MHD_WARN_POP_ # endif # ifdef MHD_USE_COMPOUND_LITERALS_EXT # define MHD_NOWARN_COMPOUND_LITERALS_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wc99-extensions) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wc99-extensions) # define MHD_RESTORE_WARN_COMPOUND_LITERALS_ MHD_WARN_POP_ # endif # define MHD_NOWARN_UNUSED_FUNC_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (-Wunused-function) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (-Wunused-function) # define MHD_RESTORE_WARN_UNUSED_FUNC_ MHD_WARN_POP_ # elif MHD_MSC_MINV (1500) # define MHD_WARN_PUSH_ __pragma(warning(push)) # define MHD_WARN_POP_ __pragma(warning(pop)) -# define MHD_WARN_INGORE_(warn) __pragma(warning(disable:warn)) +# define MHD_WARN_IGNORE_(warn) __pragma(warning(disable:warn)) # define MHD_NOWARN_UNUSED_FUNC_ \ - MHD_WARN_PUSH_ MHD_WARN_INGORE_ (4514) + MHD_WARN_PUSH_ MHD_WARN_IGNORE_ (4514) # define MHD_RESTORE_WARN_UNUSED_FUNC_ MHD_WARN_POP_ # endif #endif /*! MHD_NO__PRAGMA */ @@ -378,8 +378,8 @@ typedef SOCKET MHD_Socket; #ifndef MHD_WARN_POP_ # define MHD_WARN_POP_ /* empty */ #endif -#ifndef MHD_WARN_INGORE_ -# define MHD_WARN_INGORE_(ignored) /* empty */ +#ifndef MHD_WARN_IGNORE_ +# define MHD_WARN_IGNORE_(ignored) /* empty */ #endif #ifndef MHD_NOWARN_VARIADIC_MACROS_ # define MHD_NOWARN_VARIADIC_MACROS_ /* empty */ @@ -485,14 +485,18 @@ typedef SOCKET MHD_Socket; #endif /* !MHD_DEPR_FUNC_ */ #ifdef __has_attribute -# if __has_attribute (enum_extensibility) +# ifndef MHD_FIXED_ENUM_ +# if __has_attribute (enum_extensibility) /* Enum will not be extended */ -# define MHD_FIXED_ENUM_ __attribute__((enum_extensibility (closed))) -# endif /* enum_extensibility */ -# if __has_attribute (flag_enum) +# define MHD_FIXED_ENUM_ __attribute__((enum_extensibility (closed))) +# endif /* enum_extensibility */ +# endif +# ifndef MHD_FLAGS_ENUM_ +# if __has_attribute (flag_enum) /* Enum is a bitmap */ -# define MHD_FLAGS_ENUM_ __attribute__((flag_enum)) -# endif /* flag_enum */ +# define MHD_FLAGS_ENUM_ __attribute__((flag_enum)) +# endif /* flag_enum */ +# endif #endif /* __has_attribute */ #ifndef MHD_FIXED_ENUM_ @@ -592,6 +596,16 @@ typedef SOCKET MHD_Socket; # endif # endif /* const && !MHD_FN_CONST_ */ +/* Override detected value of MHD_FN_RETURNS_NONNULL_ by defining it before + * including the header */ +# if __has_attribute (returns_nonnull) && \ + ! defined(MHD_FN_RETURNS_NONNULL_) +/** + * MHD_FN_RETURNS_NONNULL_ indicates that function never returns NULL. + */ +# define MHD_FN_RETURNS_NONNULL_ __attribute__ ((returns_nonnull)) +# endif /* returns_nonnull && !MHD_FN_RETURNS_NONNULL_ */ + /* Override detected value of MHD_FN_MUST_CHECK_RESULT_ by defining it before * including the header */ # if __has_attribute (warn_unused_result) && \ @@ -629,7 +643,7 @@ typedef SOCKET MHD_Socket; # if __has_attribute (access) /* Override detected value of MHD_FN_PAR_IN_ by defining it before - * including the header */ + * including the header */ # if ! defined(MHD_FN_PAR_IN_) /** * MHD_FN_PAR_IN_ indicates function parameter points to data @@ -704,7 +718,7 @@ typedef SOCKET MHD_Socket; # if __has_attribute (fd_arg_read) && \ ! defined(MHD_FN_PAR_FD_READ_) /** - * MHD_FN_PAR_IN_ indicates function parameter is file descriptor that + * MHD_FN_PAR_FD_READ_ indicates function parameter is file descriptor that * must be in open state and available for reading */ # define MHD_FN_PAR_FD_READ_(param_num) \ @@ -716,34 +730,13 @@ typedef SOCKET MHD_Socket; # if __has_attribute (null_terminated_string_arg) && \ ! defined(MHD_FN_PAR_CSTR_) /** - * MHD_FN_PAR_IN_ indicates function parameter is file descriptor that + * MHD_FN_PAR_CSTR_ indicates function parameter is file descriptor that * must be in open state and available for reading */ # define MHD_FN_PAR_CSTR_(param_num) \ __attribute__ ((null_terminated_string_arg (param_num))) # endif /* null_terminated_string_arg && !MHD_FN_PAR_CSTR_ */ -/* Override detected value of MHD_FN_RETURNS_NONNULL_ by defining it before - * including the header */ -# if __has_attribute (returns_nonnull) && \ - ! defined(MHD_FN_RETURNS_NONNULL_) -/** - * MHD_FN_RETURNS_NONNULL_ indicates that function never returns NULL. - */ -# define MHD_FN_RETURNS_NONNULL_ __attribute__ ((returns_nonnull)) -# endif /* returns_nonnull && !MHD_FN_RETURNS_NONNULL_ */ - -/* Override detected value of MHD_FN_WARN_UNUSED_RESULT_ by defining it before - * including the header */ -# if __has_attribute (warn_unused_result) && \ - ! defined(MHD_FN_WARN_UNUSED_RESULT_) -/** - * MHD_FN_WARN_UNUSED_RESULT_ that function return value should not be ignored - */ -# define MHD_FN_WARN_UNUSED_RESULT_ \ - __attribute__ ((warn_unused_result)) -# endif /* warn_unused_result && !MHD_FN_WARN_UNUSED_RESULT_ */ - # endif /* __has_attribute */ #endif /* ! MHD_NO_FUNC_ATTRIBUTES */ @@ -780,6 +773,9 @@ typedef SOCKET MHD_Socket; #ifndef MHD_FN_PURE_ # define MHD_FN_PURE_ /* empty */ #endif /* ! MHD_FN_PURE_ */ +#ifndef MHD_FN_RETURNS_NONNULL_ +# define MHD_FN_RETURNS_NONNULL_ /* empty */ +#endif /* ! MHD_FN_RETURNS_NONNULL_ */ #ifndef MHD_FN_MUST_CHECK_RESULT_ # define MHD_FN_MUST_CHECK_RESULT_ /* empty */ #endif /* ! MHD_FN_MUST_CHECK_RESULT_ */ @@ -813,9 +809,3 @@ typedef SOCKET MHD_Socket; #ifndef MHD_FN_PAR_CSTR_ # define MHD_FN_PAR_CSTR_(param_num) /* empty */ #endif /* ! MHD_FN_PAR_CSTR_ */ -#ifndef MHD_FN_RETURNS_NONNULL_ -# define MHD_FN_RETURNS_NONNULL_ /* empty */ -#endif /* ! MHD_FN_RETURNS_NONNULL_ */ -#ifndef MHD_FN_WARN_UNUSED_RESULT_ -# define MHD_FN_WARN_UNUSED_RESULT_ /* empty */ -#endif /* ! MHD_FN_WARN_UNUSED_RESULT_ */ diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am new file mode 100644 index 00000000..1c569739 --- /dev/null +++ b/src/mhd2/Makefile.am @@ -0,0 +1,126 @@ +# This Makefile.am is in the public domain + +AM_CPPFLAGS = \ + -I$(srcdir)/../incl_priv \ + -I$(srcdir)/../include \ + $(CPPFLAGS_ac) + +AM_CFLAGS = $(CFLAGS_ac) + +if USE_COVERAGE + AM_CFLAGS += --coverage +endif + +AM_LDFLAGS = $(LDFLAGS_ac) + +AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) + +lib_LTLIBRARIES = \ + libmicrohttpd2.la + +noinst_DATA = +MOSTLYCLEANFILES = + +libmicrohttpd2_la_SOURCES = \ + $(CONFIG_HEADER) \ + autoinit_funcs.h \ + sys_null_macro.h sys_base_types.h sys_bool_type.h \ + sys_sockets_types.h sys_sockets_headers.h sys_ip_headers.h \ + sys_errno.h sys_malloc.h \ + sys_poll.h \ + sys_sendfile.h \ + compat_calloc.h \ + mhd_assert.h \ + mhd_tristate.h \ + mhd_socket_types.h mhd_sockets_macros.h \ + mhd_sockets_funcs.c mhd_sockets_funcs.h \ + mhd_iovec.h \ + mhd_panic.c mhd_panic.h \ + mhd_lib_init.c mhd_lib_init_impl.h mhd_lib_init.h \ + mhd_dlinked_list.h \ + mhd_locks.h \ + mhd_itc.c mhd_itc.h mhd_itc_types.h \ + mhd_threads.c mhd_threads.h sys_thread_entry_type.h \ + mhd_mono_clock.c mhd_mono_clock.h \ + mhd_mempool.c mhd_mempool.h \ + mhd_daemon.h daemon_funcs.c daemon_funcs.h \ + mhd_public_api.h \ + events_process.c events_process.h \ + daemon_logger.c daemon_logger.h \ + daemon_logger_default.c daemon_logger_default.h \ + daemon_options.h daemon_set_options.c \ + daemon_create.c \ + daemon_start.c \ + daemon_add_conn.c daemon_add_conn.h \ + mhd_connection.h mhd_request.h mhd_reply.h \ + response_from.c response_from.h + + +if ! HAVE_SYS_CALLOC +libmicrohttpd2_la_SOURCES += \ + compat_calloc.c +endif + + +libmicrohttpd2_la_CPPFLAGS = \ + $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \ + -DBUILDING_MHD_LIB=1 +libmicrohttpd2_la_CFLAGS = \ + $(AM_CFLAGS) $(MHD_LIB_CFLAGS) $(MHD_TLS_LIB_CFLAGS) +libmicrohttpd2_la_LDFLAGS = \ + $(AM_LDFLAGS) $(MHD_LIB_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS) \ + $(W32_MHD_LIB_LDFLAGS) \ + -export-dynamic -no-undefined \ + -version-info @LIB_VERSION_CURRENT@:@LIB_VERSION_REVISION@:@LIB_VERSION_AGE@ +libmicrohttpd2_la_LIBADD = \ + $(MHD_LIBDEPS) $(MHD_TLS_LIBDEPS) + + +AM_V_RC = $(am__v_RC_@AM_V@) +am__v_RC_ = $(am__v_RC_@AM_DEFAULT_V@) +am__v_RC_0 = @echo " RC " $@; +am__v_RC_1 = + +# General rule is not required, but keep it just in case +# Note: windres does not understand '-isystem' flag, so all +# possible '-isystem' flags are replaced by simple '-I' flags. +.rc.lo: + $(AM_V_RC) RC_ALL_CPPFLAGS=`echo ' $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) ' | $(SED) -e 's/ -isystem / -I/g'`; \ + $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_ALL_CPPFLAGS} $< -o $@ + +# Note: windres does not understand '-isystem' flag, so all +# possible '-isystem' flags are replaced by simple '-I' flags. +libmicrohttpd2_la-w32_lib_res.lo: $(builddir)/w32_lib_res.rc + $(AM_V_RC) RC_ALL_CPPFLAGS=`echo ' $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) ' | $(SED) -e 's/ -isystem / -I/g'`; \ + $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_ALL_CPPFLAGS} $(builddir)/w32_lib_res.rc -o $@ + +if HAVE_W32 +MHD_DLL_RES_LO = libmicrohttpd2_la-w32_lib_res.lo +else +MHD_DLL_RES_LO = +endif + +EXTRA_libmicrohttpd2_la_DEPENDENCIES = $(MHD_DLL_RES_LO) +libmicrohttpd2_la_LIBADD += $(MHD_DLL_RES_LO) + +#TESTS = $(check_PROGRAMS) + +update-po-POTFILES.in: $(top_srcdir)/po/POTFILES.in + +$(top_srcdir)/po/POTFILES.in: $(srcdir)/Makefile.am + @echo "Creating $@" + @echo src/include/microhttpd2.h > "$@" && \ + for src in $(am__libmicrohttpd2_la_SOURCES_DIST) ; do \ + echo "$(subdir)/$$src" >> "$@" ; \ + done + +.PHONY: update-po-POTFILES.in + + +$(CONFIG_HEADER): $(builddir)/../incl_priv/mhd_config.h.in $(top_srcdir)/configure $(top_builddir)/config.status + @echo "cd $(srcdir)/../incl_priv && $(MAKE) $(AM_MAKEFLAGS) mhd_config.h" && \ + $(am__cd) $(srcdir)/../incl_priv && $(MAKE) $(AM_MAKEFLAGS) mhd_config.h + +$(builddir)/../incl_priv/mhd_config.h.in: $(top_srcdir)/configure.ac + @echo "cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh" && \ + $(am__cd) $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh diff --git a/src/mhd2/action.c b/src/mhd2/action.c new file mode 100644 index 00000000..e841b693 --- /dev/null +++ b/src/mhd2/action.c @@ -0,0 +1,168 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/action.c + * @brief The definition of the MHD_action_*() and MHD_upload_action_*() + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "mhd_action.h" +#include "mhd_request.h" +#include "mhd_public_api.h" + + +MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_ +const struct MHD_Action * +MHD_action_suspend (struct MHD_Request *request) +{ + struct MHD_Action *const restrict head_act = &(request->app_act.head_act); + head_act->act = mhd_ACTION_SUSPEND; + return head_act; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) const struct MHD_Action * +MHD_action_from_response (struct MHD_Request *request, + struct MHD_Response *response) +{ + struct MHD_Action *const restrict head_act = &(request->app_act.head_act); + if (mhd_ACTION_NO_ACTION != head_act->act) + return NULL; + if (NULL == response) + return (const struct MHD_Action *) NULL; + + head_act->act = mhd_ACTION_RESPONSE; + head_act->data.response = response; + + return head_act; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) const struct MHD_Action * +MHD_action_process_upload (struct MHD_Request *request, + size_t large_buffer_size, + MHD_UploadCallback uc_full, + void *uc_full_cls, + MHD_UploadCallback uc_inc, + void *uc_inc_cls) +{ + struct MHD_Action *const restrict head_act = &(request->app_act.head_act); + if (mhd_ACTION_NO_ACTION != head_act->act) + return NULL; + if (0 == large_buffer_size) + { + if (NULL != uc_full) + return NULL; + if (NULL == uc_inc) + return NULL; + } + else + { + if (NULL == uc_full) + return NULL; + } + + head_act->act = mhd_ACTION_UPLOAD; + head_act->data.upload.large_buffer_size = large_buffer_size; + head_act->data.upload.full.cb = uc_full; + head_act->data.upload.full.cls = uc_full_cls; + head_act->data.upload.inc.cb = uc_inc; + head_act->data.upload.inc.cls = uc_inc_cls; + + return head_act; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) struct MHD_Action * +MHD_action_post_processor (struct MHD_Request *request, + size_t pp_buffer_size, + size_t pp_stream_limit, + enum MHD_HTTP_PostEncoding enc, + MHD_PostDataReader reader, + void *reader_cls, + MHD_PostDataFinished done_cb, + void *done_cb_cls) +{ + struct MHD_Action *const restrict head_act = &(request->app_act.head_act); + if (mhd_ACTION_NO_ACTION != head_act->act) + return NULL; + if (NULL == done_cb) + return NULL; + + head_act->act = mhd_ACTION_POST_PROCESS; + head_act->data.post_process.pp_buffer_size = pp_buffer_size; + head_act->data.post_process.pp_stream_limit = pp_stream_limit; + head_act->data.post_process.enc = enc; + head_act->data.post_process.reader = reader; + head_act->data.post_process.reader_cls = reader_cls; + head_act->data.post_process.done_cb = done_cb; + head_act->data.post_process.done_cb_cls = done_cb_cls; + + return head_act; +} + + +MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_ +const struct MHD_UploadAction * +MHD_upload_action_suspend (struct MHD_Request *request) +{ + struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act); + if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act) + return NULL; + + upl_act->act = mhd_UPLOAD_ACTION_SUSPEND; + + return upl_act; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) const struct MHD_UploadAction * +MHD_upload_action_from_response (struct MHD_Request *request, + struct MHD_Response *response) +{ + struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act); + if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act) + return NULL; + + upl_act->act = mhd_UPLOAD_ACTION_SUSPEND; + upl_act->data.response = response; + + return upl_act; +} + + +MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ const struct MHD_UploadAction * +MHD_upload_action_continue (struct MHD_Request *request) +{ + struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act); + if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act) + return NULL; + + upl_act->act = mhd_UPLOAD_ACTION_CONTINUE; + + return upl_act; +} diff --git a/src/include/autoinit_funcs.h b/src/mhd2/autoinit_funcs.h similarity index 81% rename from src/include/autoinit_funcs.h rename to src/mhd2/autoinit_funcs.h index 0c2e47bd..5f5d119d 100644 --- a/src/include/autoinit_funcs.h +++ b/src/mhd2/autoinit_funcs.h @@ -85,13 +85,13 @@ (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100) #define GNUC_SET_INIT_AND_DEINIT(FI,FD) \ - void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void); \ - void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \ - void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void) \ - { (void) (FI) (); } \ - void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void) \ - { (void) (FD) (); } \ - struct _GNUC_dummy_str_ ## FI {int i;} + void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void); \ + void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \ + void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void) \ + { (void) (FI) (); } \ + void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void) \ + { (void) (FD) (); } \ + struct _GNUC_dummy_str_ ## FI {int i;} #define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT (FI,FD) #define _AUTOINIT_FUNCS_ARE_SUPPORTED 1 @@ -144,7 +144,7 @@ /* Internal variable prefix (can be any) */ #define W32_INITHELPERVARNAME(f) _initHelperDummy_ ## f #define W32_INITHELPERVARNAMEDECORSTR(f) \ - W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f)) + W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f)) /* Declare section (segment), put variable pointing to init function to chosen segment, force linker to always include variable to avoid omitting by optimiser */ @@ -152,10 +152,10 @@ void __cdecl FuncName(void) */ /* "extern" with initialisation value means that variable is declared AND defined. */ #define W32_VFPTR_IN_SEG(S,F) \ - __pragma (section (S,long,read)) \ - __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ - W32_INITVARDECL __declspec(allocate (S))void \ - (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F + __pragma(section (S,long,read)) \ + __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ + W32_INITVARDECL __declspec(allocate (S))void \ + (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F /* Sections (segments) for pointers to initialisers/deinitialisers */ @@ -181,10 +181,10 @@ /* Startup process is aborted if initialiser returns non-zero */ /* "extern" with initialisation value means that variable is declared AND defined. */ #define W32_IFPTR_IN_SEG(S,F) \ - __pragma (section (S,long,read)) \ - __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ - W32_INITVARDECL __declspec(allocate (S))int \ - (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F + __pragma(section (S,long,read)) \ + __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ + W32_INITVARDECL __declspec(allocate (S))int \ + (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F /* Unsafe sections (segments) for pointers to initialisers with "int" return type */ @@ -229,9 +229,9 @@ ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) || \ defined(AUTOINIT_FUNCS_FORCE_EARLY_INIT)) && \ ! defined(AUTOINIT_FUNCS_FORCE_LATE_INIT) -#define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY(F) +#define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY (F) #else -#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE(F) +#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE (F) #endif #endif /* ! _USRDLL || ! AUTOINIT_FUNCS_DECLARE_STATIC_REG @@ -243,13 +243,13 @@ #include /* required for atexit() */ #define W32_SET_INIT_AND_DEINIT(FI,FD) \ - void __cdecl _W32_init_helper_ ## FI (void); \ - void __cdecl _W32_deinit_helper_ ## FD (void); \ - void __cdecl _W32_init_helper_ ## FI (void) \ - { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \ - void __cdecl _W32_deinit_helper_ ## FD (void) \ - { (void) (FD) (); } \ - W32_REGISTER_INIT (_W32_init_helper_ ## FI) + void __cdecl _W32_init_helper_ ## FI (void); \ + void __cdecl _W32_deinit_helper_ ## FD (void); \ + void __cdecl _W32_init_helper_ ## FI (void) \ + { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \ + void __cdecl _W32_deinit_helper_ ## FD (void) \ + { (void) (FD) (); } \ + W32_REGISTER_INIT (_W32_init_helper_ ## FI) #else /* _USRDLL */ #ifndef WIN32_LEAN_AND_MEAN @@ -262,22 +262,22 @@ and rename DllMain to usr_DllMain */ #ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN #define W32_SET_INIT_AND_DEINIT(FI,FD) \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ - { (void) hinst; (void) unused; \ - if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ - else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ - return TRUE; \ - } struct _W32_dummy_strc_ ## FI {int i;} + BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ + BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ + { (void) hinst; (void) unused; \ + if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ + else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ + return TRUE; \ + } struct _W32_dummy_strc_ ## FI {int i;} #else /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ #define W32_SET_INIT_AND_DEINIT(FI,FD) \ - BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ - { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ - else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ - return usr_DllMain (hinst,reason,unused); \ - } struct _W32_dummy_strc_ ## FI {int i;} + BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ + BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ + BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ + { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ + else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ + return usr_DllMain (hinst,reason,unused); \ + } struct _W32_dummy_strc_ ## FI {int i;} #endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ #endif /* _USRDLL */ diff --git a/src/mhd2/compat_calloc.c b/src/mhd2/compat_calloc.c index 487f4a31..7977a2f0 100644 --- a/src/mhd2/compat_calloc.c +++ b/src/mhd2/compat_calloc.c @@ -40,7 +40,7 @@ #endif /* __GNUC__ >= 5 */ MHD_INTERNAL void * -MHD_calloc_ (size_t nelem, size_t elsize) +mhd_calloc (size_t nelem, size_t elsize) { size_t alloc_size; void *ptr; diff --git a/src/mhd2/compat_calloc.h b/src/mhd2/compat_calloc.h index ffb40817..8ea46921 100644 --- a/src/mhd2/compat_calloc.h +++ b/src/mhd2/compat_calloc.h @@ -43,7 +43,7 @@ # include # endif -# define MHD_calloc_ calloc +# define mhd_calloc calloc #else # include "sys_base_types.h" /* for size_t, NULL */ @@ -58,7 +58,7 @@ * the NULL pointer on failure. */ MHD_INTERNAL void * -MHD_calloc_ (size_t nelem, size_t elsize); +mhd_calloc (size_t nelem, size_t elsize); #endif /* ! HAVE_CALLOC */ diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c new file mode 100644 index 00000000..8a4890b8 --- /dev/null +++ b/src/mhd2/daemon_add_conn.c @@ -0,0 +1,982 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2014-2024 Evgeny Grin (Karlson2k) + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/daemon_add_conn.c + * @brief The implementations of MHD functions for adding new connections + * @author Karlson2k (Evgeny Grin) + * @author Daniel Pittman + * @author Christian Grothoff + * + * @warning Imported from MHD1 with minimal changes + * TODO: + * + Rewrite, + * + add per IP limit, + * + add app policy for new conn, + */ + +#include "mhd_sys_options.h" + +#include "daemon_add_conn.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" +#include "sys_sockets_types.h" +#include "sys_sockets_headers.h" + +#include "compat_calloc.h" + +#include "mhd_panic.h" + +#include "mhd_daemon.h" +#include "mhd_connection.h" +#include "mhd_public_api.h" +#include "daemon_logger.h" +#include "sys_sockets_headers.h" +#include "sys_ip_headers.h" +#include "mhd_sockets_macros.h" +#include "mhd_sockets_funcs.h" +#include "mhd_mono_clock.h" +#include "mhd_mempool.h" +#include "events_process.h" + +#include "response_from.h" + + +/** + * Set initial internal states for the connection to start reading and + * processing incoming data. + * @param c the connection to process + */ +static void +connection_set_initial_state (struct MHD_Connection *restrict c) +{ + size_t read_buf_size; + + mhd_assert (MHD_CONNECTION_INIT == c->state); + + c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN; + c->event_loop_info = MHD_EVENT_LOOP_INFO_READ; + + memset (&c->rq, 0, sizeof(c->rq)); + memset (&c->rp, 0, sizeof(c->rp)); + +#ifndef HAVE_NULL_PTR_ALL_ZEROS + mhd_DLINKEDL_INIT_LINKS (c, all_conn); + mhd_DLINKEDL_INIT_LINKS (c, proc_ready); + mhd_DLINKEDL_INIT_LINKS (c, by_timeout); + // TODO: set all other pointers manually +#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ + + c->write_buffer = NULL; + c->write_buffer_size = 0; + c->write_buffer_send_offset = 0; + c->write_buffer_append_offset = 0; + + c->continue_message_write_offset = 0; + + c->read_buffer_offset = 0; + read_buf_size = c->daemon->conns.cfg.mem_pool_size / 2; + c->read_buffer + = MHD_pool_allocate (c->pool, + read_buf_size, + false); + c->read_buffer_size = read_buf_size; +} + + +static void +notify_app_conn (struct MHD_Daemon *restrict daemon, + struct MHD_Connection *restrict connection, + bool closed) +{ + (void) daemon, (void) connection, (void) closed; + // TODO: implement +} + + +static void +connection_clean_destroy (struct MHD_Connection *restrict c, + struct MHD_Daemon *restrict d) +{ + mhd_assert (c->daemon == d); + +#if defined(MHD_USE_THREADS) + if (mhd_D_HAS_THREADS (d) && + (! c->thread_joined) && + (! mhd_thread_handle_ID_join_thread (c->tid)) ) + MHD_PANIC ("Failed to join a thread."); +#endif + + // TODO: upgrade support + + MHD_pool_destroy (c->pool); + + // TODO: TLS deinit + + + /* clean up the connection */ + notify_app_conn (d,c, true); + + // TODO: per IP limit + + /* Connection must not be in 'process ready' list */ + mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready)); + +#ifdef MHD_USE_EPOLL + if (mhd_D_IS_USING_EPOLL (d)) + { + /* epoll documentation suggests that closing a FD + automatically removes it from the epoll set, but it + happens only if all FD copies are closed as well. + To avoid problems with new events with pointers to closed + connection, remove FD manually */ + if (0 != epoll_ctl (d->events.data.epoll.e_fd, + EPOLL_CTL_DEL, + c->socket_fd, + NULL)) + MHD_PANIC ("Failed to remove FD from epoll set.\n"); + } +#endif + if (NULL != c->rp.response) + { + mhd_response_dec_use_count (c->rp.response); + c->rp.response = NULL; + } + if (MHD_INVALID_SOCKET != c->socket_fd) + (void) mhd_socket_close (c->socket_fd); + if (NULL != c->addr) + free (c->addr); + free (c); +} + + +/** + * Free resources associated with all closed connections. + * (destroy responses, free buffers, etc.). All closed + * connections are kept in the "cleanup" doubly-linked list. + * @remark To be called only from thread that + * process daemon's select()/poll()/etc. + * + * @param daemon daemon to clean up + */ +static void +MHD_cleanup_connections (struct MHD_Daemon *daemon) // FIXME: Remove?? +{ + struct MHD_Connection *pos; + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type)); + + for (pos = mhd_DLINKEDL_GET_FIRST_D (&(daemon->conns.to_clean)); + NULL != pos; pos = mhd_DLINKEDL_GET_FIRST_D (&(daemon->conns.to_clean))) + { + mhd_DLINKEDL_DEL_D (&(daemon->conns.to_clean), pos, all_conn); + + connection_clean_destroy (pos, daemon); + + daemon->conns.count--; + daemon->conns.block_new = false; + } +} + + +/** + * Do basic preparation work on the new incoming connection. + * + * This function do all preparation that is possible outside main daemon + * thread. + * @remark Could be called from any thread. + * + * @param daemon daemon that manages the connection + * @param client_socket socket to manage (MHD will expect + * to receive an HTTP request from this socket next). + * @param addr IP address of the client + * @param addrlen number of bytes in @a addr + * @param external_add indicate that socket has been added externally + * @param non_blck indicate that socket in non-blocking mode + * @param sk_spipe_supprs indicate that the @a client_socket has + * set SIGPIPE suppression + * @param sk_is_nonip _MHD_YES if this is not a TCP/IP socket + * @return pointer to the connection on success, NULL if this daemon could + * not handle the connection (i.e. malloc failed, etc). + * The socket will be closed in case of error; 'errno' is + * set to indicate further details about the error. + */ +static enum MHD_StatusCode +new_connection_prepare_ (struct MHD_Daemon *restrict daemon, + MHD_Socket client_socket, + const struct sockaddr_storage *restrict addr, + size_t addrlen, + bool external_add, + bool non_blck, + bool sk_spipe_supprs, + enum mhd_Tristate sk_is_nonip, + struct MHD_Connection **restrict conn_out) +{ + struct MHD_Connection *connection; + *conn_out = NULL; + + if (NULL == (connection = mhd_calloc (1, sizeof (struct MHD_Connection)))) + { + MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE, + "Failed to allocate memory for the new connection"); + mhd_socket_close (client_socket); + return MHD_SC_CONNECTION_MALLOC_FAILURE; + } + + if (! external_add) + { + connection->sk_corked = mhd_T_NO; + connection->sk_nodelay = mhd_T_NO; + } + else + { + connection->sk_corked = mhd_T_MAYBE; + connection->sk_nodelay = mhd_T_MAYBE; + } + + if (0 < addrlen) + { + if (NULL == (connection->addr = malloc (addrlen))) + { + MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE, + "Failed to allocate memory for the new connection"); + mhd_socket_close (client_socket); + free (connection); + return MHD_SC_CONNECTION_MALLOC_FAILURE; + } + memcpy (connection->addr, + addr, + addrlen); + } + else + connection->addr = NULL; + connection->addr_len = addrlen; + connection->socket_fd = client_socket; + connection->sk_nonblck = non_blck; + connection->is_nonip = sk_is_nonip; + connection->sk_spipe_suppress = sk_spipe_supprs; +#ifdef MHD_USE_THREADS + mhd_thread_handle_ID_set_invalid (&connection->tid); +#endif /* MHD_USE_THREADS */ + connection->daemon = daemon; + connection->connection_timeout_ms = daemon->conns.cfg.timeout; + connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ; + if (0 != connection->connection_timeout_ms) + connection->last_activity = MHD_monotonic_msec_counter (); + + // TODO: init TLS + *conn_out = connection; + + return MHD_SC_OK; +} + + +/** + * Finally insert the new connection to the list of connections + * served by the daemon and start processing. + * @remark To be called only from thread that process + * daemon's select()/poll()/etc. + * + * @param daemon daemon that manages the connection + * @param connection the newly created connection + * @return #MHD_YES on success, #MHD_NO on error + */ +static enum MHD_StatusCode +new_connection_process_ (struct MHD_Daemon *restrict daemon, + struct MHD_Connection *restrict connection) +{ + enum MHD_StatusCode res; + mhd_assert (connection->daemon == daemon); + + res = MHD_SC_OK; + /* Allocate memory pool in the processing thread so + * intensively used memory area is allocated in "good" + * (for the thread) memory region. It is important with + * NUMA and/or complex cache hierarchy. */ + connection->pool = MHD_pool_create (daemon->conns.cfg.mem_pool_size); + if (NULL == connection->pool) + { /* 'pool' creation failed */ + MHD_LOG_MSG (daemon, MHD_SC_POOL_MALLOC_FAILURE, \ + "Failed to allocate memory for the connection memory pool."); + res = MHD_SC_POOL_MALLOC_FAILURE; + } + else + { /* 'pool' creation succeed */ + + if (daemon->conns.block_new) + { /* Connections limit */ + MHD_LOG_MSG (daemon, MHD_SC_LIMIT_CONNECTIONS_REACHED, \ + "Server reached connection limit. " \ + "Closing inbound connection."); + res = MHD_SC_LIMIT_CONNECTIONS_REACHED; + } + else + { /* Have space for new connection */ + mhd_assert (daemon->conns.count < daemon->conns.cfg.count_limit); + daemon->conns.count++; + daemon->conns.block_new = + (daemon->conns.count >= daemon->conns.cfg.count_limit); + mhd_DLINKEDL_INS_LAST (&(daemon->conns), connection, all_conn); + if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != daemon->wmode_int) + mhd_DLINKEDL_INS_FIRST_D (&(daemon->conns.def_timeout), \ + connection, by_timeout); + + connection_set_initial_state (connection); + + notify_app_conn (daemon, connection, false); + +#ifdef MHD_USE_THREADS + if (mhd_DAEMON_TYPE_LISTEN_ONLY == daemon->threading.d_type) + { + mhd_assert ((mhd_POLL_TYPE_SELECT == daemon->events.poll_type) || \ + (mhd_POLL_TYPE_POLL == daemon->events.poll_type)); + if (! mhd_create_named_thread (&connection->tid, + "MHD-connection", + daemon->threading.cfg.stack_size, + &mhd_worker_connection, + connection)) + { +#ifdef EAGAIN + if (EAGAIN == errno) + { + MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED, + "Failed to create a new thread because it would " + "have exceeded the system limit on the number of " + "threads or no system resources available."); + res = MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED; + } + else +#endif /* EAGAIN */ + if (1) + { + MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE, + "Failed to create a thread."); + res = MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE; + } + } + else /* New thread has been created successfully */ + return MHD_SC_OK; /* *** Function success exit point *** */ + } + else +#else /* ! MHD_USE_THREADS */ + if (1) +#endif /* ! MHD_USE_THREADS */ + { /* No 'thread-per-connection' */ +#ifdef MHD_USE_THREADS + connection->tid = daemon->threading.tid; +#endif /* MHD_USE_THREADS */ +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == daemon->events.poll_type) + { + struct epoll_event event; + + event.events = EPOLLIN | EPOLLOUT | EPOLLET; + event.data.ptr = connection; + if (0 != epoll_ctl (daemon->epoll_fd, + EPOLL_CTL_ADD, + connection->socket_fd, + &event)) + { + MHD_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED, + "Failed to add connection socket ."); + res = MHD_SC_EPOLL_CTL_ADD_FAILED; + } + else + { + if (0) // TODO: implement turbo + { + connection->sk_ready = mhd_SOCKET_NET_STATE_RECV_READY + | mhd_SOCKET_NET_STATE_SEND_READY; + mhd_DLINKEDL_INS_LAST (&(daemon->events), connection, proc_ready); + } + return MHD_SC_OK; /* *** Function success exit point *** */ + } + } + else /* No 'epoll' */ +#endif /* MHD_USE_EPOLL */ + return MHD_SC_OK; /* *** Function success exit point *** */ + } + + /* ** Below is a cleanup path ** */ + mhd_assert (MHD_SC_OK != res); + notify_app_conn (daemon, connection, true); + + if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != daemon->wmode_int) + mhd_DLINKEDL_DEL_D (&(daemon->conns.def_timeout), \ + connection, by_timeout); + + mhd_DLINKEDL_DEL (&(daemon->conns), connection, all_conn); + daemon->conns.count--; + daemon->conns.block_new = false; + } + MHD_pool_destroy (connection->pool); + } + /* Free resources allocated before the call of this functions */ + + // TODO: TLS support + + // TODO: per IP limit + + if (NULL != connection->addr) + free (connection->addr); + (void) mhd_socket_close (connection->socket_fd); + free (connection); + mhd_assert (MHD_SC_OK != res); + return res; /* *** Function failure exit point *** */ +} + + +/** + * The given client socket will be managed (and closed!) by MHD after + * this call and must no longer be used directly by the application + * afterwards. + * + * @param daemon daemon that manages the connection + * @param client_socket socket to manage (MHD will expect + * to receive an HTTP request from this socket next). + * @param addr IP address of the client + * @param addrlen number of bytes in @a addr + * @param external_add perform additional operations needed due + * to the application calling us directly + * @param non_blck indicate that socket in non-blocking mode + * @param sk_spipe_supprs indicate that the @a client_socket has + * set SIGPIPE suppression + * @param sk_is_nonip _MHD_YES if this is not a TCP/IP socket + * @return #MHD_YES on success, #MHD_NO if this daemon could + * not handle the connection (i.e. malloc failed, etc). + * The socket will be closed in any case; 'errno' is + * set to indicate further details about the error. + */ +static enum MHD_StatusCode +internal_add_connection (struct MHD_Daemon *daemon, + MHD_Socket client_socket, + const struct sockaddr_storage *addr, + size_t addrlen, + bool external_add, + bool non_blck, + bool sk_spipe_supprs, + enum mhd_Tristate sk_is_nonip) +{ + struct MHD_Connection *connection; + enum MHD_StatusCode res; + + /* Direct add to master daemon could never happen. */ + mhd_assert (! mhd_D_HAS_WORKERS (daemon)); + + if (! mhd_FD_FITS_DAEMON (d, client_socket)) + { + MHD_LOG_MSG (daemon, MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE, \ + "New connection socket descriptor value is too large for " \ + "the daemon configuration."); + (void) mhd_socket_close (client_socket); + return MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE; + } + + if ((! non_blck) && + ((mhd_POLL_TYPE_EPOLL == daemon->events.poll_type) || + (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == daemon->wmode_int))) + { + MHD_LOG_MSG (daemon, MHD_SC_NONBLOCKING_REQUIRED, \ + "The daemon configuration requires non-blocking sockets, " + "the new socket has not been added."); + (void) mhd_socket_close (client_socket); + return MHD_SC_NONBLOCKING_REQUIRED; + } + res = new_connection_prepare_ (daemon, + client_socket, + addr, addrlen, + external_add, + non_blck, + sk_spipe_supprs, + sk_is_nonip, + &connection); + if (MHD_SC_OK != res) + return res; + + if (external_add) // TODO: support thread-unsafe + { + mhd_assert (0 && "Not implemented yet"); +#if 0 // TODO: support externally added + /* Connection is added externally and MHD is thread safe mode. */ + MHD_mutex_lock_chk_ (&daemon->new_connections_mutex); + DLL_insert (daemon->new_connections_head, + daemon->new_connections_tail, + connection); + daemon->have_new = true; + MHD_mutex_unlock_chk_ (&daemon->new_connections_mutex); + + /* The rest of connection processing must be handled in + * the daemon thread. */ + if ((mhd_ITC_IS_VALID (daemon->itc)) && + (! mhd_itc_activate (daemon->itc, "n"))) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + _ ("Failed to signal new connection via inter-thread " \ + "communication channel.\n")); +#endif + } + return MHD_YES; +#endif + } + + return new_connection_process_ (daemon, connection); +} + + +#if 0 // TODO: implement +static void +new_connections_list_process_ (struct MHD_Daemon *daemon) +{ + struct MHD_Connection *local_head; + struct MHD_Connection *local_tail; + mhd_assert (daemon->events.act_req); + // mhd_assert (MHD_D_IS_THREAD_SAFE_ (daemon)); + + /* Detach DL-list of new connections from the daemon for + * following local processing. */ + MHD_mutex_lock_chk_ (&daemon->new_connections_mutex); + mhd_assert (NULL != daemon->new_connections_head); + local_head = daemon->new_connections_head; + local_tail = daemon->new_connections_tail; + daemon->new_connections_head = NULL; + daemon->new_connections_tail = NULL; + daemon->have_new = false; + MHD_mutex_unlock_chk_ (&daemon->new_connections_mutex); + (void) local_head; /* Mute compiler warning */ + + /* Process new connections in FIFO order. */ + do + { + struct MHD_Connection *c; /**< Currently processed connection */ + + c = local_tail; + DLL_remove (local_head, + local_tail, + c); + mhd_assert (daemon == c->daemon); + if (MHD_NO == new_connection_process_ (daemon, c)) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + _ ("Failed to start serving new connection.\n")); +#endif + (void) 0; + } + } while (NULL != local_tail); + +} + + +#endif + + +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_ (4) MHD_EXTERN_ enum MHD_StatusCode +MHD_daemon_add_connection (struct MHD_Daemon *daemon, + MHD_Socket client_socket, + size_t addrlen, + const struct sockaddr *addr, + void *connection_cntx) +{ + bool sk_nonbl; + bool sk_spipe_supprs; + struct sockaddr_storage addrstorage; + // TODO: global daemon lock for external events + (void) connection_cntx; // FIXME: is it really needed? Where it is used? + + if ((! mhd_D_HAS_THREADS (daemon)) && + (daemon->conns.block_new)) + MHD_cleanup_connections (daemon); + + if (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type) + && daemon->conns.block_new) + return MHD_SC_LIMIT_CONNECTIONS_REACHED; + + if (0 != addrlen) + { + if (AF_INET == addr->sa_family) + { + if (sizeof(struct sockaddr_in) > addrlen) + { + MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ + "MHD_add_connection() has been called with " \ + "incorrect 'addrlen' value."); + return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; + } +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + if ((0 != addr->sa_len) && + (sizeof(struct sockaddr_in) > (size_t) addr->sa_len) ) + { + MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ + "MHD_add_connection() has been called with " \ + "non-zero value of 'sa_len' member of " \ + "'struct sockaddr' which does not match 'sa_family'."); + return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; + } +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ + } +#ifdef HAVE_INET6 + if (AF_INET6 == addr->sa_family) + { + if (sizeof(struct sockaddr_in6) > addrlen) + { + MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ + "MHD_add_connection() has been called with " \ + "incorrect 'addrlen' value."); + return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; + } +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + if ((0 != addr->sa_len) && + (sizeof(struct sockaddr_in6) > (size_t) addr->sa_len) ) + { + MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ + "MHD_add_connection() has been called with " \ + "non-zero value of 'sa_len' member of " \ + "'struct sockaddr' which does not match 'sa_family'."); + return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; + } +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ + } +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + if ((0 != addr->sa_len) && + (addrlen > (size_t) addr->sa_len)) + addrlen = (size_t) addr->sa_len; /* Use safest value */ +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ +#endif /* HAVE_INET6 */ + } + + if (! mhd_socket_nonblocking (client_socket)) + { + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \ + "Failed to set nonblocking mode on the new client socket."); + sk_nonbl = false; + } + else + sk_nonbl = true; + +#ifndef MHD_WINSOCK_SOCKETS + sk_spipe_supprs = false; +#else /* MHD_WINSOCK_SOCKETS */ + sk_spipe_supprs = true; /* Nothing to suppress on W32 */ +#endif /* MHD_WINSOCK_SOCKETS */ +#if defined(MHD_socket_nosignal_) + if (! sk_spipe_supprs) + sk_spipe_supprs = MHD_socket_nosignal_ (client_socket); + if (! sk_spipe_supprs) + { + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, \ + "Failed to suppress SIGPIPE on the new client socket."); +#ifndef MSG_NOSIGNAL + /* Application expects that SIGPIPE will be suppressed, + * but suppression failed and SIGPIPE cannot be suppressed with send(). */ + if (! daemon->sigpipe_blocked) + { + int err = MHD_socket_get_error_ (); + MHD_socket_close_ (client_socket); + MHD_socket_fset_error_ (err); + return MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED; + } +#endif /* MSG_NOSIGNAL */ + } +#endif /* MHD_socket_nosignal_ */ + + if (1) // TODO: implement turbo + { + if (! mhd_socket_noninheritable (client_socket)) + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \ + "Failed to set noninheritable mode on new client socket."); + } + + /* Copy to sockaddr_storage structure to avoid alignment problems */ + if (0 < addrlen) + memcpy (&addrstorage, addr, addrlen); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + addrstorage.ss_len = addrlen; /* Force set the right length */ +#endif /* HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */ + +#if defined(MHD_USE_THREADS) + if (mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type)) + { + unsigned int i; + /* have a pool, try to find a pool with capacity; we use the + socket as the initial offset into the pool for load + balancing */ + unsigned int offset; +#ifdef MHD_WINSOCK_SOCKETS + uint_fast64_t osb = (uint_fast64_t) client_socket; + osb ^= (((uint_fast64_t) client_socket) >> 9); + osb ^= (((uint_fast64_t) client_socket) >> 18); + osb ^= (((uint_fast64_t) client_socket) >> 27); + osb ^= (((uint_fast64_t) client_socket) >> 36); + osb ^= (((uint_fast64_t) client_socket) >> 45); + osb ^= (((uint_fast64_t) client_socket) >> 54); + osb ^= (((uint_fast64_t) client_socket) >> 63); + offset = (unsigned int) osb; +#else + offset = (unsigned int) client_socket; +#endif + + for (i = 0; i < daemon->threading.hier.pool.num; ++i) + { + struct MHD_Daemon *const volatile worker = + daemon->threading.hier.pool.workers + + (i + offset) % daemon->threading.hier.pool.num; + if (worker->conns.block_new) + continue; + return internal_add_connection (worker, + client_socket, + &addrstorage, + addrlen, + true, + sk_nonbl, + sk_spipe_supprs, + mhd_T_MAYBE); + } + + /* all pools are at their connection limit, must refuse */ + return MHD_SC_LIMIT_CONNECTIONS_REACHED; + } +#endif /* MHD_USE_THREADS */ + + return internal_add_connection (daemon, + client_socket, + &addrstorage, + addrlen, + true, + sk_nonbl, + sk_spipe_supprs, + mhd_T_MAYBE); +} + + +MHD_INTERNAL enum mhd_DaemonAcceptResult +mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon) +{ + struct sockaddr_storage addrstorage; + socklen_t addrlen; + MHD_Socket s; + MHD_Socket fd; + bool sk_nonbl; + bool sk_spipe_supprs; + bool sk_cloexec; + enum mhd_Tristate sk_non_ip; +#if defined(_DEBUG) && defined (USE_ACCEPT4) + const bool use_accept4 = ! daemon->dbg.avoid_accept4; +#elif defined (USE_ACCEPT4) + static const bool use_accept4 = true; +#else /* ! USE_ACCEPT4 && ! _DEBUG */ + static const bool use_accept4 = false; +#endif /* ! USE_ACCEPT4 && ! _DEBUG */ + +#ifdef MHD_USE_THREADS + mhd_assert ((! mhd_D_HAS_THREADS (daemon)) || \ + mhd_thread_handle_ID_is_current_thread (daemon->threading.tid)); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (daemon->threading.d_type)); +#endif /* MHD_USE_THREADS */ + + fd = daemon->net.listen.fd; + mhd_assert (MHD_INVALID_SOCKET != fd); + + addrlen = (socklen_t) sizeof (addrstorage); + memset (&addrstorage, + 0, + (size_t) addrlen); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + addrstorage.ss_len = addrlen; +#endif /* HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */ + + /* Initialise with default values to avoid compiler warnings */ + sk_nonbl = false; + sk_spipe_supprs = false; + sk_cloexec = false; + s = MHD_INVALID_SOCKET; + +#ifdef USE_ACCEPT4 + if (use_accept4 && + (MHD_INVALID_SOCKET != + (s = accept4 (fd, + (struct sockaddr *) &addrstorage, + &addrlen, + SOCK_CLOEXEC_OR_ZERO | SOCK_NONBLOCK_OR_ZERO + | SOCK_NOSIGPIPE_OR_ZERO)))) + { + sk_nonbl = (SOCK_NONBLOCK_OR_ZERO != 0); +#ifndef MHD_WINSOCK_SOCKETS + sk_spipe_supprs = (SOCK_NOSIGPIPE_OR_ZERO != 0); +#else /* MHD_WINSOCK_SOCKETS */ + sk_spipe_supprs = true; /* Nothing to suppress on W32 */ +#endif /* MHD_WINSOCK_SOCKETS */ + sk_cloexec = (SOCK_CLOEXEC_OR_ZERO != 0); + } +#endif /* USE_ACCEPT4 */ +#if defined(_DEBUG) || ! defined(USE_ACCEPT4) + if (! use_accept4 && + (MHD_INVALID_SOCKET != + (s = accept (fd, + (struct sockaddr *) &addrstorage, + &addrlen)))) + { +#ifdef MHD_ACCEPT_INHERIT_NONBLOCK + sk_nonbl = daemon->listen_nonblk; +#else /* ! MHD_ACCEPT_INHERIT_NONBLOCK */ + sk_nonbl = false; +#endif /* ! MHD_ACCEPT_INHERIT_NONBLOCK */ +#ifndef MHD_WINSOCK_SOCKETS + sk_spipe_supprs = false; +#else /* MHD_WINSOCK_SOCKETS */ + sk_spipe_supprs = true; /* Nothing to suppress on W32 */ +#endif /* MHD_WINSOCK_SOCKETS */ + sk_cloexec = false; + } +#endif /* _DEBUG || !USE_ACCEPT4 */ + + if (MHD_INVALID_SOCKET == s) + { /* This could be a common occurrence with multiple worker threads */ + const int err = mhd_SCKT_GET_LERR (); + + if (mhd_SCKT_ERR_IS_EINVAL (err)) + return mhd_DAEMON_ACCEPT_NO_MORE_PENDING; /* can happen during shutdown */ // FIXME: remove? + if (mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT (err)) + return mhd_DAEMON_ACCEPT_NO_MORE_PENDING; /* do not print error if client just disconnects early */ + if (mhd_SCKT_ERR_IS_EINTR (err)) + return mhd_DAEMON_ACCEPT_SKIPPED; + if (mhd_SCKT_ERR_IS_EAGAIN (err)) + return mhd_DAEMON_ACCEPT_NO_MORE_PENDING; + if (mhd_SCKT_ERR_IS_LOW_RESOURCES (err) ) + { + /* system/process out of resources */ + if (0 == daemon->conns.count) + { + /* Not setting 'block_new' flag, as there is no way it + would ever be cleared. Instead trying to produce + bit fat ugly warning. */ + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY, \ + "Hit process or system resource limit at FIRST " \ + "connection. This is really bad as there is no sane " \ + "way to proceed. Will try busy waiting for system " \ + "resources to become magically available."); + } + else + { + daemon->conns.block_new = true; + MHD_LOG_PRINT (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED, \ + MHD_LOG_FMT ("Hit process or system resource limit " \ + "at %u connections, temporarily " \ + "suspending accept(). Consider setting " \ + "a lower MHD_OPTION_CONNECTION_LIMIT."), \ + daemon->conns.count); + } + return mhd_DAEMON_ACCEPT_FAILED; + } + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY, + "Error accepting connection."); + return mhd_DAEMON_ACCEPT_FAILED; + } + + if (mhd_FD_FITS_DAEMON (daemon, s)) + { + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_OUTSIDE_OF_SET_RANGE, \ + "The accepted socket has value outside of allowed range."); + (void) mhd_socket_close (s); + return mhd_DAEMON_ACCEPT_FAILED; + } + if (mhd_SOCKET_TYPE_IP == daemon->net.listen.type) + sk_non_ip = mhd_T_NO; + else if (mhd_SOCKET_TYPE_UNKNOWN == daemon->net.listen.type) + sk_non_ip = mhd_T_MAYBE; + else + sk_non_ip = mhd_T_YES; + if (0 >= addrlen) + { + if (mhd_SOCKET_TYPE_IP == daemon->net.listen.type) + MHD_LOG_MSG (daemon, MHD_SC_ACCEPTED_UNKNOWN_TYPE, \ + "Accepted socket has non-positive length of the address. " \ + "Processing the new socket as a socket with " \ + "unknown type."); + addrlen = 0; + sk_non_ip = mhd_T_MAYBE; + } + else if (((socklen_t) sizeof (addrstorage)) < addrlen) + { + /* Should not happen as 'sockaddr_storage' must be large enough to + * store any address supported by the system. */ + MHD_LOG_MSG (daemon, MHD_SC_ACCEPTED_SOCKADDR_TOO_LARGE, \ + "Accepted socket address is larger than expected by " \ + "system headers. Processing the new socket as a socket with " \ + "unknown type."); + addrlen = 0; + sk_non_ip = mhd_T_MAYBE; /* IP-type addresses must fit */ + } + else if (mhd_T_MAYBE == sk_non_ip) + { + if (AF_INET == ((struct sockaddr *) &addrstorage)->sa_family) + sk_non_ip = mhd_T_NO; +#ifdef HAVE_INET6 + else if (AF_INET6 == ((struct sockaddr *) &addrstorage)->sa_family) + sk_non_ip = mhd_T_NO; +#endif /* HAVE_INET6 */ + } + + if (! sk_nonbl && ! mhd_socket_nonblocking (s)) + { + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \ + "Failed to set nonblocking mode on incoming connection " \ + "socket."); + } + else + sk_nonbl = true; + + if (! sk_cloexec && ! mhd_socket_noninheritable (s)) + { + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \ + "Failed to set non-inheritable mode on incoming connection " \ + "socket."); + } + +#if defined(MHD_socket_nosignal_) + if (! sk_spipe_supprs && ! MHD_socket_nosignal_ (s)) + { + MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, + "Failed to suppress SIGPIPE on incoming connection " \ + "socket."); +#ifndef MSG_NOSIGNAL + /* Application expects that SIGPIPE will be suppressed, + * but suppression failed and SIGPIPE cannot be suppressed with send(). */ + if (! daemon->sigpipe_blocked) + { + (void) MHD_socket_close_ (s); + return MHD_NO; + } +#endif /* MSG_NOSIGNAL */ + } + else + sk_spipe_supprs = true; +#endif /* MHD_socket_nosignal_ */ + return (MHD_SC_OK == internal_add_connection (daemon, + s, + &addrstorage, + (size_t) addrlen, + false, + sk_nonbl, + sk_spipe_supprs, + sk_non_ip)) ? + mhd_DAEMON_ACCEPT_FAILED : mhd_DAEMON_ACCEPT_SUCCESS; +} diff --git a/src/mhd2/daemon_add_conn.h b/src/mhd2/daemon_add_conn.h new file mode 100644 index 00000000..3238d02f --- /dev/null +++ b/src/mhd2/daemon_add_conn.h @@ -0,0 +1,83 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2014-2024 Evgeny Grin (Karlson2k) + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/daemon_add_conn.h + * @brief The declaration of internal functions for adding new connections + * @author Karlson2k (Evgeny Grin) + * @author Daniel Pittman + * @author Christian Grothoff + */ + +#ifndef MHD_DAEMON_ADD_CONN +#define MHD_DAEMON_ADD_CONN 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +struct MHD_Daemon; /* Forward declaration */ + +/** + * The result of the accepting of the new connection + */ +enum mhd_DaemonAcceptResult +{ + /** + * New connection has been accepted successfully. + * Probably more connections are pending to be accepted. + */ + mhd_DAEMON_ACCEPT_SUCCESS = 0 + , + /** + * New connection has been skipped for some reason. + * It is OK to try to accept more connections right now. + */ + mhd_DAEMON_ACCEPT_SKIPPED = 1 << 0 + , + /** + * No more new connections are pending, the listen backlog is empty + */ + mhd_DAEMON_ACCEPT_NO_MORE_PENDING = 1 << 1 + , + /** + * Connection accept failed, but the listen backlog could be not empty. + * Do not try to accept more connection right now. + */ + mhd_DAEMON_ACCEPT_FAILED = 1 << 2 +}; + + +/** + * Accept an incoming connection and create the MHD_Connection object for + * it. This function also enforces policy by way of checking with the + * accept policy callback. + * @remark To be called only from thread that process + * daemon's select()/poll()/etc. + * + * @param daemon handle with the listen socket + * @return a #mhd_DaemonAcceptResult value + */ +MHD_INTERNAL enum mhd_DaemonAcceptResult +mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon); + + +#endif /* ! MHD_DAEMON_ADD_CONN */ diff --git a/src/mhd2/daemon_create.c b/src/mhd2/daemon_create.c new file mode 100644 index 00000000..25d5a8f8 --- /dev/null +++ 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_cb = req_cb; + d->req_cb_cls = req_cb_cls; + d->settings = s; + + return d; +} + + +/* This is a workaround for GCC/binutils bug. + * To make sure that initialisation functions are called when MHD is used as + * a static library, put initialisation functions to the file with function + * that always referred/used by application/user of the library. + * If application does not refer any function, the initialiser call could be + * missed for the static library. + */ +#define MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C 1 +#include "mhd_lib_init_impl.h" diff --git a/src/mhd2/daemon_funcs.c b/src/mhd2/daemon_funcs.c new file mode 100644 index 00000000..6640ef43 --- /dev/null +++ b/src/mhd2/daemon_funcs.c @@ -0,0 +1,51 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/daemon_funcs.c + * @brief The implementation of internal daemon-related functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#ifdef MHD_USE_THREADS + +#include "daemon_funcs.h" + +#include "mhd_itc.h" +#include "mhd_daemon.h" +#include "daemon_logger.h" + +MHD_INTERNAL bool +mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d) +{ + mhd_assert (mhd_ITC_IS_VALID (d->threading.itc)); + if (! mhd_itc_activate (d->threading.itc)) + { + MHD_LOG_MSG (d, MHD_SC_ITC_USE_FAILED, \ + "Failed to communicate by ITC with the daemon thread."); + return false; + } + return true; +} + + +#endif /* MHD_USE_THREADS */ diff --git a/src/mhd2/daemon_funcs.h b/src/mhd2/daemon_funcs.h new file mode 100644 index 00000000..cec3eb11 --- /dev/null +++ b/src/mhd2/daemon_funcs.h @@ -0,0 +1,51 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/daemon_funcs.h + * @brief The declarations of internal daemon-related functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_DAEMON_FUNCS_H +#define MHD_DAEMON_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#ifdef MHD_USE_THREADS + +#include "sys_bool_type.h" + +struct MHD_Daemon; /* forward declaration */ + + +/** + * Trigger daemon ITC. + * This should cause daemon's thread to stop waiting for the network events + * and process pending information + * @param d the daemon object, ITC should be initialised + * @return true if succeed, false otherwise + */ +MHD_INTERNAL bool +mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d); + +#endif /* MHD_USE_THREADS */ + +#endif /* ! MHD_DAEMON_FUNCS_H */ diff --git a/src/mhd2/daemon_logger.c b/src/mhd2/daemon_logger.c new file mode 100644 index 00000000..6624455b --- /dev/null +++ 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 +#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 new file mode 100644 index 00000000..5f9d81a4 --- /dev/null +++ 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 new file mode 100644 index 00000000..b783a594 --- /dev/null +++ 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 + +#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 new file mode 100644 index 00000000..6b213eb7 --- /dev/null +++ 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 + +/** + * 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_start.c b/src/mhd2/daemon_start.c new file mode 100644 index 00000000..a7f4986a --- /dev/null +++ b/src/mhd2/daemon_start.c @@ -0,0 +1,2777 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/daemon_start.c + * @brief The implementation of the MHD_daemon_start() + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" +#include "sys_malloc.h" + +#include +#include "sys_sockets_headers.h" +#include "sys_ip_headers.h" + +#ifdef MHD_POSIX_SOCKETS +# include +#endif +#ifdef MHD_USE_EPOLL +# include +#endif + +#include "mhd_public_api.h" + +#include "mhd_daemon.h" +#include "daemon_options.h" + +#include "daemon_logger.h" +#include "mhd_assert.h" +#include "mhd_sockets_funcs.h" +#include "mhd_sockets_macros.h" +#ifdef MHD_POSIX_SOCKETS +# ifdef MHD_USE_SELECT +# ifdef HAVE_SYS_SELECT_H +# include /* For FD_SETSIZE */ +# else +# ifdef HAVE_SYS_TIME_H +# include +# endif +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_UNISTD_H +# include +# endif +# endif +# endif +#endif + +#ifdef MHD_USE_THREADS +# include "mhd_itc.h" +# include "mhd_threads.h" +# include "events_process.h" +# include "daemon_funcs.h" +#endif + + +/** + * The default value for fastopen queue length (currently GNU/Linux only) + */ +#define MHD_TCP_FASTOPEN_DEF_QUEUE_LEN 64 + +/** + * Release any internally allocated pointers, then deallocate the settings. + * @param s the pointer to the settings to release + */ +static void +dsettings_release (struct DaemonOptions *s) +{ + /* Release starting from the last member */ + if (NULL != s->random_entropy.v_buf) + free (s->random_entropy.v_buf); + if (MHD_INVALID_SOCKET != s->listen_socket) + mhd_socket_close (s->listen_socket); + if (NULL != s->bind_sa.v_sa) + free (s->bind_sa.v_sa); + free (s); +} + + +/** + * Set the daemon work mode and perform some related checks. + * @param d the daemon object + * @param s the user settings + * @return MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) +MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +daemon_set_work_mode (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + switch (s->work_mode.mode) + { + case MHD_WM_EXTERNAL_PERIODIC: + d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS; + break; + case MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL: + case MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE: + if (MHD_SPS_AUTO != s->poll_syscall) + { + MHD_LOG_MSG ( \ + d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \ + "The requested work mode is not compatible with setting " \ + "socket polling syscall."); + return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID; + } + if (MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL == s->work_mode.mode) + d->wmode_int = mhd_WM_INT_EXTERNAL_EVENTS_LEVEL; + else + d->wmode_int = mhd_WM_INT_EXTERNAL_EVENTS_EDGE; + break; + case MHD_WM_EXTERNAL_SINGLE_FD_WATCH: + if ((MHD_SPS_AUTO != s->poll_syscall) && + (MHD_SPS_EPOLL != s->poll_syscall)) + { + MHD_LOG_MSG ( \ + d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \ + "The requested work mode MHD_WM_EXTERNAL_SINGLE_FD_WATCH " \ + "is not compatible with requested socket polling syscall."); + return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID; + } +#ifndef MHD_USE_EPOLL + MHD_LOG_MSG ( \ + d, MHD_SC_FEATURE_DISABLED, \ + "The epoll is required for the requested work mode " \ + "MHD_WM_EXTERNAL_SINGLE_FD_WATCH, but not available on this " \ + "platform or MHD build."); + return MHD_SC_FEATURE_DISABLED; +#else + d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS; +#endif + break; + case MHD_WM_THREAD_PER_CONNECTION: + if (MHD_SPS_EPOLL == s->poll_syscall) + { + MHD_LOG_MSG ( \ + d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \ + "The requested work mode MHD_WM_THREAD_PER_CONNECTION " \ + "is not compatible with 'epoll' sockets polling."); + return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID; + } + /* Intentional fallthrough */ + case MHD_WM_WORKER_THREADS: +#ifndef MHD_USE_THREADS + MHD_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \ + "The internal threads modes are not supported by this " \ + "build of MHD."); + return MHD_SC_FEATURE_DISABLED; +#else /* MHD_USE_THREADS */ + if (MHD_WM_THREAD_PER_CONNECTION == s->work_mode.mode) + d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION; + else if (1 >= s->work_mode.params.num_worker_threads) /* && (MHD_WM_WORKER_THREADS == s->work_mode.mode) */ + d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD; + else + d->wmode_int = mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL; +#endif /* MHD_USE_THREADS */ + break; + default: + MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_WM, \ + "Wrong requested work mode."); + return MHD_SC_CONFIGURATION_UNEXPECTED_WM; + } + + return MHD_SC_OK; +} + + +union mhd_SockaddrAny +{ + struct sockaddr sa; + struct sockaddr_in sa_i4; +#ifdef HAVE_INET6 + struct sockaddr_in6 sa_i6; +#endif /* HAVE_INET6 */ + struct sockaddr_storage sa_stor; +}; + + +/** + * The type of the socket to create + */ +enum mhd_CreateSktType +{ + /** + * Unknown address family (could be IP or not IP) + */ + mhd_SKT_UNKNOWN = -4 + , + /** + * The socket is not IP. + */ + mhd_SKT_NON_IP = -2 + , + /** + * The socket is UNIX. + */ + mhd_SKT_UNIX = -1 + , + /** + * No socket + */ + mhd_SKT_NO_SOCKET = MHD_AF_NONE + , + /** + * IPv4 only + */ + mhd_SKT_IP_V4_ONLY = MHD_AF_INET4 + , + /** + * IPv6 only + */ + mhd_SKT_IP_V6_ONLY = MHD_AF_INET6 + , + /** + * IPv6 with dual stack enabled + */ + mhd_SKT_IP_DUAL_REQUIRED = MHD_AF_DUAL + , + /** + * Try IPv6 with dual stack then IPv4 + */ + mhd_SKT_IP_V4_WITH_V6_OPT = MHD_AF_DUAL_v6_OPTIONAL + , + /** + * IPv6 with optional dual stack + */ + mhd_SKT_IP_V6_WITH_V4_OPT = MHD_AF_DUAL_v4_OPTIONAL + , + /** + * Try IPv4 then IPv6 with optional dual stack + */ + mhd_SKT_IP_V4_WITH_FALLBACK = 16 +}; + +/** + * Create socket, bind to the address and start listening on the socket. + * + * The socket is assigned to the daemon as listening FD. + * @param d the daemon to use + * @param s the user settings + * @param v6_tried true if IPv6 has been tried already + * @param force_v6_any_dual true if IPv6 is forced with dual stack either + * enabled or not + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +static enum MHD_StatusCode +create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s, + bool v6_tried, + bool force_v6_any_dual) +{ + MHD_Socket sk; + enum mhd_CreateSktType sk_type; + bool sk_already_listening; + union mhd_SockaddrAny sa_all; + const struct sockaddr *p_use_sa; + socklen_t use_sa_size; + uint_fast16_t sk_port; + bool is_non_block; + bool is_non_inhr; + enum MHD_StatusCode ret; + + sk = MHD_INVALID_SOCKET; + sk_type = mhd_SKT_NO_SOCKET; + sk_already_listening = false; + p_use_sa = NULL; + use_sa_size = 0; + sk_port = 0; + +#ifndef HAVE_INET6 + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); +#endif + + if (MHD_INVALID_SOCKET != s->listen_socket) + { + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); + /* Check for options conflicts */ + if (0 != s->bind_sa.v_sa_len) + { + MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \ + "MHD_D_O_BIND_SA cannot be used together " \ + "with MHD_D_O_LISTEN_SOCKET"); + return MHD_SC_OPTIONS_CONFLICT; + } + else if (MHD_AF_NONE != s->bind_port.v_af) + { + MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \ + "MHD_D_O_BIND_PORT cannot be used together " \ + "with MHD_D_O_LISTEN_SOCKET"); + return MHD_SC_OPTIONS_CONFLICT; + } + + /* No options conflicts */ + sk = s->listen_socket; + s->listen_socket = MHD_INVALID_SOCKET; /* Prevent closing with settings cleanup */ + sk_type = mhd_SKT_UNKNOWN; + sk_already_listening = true; + } + else if ((0 != s->bind_sa.v_sa_len) || (MHD_AF_NONE != s->bind_port.v_af)) + { + if (0 != s->bind_sa.v_sa_len) + { + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); + + /* Check for options conflicts */ + if (MHD_AF_NONE != s->bind_port.v_af) + { + MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \ + "MHD_D_O_BIND_SA cannot be used together " \ + "with MHD_D_O_BIND_PORT"); + return MHD_SC_OPTIONS_CONFLICT; + } + + /* No options conflicts */ + switch (s->bind_sa.v_sa->sa_family) + { + case AF_INET: + sk_type = mhd_SKT_IP_V4_ONLY; + if (sizeof(sa_all.sa_i4) > s->bind_sa.v_sa_len) + { + MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ + "The size of the provided sockaddr does not match " + "used address family"); + return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; + } + memcpy (&(sa_all.sa_i4), s->bind_sa.v_sa, sizeof(sa_all.sa_i4)); + sk_port = (uint_fast16_t) ntohs (sa_all.sa_i4.sin_port); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sa_all.sa_i4.sin_len = (socklen_t) sizeof(sa_all.sa_i4); +#endif + p_use_sa = (struct sockaddr *) &(sa_all.sa_i4); + use_sa_size = (socklen_t) sizeof(sa_all.sa_i4); + break; +#ifdef HAVE_INET6 + case AF_INET6: + sk_type = mhd_SKT_IP_V6_ONLY; + if (sizeof(sa_all.sa_i6) > s->bind_sa.v_sa_len) + { + MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ + "The size of the provided sockaddr does not match " + "used address family"); + return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; + } + memcpy (&(sa_all.sa_i6), s->bind_sa.v_sa, s->bind_sa.v_sa_len); + sk_port = (uint_fast16_t) ntohs (sa_all.sa_i6.sin6_port); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + sa_all.sa_i6.sin6_len = (socklen_t) s->bind_sa.v_sa_len; +#endif + p_use_sa = (struct sockaddr *) &(sa_all.sa_i6); + use_sa_size = (socklen_t) sizeof(sa_all.sa_i6); + break; +#endif /* HAVE_INET6 */ +#ifdef MHD_AF_UNIX + case MHD_AF_UNIX: + sk_type = mhd_SKT_UNIX; + p_use_sa = NULL; /* To be set below */ + break; +#endif /* MHD_AF_UNIX */ + default: + sk_type = mhd_SKT_UNKNOWN; + p_use_sa = NULL; /* To be set below */ + } + + if (s->bind_sa.v_dual) + { + if (mhd_SKT_IP_V6_ONLY != sk_type) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_DUAL_STACK_NOT_SUITABLE, \ + "IP dual stack is not possible for provided sockaddr"); + } +#ifdef HAVE_INET6 + else + { +#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure + sk_type = mhd_SKT_IP_DUAL_REQUIRED; +#else /* ! IPV6_V6ONLY */ + MHD_LOG_MSG (d, \ + MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, \ + "IP dual stack is not supported by this platform or " \ + "by this MHD build"); +#endif /* ! IPV6_V6ONLY */ + } +#endif /* HAVE_INET6 */ + } + + if (NULL == p_use_sa) + { +#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) && \ + defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN) + if ((((size_t) s->bind_sa.v_sa->sa_len) != s->bind_sa.v_sa_len) && + (sizeof(sa_all) >= s->bind_sa.v_sa_len)) + { + /* Fix embedded 'sa_len' member if possible */ + memcpy (&sa_all, s->bind_sa.v_sa, s->bind_sa.v_sa_len); + sa_all.sa_stor.ss_len = (socklen_t) s->bind_sa.v_sa_len; + p_use_sa = (const struct sockaddr *) &(sa_all.sa_stor); + } + else +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN && HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */ + p_use_sa = s->bind_sa.v_sa; + use_sa_size = (socklen_t) s->bind_sa.v_sa_len; + } + } + else /* if (MHD_AF_NONE != s->bind_port.v_af) */ + { + /* No options conflicts */ + switch (s->bind_port.v_af) + { + case MHD_AF_NONE: + mhd_assert (0); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; + case MHD_AF_AUTO: +#ifdef HAVE_INET6 +#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure + if (force_v6_any_dual) + sk_type = mhd_SKT_IP_V6_WITH_V4_OPT; + else if (v6_tried) + sk_type = mhd_SKT_IP_V4_WITH_FALLBACK; + else + sk_type = mhd_SKT_IP_V4_WITH_V6_OPT; +#else /* ! IPV6_V6ONLY */ + mhd_assert (! v6_tried); + if (force_v6_any_dual) + sk_type = mhd_SKT_IP_V6_ONLY; + else + sk_type = mhd_SKT_IP_V4_WITH_FALLBACK; +#endif /* ! IPV6_V6ONLY */ +#else /* ! HAVE_INET6 */ + sk_type = mhd_SKT_IP_V4_ONLY; +#endif /* ! HAVE_INET6 */ + break; + case MHD_AF_INET4: + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); + sk_type = mhd_SKT_IP_V4_ONLY; + break; + case MHD_AF_INET6: + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); +#ifdef HAVE_INET6 + sk_type = mhd_SKT_IP_V6_ONLY; +#else /* ! HAVE_INET6 */ + MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \ + "IPv6 is not supported by this MHD build or " \ + "by this platform"); + return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD; +#endif /* ! HAVE_INET6 */ + break; + case MHD_AF_DUAL: + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); +#ifdef HAVE_INET6 +#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure + sk_type = mhd_SKT_IP_DUAL_REQUIRED; +#else /* ! IPV6_V6ONLY */ + MHD_LOG_MSG (d, + MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, \ + "IP dual stack is not supported by this platform or " \ + "by this MHD build"); + sk_type = mhd_SKT_IP_V6_ONLY; +#endif /* ! IPV6_V6ONLY */ +#else /* ! HAVE_INET6 */ + MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \ + "IPv6 is not supported by this MHD build or " \ + "by this platform"); + return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD; +#endif /* ! HAVE_INET6 */ + break; + case MHD_AF_DUAL_v4_OPTIONAL: + mhd_assert (! v6_tried); + mhd_assert (! force_v6_any_dual); +#ifdef HAVE_INET6 +#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure + sk_type = mhd_SKT_IP_V6_WITH_V4_OPT; +#else /* ! IPV6_V6ONLY */ + sk_type = mhd_SKT_IP_V6_ONLY; +#endif /* ! IPV6_V6ONLY */ +#else /* ! HAVE_INET6 */ + MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \ + "IPv6 is not supported by this MHD build or " \ + "by this platform"); + return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD; +#endif /* ! HAVE_INET6 */ + break; + case MHD_AF_DUAL_v6_OPTIONAL: + mhd_assert (! force_v6_any_dual); +#ifdef HAVE_INET6 +#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure + sk_type = (! v6_tried) ? + mhd_SKT_IP_V4_WITH_V6_OPT : mhd_SKT_IP_V4_ONLY; +#else /* ! IPV6_V6ONLY */ + mhd_assert (! v6_tried); + sk_type = mhd_SKT_IP_V4_ONLY; +#endif /* ! IPV6_V6ONLY */ +#else /* ! HAVE_INET6 */ + mhd_assert (! v6_tried); + sk_type = mhd_SKT_IP_V4_ONLY; +#endif /* ! HAVE_INET6 */ + break; + default: + MHD_LOG_MSG (d, MHD_SC_AF_NOT_SUPPORTED_BY_BUILD, \ + "Unknown address family specified"); + return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD; + } + + mhd_assert (mhd_SKT_NO_SOCKET < sk_type); + + switch (sk_type) + { + case mhd_SKT_IP_V4_ONLY: + case mhd_SKT_IP_V4_WITH_FALLBACK: + /* Zeroing is not required, but may help on exotic platforms */ + memset (&(sa_all.sa_i4), 0, sizeof(sa_all.sa_i4)); + sa_all.sa_i4.sin_family = AF_INET; + sa_all.sa_i4.sin_port = htons (s->bind_port.v_port); + if (0 == INADDR_ANY) /* Optimised at compile time */ + sa_all.sa_i4.sin_addr.s_addr = INADDR_ANY; + else + sa_all.sa_i4.sin_addr.s_addr = htonl (INADDR_ANY); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sa_all.sa_i4.sin_len = sizeof (sa_all.sa_i4); +#endif + p_use_sa = (const struct sockaddr *) &(sa_all.sa_i4); + use_sa_size = (socklen_t) sizeof (sa_all.sa_i4); + break; + case mhd_SKT_IP_V6_ONLY: + case mhd_SKT_IP_DUAL_REQUIRED: + case mhd_SKT_IP_V4_WITH_V6_OPT: + case mhd_SKT_IP_V6_WITH_V4_OPT: +#ifdef HAVE_INET6 + if (1) + { +#ifdef IN6ADDR_ANY_INIT + static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT; +#endif + /* Zeroing is required by POSIX */ + memset (&(sa_all.sa_i6), 0, sizeof(sa_all.sa_i6)); + sa_all.sa_i6.sin6_family = AF_INET6; + sa_all.sa_i6.sin6_port = htons (s->bind_port.v_port); +#ifdef IN6ADDR_ANY_INIT /* Optional assignment at the address is all zeros anyway */ + sa_all.sa_i6.sin6_addr = static_in6any; +#endif +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + sa_all.sa_i6.sin6_len = sizeof (sa_all.sa_i6); +#endif + p_use_sa = (const struct sockaddr *) &(sa_all.sa_i6); + use_sa_size = (socklen_t) sizeof (sa_all.sa_i6); + } + break; +#endif /* HAVE_INET6 */ + case mhd_SKT_UNKNOWN: + case mhd_SKT_NON_IP: + case mhd_SKT_UNIX: + case mhd_SKT_NO_SOCKET: + default: + mhd_assert (0); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; + } + + sk_port = s->bind_port.v_port; + + } + } + else + { + /* No listen socket */ + d->net.listen.fd = MHD_INVALID_SOCKET; + d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN; + d->net.listen.non_block = false; + d->net.listen.port = 0; + + return MHD_SC_OK; + } + + mhd_assert (mhd_SKT_NO_SOCKET != sk_type); + mhd_assert ((NULL != p_use_sa) || sk_already_listening); + mhd_assert ((MHD_INVALID_SOCKET == sk) || sk_already_listening); + + if (MHD_INVALID_SOCKET == sk) + { + mhd_assert (NULL != p_use_sa); +#if defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT) + /* May fail before Win7 SP1 */ + sk = WSASocketW (p_use_sa->sa_family, SOCK_STREAM, 0, + NULL, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + + if (MHD_INVALID_SOCKET == sk) +#endif /* MHD_WINSOCK_SOCKETS && WSA_FLAG_NO_HANDLE_INHERIT */ + sk = socket (p_use_sa->sa_family, + SOCK_STREAM | MHD_SOCK_NONBLOCK + | MHD_SOCK_CLOEXEC | MHD_SOCK_NOSIGPIPE, 0); + + if (MHD_INVALID_SOCKET == sk) + { + bool is_af_err = mhd_SCKT_LERR_IS_AF (); + + if (is_af_err) + MHD_LOG_MSG (d, MHD_SC_AF_NOT_AVAILABLE, \ + "The requested socket address family is rejected " \ + "by the OS"); + +#ifdef HAVE_INET6 + if (mhd_SKT_IP_V4_WITH_FALLBACK == sk_type) + return create_bind_listen_stream_socket (d, s, v6_tried, true); + if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) + return create_bind_listen_stream_socket (d, s, true, false); +#endif /* HAVE_INET6 */ + + if (! is_af_err) + MHD_LOG_MSG (d, MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET, \ + "Failed to open listen socket"); + + return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET; + } + is_non_block = (0 != MHD_SOCK_NONBLOCK); + is_non_inhr = (0 == MHD_SOCK_CLOEXEC); + } + else + { + is_non_block = false; /* Try to set non-block */ + is_non_inhr = false; /* Try to set non-inheritable */ + } + + /* The listen socket must be closed if error code returned + beyond this point */ + + ret = MHD_SC_OK; + + do + { /* The scope for automatic socket close for error returns */ + if (! mhd_FD_FITS_DAEMON (d,sk)) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE, \ + "The listen FD value is higher than allowed"); + ret = MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE; + break; + } + + if (! is_non_inhr) + { + if (! mhd_socket_noninheritable (sk)) + MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NOINHERIT_FAILED, \ + "OS refused to make the listen socket non-inheritable"); + } + + if (! sk_already_listening) + { +#ifdef HAVE_INET6 +#ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure + if ((mhd_SKT_IP_V6_ONLY == sk_type) || + (mhd_SKT_IP_DUAL_REQUIRED == sk_type) || + (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) || + (mhd_SKT_IP_V6_WITH_V4_OPT == sk_type) || + (mhd_SKT_UNKNOWN == sk_type)) + { + mhd_SCKT_OPT_BOOL no_dual_to_set; + bool use_dual; + + use_dual = ((mhd_SKT_IP_DUAL_REQUIRED == sk_type) || + (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) || + (mhd_SKT_IP_V6_WITH_V4_OPT == sk_type)); + no_dual_to_set = use_dual ? 0 : 1; + + if (0 != setsockopt (sk, IPPROTO_IPV6, IPV6_V6ONLY, + (void *) &no_dual_to_set, sizeof (no_dual_to_set))) + { + mhd_SCKT_OPT_BOOL no_dual_current; + socklen_t opt_size; + bool state_unknown; + bool state_match; + + no_dual_current = 0; + opt_size = sizeof(no_dual_current); + + /* Some platforms forbid setting this options, but allow + reading. */ + if ((0 != getsockopt (sk, IPPROTO_IPV6, IPV6_V6ONLY, + (void*) &no_dual_current, &opt_size)) + || (((socklen_t) sizeof(no_dual_current)) < opt_size)) + { + state_unknown = true; + state_match = false; + } + else + { + state_unknown = false; + state_match = ((! ! no_dual_current) == (! ! no_dual_to_set)); + } + + if (state_unknown || ! state_match) + { + if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) + { + (void) mhd_socket_close (sk); + return create_bind_listen_stream_socket (d, s, true, false); + } + if (! state_unknown) + { + /* The dual-stack state is definitely wrong */ + if (mhd_SKT_IP_V6_ONLY == sk_type) + { + MHD_LOG_MSG ( \ + d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED, \ + "Failed to disable IP dual-stack configuration " \ + "for the listen socket"); + ret = MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED; + break; + } + else if (mhd_SKT_UNKNOWN != sk_type) + { + MHD_LOG_MSG ( \ + d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED, \ + "Cannot enable IP dual-stack configuration " \ + "for the listen socket"); + if (mhd_SKT_IP_DUAL_REQUIRED == sk_type) + { + ret = MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED; + break; + } + } + } + else + { + /* The dual-stack state is unknown */ + if (mhd_SKT_UNKNOWN != sk_type) + MHD_LOG_MSG ( + d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_UNKNOWN, \ + "Failed to set dual-stack (IPV6_ONLY) configuration " \ + "for the listen socket, using system defaults"); + } + } + } + } +#else /* ! IPV6_V6ONLY */ + mhd_assert (mhd_SKT_IP_DUAL_REQUIRED != sk_type); + mhd_assert (mhd_SKT_IP_V4_WITH_V6_OPT != sk_type); + mhd_assert (mhd_SKT_IP_V6_WITH_V4_OPT != sk_type); +#endif /* ! IPV6_V6ONLY */ +#endif /* HAVE_INET6 */ + + if (MHD_FOM_AUTO <= d->settings->tcp_fastopen.v_option) + { +#if defined(TCP_FASTOPEN) + int fo_param; +#ifdef __linux__ + /* The parameter is the queue length */ + fo_param = (int) d->settings->tcp_fastopen.v_queue_length; + if (0 == fo_param) + fo_param = MHD_TCP_FASTOPEN_DEF_QUEUE_LEN; +#else /* ! __linux__ */ + fo_param = 1; /* The parameter is on/off type of setting */ +#endif /* ! __linux__ */ + if (0 != setsockopt (sk, IPPROTO_TCP, TCP_FASTOPEN, + (const void *) &fo_param, + sizeof (fo_param))) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \ + "OS refused to enable TCP Fast Open on " \ + "the listen socket"); + if (MHD_FOM_AUTO < d->settings->tcp_fastopen.v_option) + { + ret = MHD_SC_LISTEN_FAST_OPEN_FAILURE; + break; + } + } +#else /* ! TCP_FASTOPEN */ + if (MHD_FOM_AUTO < d->settings->tcp_fastopen.v_option) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \ + "The OS does not support TCP Fast Open"); + ret = MHD_SC_LISTEN_FAST_OPEN_FAILURE; + break; + } +#endif + } + + if (MHD_D_OPTION_BIND_TYPE_NOT_SHARED >= d->settings->listen_addr_reuse) + { +#ifndef MHD_WINSOCK_SOCKETS +#ifdef SO_REUSEADDR + mhd_SCKT_OPT_BOOL on_val1 = 1; + if (0 != setsockopt (sk, IPPROTO_TCP, SO_REUSEADDR, + (const void *) &on_val1, sizeof (on_val1))) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_PORT_REUSE_ENABLE_FAILED, \ + "OS refused to enable address reuse on " \ + "the listen socket"); + } +#else /* ! SO_REUSEADDR */ + MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \ + "The OS does not support address reuse for sockets"); +#endif /* ! SO_REUSEADDR */ +#endif /* ! MHD_WINSOCK_SOCKETS */ + if (MHD_D_OPTION_BIND_TYPE_NOT_SHARED > d->settings->listen_addr_reuse) + { +#if defined(SO_REUSEPORT) || defined(MHD_WINSOCK_SOCKETS) + mhd_SCKT_OPT_BOOL on_val2 = 1; + if (0 != setsockopt (sk, IPPROTO_TCP, +#ifndef MHD_WINSOCK_SOCKETS + SO_REUSEPORT, +#else /* ! MHD_WINSOCK_SOCKETS */ + SO_REUSEADDR, /* On W32 it is the same as SO_REUSEPORT on other platforms */ +#endif /* ! MHD_WINSOCK_SOCKETS */ + (const void *) &on_val2, sizeof (on_val2))) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, \ + "OS refused to enable address sharing " \ + "on the listen socket"); + ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED; + break; + } +#else /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */ + MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \ + "The OS does not support address sharing for sockets"); + ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED; + break; +#endif /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */ + } + } +#if defined(SO_EXCLUSIVEADDRUSE) || defined(SO_EXCLBIND) + else if (MHD_D_OPTION_BIND_TYPE_EXCLUSIVE <= + d->settings->listen_addr_reuse) + { + mhd_SCKT_OPT_BOOL on_val = 1; + if (0 != setsockopt (sk, IPPROTO_TCP, +#ifdef SO_EXCLUSIVEADDRUSE + SO_EXCLUSIVEADDRUSE, +#else + SO_EXCLBIND, +#endif + (const void *) &on_val, sizeof (on_val))) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED, \ + "OS refused to enable exclusive address use " \ + "on the listen socket"); + ret = MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED; + break; + } + } +#endif /* SO_EXCLUSIVEADDRUSE || SO_EXCLBIND */ + + mhd_assert (NULL != p_use_sa); + mhd_assert (0 != use_sa_size); + if (0 != bind (sk, p_use_sa, use_sa_size)) + { +#ifdef HAVE_INET6 + if (mhd_SKT_IP_V4_WITH_FALLBACK == sk_type) + { + (void) mhd_socket_close (sk); + return create_bind_listen_stream_socket (d, s, v6_tried, true); + } + if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) + { + (void) mhd_socket_close (sk); + return create_bind_listen_stream_socket (d, s, true, false); + } +#endif /* HAVE_INET6 */ + MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_BIND_FAILED, \ + "Failed to bind the listen socket"); + ret = MHD_SC_LISTEN_SOCKET_BIND_FAILED; + break; + } + + if (1) + { + int accept_queue_len; + accept_queue_len = (int) s->listen_backlog; + if (0 > accept_queue_len) + accept_queue_len = 0; + if (0 == accept_queue_len) + { +#ifdef SOMAXCONN + accept_queue_len = SOMAXCONN; +#else /* ! SOMAXCONN */ + accept_queue_len = 127; /* Should be the safe value */ +#endif /* ! SOMAXCONN */ + } + if (0 != listen (sk, accept_queue_len)) + { +#ifdef HAVE_INET6 + if (mhd_SKT_IP_V4_WITH_FALLBACK == sk_type) + { + (void) mhd_socket_close (sk); + return create_bind_listen_stream_socket (d, s, v6_tried, true); + } + if (mhd_SKT_IP_V4_WITH_V6_OPT == sk_type) + { + (void) mhd_socket_close (sk); + return create_bind_listen_stream_socket (d, s, true, false); + } +#endif /* HAVE_INET6 */ + MHD_LOG_MSG (d, MHD_SC_LISTEN_FAILURE, \ + "Failed to start listening on the listen socket"); + ret = MHD_SC_LISTEN_FAILURE; + break; + } + } + } + /* A valid listening socket is ready here */ + + if (! is_non_block) + { + is_non_block = mhd_socket_nonblocking (sk); + if (! is_non_block) + MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \ + "OS refused to make the listen socket non-blocking"); + } + + /* Set to the daemon only when the listening socket is fully ready */ + d->net.listen.fd = sk; + switch (sk_type) + { + case mhd_SKT_UNKNOWN: + d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN; + break; + case mhd_SKT_NON_IP: + d->net.listen.type = mhd_SOCKET_TYPE_NON_IP; + break; + case mhd_SKT_UNIX: + d->net.listen.type = mhd_SOCKET_TYPE_UNIX; + break; + case mhd_SKT_IP_V4_ONLY: + case mhd_SKT_IP_V6_ONLY: + case mhd_SKT_IP_DUAL_REQUIRED: + case mhd_SKT_IP_V4_WITH_V6_OPT: + case mhd_SKT_IP_V6_WITH_V4_OPT: + case mhd_SKT_IP_V4_WITH_FALLBACK: + d->net.listen.type = mhd_SOCKET_TYPE_IP; + break; + case mhd_SKT_NO_SOCKET: + default: + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; + } + d->net.listen.non_block = is_non_block; + d->net.listen.port = sk_port; + + mhd_assert (ret == MHD_SC_OK); + + return MHD_SC_OK; + + } while (0); + + mhd_assert (MHD_SC_OK != ret); /* This should be only error returns here */ + mhd_assert (MHD_INVALID_SOCKET != sk); + (void) mhd_socket_close (sk); + return ret; +} + + +/** + * Detect and set the type and port of the listening socket + * @param d the daemon to use + */ +MHD_FN_PAR_NONNULL_ (1) static void +detect_listen_type_and_port (struct MHD_Daemon *restrict d) +{ + union mhd_SockaddrAny sa_all; + socklen_t sa_size; + enum mhd_SocketType declared_type; + + mhd_assert (MHD_INVALID_SOCKET != d->net.listen.fd); + mhd_assert (0 == d->net.listen.port); + memset (&sa_all, 0, sizeof(sa_all)); /* Actually not required */ + sa_size = (socklen_t) sizeof(sa_all); + + if (0 != getsockname (d->net.listen.fd, &(sa_all.sa), &sa_size)) + { + if (mhd_SOCKET_TYPE_IP == d->net.listen.type) + MHD_LOG_MSG (d, MHD_SC_LISTEN_PORT_DETECT_FAILURE, \ + "Failed to detect the port number on the listening socket"); + return; + } + + declared_type = d->net.listen.type; + if (0 == sa_size) + { +#ifndef __linux__ + /* Used on some non-Linux platforms */ + d->net.listen.type = mhd_SOCKET_TYPE_UNIX; + d->net.listen.port = 0; +#else /* ! __linux__ */ + (void) 0; +#endif /* ! __linux__ */ + } + else + { + switch (sa_all.sa.sa_family) + { + case AF_INET: + d->net.listen.type = mhd_SOCKET_TYPE_IP; + d->net.listen.port = (uint_fast16_t) ntohs (sa_all.sa_i4.sin_port); + break; +#ifdef HAVE_INET6 + case AF_INET6: + d->net.listen.type = mhd_SOCKET_TYPE_IP; + d->net.listen.port = (uint_fast16_t) ntohs (sa_all.sa_i6.sin6_port); + break; +#endif /* HAVE_INET6 */ +#ifdef MHD_AF_UNIX + case MHD_AF_UNIX: + d->net.listen.type = mhd_SOCKET_TYPE_UNIX; + d->net.listen.port = 0; + break; +#endif /* MHD_AF_UNIX */ + default: + d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN; + d->net.listen.port = 0; + break; + } + } + + if ((declared_type != d->net.listen.type) + && (mhd_SOCKET_TYPE_IP == declared_type)) + MHD_LOG_MSG (d, MHD_SC_UNEXPECTED_SOCKET_ERROR, \ + "The type of listen socket is detected as non-IP, while " \ + "the socket has been created as an IP socket"); +} + + +#ifdef MHD_USE_EPOLL + +/** + * Initialise daemon's epoll FD + */ +MHD_FN_PAR_NONNULL_ (1) static enum MHD_StatusCode +init_epoll (struct MHD_Daemon *restrict d) +{ + int e_fd; + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int); + mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \ + ((mhd_POLL_TYPE_EPOLL == d->events.poll_type) && \ + (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int))); + mhd_assert ((! d->dbg.net_inited) || \ + (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)); + mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \ + (NULL == d->events.data.epoll.events)); + mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \ + (MHD_INVALID_SOCKET == d->events.data.epoll.e_fd)); +#ifdef HAVE_EPOLL_CREATE1 + e_fd = epoll_create1 (FD_CLOEXEC); +#else /* ! HAVE_EPOLL_CREATE1 */ + e_fd = epoll_create (128); /* The number is usually ignored */ + if (0 <= e_fd) + { + if (! mhd_socket_noninheritable (e_fd)) + MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED, \ + "Failed to make epoll control FD non-inheritable"); + } +#endif /* ! HAVE_EPOLL_CREATE1 */ + if (0 > e_fd) + { + MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_CREATE_FAILED, \ + "Failed to create epoll control FD"); + return MHD_SC_EPOLL_CTL_CREATE_FAILED; /* Failure exit point */ + } + + if (! mhd_FD_FITS_DAEMON (d, e_fd)) + { + MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE, \ + "The epoll control FD value is higher than allowed"); + (void) close (e_fd); + return MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE; /* Failure exit point */ + } + + d->events.poll_type = mhd_POLL_TYPE_EPOLL; + d->events.data.epoll.e_fd = e_fd; + d->events.data.epoll.events = NULL; /* Memory allocated during event and threads init */ + d->events.data.epoll.num_elements = 0; + return MHD_SC_OK; /* Success exit point */ +} + + +/** + * Deinitialise daemon's epoll FD + */ +MHD_FN_PAR_NONNULL_ (1) static void +deinit_epoll (struct MHD_Daemon *restrict d) +{ + mhd_assert (mhd_POLL_TYPE_EPOLL == d->events.poll_type); + mhd_assert (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd); + /* With thread pool the epoll control FD could be migrated to the + * first worker daemon. */ + mhd_assert ((MHD_INVALID_SOCKET != d->events.data.epoll.e_fd) || \ + (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)); + mhd_assert ((MHD_INVALID_SOCKET != d->events.data.epoll.e_fd) || \ + (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY == d->threading.d_type)); + if (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd) + close (d->events.data.epoll.e_fd); +} + + +#endif /* MHD_USE_EPOLL */ + +/** + * Choose sockets monitoring syscall and pre-initialise it + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + enum mhd_IntPollType chosen_type; + + mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \ + (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) || \ + (mhd_WM_INT_EXTERNAL_EVENTS_LEVEL == d->wmode_int) || \ + (MHD_WM_EXTERNAL_SINGLE_FD_WATCH == s->work_mode.mode)); + mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \ + (d->events.poll_type == (enum mhd_IntPollType) s->poll_syscall) \ + || ((MHD_SPS_AUTO == s->poll_syscall) && \ + ((mhd_POLL_TYPE_EXT == d->events.poll_type) || \ + (mhd_POLL_TYPE_EPOLL == d->events.poll_type)))); + + /* Check whether the provided parameter is in the range of expected values */ + switch (s->poll_syscall) + { + case MHD_SPS_AUTO: + chosen_type = mhd_POLL_TYPE_NOT_SET_YET; + break; + case MHD_SPS_SELECT: + mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); +#ifndef MHD_USE_SELECT + MHD_LOG_MSG (d, MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE, \ + "'select()' is not supported by the platform or " \ + "this MHD build"); + return MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE; +#else /* MHD_USE_SELECT */ + chosen_type = mhd_POLL_TYPE_SELECT; +#endif /* MHD_USE_SELECT */ + break; + case MHD_SPS_POLL: + mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); +#ifndef MHD_USE_POLL + MHD_LOG_MSG (d, MHD_SC_POLL_SYSCALL_NOT_AVAILABLE, \ + "'poll()' is not supported by the platform or " \ + "this MHD build"); + return MHD_SC_POLL_SYSCALL_NOT_AVAILABLE; +#else /* MHD_USE_POLL */ + chosen_type = mhd_POLL_TYPE_POLL; +#endif /* MHD_USE_POLL */ + break; + case MHD_SPS_EPOLL: + mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); +#ifndef MHD_USE_EPOLL + MHD_LOG_MSG (d, MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE, \ + "'epoll' is not supported by the platform or " \ + "this MHD build"); + return MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE; +#else /* MHD_USE_EPOLL */ + chosen_type = mhd_POLL_TYPE_EPOLL; +#endif /* MHD_USE_EPOLL */ + break; + default: + MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_SPS, + "Wrong socket polling syscall specified"); + return MHD_SC_CONFIGURATION_UNEXPECTED_SPS; + } + + mhd_assert (mhd_POLL_TYPE_EXT != chosen_type); + + if (mhd_POLL_TYPE_NOT_SET_YET == chosen_type) + { + if (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)) + chosen_type = mhd_POLL_TYPE_EXT; +#ifdef MHD_USE_EPOLL + else if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int) + chosen_type = mhd_POLL_TYPE_EPOLL; /* with possible fallback */ +#endif + else + { +#if defined(MHD_USE_POLL) + chosen_type = mhd_POLL_TYPE_POLL; +#elif defined(MHD_USE_SELECT) + chosen_type = mhd_POLL_TYPE_SELECT; +#else + (void) 0; /* Do nothing. Mute compiler warning */ +#endif + } + } + + /* Try 'epoll' if possible */ +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == chosen_type) + { + enum MHD_StatusCode epoll_res; + + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != \ + d->wmode_int); + epoll_res = init_epoll (d); + + if (MHD_SC_OK != epoll_res) + { + if ((MHD_SPS_EPOLL == s->poll_syscall) || + (MHD_WM_EXTERNAL_SINGLE_FD_WATCH == s->work_mode.mode)) + return epoll_res; /* Cannot init epoll, but epoll is required */ + chosen_type = mhd_POLL_TYPE_NOT_SET_YET; /* Choose again */ + } + } + mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \ + (0 < d->events.data.epoll.e_fd)); +#endif /* MHD_USE_EPOLL */ + + if (mhd_POLL_TYPE_NOT_SET_YET == chosen_type) + { +#if defined(MHD_USE_POLL) + chosen_type = mhd_POLL_TYPE_POLL; +#elif defined(MHD_USE_SELECT) + chosen_type = mhd_POLL_TYPE_SELECT; +#else + MHD_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \ + "All suitable internal sockets polling technologies are " \ + "disabled in this MHD build"); + return MHD_SC_FEATURE_DISABLED; +#endif + } + + switch (chosen_type) + { + case mhd_POLL_TYPE_EXT: + mhd_assert ((MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL == s->work_mode.mode) || \ + (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE != s->work_mode.mode)); + mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode); + d->events.poll_type = mhd_POLL_TYPE_EXT; + d->events.data.ext.cb = + s->work_mode.params.v_external_event_loop_cb.reg_cb; + d->events.data.ext.cls = + s->work_mode.params.v_external_event_loop_cb.reg_cb_cls; + break; +#ifdef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: + mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode); + d->events.poll_type = mhd_POLL_TYPE_SELECT; + d->events.data.select.rfds = NULL; /* Memory allocated during event and threads init */ + d->events.data.select.wfds = NULL; /* Memory allocated during event and threads init */ + d->events.data.select.efds = NULL; /* Memory allocated during event and threads init */ + break; +#endif /* MHD_USE_SELECT */ +#ifdef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: + mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode); + d->events.poll_type = mhd_POLL_TYPE_POLL; + d->events.data.poll.fds = NULL; /* Memory allocated during event and threads init */ + d->events.data.poll.rel = NULL; /* Memory allocated during event and threads init */ + break; +#endif /* MHD_USE_POLL */ +#ifdef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: + mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + /* Pre-initialised by init_epoll() */ + mhd_assert (mhd_POLL_TYPE_EPOLL == d->events.poll_type); + mhd_assert (0 <= d->events.data.epoll.e_fd); + mhd_assert (NULL == d->events.data.epoll.events); + break; +#endif /* MHD_USE_EPOLL */ +#ifndef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: +#endif /* ! MHD_USE_SELECT */ +#ifndef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: +#endif /* ! MHD_USE_POLL */ +#ifndef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: +#endif /* ! MHD_USE_EPOLL */ + case mhd_POLL_TYPE_NOT_SET_YET: + default: + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; + break; + } + return MHD_SC_OK; +} + + +/** + * Initialise network/sockets for the daemon. + * Also choose events mode / sockets polling syscall. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +daemon_init_net (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; + + mhd_assert (! d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); +#ifdef MHD_POSIX_SOCKETS + d->net.cfg.max_fd_num = s->fd_number_limit; +#endif /* MHD_POSIX_SOCKETS */ + + ret = daemon_choose_and_preinit_events (d, s); + if (MHD_SC_OK != ret) + return ret; + + mhd_assert (mhd_POLL_TYPE_NOT_SET_YET != d->events.poll_type); + + /* No direct return of error codes is allowed beyond this point. + Deinit/cleanup must be performed before return of any error. */ + +#if defined(MHD_POSIX_SOCKETS) && defined(MHD_USE_SELECT) + if (mhd_POLL_TYPE_SELECT == d->events.poll_type) + { + if ((MHD_INVALID_SOCKET == d->net.cfg.max_fd_num) || + (FD_SETSIZE < d->net.cfg.max_fd_num)) + d->net.cfg.max_fd_num = FD_SETSIZE; + } +#endif /* MHD_POSIX_SOCKETS && MHD_USE_SELECT */ + + if (MHD_SC_OK == ret) + { + ret = create_bind_listen_stream_socket (d, s, false, false); + + if (MHD_SC_OK == ret) + { + if ((MHD_INVALID_SOCKET != d->net.listen.fd) + && ! d->net.listen.non_block + && ((mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) || + (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int))) + { + MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \ + "The selected daemon work mode requires listening socket " + "in non-blocking mode"); + ret = MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE; + } + + if (MHD_SC_OK == ret) + { + if ((MHD_INVALID_SOCKET != d->net.listen.fd) && + ((0 == d->net.listen.port) || + (mhd_SOCKET_TYPE_UNKNOWN == d->net.listen.type))) + detect_listen_type_and_port (d); + +#ifndef NDEBUG + d->dbg.net_inited = true; +#endif + return MHD_SC_OK; /* Success exit point */ + } + + /* Below is a cleanup path */ + if (MHD_INVALID_SOCKET != d->net.listen.fd) + mhd_socket_close (d->net.listen.fd); + } + } + +#ifdef MHD_USE_EPOLL + if ((mhd_POLL_TYPE_EPOLL == d->events.poll_type)) + close (d->events.data.epoll.e_fd); +#endif /* MHD_USE_EPOLL */ + + mhd_assert (MHD_SC_OK != ret); + + return ret; +} + + +/** + * Deinitialise daemon's network data + * @param d the daemon object + */ +MHD_FN_PAR_NONNULL_ (1) static void +daemon_deinit_net (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (mhd_POLL_TYPE_NOT_SET_YET != d->events.poll_type); +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == d->events.poll_type) + deinit_epoll (d); +#endif /* MHD_USE_EPOLL */ + if (MHD_INVALID_SOCKET != d->net.listen.fd) + mhd_socket_close (d->net.listen.fd); + +#ifndef NDEBUG + d->dbg.net_deinited = true; +#endif +} + + +#if 0 +void +dauth_init (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + mhd_assert ((NULL == s->random_entropy.v_buf) || \ + (0 != s->random_entropy.v_buf_size)); + mhd_assert ((0 == s->random_entropy.v_buf_size) || \ + (NULL != s->random_entropy.v_buf)); +} + + +#endif + + +/** + * Finish initialisation of events processing + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +allocate_events (struct MHD_Daemon *restrict d) +{ + mhd_assert (0 != d->conns.cfg.count_limit); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); + + mhd_DLINKEDL_INIT_LIST (&(d->events),proc_ready); + + switch (d->events.poll_type) + { + case mhd_POLL_TYPE_EXT: + mhd_assert (NULL != d->events.data.ext.cb); +#ifndef NDEBUG + d->dbg.events_allocated = true; +#endif + return MHD_SC_OK; /* Success exit point */ + break; +#ifdef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: + /* The pointers have been set to NULL during pre-initialisations of the events */ + mhd_assert (NULL == d->events.data.select.rfds); + mhd_assert (NULL == d->events.data.select.wfds); + mhd_assert (NULL == d->events.data.select.efds); + d->events.data.select.rfds = (fd_set *) malloc (sizeof(fd_set)); + if (NULL != d->events.data.select.rfds) + { + d->events.data.select.wfds = (fd_set *) malloc (sizeof(fd_set)); + if (NULL != d->events.data.select.wfds) + { + d->events.data.select.efds = (fd_set *) malloc (sizeof(fd_set)); + if (NULL != d->events.data.select.efds) + { +#ifndef NDEBUG + d->dbg.events_allocated = true; +#endif + return MHD_SC_OK; /* Success exit point */ + } + + free (d->events.data.select.wfds); + } + free (d->events.data.select.rfds); + } + MHD_LOG_MSG (d, MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE, \ + "Failed to allocate memory for fd_sets for the daemon"); + return MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE; + break; +#endif /* MHD_USE_SELECT */ +#ifdef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: + /* The pointers have been set to NULL during pre-initialisations of the events */ + mhd_assert (NULL == d->events.data.poll.fds); + mhd_assert (NULL == d->events.data.poll.rel); + if (1) + { + unsigned int num_slots; + num_slots = 2; /* For ITC and listening, even if they are unused */ + if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int) + num_slots += d->conns.cfg.count_limit; + d->events.data.poll.fds = + (struct pollfd *) malloc (sizeof(struct pollfd) * num_slots); + if (NULL != d->events.data.poll.fds) + { + d->events.data.poll.rel = + (union mhd_SocketRelation *) malloc (sizeof(union mhd_SocketRelation) + * num_slots); + if (NULL != d->events.data.poll.rel) + { +#ifndef NDEBUG + d->dbg.events_allocated = true; +#endif + return MHD_SC_OK; /* Success exit point */ + } + free (d->events.data.poll.fds); + } + } + MHD_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \ + "Failed to allocate memory for fd_sets for the daemon"); + return MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE; + break; +#endif /* MHD_USE_POLL */ +#ifdef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: + /* The event FD has been created during pre-initialisations of the events */ + mhd_assert (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd); + /* The pointer has been set to NULL during pre-initialisations of the events */ + mhd_assert (NULL == d->events.data.epoll.events); + mhd_assert (0 == d->events.data.epoll.num_elements); + if (1) + { + size_t num_elements; + const size_t upper_limit = (sizeof(void*) >= 8) ? 4096 : 1024; + + num_elements = d->conns.cfg.count_limit; +#ifdef MHD_USE_THREADS + ++num_elements; /* For ITC */ +#endif + if (MHD_INVALID_SOCKET != d->net.listen) + ++num_elements; + + /* Trade neglectable performance penalty for memory saving */ + /* Very large amount of new events processed in batches */ + if (num_elements > upper_limit) + num_elements = upper_limit; + + d->events.data.epoll.events = + (struct epoll_event *) malloc (sizeof(struct epoll_event) + * num_elements); + if (NULL != d->events.data.epoll.events) + { + d->events.data.epoll.num_elements = num_elements; +#ifndef NDEBUG + d->dbg.events_allocated = true; +#endif + return MHD_SC_OK; /* Success exit point */ + } + } + MHD_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \ + "Failed to allocate memory for fd_sets for the daemon"); + return MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE; + break; +#endif /* MHD_USE_EPOLL */ +#ifndef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: +#endif /* ! MHD_USE_SELECT */ +#ifndef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: +#endif /* ! MHD_USE_POLL */ +#ifndef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: +#endif /* ! MHD_USE_EPOLL */ + case mhd_POLL_TYPE_NOT_SET_YET: + default: + mhd_assert (0 && "Impossible value"); + } + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; +} + + +/** + * Deallocate events data + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +deallocate_events (struct MHD_Daemon *restrict d) +{ + mhd_assert (0 != d->conns.cfg.count_limit); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); + if (mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) + { + mhd_assert (0 && "Wrong workflow"); + MHD_UNREACHABLE_; + return; + } +#ifdef MHD_USE_SELECT + else if (mhd_POLL_TYPE_SELECT == d->events.poll_type) + { + mhd_assert (NULL != d->events.data.select.efds); + mhd_assert (NULL != d->events.data.select.wfds); + mhd_assert (NULL != d->events.data.select.rfds); + free (d->events.data.select.efds); + free (d->events.data.select.wfds); + free (d->events.data.select.rfds); + } +#endif /* MHD_USE_SELECT */ +#ifdef MHD_USE_POLL + else if (mhd_POLL_TYPE_POLL == d->events.poll_type) + { + mhd_assert (NULL != d->events.data.poll.rel); + mhd_assert (NULL != d->events.data.poll.fds); + free (d->events.data.poll.rel); + free (d->events.data.poll.fds); + } +#endif /* MHD_USE_POLL */ +#ifdef MHD_USE_EPOLL + else if (mhd_POLL_TYPE_EPOLL == d->events.poll_type) + { + mhd_assert (0 != d->events.data.epoll.num_elements); + mhd_assert (NULL != d->events.data.epoll.events); + free (d->events.data.epoll.events); + } +#endif /* MHD_USE_EPOLL */ +#ifndef NDEBUG + d->dbg.events_allocated = false; +#endif + return; +} + + +/** + * Initialise daemon's ITC + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +init_itc (struct MHD_Daemon *restrict d) +{ + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); +#ifdef MHD_USE_THREADS + // TODO: add and process "thread unsafe" daemon's option + if (! mhd_itc_init (&(d->threading.itc))) + { +#if defined(MHD_ITC_EVENTFD_) + MHD_LOG_MSG ( \ + d, MHD_SC_ITC_INITIALIZATION_FAILED, \ + "Failed to initialise eventFD for inter-thread communication"); +#elif defined(MHD_ITC_PIPE_) + MHD_LOG_MSG ( \ + d, MHD_SC_ITC_INITIALIZATION_FAILED, \ + "Failed to create a pipe for inter-thread communication"); +#elif defined(MHD_ITC_SOCKETPAIR_) + MHD_LOG_MSG ( \ + d, MHD_SC_ITC_INITIALIZATION_FAILED, \ + "Failed to create a socketpair for inter-thread communication"); +#else +#warning Missing expicit handling of the ITC type + MHD_LOG_MSG ( \ + d, MHD_SC_ITC_INITIALIZATION_FAILED, \ + "Failed to initialise inter-thread communication"); +#endif + return MHD_SC_ITC_INITIALIZATION_FAILED; + } +#endif /* MHD_USE_THREADS */ + return MHD_SC_OK; +} + + +/** + * Deallocate events data + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +deinit_itc (struct MHD_Daemon *restrict d) +{ + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); +#ifdef MHD_USE_THREADS + // TODO: add and process "thread unsafe" daemon's option + mhd_assert (! mhd_ITC_IS_INVALID (d->threading.itc)); + mhd_itc_destroy (d->threading.itc); +#endif /* MHD_USE_THREADS */ +} + + +/** + * The final part of events initialisation: pre-add ITC and listening FD to + * the monitored items (if supported by monitoring syscall). + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (d->dbg.events_allocated); + mhd_assert (! d->dbg.events_fully_inited); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); +#ifdef MHD_USE_THREADS + mhd_assert (mhd_ITC_IS_VALID (d->threading.itc)); +#endif + + switch (d->events.poll_type) + { + case mhd_POLL_TYPE_EXT: + mhd_assert (NULL != d->events.data.ext.cb); + /* Nothing to do with the external events */ + // FIXME: Register the ITC and the listening NOW? + return MHD_SC_OK; + break; +#ifdef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: + mhd_assert (NULL != d->events.data.select.rfds); + mhd_assert (NULL != d->events.data.select.wfds); + mhd_assert (NULL != d->events.data.select.efds); + /* Nothing to do when using 'select()' */ + return MHD_SC_OK; + break; +#endif /* MHD_USE_SELECT */ +#ifdef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: + mhd_assert (NULL != d->events.data.poll.fds); + mhd_assert (NULL != d->events.data.poll.rel); + if (1) + { + unsigned int i; + i = 0; +#ifdef MHD_USE_THREADS + d->events.data.poll.fds[i].fd = mhd_itc_r_fd (d->threading.itc); + d->events.data.poll.fds[i].events = MHD_POLL_IN; + d->events.data.poll.rel[i].fd_id = mhd_SOCKET_REL_MARKER_ITC; + ++i; +#endif + if (MHD_INVALID_SOCKET != d->net.listen.fd) + { + d->events.data.poll.fds[i].fd = d->net.listen.fd; + d->events.data.poll.fds[i].events = MHD_POLL_IN; + d->events.data.poll.rel[i].fd_id = mhd_SOCKET_REL_MARKER_LISTEN; + } + } + return MHD_SC_OK; + break; +#endif /* MHD_USE_POLL */ +#ifdef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: + mhd_assert (MHD_INVALID_SOCKET != d->events.data.epoll.e_fd); + mhd_assert (NULL != d->events.data.epoll.events); + mhd_assert (0 < d->events.data.epoll.num_elements); + if (1) + { + struct epoll_event reg_event; +#ifdef MHD_USE_THREADS + reg_event.events = EPOLLIN; + reg_event.data.u64 = (uint64_t) mhd_SOCKET_REL_MARKER_ITC; /* uint64_t is used in the epoll header */ + if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD, + mhd_itc_r_fd (d->threading.itc), reg_event)) + { + MHD_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \ + "Failed to add ITC fd to the epoll monitoring."); + return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE; + } +#endif + if (MHD_INVALID_SOCKET != d->net.listen.fd) + { + reg_event.events = EPOLLIN; + reg_event.data.u64 = (uint64_t) mhd_SOCKET_REL_MARKER_LISTEN; /* uint64_t is used in the epoll header */ + if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD, + d->net.listen.fd, reg_event)) + { + MHD_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \ + "Failed to add listening fd to the epoll monitoring."); + return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE; + } + } + } + return MHD_SC_OK; + break; +#endif /* MHD_USE_EPOLL */ +#ifndef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: +#endif /* ! MHD_USE_SELECT */ +#ifndef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: +#endif /* ! MHD_USE_POLL */ +#ifndef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: +#endif /* ! MHD_USE_EPOLL */ + case mhd_POLL_TYPE_NOT_SET_YET: + default: + mhd_assert (0 && "Impossible value"); + } + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; +} + + +/** + * Initialise daemon connections' data. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +init_individual_conns (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (0 != d->conns.cfg.count_limit); + + mhd_DLINKEDL_INIT_LIST (&(d->conns),all_conn); + mhd_DLINKEDL_INIT_LIST (&(d->conns),def_timeout); + mhd_DLINKEDL_INIT_LIST (&(d->conns),to_clean); + d->conns.count = 0; + d->conns.block_new = false; + + d->conns.cfg.mem_pool_size = s->conn_memory_limit; + if (0 == d->conns.cfg.mem_pool_size) + d->conns.cfg.mem_pool_size = 32 * 1024; + else if (256 > d->conns.cfg.mem_pool_size) + d->conns.cfg.mem_pool_size = 256; + +#ifndef NDEBUG + d->dbg.connections_inited = true; +#endif + return MHD_SC_OK; +} + + +/** + * Prepare daemon-local (worker daemon for thread pool mode) threading data + * and finish events initialising. + * To be used only with non-master daemons. + * Do not start the thread even if configured for the internal threads. + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +init_individual_thread_data_events_conns (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode res; + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (! d->dbg.connections_inited); + + res = allocate_events (d); + if (MHD_SC_OK != res) + return res; + + res = init_itc (d); + if (MHD_SC_OK == res) + { + res = add_itc_and_listen_to_monitoring (d); + + if (MHD_SC_OK == res) + { +#ifndef NDEBUG + d->dbg.events_fully_inited = true; +#endif +#ifdef MHD_USE_THREADS + mhd_thread_handle_ID_set_invalid (&(d->threading.tid)); + d->threading.stop_requested = false; +#endif /* MHD_USE_THREADS */ +#ifndef NDEBUG + d->dbg.threading_inited = true; +#endif + + res = init_individual_conns (d, s); + if (MHD_SC_OK == res) + return MHD_SC_OK; + } + deinit_itc (d); + } + deallocate_events (d); + mhd_assert (MHD_SC_OK != res); + return res; +} + + +/** + * Deinit daemon-local (worker daemon for thread pool mode) threading data + * and deallocate events. + * To be used only with non-master daemons. + * Do not start the thread even if configured for the internal threads. + * @param d the daemon object + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +deinit_individual_thread_data_events_conns (struct MHD_Daemon *restrict d) +{ + deinit_itc (d); + deallocate_events (d); + mhd_assert (NULL == mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn)); + mhd_assert (NULL == mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready)); +#ifndef NDEBUG + d->dbg.events_fully_inited = false; +#endif +} + + +/** + * Set the maximum number of handled connections for the daemon. + * Works only for global limit, does not work for the worker daemon. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +set_connections_total_limits (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + unsigned int limit_by_conf; + unsigned int limit_by_num; + unsigned int limit_by_select; + unsigned int resulting_limit; + bool error_by_fd_setsize; + unsigned int num_worker_daemons; + + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + + if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int) + { + mhd_assert (MHD_WM_WORKER_THREADS == s->work_mode.mode); + if ((0 != s->global_connection_limit) && + (0 != s->work_mode.params.num_worker_threads) && + (s->global_connection_limit < s->work_mode.params.num_worker_threads)) + { + MHD_LOG_MSG ( \ + d, MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL, \ + "The limit specified by MHD_D_O_GLOBAL_CONNECTION_LIMIT is smaller " \ + "then the number of worker threads."); + return MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL; + } + } + num_worker_daemons = 1; +#ifdef MHD_USE_THREADS + if (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)) + num_worker_daemons = s->work_mode.params.num_worker_threads; +#endif /* MHD_USE_THREADS */ + + limit_by_conf = s->global_connection_limit; + limit_by_num = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */ + limit_by_select = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */ + + error_by_fd_setsize = false; +#ifdef MHD_POSIX_SOCKETS + if (1) + { + limit_by_num = d->net.cfg.max_fd_num; + if (0 != limit_by_num) + { + /* Find the upper limit. + The real limit is lower, as any other process FDs will use the slots + in the allowed numbers range */ + limit_by_num -= 3; /* The numbers zero, one and two are used typically */ +#ifdef MHD_USE_THREADS + limit_by_num -= mhd_ITC_NUM_FDS * num_worker_daemons; +#endif /* MHD_USE_THREADS */ + if (MHD_INVALID_SOCKET != d->net.listen.fd) + --limit_by_num; /* One FD is used for the listening socket */ + if ((num_worker_daemons > limit_by_num) || + (limit_by_num > d->net.cfg.max_fd_num) /* Underflow */) + { + if (d->net.cfg.max_fd_num == s->fd_number_limit) + { + MHD_LOG_MSG ( \ + d, MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT, \ + "The limit specified by MHD_D_O_FD_NUMBER_LIMIT is too strict " \ + "for this daemon settings."); + return MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT; + } + else + { + mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type); + error_by_fd_setsize = true; + } + } + } + else + limit_by_num = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */ + } +#elif defined(MHD_WINSOCK_SOCKETS) + if (1) + { +#ifdef MHD_USE_SELECT + if ((mhd_DAEMON_TYPE_SINGLE == d->threading.d_type) && + (mhd_POLL_TYPE_SELECT == d->events.poll_type)) + { + /* W32 limits the total number (count) of sockets used for select() */ + unsigned int limit_per_worker; + + limit_per_worker = FD_SETSIZE; + if (MHD_INVALID_SOCKET != d->net.listen.fd) + --limit_per_worker; /* The slot for the listening socket */ +#ifdef MHD_USE_THREADS + --limit_per_worker; /* The slot for the ITC */ +#endif /* MHD_USE_THREADS */ + if ((0 == limit_per_worker) || (limit_per_worker > FD_SETSIZE)) + error_by_fd_setsize = true; + else + { + limit_by_select = limit_per_worker * num_worker_daemons; + if (limit_by_select / limit_per_worker != num_worker_daemons) + limit_by_select = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */ + } + } +#endif /* MHD_USE_SELECT */ + (void) 0; /* Mute compiler warning */ + } +#endif /* MHD_POSIX_SOCKETS */ + if (error_by_fd_setsize) + { + MHD_LOG_MSG ( \ + d, MHD_SC_SYS_FD_SETSIZE_TOO_STRICT, \ + "The FD_SETSIZE is too strict to run daemon with the polling " \ + "by select() and with the specified number of workers."); + return MHD_SC_SYS_FD_SETSIZE_TOO_STRICT; + } + + if (0 != limit_by_conf) + { + /* The number has bet set explicitly */ + resulting_limit = limit_by_conf; + } + else + { + /* No user configuration provided */ + unsigned int suggested_limit; +#ifndef MHD_WINSOCK_SOCKETS +#define TYPICAL_NOFILES_LIMIT (1024) /* The usual limit for the number of open FDs */ + suggested_limit = TYPICAL_NOFILES_LIMIT; + suggested_limit -= 3; /* The numbers zero, one and two are used typically */ +#ifdef MHD_USE_THREADS + suggested_limit -= mhd_ITC_NUM_FDS * num_worker_daemons; +#endif /* MHD_USE_THREADS */ + if (MHD_INVALID_SOCKET != d->net.listen.fd) + --suggested_limit; /* One FD is used for the listening socket */ + if (suggested_limit > TYPICAL_NOFILES_LIMIT) + suggested_limit = 0; /* Overflow */ +#else /* MHD_WINSOCK_SOCKETS */ +#ifdef _WIN64 + suggested_limit = 2048; +#else + suggested_limit = 1024; +#endif +#endif /* MHD_WINSOCK_SOCKETS */ + if (suggested_limit < num_worker_daemons) + { + /* Use at least one connection for every worker daemon and + let the system to restrict the new connections if they are above + the system limits. */ + suggested_limit = num_worker_daemons; + } + resulting_limit = suggested_limit; + } + if (resulting_limit > limit_by_num) + resulting_limit = limit_by_num; + + if (resulting_limit > limit_by_select) + resulting_limit = limit_by_select; + + mhd_assert (resulting_limit >= num_worker_daemons); + d->conns.cfg.count_limit = resulting_limit; + + return MHD_SC_OK; +} + + +/** + * Set correct daemon threading type. + * Set the number of workers for thread pool type. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) \ + MHD_FN_MUST_CHECK_RESULT_ static inline enum MHD_StatusCode +set_d_threading_type (struct MHD_Daemon *restrict d) +{ + switch (d->wmode_int) + { + case mhd_WM_INT_EXTERNAL_EVENTS_EDGE: + case mhd_WM_INT_EXTERNAL_EVENTS_LEVEL: + mhd_assert (! mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT == d->events.poll_type); + mhd_assert (NULL != d->events.data.ext.cb); +#ifdef MHD_USE_THREADS + d->threading.d_type = mhd_DAEMON_TYPE_SINGLE; +#endif /* MHD_USE_THREADS */ + return MHD_SC_OK; + case mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS: + mhd_assert (! mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type); +#ifdef MHD_USE_THREADS + d->threading.d_type = mhd_DAEMON_TYPE_SINGLE; +#endif /* MHD_USE_THREADS */ + return MHD_SC_OK; +#ifdef MHD_USE_THREADS + case mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD: + mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type); + d->threading.d_type = mhd_DAEMON_TYPE_SINGLE; + return MHD_SC_OK; + case mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION: + mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type); + d->threading.d_type = mhd_DAEMON_TYPE_LISTEN_ONLY; + return MHD_SC_OK; + case mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL: + mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT != d->events.poll_type); + mhd_assert (mhd_POLL_TYPE_EPOLL != d->events.poll_type); + d->threading.d_type = mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY; + return MHD_SC_OK; +#else /* ! MHD_USE_THREADS */ + case mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD: + case mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION: + case mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL: +#endif /* ! MHD_USE_THREADS */ + default: + mhd_assert (0 && "Impossible value"); + } + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; +} + + +#ifdef MHD_USE_THREADS + +/** + * De-initialise workers pool, including workers daemons. + * The threads must be not running. + * @param d the daemon object + * @param num_workers the number of workers to deinit + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +deinit_workers_pool (struct MHD_Daemon *restrict d, + unsigned int num_workers) +{ + unsigned int i; + mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (NULL != d->threading.hier.pool.workers); + mhd_assert ((2 <= d->threading.hier.pool.num) || \ + (mhd_DAEMON_STATE_STARTING == d->state)); + mhd_assert ((num_workers == d->threading.hier.pool.num) || \ + (mhd_DAEMON_STATE_STARTING == d->state)); + mhd_assert ((mhd_DAEMON_STATE_STOPPING == d->state) || \ + (mhd_DAEMON_STATE_STARTING == d->state)); + + /* Deinitialise in reverse order */ + for (i = num_workers - 1; num_workers > i; --i) + { /* Note: loop exits after underflow of 'i' */ + struct MHD_Daemon *const worker = d->threading.hier.pool.workers + i; + deinit_individual_thread_data_events_conns (worker); +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type) + deinit_epoll (worker); +#endif /* MHD_USE_EPOLL */ + } + free (d->threading.hier.pool.workers); +#ifndef NDEBUG + d->dbg.thread_pool_inited = false; +#endif +} + + +/** + * Initialise workers pool, including workers daemons. + * Do not start the threads. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +init_workers_pool (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode res; + size_t workers_pool_size; + unsigned int conn_per_daemon; + unsigned int num_workers; + unsigned int conn_remainder; + unsigned int i; + + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int); + mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (mhd_POLL_TYPE_NOT_SET_YET < d->events.poll_type); + mhd_assert (1 < s->work_mode.params.num_worker_threads); + mhd_assert (0 != d->conns.cfg.count_limit); + mhd_assert (s->work_mode.params.num_worker_threads <= \ + d->conns.cfg.count_limit); + mhd_assert (! d->dbg.thread_pool_inited); + + num_workers = s->work_mode.params.num_worker_threads; + workers_pool_size = + (sizeof(struct MHD_Daemon) * num_workers); + if (workers_pool_size / num_workers != sizeof(struct MHD_Daemon)) + { /* Overflow */ + MHD_LOG_MSG ( \ + d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \ + "The size of the thread pool is too large."); + return MHD_SC_THREAD_POOL_MALLOC_FAILURE; + } + +#ifndef NDEBUG + mhd_itc_set_invalid (&(d->threading.itc)); + mhd_thread_handle_ID_set_invalid (&(d->threading.tid)); +#endif + + d->threading.hier.pool.workers = malloc (workers_pool_size); + if (NULL == d->threading.hier.pool.workers) + { + MHD_LOG_MSG ( \ + d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \ + "Failed to allocate memory for the thread pool."); + return MHD_SC_THREAD_POOL_MALLOC_FAILURE; + } + + conn_per_daemon = d->conns.cfg.count_limit / num_workers; + conn_remainder = d->conns.cfg.count_limit % num_workers; + res = MHD_SC_OK; + for (i = 0; num_workers > i; ++i) + { + struct MHD_Daemon *restrict const worker = + d->threading.hier.pool.workers + i; + memcpy (worker, d, sizeof(struct MHD_Daemon)); + worker->threading.d_type = mhd_DAEMON_TYPE_WORKER; + worker->threading.hier.master = d; + worker->conns.cfg.count_limit = conn_per_daemon; + if (conn_remainder > i) + worker->conns.cfg.count_limit++; /* Distribute the reminder */ +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type) + { + if (0 == i) + { + mhd_assert (0 <= d->events.data.epoll.e_fd); + /* Move epoll control FD from the master daemon to the first worker */ + /* The FD has been copied by memcpy(). Clean-up the master daemon. */ + d->events.data.epoll.e_fd = MHD_INVALID_SOCKET; + } + else + res = init_epoll (worker); + } +#endif /* MHD_USE_EPOLL */ + if (MHD_SC_OK == res) + { + res = init_individual_thread_data_events_conns (worker, s); + if (MHD_SC_OK == res) + continue; /* Process the next worker */ + +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type) + deinit_epoll (worker); +#endif /* MHD_USE_EPOLL */ + + /* Below is the clean-up of the current slot */ + } + free (worker); + break; + } + if (num_workers == i) + { + mhd_assert (MHD_SC_OK == res); +#ifndef NDEBUG + d->dbg.thread_pool_inited = true; + d->dbg.threading_inited = true; +#endif + d->threading.hier.pool.num = num_workers; + return MHD_SC_OK; + } + + /* Below is a clean-up */ + + mhd_assert (MHD_SC_OK != res); + deinit_workers_pool (d, i); + return res; +} + + +#endif /* MHD_USE_THREADS */ + +/** + * Initialise threading and inter-thread communications. + * Also finish initialisation of events processing and initialise daemon's + * connection data. + * Do not start the thread even if configured for the internal threads. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +daemon_init_threading_and_conn (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode res; + + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (mhd_POLL_TYPE_NOT_SET_YET != d->events.poll_type); + + res = set_d_threading_type (d); + if (MHD_SC_OK != res) + return res; + + res = set_connections_total_limits (d, s); + if (MHD_SC_OK != res) + return res; + + d->threading.cfg.stack_size = s->stack_size; + + if (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)) + res = init_individual_thread_data_events_conns (d, s); + else + { +#ifdef MHD_USE_THREADS + res = init_workers_pool (d, s); +#else /* ! MHD_USE_THREADS */ + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; +#endif /* ! MHD_USE_THREADS */ + } + mhd_assert (d->dbg.events_allocated || \ + mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type) || \ + ! d->dbg.events_allocated); + mhd_assert (! d->dbg.thread_pool_inited || \ + mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type) || \ + d->dbg.thread_pool_inited); + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + mhd_assert (! d->dbg.events_allocated || d->dbg.connections_inited); + mhd_assert (! d->dbg.connections_inited || d->dbg.events_allocated); + return res; +} + + +/** + * De-initialise threading and inter-thread communications. + * Also deallocate events and de-initialise daemon's connection data. + * No daemon-manged threads should be running. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +daemon_deinit_threading_and_conn (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (d->dbg.threading_inited); + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + if (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)) + { + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL != d->wmode_int); + mhd_assert (d->dbg.connections_inited); + mhd_assert (d->dbg.events_allocated); + mhd_assert (! d->dbg.thread_pool_inited); + deinit_individual_thread_data_events_conns (d); + } + else + { +#ifdef MHD_USE_THREADS + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int); + mhd_assert (! d->dbg.connections_inited); + mhd_assert (! d->dbg.events_allocated); + mhd_assert (d->dbg.thread_pool_inited); + deinit_workers_pool (d, d->threading.hier.pool.num); +#else /* ! MHD_USE_THREADS */ + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + (void) 0; +#endif /* ! MHD_USE_THREADS */ + } +} + + +#ifdef MHD_USE_THREADS + +/** + * Start the daemon individual single thread. + * Works both for single thread daemons and for worker daemon for thread + * pool mode. + * Must be called only for daemons with internal threads. + * @param d the daemon object, must be completely initialised + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +start_individual_daemon_thread (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.threading_inited); + mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (! mhd_thread_handle_ID_is_valid_handle (d->threading.tid)); + + if (mhd_DAEMON_TYPE_SINGLE == d->threading.d_type) + { + if (! mhd_create_named_thread ( \ + &(d->threading.tid), "MHD-single", \ + d->threading.cfg.stack_size, \ + &mhd_worker_all_events, \ + (void*) d)) + { + MHD_LOG_MSG (d, MHD_SC_THREAD_MAIN_LAUNCH_FAILURE, \ + "Failed to start daemon main thread."); + return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE; + } + } + else if (mhd_DAEMON_TYPE_WORKER == d->threading.d_type) + { + if (! mhd_create_named_thread ( \ + &(d->threading.tid), "MHD-worker", \ + d->threading.cfg.stack_size, \ + &mhd_worker_all_events, \ + (void*) d)) + { + MHD_LOG_MSG (d, MHD_SC_THREAD_WORKER_LAUNCH_FAILURE, \ + "Failed to start daemon worker thread."); + return MHD_SC_THREAD_WORKER_LAUNCH_FAILURE; + } + } + else if (mhd_DAEMON_TYPE_LISTEN_ONLY == d->threading.d_type) + { + if (! mhd_create_named_thread ( \ + &(d->threading.tid), "MHD-listen", \ + d->threading.cfg.stack_size, \ + &mhd_worker_listening_only, \ + (void*) d)) + { + MHD_LOG_MSG (d, MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE, \ + "Failed to start daemon listening thread."); + return MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE; + } + } + else + { + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; + } + mhd_assert (mhd_thread_handle_ID_is_valid_handle (d->threading.tid)); + return MHD_SC_OK; +} + + +/** + * Stop the daemon individual single thread. + * Works both for single thread daemons and for worker daemon for thread + * pool mode. + * Must be called only for daemons with internal threads. + * @param d the daemon object, must be completely initialised + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +stop_individual_daemon_thread (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.threading_inited); + mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert ((mhd_DAEMON_STATE_STOPPING == d->state) || \ + (mhd_DAEMON_STATE_STARTING == d->state)); + mhd_assert (mhd_thread_handle_ID_is_valid_handle (d->threading.tid)); + + mhd_daemon_trigger_itc (d); + if (! mhd_thread_handle_ID_join_thread (d->threading.tid)) + { + MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \ + "Failed to stop daemon main thread."); + } +} + + +/** + * Stop all worker threads in the thread pool. + * Must be called only for master daemons with thread pool. + * @param d the daemon object, the workers threads must be running + * @param num_workers the number of threads to stop + */ +MHD_FN_PAR_NONNULL_ (1) static void +stop_worker_pool_threads (struct MHD_Daemon *restrict d, + unsigned int num_workers) +{ + unsigned int i; + mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (NULL != d->threading.hier.pool.workers); + mhd_assert (0 != d->threading.hier.pool.num); + mhd_assert (d->dbg.thread_pool_inited); + mhd_assert (2 <= d->threading.hier.pool.num); + mhd_assert ((num_workers == d->threading.hier.pool.num) || \ + (mhd_DAEMON_STATE_STARTING == d->state)); + mhd_assert ((mhd_DAEMON_STATE_STOPPING == d->state) || \ + (mhd_DAEMON_STATE_STARTING == d->state)); + + /* Process all the threads in the reverse order */ + + /* Trigger all threads */ + for (i = num_workers - 1; num_workers > i; --i) + { /* Note: loop exits after underflow of 'i' */ + mhd_assert (mhd_ITC_IS_VALID ( \ + d->threading.hier.pool.workers[i].threading.itc)); + mhd_daemon_trigger_itc (d->threading.hier.pool.workers + i); + } + + /* Collect all threads */ + for (i = num_workers - 1; num_workers > i; --i) + { /* Note: loop exits after underflow of 'i' */ + struct MHD_Daemon *const restrict worker = + d->threading.hier.pool.workers + i; + mhd_assert (mhd_thread_handle_ID_is_valid_handle (worker->threading.tid)); + if (! mhd_thread_handle_ID_join_thread (worker->threading.tid)) + { + MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \ + "Failed to stop a worker thread."); + } + } +} + + +/** + * Start the workers pool threads. + * Must be called only for master daemons with thread pool. + * @param d the daemon object, must be completely initialised + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +start_worker_pool_threads (struct MHD_Daemon *restrict d) +{ + enum MHD_StatusCode res; + unsigned int i; + + mhd_assert (d->dbg.threading_inited); + mhd_assert (mhd_WM_INT_HAS_THREADS (d->wmode_int)); + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (d->dbg.thread_pool_inited); + mhd_assert (2 <= d->threading.hier.pool.num); + + res = MHD_SC_OK; + + for (i = 0; d->threading.hier.pool.num > i; ++i) + { + res = start_individual_daemon_thread (d->threading.hier.pool.workers + i); + if (MHD_SC_OK != res) + break; + } + if (d->threading.hier.pool.num == i) + { + mhd_assert (MHD_SC_OK == res); + return MHD_SC_OK; + } + + stop_worker_pool_threads (d, i); + mhd_assert (MHD_SC_OK != res); + return res; +} + + +#endif /* MHD_USE_THREADS */ + +/** + * Start the daemon internal threads, if the daemon configured to use them. + * @param d the daemon object, must be completely initialised + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +daemon_start_threads (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (d->dbg.threading_inited); + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + if (mhd_WM_INT_HAS_THREADS (d->wmode_int)) + { +#ifdef MHD_USE_THREADS + if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL != d->wmode_int) + { + mhd_assert (d->dbg.threading_inited); + mhd_assert (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY != d->threading.d_type); + return start_individual_daemon_thread (d); + } + else + { + mhd_assert (d->dbg.thread_pool_inited); + mhd_assert (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY == d->threading.d_type); + return start_worker_pool_threads (d); + } +#else /* ! MHD_USE_THREADS */ + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; +#endif /* ! MHD_USE_THREADS */ + } + return MHD_SC_OK; +} + + +/** + * Stop the daemon internal threads, if the daemon configured to use them. + * @param d the daemon object, the threads (if any) must be started + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) static void +daemon_stop_threads (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (d->dbg.threading_inited); + if (mhd_WM_INT_HAS_THREADS (d->wmode_int)) + { +#ifdef MHD_USE_THREADS + if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL != d->wmode_int) + { + mhd_assert (d->dbg.threading_inited); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + stop_individual_daemon_thread (d); + return; + } + else + { + mhd_assert (d->dbg.thread_pool_inited); + mhd_assert (mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + stop_worker_pool_threads (d, d->threading.hier.pool.num); + return; + } +#else /* ! MHD_USE_THREADS */ + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + return MHD_SC_INTERNAL_ERROR; +#endif /* ! MHD_USE_THREADS */ + } +} + + +/** + * Internal daemon initialisation function. + * This function calls all required initialisation stages one-by-one. + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) +MHD_FN_MUST_CHECK_RESULT_ static enum MHD_StatusCode +daemon_start_internal (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode res; + + res = daemon_set_work_mode (d, s); + if (MHD_SC_OK != res) + return res; + + res = daemon_init_net (d, s); + if (MHD_SC_OK != res) + return res; + + // TODO: Other init + + res = daemon_init_threading_and_conn (d, s); + if (MHD_SC_OK == res) + { + mhd_assert (d->dbg.net_inited); + mhd_assert (d->dbg.threading_inited); + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + + res = daemon_start_threads (d); + if (MHD_SC_OK == res) + { + return MHD_SC_OK; + } + + /* Below is a clean-up path */ + daemon_deinit_threading_and_conn (d); + } + + daemon_deinit_net (d); + mhd_assert (MHD_SC_OK != res); + return res; +} + + +MHD_FN_PAR_NONNULL_ (1) MHD_FN_MUST_CHECK_RESULT_ \ + MHD_EXTERN_ enum MHD_StatusCode +MHD_daemon_start (struct MHD_Daemon *daemon) +{ + struct MHD_Daemon *const d = daemon; /* a short alias */ + struct DaemonOptions *const s = daemon->settings; /* a short alias */ + enum MHD_StatusCode res; + + if (mhd_DAEMON_STATE_NOT_STARTED != daemon->state) + return MHD_SC_TOO_LATE; + + mhd_assert (NULL != s); + + d->state = mhd_DAEMON_STATE_STARTING; + res = daemon_start_internal (d, s); + + d->settings = NULL; + dsettings_release (s); + + d->state = + (MHD_SC_OK == res) ? mhd_DAEMON_STATE_STARTED : mhd_DAEMON_STATE_FAILED; + + return res; +} + + +MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_ void +MHD_daemon_destroy (struct MHD_Daemon *daemon) +{ + bool not_yet_started = (mhd_DAEMON_STATE_NOT_STARTED == daemon->state); + bool has_failed = (mhd_DAEMON_STATE_FAILED == daemon->state); + mhd_assert (mhd_DAEMON_STATE_STOPPING > daemon->state); + mhd_assert (mhd_DAEMON_STATE_STARTING != daemon->state); + + daemon->state = mhd_DAEMON_STATE_STOPPING; + if (not_yet_started) + { + mhd_assert (NULL != daemon->settings); + dsettings_release (daemon->settings); + return; + } + else if (! has_failed) + { + mhd_assert (NULL == daemon->settings); + mhd_assert (daemon->dbg.threading_inited); + + daemon_stop_threads (daemon); + + daemon_deinit_threading_and_conn (daemon); + + daemon_deinit_net (daemon); + } + daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */ + + free (daemon); +} diff --git a/src/mhd2/data_process.c b/src/mhd2/data_process.c new file mode 100644 index 00000000..63002e6a --- /dev/null +++ b/src/mhd2/data_process.c @@ -0,0 +1,303 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2015-2024 Evgeny Grin (Karlson2k) + Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/data_process.c + * @brief The implementation of data receiving, sending and processing + * functions for connection + * @author Karlson2k (Evgeny Grin) + * + * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other + * contributors. + */ + +#include "mhd_sys_options.h" + +#include "data_process.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_assert.h" + +#include "mhd_daemon.h" +#include "mhd_connection.h" + +#include "mhd_recv.h" + +#include "daemon_logger.h" + +/** + * Perform data receiving for the connection and try to detect the socket error + * type. + * + * @param c the connection to use + * @param has_err if 'true' then just check for the network error + * type is performed + */ +void +conn_process_recv (struct MHD_Connection restrict *c, + bool has_err) +{ + ssize_t bytes_read; + uint_fast64_t dummy_buf; + void *buf; + size_t buf_size; + size_t received; + enum mhd_SocketError res; + + mhd_assert (MHD_CONNECTION_CLOSED != c->state); + mhd_assert ((NULL != c->read_buffer) || has_err); + mhd_assert ((c->read_buffer_size > c->read_buffer_offset) || has_err); + mhd_assert (! has_err || \ + (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY))); + mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \ + has_err); + + // TODO: TLS support: handshake/transport layer + + if (! has_err || + (NULL != c->read_buffer) || (c->read_buffer_size > c->read_buffer_offset)) + { + buf = c->read_buffer + c->read_buffer_offset; + buf_size = c->read_buffer_size - c->read_buffer_offset; + } + else + { + buf = &dummy_buf; + buf_size = sizeof(buf); + } + + res = mhd_recv (c,buf_size, buf, &received); + + if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err) + { + /* Handle errors */ + if (! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck) + { + /* Re-try last time to detect the error */ + res = mhd_recv (c, &dummy_buf, sizeof(dummy_buf), &received); + } + if (mhd_SOCKET_ERR_IS_HARD (res)) + { + c->sk_discnt_err = res; + c->sk_ready = + (enum mhd_SocketNetState) (((unsigned int) c->sk_ready) + | mhd_SOCKET_NET_STATE_ERROR_READY); + } + return; + } + + if (0 == bytes_read) + c->sk_rmt_shut_wr = true; + + c->read_buffer_offset += (size_t) bytes_read; + MHD_update_last_activity_ (c); // TODO: centralise activity update + return; +} + + +#if 0 // TODO: report disconnect +if ((bytes_read < 0) || socket_error) +{ + if (MHD_ERR_CONNRESET_ == bytes_read) + { + if ( (MHD_CONNECTION_INIT < c->state) && + (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) ) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (c->daemon, + _ ("Socket has been disconnected when reading request.\n")); +#endif + c->discard_request = true; + } + MHD_connection_close_ (c, + MHD_REQUEST_TERMINATED_READ_ERROR); + return; + } + +#ifdef HAVE_MESSAGES + if (MHD_CONNECTION_INIT != c->state) + MHD_DLOG (c->daemon, + _ ("Connection socket is closed when reading " \ + "request due to the error: %s\n"), + (bytes_read < 0) ? str_conn_error_ (bytes_read) : + "detected c closure"); +#endif + CONNECTION_CLOSE_ERROR (c, + NULL); + return; +} + +#if 0 // TODO: handle remote shut WR +if (0 == bytes_read) +{ /* Remote side closed c. */ // FIXME: Actually NOT! + c->sk_rmt_shut_wr = true; + if ( (MHD_CONNECTION_INIT < c->state) && + (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) ) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (c->daemon, + _ ("Connection was closed by remote side with incomplete " + "request.\n")); +#endif + c->discard_request = true; + MHD_connection_close_ (c, + MHD_REQUEST_TERMINATED_CLIENT_ABORT); + } + else if (MHD_CONNECTION_INIT == c->state) + /* This termination code cannot be reported to the application + * because application has not been informed yet about this request */ + MHD_connection_close_ (c, + MHD_REQUEST_TERMINATED_COMPLETED_OK); + else + MHD_connection_close_ (c, + MHD_REQUEST_TERMINATED_WITH_ERROR); + return; +} +#endif +#endif + +MHD_INTERNAL bool +mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c) +{ + const bool send_ready_state_known = + ((0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)) || + mhd_D_IS_USING_EDGE_TRIG (c->daemon)); + const bool has_sock_err = + (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready)); + bool data_processed; + bool need_send; + + data_processed = false; + + if ( ((0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready)) && + ((0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info)) + || has_sock_err)) + || (has_sock_err && c->sk_nonblck)) + { + conn_process_recv (c, has_sock_err); + if (! conn_process_data (c)) + return false; + data_processed = true; + } + need_send = ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) && + (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))); + + if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)) + { + /* Perform sending if: + * + connection is ready for sending or + * + just formed send data, connection send ready status is not known and + * connection socket is non-blocking */ + /* Assuming that after finishing receiving phase, connection send buffers + should have some space as sending was performed before receiving or has + not been performed yet. */ + if ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) || + ((data_processed && ! send_ready_state_known && c->sk_nonblck) || + (has_sock_err && c->sk_nonblck))) + { + conn_process_send (c, has_sock_err); + if (! conn_process_data (c)) + return false; + data_processed = true; + } + } + if (! data_processed) + return conn_process_data (c); + return true; + +#if 0 // TODO: re-implement fasttrack as a single unified buffer sending + if (! force_close) + { + /* No need to check value of 'ret' here as closed connection + * cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */ + if ( (MHD_EVENT_LOOP_INFO_WRITE == c->event_loop_info) && + write_ready) + { + MHD_connection_handle_write (c); + ret = MHD_connection_handle_idle (c); + states_info_processed = true; + } + } + else + { + MHD_connection_close_ (c, + MHD_REQUEST_TERMINATED_WITH_ERROR); + return MHD_connection_handle_idle (c); + } + + if (! states_info_processed) + { /* Connection is not read or write ready, but external conditions + * may be changed and need to be processed. */ + ret = MHD_connection_handle_idle (c); + } + /* Fast track for fast connections. */ + /* If full request was read by single read_handler() invocation + and headers were completely prepared by single MHD_connection_handle_idle() + then try not to wait for next sockets polling and send response + immediately. + As writeability of socket was not checked and it may have + some data pending in system buffers, use this optimization + only for non-blocking sockets. */ + /* No need to check 'ret' as connection is always in + * MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */ + else if (on_fasttrack && c->sk_nonblck) + { + if (MHD_CONNECTION_HEADERS_SENDING == c->state) + { + MHD_connection_handle_write (c); + /* Always call 'MHD_connection_handle_idle()' after each read/write. */ + ret = MHD_connection_handle_idle (c); + } + /* If all headers were sent by single write_handler() and + * response body is prepared by single MHD_connection_handle_idle() + * call - continue. */ + if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) || + (MHD_CONNECTION_CHUNKED_BODY_READY == c->state)) + { + MHD_connection_handle_write (c); + ret = MHD_connection_handle_idle (c); + } + } + + /* All connection's data and states are processed for this turn. + * If connection already has more data to be processed - use + * zero timeout for next select()/poll(). */ + /* Thread-per-connection do not need global zero timeout as + * connections are processed individually. */ + /* Note: no need to check for read buffer availability for + * TLS read-ready connection in 'read info' state as connection + * without space in read buffer will be marked as 'info block'. */ + if ( (! c->daemon->data_already_pending) && + (! MHD_D_IS_USING_THREAD_PER_CONN_ (c->daemon)) ) + { + if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info)) + c->daemon->data_already_pending = true; +#ifdef HTTPS_SUPPORT + else if ( (c->tls_read_ready) && + (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info)) ) + c->daemon->data_already_pending = true; +#endif /* HTTPS_SUPPORT */ + } + return ret; +#endif +} diff --git a/src/mhd2/data_process.h b/src/mhd2/data_process.h new file mode 100644 index 00000000..6a26b596 --- /dev/null +++ b/src/mhd2/data_process.h @@ -0,0 +1,44 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/data_process.h + * @brief The declarations of data processing functions for connection + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_DATA_PROCESS_H +#define MHD_DATA_PROCESS_H 1 + +#include "mhd_sys_options.h" +#include "sys_bool_type.h" + +struct MHD_Connection; /* forward declaration */ + +/** + * Perform connection receiving, sending and processing data. + * @param c the connection to use + * @return true if data processed successfully, + * false if connection needs to be closed + */ +MHD_INTERNAL bool +mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c); + +#endif /* ! MHD_DATA_PROCESS_H */ diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c new file mode 100644 index 00000000..213622ac --- /dev/null +++ b/src/mhd2/events_process.c @@ -0,0 +1,604 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/events_process.c + * @brief The implementation of events processing functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "events_process.h" + +#include "sys_poll.h" + +#include "mhd_public_api.h" + +#include "mhd_itc.h" + +#include "mhd_panic.h" + +#include "mhd_sockets_macros.h" + +#include "mhd_daemon.h" +#include "daemon_logger.h" +#include "mhd_connection.h" +#include "daemon_add_conn.h" + + +MHD_FN_PAR_NONNULL_ (1) static void +update_conn_net_status (struct MHD_Daemon *restrict d, + struct MHD_Connection *restrict c, + bool recv_ready, + bool send_ready, + bool err_state) +{ + enum mhd_SocketNetState sk_state; + + mhd_assert (d == c->daemon); + + sk_state = mhd_SOCKET_NET_STATE_NOTHING; + if (recv_ready) + sk_state = (enum mhd_SocketNetState) + (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_RECV_READY); + if (send_ready) + sk_state = (enum mhd_SocketNetState) + (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_SEND_READY); + if (err_state) + sk_state = (enum mhd_SocketNetState) + (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_ERROR_READY); + c->sk_ready = sk_state; + + if (NULL != mhd_DLINKEDL_GET_PREV (c,proc_ready)) + return; /* In the 'proc_ready' list already */ + + mhd_assert (0 != (((unsigned int) c->event_loop_info) \ + & MHD_EVENT_LOOP_INFO_PROCESS)); + + if ((0 != + (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info) + & (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_WRITE))) || + err_state) + { + mhd_DLINKEDL_INS_LAST (&(d->events),c,proc_ready); + } +} + + +/** + * Accept new connections on the daemon + * @param d the daemon to use + * @return true if all incoming connections has been accepted, + * false if some connection may still wait to be accepted + */ +MHD_FN_PAR_NONNULL_ (1) static bool +daemon_accept_new_conns (struct MHD_Daemon *restrict d) +{ + unsigned int num_to_accept; + mhd_assert (MHD_INVALID_SOCKET != d->net.listen.fd); + mhd_assert (! d->conns.block_new); + mhd_assert (d->conns.count < d->conns.cfg.count_limit); + mhd_assert (! mhd_D_HAS_WORKERS (d)); + + if (! d->net.listen.non_block) + num_to_accept = 1; /* listen socket is blocking, only one connection can be processed */ + else + { + const unsigned int slots_left = d->conns.cfg.count_limit - d->conns.count; + if (! mhd_D_HAS_MASTER (d)) + { + /* Fill up to one quarter of allowed limit in one turn */ + num_to_accept = d->conns.cfg.count_limit / 4; + /* Limit to a reasonable number */ + if (((sizeof(void *) > 4) ? 4096 : 1024) < num_to_accept) + num_to_accept = ((sizeof(void *) > 4) ? 4096 : 1024); + } +#ifdef MHD_USE_THREADS + else + { + /* Has workers thread pool. Care must be taken to evenly distribute + new connections in the workers pool. + At the same time, the burst of new connections should be handled as + quick as possible. */ + const unsigned int num_conn = d->conns.count; + const unsigned int limit = d->conns.cfg.count_limit; + const unsigned int num_workers = + d->threading.hier.master->threading.hier.pool.num; + if (num_conn < limit / 16) + { + num_to_accept = num_conn / num_workers; + if (8 > num_to_accept) + { + if (8 > slots_left / 16) + num_to_accept = slots_left / 16; + else + num_to_accept = 8; + } + if (64 < num_to_accept) + num_to_accept = 64; + } + else if (num_conn < limit / 8) + { + num_to_accept = num_conn * 2 / num_workers; + if (8 > num_to_accept) + { + if (8 > slots_left / 8) + num_to_accept = slots_left / 8; + else + num_to_accept = 8; + } + if (128 < num_to_accept) + num_to_accept = 128; + } + else if (num_conn < limit / 4) + { + num_to_accept = num_conn * 4 / num_workers; + if (8 > num_to_accept) + num_to_accept = 8; + if (slots_left / 4 < num_to_accept) + num_to_accept = slots_left / 4; + if (256 < num_to_accept) + num_to_accept = 256; + } + else if (num_conn < limit / 2) + { + num_to_accept = num_conn * 8 / num_workers; + if (16 > num_to_accept) + num_to_accept = 16; + if (slots_left / 4 < num_to_accept) + num_to_accept = slots_left / 4; + if (256 < num_to_accept) + num_to_accept = 256; + } + else if (slots_left > limit / 4) + { + num_to_accept = slots_left * 4 / num_workers; + if (slots_left / 8 < num_to_accept) + num_to_accept = slots_left / 8; + if (128 < num_to_accept) + num_to_accept = 128; + } + else if (slots_left > limit / 8) + { + num_to_accept = slots_left * 2 / num_workers; + if (slots_left / 16 < num_to_accept) + num_to_accept = slots_left / 16; + if (64 < num_to_accept) + num_to_accept = 64; + } + else /* (slots_left <= limit / 8) */ + num_to_accept = slots_left / 16; + + if (0 == num_to_accept) + num_to_accept = 1; + else if (slots_left > num_to_accept) + num_to_accept = slots_left; + } +#endif /* MHD_USE_THREADS */ + } + + while (0 != --num_to_accept) + { + enum mhd_DaemonAcceptResult res; + res = mhd_daemon_accept_connection (d); + if (mhd_DAEMON_ACCEPT_NO_MORE_PENDING == res) + return true; + if (mhd_DAEMON_ACCEPT_FAILED == res) + break; + } + return false; +} + + +static bool +daemon_process_all_act_coons (struct MHD_Daemon *restrict d) +{ + struct MHD_Connection *restrict c; + mhd_assert (! mhd_D_HAS_WORKERS (d)); + + for (c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready); c != NULL; + c = mhd_DLINKEDL_GET_NEXT (c, proc_ready)) + { + + } + return false; +} + + +#ifdef MHD_USE_POLL + +MHD_FN_PAR_NONNULL_ (1) static unsigned int +poll_update_fds (struct MHD_Daemon *restrict d, + bool listen_only) +{ + unsigned int i_s; + unsigned int i_c; + struct MHD_Connection *restrict c; + mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type); + + i_s = 0; +#ifdef MHD_USE_THREADS + mhd_assert (mhd_ITC_IS_VALID (d->threading.itc)); + mhd_assert (d->events.data.poll.fds[i_s].fd == \ + mhd_itc_r_fd (d->threading.itc)); + mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \ + d->events.data.poll.rel[i_s].fd_id); + ++i_s; +#endif + if (MHD_INVALID_SOCKET != d->net.listen.fd) + { + mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd); + mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \ + d->events.data.poll.rel[i_s].fd_id); + ++i_s; + } + if (listen_only) + return i_s; + + i_c = i_s; + for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn); NULL != c; + c = mhd_DLINKEDL_GET_NEXT (c,all_conn)) + { + unsigned short events; /* 'unsigned' for correct bits manipulations */ + mhd_assert ((i_c - i_s) < d->conns.cfg.count_limit); + + d->events.data.poll.fds[i_c].fd = c->socket_fd; + d->events.data.poll.rel[i_c].connection = c; + events = POLLHUP; /* Actually, not needed and should be ignored in 'events' */ + if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ)) + events |= MHD_POLL_IN; + if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE)) + events |= MHD_POLL_OUT; + + d->events.data.poll.fds[i_c].events = (short) events; + ++i_c; + } + mhd_assert (d->conns.count == (i_c - i_s)); + return i_c; +} + + +MHD_FN_PAR_NONNULL_ (1) static bool +poll_update_statuses_from_fds (struct MHD_Daemon *restrict d, + int num_events) +{ + unsigned int i_s; + unsigned int i_c; + mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type); + mhd_assert (0 <= num_events); + + if (0 == num_events) + return true; + + i_s = 0; +#ifdef MHD_USE_THREADS + mhd_assert (mhd_ITC_IS_VALID (d->threading.itc)); + mhd_assert (d->events.data.poll.fds[i_s].fd == \ + mhd_itc_r_fd (d->threading.itc)); + mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \ + d->events.data.poll.rel[i_s].fd_id); + if (0 != (d->events.data.poll.fds[i_s].revents & (POLLERR | POLLNVAL))) + { + MHD_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \ + "System reported that ITC has an error status."); + /* ITC is broken, need to stop the daemon thread now as otherwise + application will not be able to stop the thread. */ + return false; + } + if (0 != (d->events.data.poll.fds[i_s].revents & MHD_POLL_IN)) + { + --num_events; + /* Clear ITC here, as before any other data processing. + * Any external events may activate ITC again if any data to process is + * added externally. Cleaning ITC early ensures guaranteed that new data + * will not be missed. */ + mhd_itc_clear (d->threading.itc); + } + ++i_s; + + if (0 == num_events) + return true; +#endif /* MHD_USE_THREADS */ + + if (MHD_INVALID_SOCKET != d->net.listen.fd) + { + const short revents = d->events.data.poll.fds[i_s].revents; + + mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd); + mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \ + d->events.data.poll.rel[i_s].fd_id); + if (0 != (revents & (POLLERR | POLLNVAL | POLLHUP))) + { + --num_events; + MHD_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \ + "System reported that the listening socket has an error " \ + "status. The daemon will not listen any more."); + /* Close the listening socket unless the master daemon should close it */ + if (! mhd_D_TYPE_HAS_MASTER_DAEMON (d->threading.d_type)) + mhd_socket_close (d->net.listen.fd); + + /* Stop monitoring socket to avoid spinning with busy-waiting */ + d->net.listen.fd = MHD_INVALID_SOCKET; + } + else if (0 != (d->events.data.poll.fds[i_s].revents & MHD_POLL_IN)) + { + --num_events; + d->events.act_req.accept = true; + } + ++i_s; + } + + mhd_assert ((0 == num_events) || \ + (mhd_DAEMON_TYPE_LISTEN_ONLY != d->threading.d_type)); + + for (i_c = i_s; (i_c < i_s + d->conns.count) && (0 < num_events); ++i_c) + { + struct MHD_Connection *restrict c; + bool recv_ready; + bool send_ready; + bool err_state; + short revents; + mhd_assert (mhd_SOCKET_REL_MARKER_EMPTY != \ + d->events.data.poll.rel[i_c].fd_id); + mhd_assert (mhd_SOCKET_REL_MARKER_ITC != \ + d->events.data.poll.rel[i_c].fd_id); + mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN != \ + d->events.data.poll.rel[i_c].fd_id); + + c = d->events.data.poll.rel[i_c].connection; + mhd_assert (c->socket_fd == d->events.data.poll.fds[i_c].fd); + revents = d->events.data.poll.fds[i_c].revents; + recv_ready = (0 != (revents & MHD_POLL_IN)); + send_ready = (0 != (revents & MHD_POLL_OUT)); +#ifndef MHD_POLLHUP_ON_REM_SHUT_WR + err_state = (0 != (revents & (POLLHUP | POLLERR | POLLNVAL))); +#else + err_state = (0 != (revents & (POLLERR | POLLNVAL))); + if (0 != (revents & POLLHUP)) + { /* This can be a disconnect OR remote side set SHUT_WR */ + recv_ready = true; /* Check the socket by reading */ + if (0 != (d->events.data.poll.fds[i_c].events | MHD_POLL_IN)) + err_state = true; /* The socket will not be checked by reading, the only way to avoid spinning */ + } +#endif + if (0 != (revents & (MHD_POLLPRI | MHD_POLLRDBAND))) + { /* Statuses were not requested, but returned */ + if (! recv_ready || + (0 != (d->events.data.poll.fds[i_c].events | MHD_POLL_IN))) + err_state = true; /* The socket will not be read, the only way to avoid spinning */ + } + if (0 != (revents & MHD_POLLWRBAND)) + { /* Status was not requested, but returned */ + if (! send_ready || + (0 != (d->events.data.poll.fds[i_c].events | MHD_POLL_OUT))) + err_state = true; /* The socket will not be written, the only way to avoid spinning */ + } + + update_conn_net_status (d, c, recv_ready, send_ready, err_state); + } + mhd_assert (d->conns.count >= (i_c - i_s)); + return true; +} + + +MHD_FN_PAR_NONNULL_ (1) static bool +get_all_net_updates_by_poll (struct MHD_Daemon *restrict d, + bool listen_only) +{ + unsigned int num_fds; + int num_events; + mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type); + + num_fds = poll_update_fds (d, listen_only); + + // TODO: handle empty list situation + + num_events = mhd_poll (d->events.data.poll.fds, num_fds, -1); + if (0 > num_events) + { + int err; + bool is_hard_error; + bool is_ignored_error; + is_hard_error = false; + is_ignored_error = false; +#if defined(MHD_POSIX_SOCKETS) + err = errno; + if (0 != err) + { + is_hard_error = + ((MHD_EFAULT_OR_ZERO == err) || (MHD_EINVAL_OR_ZERO == err)); + is_ignored_error = (MHD_EINTR_OR_ZERO == err); + } +#elif defined(MHD_WINSOCK_SOCKETS) + err = WSAGetLastError (); + is_hard_error = + ((WSAENETDOWN == err) || (WSAEFAULT == err) || (WSAEINVAL == err)); +#endif + if (! is_ignored_error) + { + if (is_hard_error) + { + MHD_LOG_MSG (d, MHD_SC_POLL_HARD_ERROR, \ + "The poll() encountered unrecoverable error."); + return false; + } + MHD_LOG_MSG (d, MHD_SC_POLL_SOFT_ERROR, \ + "The poll() encountered error."); + } + } + + if (! poll_update_statuses_from_fds (d, num_events)) + return false; + + return true; +} + + +#endif /* MHD_USE_POLL */ + + +MHD_FN_PAR_NONNULL_ (1) static bool +process_all_events_and_data (struct MHD_Daemon *restrict d) +{ + switch (d->events.poll_type) + { + case mhd_POLL_TYPE_EXT: + return false; // TODO: implement + break; +#ifdef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: + return false; // TODO: implement + break; +#endif /* MHD_USE_SELECT */ +#ifdef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: + if (! get_all_net_updates_by_poll (d, false)) + return false; + break; +#endif /* MHD_USE_POLL */ +#ifdef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: + return false; // TODO: implement + break; +#endif /* MHD_USE_EPOLL */ +#ifndef MHD_USE_SELECT + case mhd_POLL_TYPE_SELECT: +#endif /* ! MHD_USE_SELECT */ +#ifndef MHD_USE_POLL + case mhd_POLL_TYPE_POLL: +#endif /* ! MHD_USE_POLL */ +#ifndef MHD_USE_EPOLL + case mhd_POLL_TYPE_EPOLL: +#endif /* ! MHD_USE_EPOLL */ + case mhd_POLL_TYPE_NOT_SET_YET: + default: + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + MHD_PANIC ("Daemon data integrity broken"); + } + if (d->events.act_req.accept) + { + if (daemon_accept_new_conns (d)) + d->events.act_req.accept = false; + else if (! d->net.listen.non_block) + d->events.act_req.accept = false; + } + daemon_process_all_act_coons (d); + return false; +} + + +/** + * The entry point for the daemon worker thread + * @param cls the closure + */ +mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC +mhd_worker_all_events (void *cls) +{ + struct MHD_Daemon *const restrict d = (struct MHD_Daemon *) cls; + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type)); + mhd_assert (mhd_D_TYPE_HAS_EVENTS_PROCESSING (d->threading.d_type)); + mhd_assert (mhd_DAEMON_TYPE_LISTEN_ONLY != d->threading.d_type); + mhd_assert (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type)); + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int); + mhd_assert (d->dbg.events_fully_inited); + mhd_assert (d->dbg.connections_inited); + + while (! d->threading.stop_requested) + { + if (! process_all_events_and_data (d)) + break; + } + if (! d->threading.stop_requested) + { + MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \ + "The daemon thread is stopping, but termination has not " \ + "been requested by the daemon."); + } + return (mhd_THRD_RTRN_TYPE) 0; +} + + +MHD_FN_PAR_NONNULL_ (1) static bool +process_listening_and_itc_only (struct MHD_Daemon *restrict d) +{ + if (false) + (void) 0; +#ifdef MHD_USE_SELECT + else if (mhd_POLL_TYPE_SELECT == d->events.poll_type) + { + return false; // TODO: implement + } +#endif /* MHD_USE_SELECT */ +#ifdef MHD_USE_POLL + else if (mhd_POLL_TYPE_POLL == d->events.poll_type) + { + if (! get_all_net_updates_by_poll (d, true)) + return false; + } +#endif /* MHD_USE_POLL */ + else + { + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; + MHD_PANIC ("Daemon data integrity broken"); + } + // TODO: Accept connections + return false; +} + + +/** + * The entry point for the daemon listening thread + * @param cls the closure + */ +mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC +mhd_worker_listening_only (void *cls) +{ + struct MHD_Daemon *const restrict d = (struct MHD_Daemon *) cls; + mhd_assert (d->dbg.net_inited); + mhd_assert (! d->dbg.net_deinited); + mhd_assert (mhd_DAEMON_TYPE_LISTEN_ONLY == d->threading.d_type); + mhd_assert (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION == d->wmode_int); + mhd_assert (d->dbg.events_fully_inited); + mhd_assert (d->dbg.connections_inited); + + while (! d->threading.stop_requested) + { + if (! process_listening_and_itc_only (d)) + break; + } + if (! d->threading.stop_requested) + { + MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \ + "The daemon thread is stopping, but termination has not been " \ + "requested by the daemon."); + } + return (mhd_THRD_RTRN_TYPE) 0; +} + + +mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC +mhd_worker_connection (void *cls) +{ + mhd_assert (! cls && "Not yet implemented"); + return (mhd_THRD_RTRN_TYPE) 0; +} diff --git a/src/mhd2/events_process.h b/src/mhd2/events_process.h new file mode 100644 index 00000000..8807d389 --- /dev/null +++ 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 new file mode 100644 index 00000000..119cf356 --- /dev/null +++ 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 new file mode 100644 index 00000000..41cb708a --- /dev/null +++ b/src/mhd2/http_prot_ver.h @@ -0,0 +1,58 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2021-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/http_prot_ver.h + * @brief The definition of the HTTP versions enum + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_HTTP_PROT_VER_H +#define MHD_HTTP_PROT_VER_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_HTTP_PROTOCOL_VER_DEFINED + +/** + * @brief HTTP protocol versions + * @defgroup versions HTTP versions + * @{ + */ +enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_ProtocolVersion +{ + MHD_HTTP_VERSION_INVALID = 0 + , + MHD_HTTP_VERSION_1_0 = 1 + , + MHD_HTTP_VERSION_1_1 = 2 + , + MHD_HTTP_VERSION_2_0 = 3 + , + MHD_HTTP_VERSION_3_0 = 4 + , + MHD_HTTP_VERSION_FUTURE = 255 +}; + + +#define MHD_HTTP_PROTOCOL_VER_DEFINED 1 +#endif /* ! MHD_HTTP_PROTOCOL_VER_DEFINED */ + +#endif /* ! MHD_HTTP_PROT_VER_H */ diff --git a/src/mhd2/http_status_str.c b/src/mhd2/http_status_str.c new file mode 100644 index 00000000..cce274c8 --- /dev/null +++ b/src/mhd2/http_status_str.c @@ -0,0 +1,207 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2017-2024 Evgeny Grin (Karlson2k) + Copyright (C) 2007, 2011, 2017, 2019 Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/** + * @file src/mhd2/http_status_str.c + * @brief Tables of the string response phrases + * @author Elliot Glaysher + * @author Christian Grothoff (minor code clean up) + * @author Karlson2k (Evgeny Grin) (massively refactored and updated) + */ +#include "mhd_sys_options.h" + +#include "sys_base_types.h" +#include "mhd_public_api.h" +#include "mhd_str_macros.h" + +#define UNUSED_STATUS {0, NULL} + +static const struct MHD_String invalid_hundred[] = { + { 0, NULL } +}; + +static const struct MHD_String one_hundred[] = { + /* 100 */ mhd_MSTR_INIT ("Continue"), /* RFC9110, Section 15.2.1 */ + /* 101 */ mhd_MSTR_INIT ("Switching Protocols"), /* RFC9110, Section 15.2.2 */ + /* 102 */ mhd_MSTR_INIT ("Processing"), /* RFC2518 */ + /* 103 */ mhd_MSTR_INIT ("Early Hints") /* RFC8297 */ +}; + +static const struct MHD_String two_hundred[] = { + /* 200 */ mhd_MSTR_INIT ("OK"), /* RFC9110, Section 15.3.1 */ + /* 201 */ mhd_MSTR_INIT ("Created"), /* RFC9110, Section 15.3.2 */ + /* 202 */ mhd_MSTR_INIT ("Accepted"), /* RFC9110, Section 15.3.3 */ + /* 203 */ mhd_MSTR_INIT ("Non-Authoritative Information"), /* RFC9110, Section 15.3.4 */ + /* 204 */ mhd_MSTR_INIT ("No Content"), /* RFC9110, Section 15.3.5 */ + /* 205 */ mhd_MSTR_INIT ("Reset Content"), /* RFC9110, Section 15.3.6 */ + /* 206 */ mhd_MSTR_INIT ("Partial Content"), /* RFC9110, Section 15.3.7 */ + /* 207 */ mhd_MSTR_INIT ("Multi-Status"), /* RFC4918 */ + /* 208 */ mhd_MSTR_INIT ("Already Reported"), /* RFC5842 */ + /* 209 */ UNUSED_STATUS, /* Not used */ + /* 210 */ UNUSED_STATUS, /* Not used */ + /* 211 */ UNUSED_STATUS, /* Not used */ + /* 212 */ UNUSED_STATUS, /* Not used */ + /* 213 */ UNUSED_STATUS, /* Not used */ + /* 214 */ UNUSED_STATUS, /* Not used */ + /* 215 */ UNUSED_STATUS, /* Not used */ + /* 216 */ UNUSED_STATUS, /* Not used */ + /* 217 */ UNUSED_STATUS, /* Not used */ + /* 218 */ UNUSED_STATUS, /* Not used */ + /* 219 */ UNUSED_STATUS, /* Not used */ + /* 220 */ UNUSED_STATUS, /* Not used */ + /* 221 */ UNUSED_STATUS, /* Not used */ + /* 222 */ UNUSED_STATUS, /* Not used */ + /* 223 */ UNUSED_STATUS, /* Not used */ + /* 224 */ UNUSED_STATUS, /* Not used */ + /* 225 */ UNUSED_STATUS, /* Not used */ + /* 226 */ mhd_MSTR_INIT ("IM Used") /* RFC3229 */ +}; + +static const struct MHD_String three_hundred[] = { + /* 300 */ mhd_MSTR_INIT ("Multiple Choices"), /* RFC9110, Section 15.4.1 */ + /* 301 */ mhd_MSTR_INIT ("Moved Permanently"), /* RFC9110, Section 15.4.2 */ + /* 302 */ mhd_MSTR_INIT ("Found"), /* RFC9110, Section 15.4.3 */ + /* 303 */ mhd_MSTR_INIT ("See Other"), /* RFC9110, Section 15.4.4 */ + /* 304 */ mhd_MSTR_INIT ("Not Modified"), /* RFC9110, Section 15.4.5 */ + /* 305 */ mhd_MSTR_INIT ("Use Proxy"), /* RFC9110, Section 15.4.6 */ + /* 306 */ mhd_MSTR_INIT ("Switch Proxy"), /* Not used! RFC9110, Section 15.4.7 */ + /* 307 */ mhd_MSTR_INIT ("Temporary Redirect"), /* RFC9110, Section 15.4.8 */ + /* 308 */ mhd_MSTR_INIT ("Permanent Redirect") /* RFC9110, Section 15.4.9 */ +}; + +static const struct MHD_String four_hundred[] = { + /* 400 */ mhd_MSTR_INIT ("Bad Request"), /* RFC9110, Section 15.5.1 */ + /* 401 */ mhd_MSTR_INIT ("Unauthorized"), /* RFC9110, Section 15.5.2 */ + /* 402 */ mhd_MSTR_INIT ("Payment Required"), /* RFC9110, Section 15.5.3 */ + /* 403 */ mhd_MSTR_INIT ("Forbidden"), /* RFC9110, Section 15.5.4 */ + /* 404 */ mhd_MSTR_INIT ("Not Found"), /* RFC9110, Section 15.5.5 */ + /* 405 */ mhd_MSTR_INIT ("Method Not Allowed"), /* RFC9110, Section 15.5.6 */ + /* 406 */ mhd_MSTR_INIT ("Not Acceptable"), /* RFC9110, Section 15.5.7 */ + /* 407 */ mhd_MSTR_INIT ("Proxy Authentication Required"), /* RFC9110, Section 15.5.8 */ + /* 408 */ mhd_MSTR_INIT ("Request Timeout"), /* RFC9110, Section 15.5.9 */ + /* 409 */ mhd_MSTR_INIT ("Conflict"), /* RFC9110, Section 15.5.10 */ + /* 410 */ mhd_MSTR_INIT ("Gone"), /* RFC9110, Section 15.5.11 */ + /* 411 */ mhd_MSTR_INIT ("Length Required"), /* RFC9110, Section 15.5.12 */ + /* 412 */ mhd_MSTR_INIT ("Precondition Failed"), /* RFC9110, Section 15.5.13 */ + /* 413 */ mhd_MSTR_INIT ("Content Too Large"), /* RFC9110, Section 15.5.14 */ + /* 414 */ mhd_MSTR_INIT ("URI Too Long"), /* RFC9110, Section 15.5.15 */ + /* 415 */ mhd_MSTR_INIT ("Unsupported Media Type"), /* RFC9110, Section 15.5.16 */ + /* 416 */ mhd_MSTR_INIT ("Range Not Satisfiable"), /* RFC9110, Section 15.5.17 */ + /* 417 */ mhd_MSTR_INIT ("Expectation Failed"), /* RFC9110, Section 15.5.18 */ + /* 418 */ UNUSED_STATUS, /* Not used */ + /* 419 */ UNUSED_STATUS, /* Not used */ + /* 420 */ UNUSED_STATUS, /* Not used */ + /* 421 */ mhd_MSTR_INIT ("Misdirected Request"), /* RFC9110, Section 15.5.20 */ + /* 422 */ mhd_MSTR_INIT ("Unprocessable Content"), /* RFC9110, Section 15.5.21 */ + /* 423 */ mhd_MSTR_INIT ("Locked"), /* RFC4918 */ + /* 424 */ mhd_MSTR_INIT ("Failed Dependency"), /* RFC4918 */ + /* 425 */ mhd_MSTR_INIT ("Too Early"), /* RFC8470 */ + /* 426 */ mhd_MSTR_INIT ("Upgrade Required"), /* RFC9110, Section 15.5.22 */ + /* 427 */ UNUSED_STATUS, /* Not used */ + /* 428 */ mhd_MSTR_INIT ("Precondition Required"), /* RFC6585 */ + /* 429 */ mhd_MSTR_INIT ("Too Many Requests"), /* RFC6585 */ + /* 430 */ UNUSED_STATUS, /* Not used */ + /* 431 */ mhd_MSTR_INIT ("Request Header Fields Too Large"), /* RFC6585 */ + /* 432 */ UNUSED_STATUS, /* Not used */ + /* 433 */ UNUSED_STATUS, /* Not used */ + /* 434 */ UNUSED_STATUS, /* Not used */ + /* 435 */ UNUSED_STATUS, /* Not used */ + /* 436 */ UNUSED_STATUS, /* Not used */ + /* 437 */ UNUSED_STATUS, /* Not used */ + /* 438 */ UNUSED_STATUS, /* Not used */ + /* 439 */ UNUSED_STATUS, /* Not used */ + /* 440 */ UNUSED_STATUS, /* Not used */ + /* 441 */ UNUSED_STATUS, /* Not used */ + /* 442 */ UNUSED_STATUS, /* Not used */ + /* 443 */ UNUSED_STATUS, /* Not used */ + /* 444 */ UNUSED_STATUS, /* Not used */ + /* 445 */ UNUSED_STATUS, /* Not used */ + /* 446 */ UNUSED_STATUS, /* Not used */ + /* 447 */ UNUSED_STATUS, /* Not used */ + /* 448 */ UNUSED_STATUS, /* Not used */ + /* 449 */ mhd_MSTR_INIT ("Reply With"), /* MS IIS extension */ + /* 450 */ mhd_MSTR_INIT ("Blocked by Windows Parental Controls"), /* MS extension */ + /* 451 */ mhd_MSTR_INIT ("Unavailable For Legal Reasons") /* RFC7725 */ +}; + +static const struct MHD_String five_hundred[] = { + /* 500 */ mhd_MSTR_INIT ("Internal Server Error"), /* RFC9110, Section 15.6.1 */ + /* 501 */ mhd_MSTR_INIT ("Not Implemented"), /* RFC9110, Section 15.6.2 */ + /* 502 */ mhd_MSTR_INIT ("Bad Gateway"), /* RFC9110, Section 15.6.3 */ + /* 503 */ mhd_MSTR_INIT ("Service Unavailable"), /* RFC9110, Section 15.6.4 */ + /* 504 */ mhd_MSTR_INIT ("Gateway Timeout"), /* RFC9110, Section 15.6.5 */ + /* 505 */ mhd_MSTR_INIT ("HTTP Version Not Supported"), /* RFC9110, Section 15.6.6 */ + /* 506 */ mhd_MSTR_INIT ("Variant Also Negotiates"), /* RFC2295 */ + /* 507 */ mhd_MSTR_INIT ("Insufficient Storage"), /* RFC4918 */ + /* 508 */ mhd_MSTR_INIT ("Loop Detected"), /* RFC5842 */ + /* 509 */ mhd_MSTR_INIT ("Bandwidth Limit Exceeded"), /* Apache extension */ + /* 510 */ mhd_MSTR_INIT ("Not Extended"), /* (OBSOLETED) RFC2774; status-change-http-experiments-to-historic */ + /* 511 */ mhd_MSTR_INIT ("Network Authentication Required") /* RFC6585 */ +}; + + +struct mhd_HttpStatusesBlock +{ + size_t max; + const struct MHD_String *const data; +}; + +#define STATUSES_BLOCK(m) { (sizeof(m) / sizeof(m[0])), m} + +static const struct mhd_HttpStatusesBlock statuses[] = { + STATUSES_BLOCK (invalid_hundred), + STATUSES_BLOCK (one_hundred), + STATUSES_BLOCK (two_hundred), + STATUSES_BLOCK (three_hundred), + STATUSES_BLOCK (four_hundred), + STATUSES_BLOCK (five_hundred) +}; + +MHD_EXTERN_ MHD_FN_CONST_ const struct MHD_String * +MHD_HTTP_status_code_to_string (enum MHD_StatusCode code) +{ + struct MHD_String *res; + const unsigned int code_i = (unsigned int) code; + if (100 > code_i) + return NULL; + if (600 < code) + return NULL; + if (statuses[code_i / 100].max > (code_i % 100)) + return NULL; + res = statuses[code_i / 100] + (code_i % 100); + if (NULL == res->cstr) + return NULL; + return res; +} + + +MHD_INTERNAL MHD_FN_CONST_ const struct MHD_String * +mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code) +{ + static const struct MHD_String no_status = + mhd_MSTR_INIT ("Nonstandard Status"); + const struct MHD_String *res; + + res = MHD_status_code_to_string (code); + if (NULL != res) + return res; + + return &no_status; +} diff --git a/src/mhd2/http_status_str.h b/src/mhd2/http_status_str.h new file mode 100644 index 00000000..4c700fe7 --- /dev/null +++ b/src/mhd2/http_status_str.h @@ -0,0 +1,46 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/http_status_str.h + * @brief The declaration for internal HTTP status string functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_HTTP_STATUS_STR_H +#define MHD_HTTP_STATUS_STR_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_str_types.h" + +/** + * Get string for provided HTTP status code. + * Substitute a replacement string for unknown codes. + * + * @param code the HTTP status code + * @return pointer to MHD_String, never NULL. + */ +MHD_INTERNAL const struct MHD_String * +mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code) +MHD_FN_CONST_; + + +#endif /* ! MHD_HTTP_STATUS_STR_H */ diff --git a/src/mhd2/mhd_action.h b/src/mhd2/mhd_action.h new file mode 100644 index 00000000..a56fbf32 --- /dev/null +++ b/src/mhd2/mhd_action.h @@ -0,0 +1,293 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_action.h + * @brief The definition of the MHD_Action and MHD_UploadAction structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_ACTION_H +#define MHD_ACTION_H 1 + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" + +#include "mhd_str_types.h" + +/** + * The type of the action requested by application + */ +enum mhd_ActionType +{ + /** + * Action has not been set yet. + */ + mhd_ACTION_NO_ACTION = 0 + , + /** + * Start replying with the response + */ + mhd_ACTION_RESPONSE + , + /** + * Suspend requests (connection) + */ + mhd_ACTION_SUSPEND + , + /** + * Hard close request with no response + */ + mhd_ACTION_CLOSE + , + /** + * Process clients upload by application callback + */ + mhd_ACTION_UPLOAD + , + /** + * Process clients upload by POST processor + */ + mhd_ACTION_POST_PROCESS +}; + + +struct MHD_Response; /* forward declaration */ + + +#ifndef MHD_UPLOADCALLBACK_DEFINED + +typedef const struct MHD_UploadAction * +(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_INOUT_SIZE_ (4,3) + *MHD_UploadCallback)(void *upload_cls, + struct MHD_Request *request, + size_t content_data_size, + void *content_data); + +#define MHD_UPLOADCALLBACK_DEFINED 1 +#endif /* ! MHD_UPLOADCALLBACK_DEFINED */ + +/** + * Upload callback data + */ +struct mhd_UploadCallbackData +{ + /** + * The callback + */ + MHD_UploadCallback cb; + + /** + * The closure for @a cb + */ + void *cls; +}; + +/** + * The data for upload callbacks + */ +struct mhd_UploadCallbacks +{ + /** + * The size of the buffer for the @a full upload callback + */ + size_t large_buffer_size; + + /** + * The data for the callback that processes only complete upload + */ + struct mhd_UploadCallbackData full; + + /** + * The data for the callback that processes only incremental uploads + */ + struct mhd_UploadCallbackData inc; +}; + +#ifndef MHD_POST_DATA_READER_DEFINED + +typedef const struct MHD_UploadAction * +(*MHD_PostDataReader) (void *cls, + const struct MHD_String *name, + const struct MHD_String *filename, + const struct MHD_String *content_type, + const struct MHD_String *encoding, + const void *data, + uint_fast64_t off, + size_t size); + + +typedef const struct MHD_UploadAction * +(*MHD_PostDataFinished) (struct MHD_Request *req, + void *cls); + +#define MHD_POST_DATA_READER_DEFINED 1 +#endif /* ! MHD_POST_DATA_READER_DEFINED */ + +#ifndef MHD_HTTP_POSTENCODING_DEFINED + +enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_HTTP_PostEncoding +{ + /** + * No post encoding / broken data / unknown encoding + */ + MHD_HTTP_POST_ENCODING_OTHER = 0 + , + /** + * "application/x-www-form-urlencoded" + * See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#url-encoded-form-data + * See https://url.spec.whatwg.org/#application/x-www-form-urlencoded + * See https://datatracker.ietf.org/doc/html/rfc3986#section-2 + */ + MHD_HTTP_POST_ENCODING_FORM_URLENCODED = 1 + , + /** + * "multipart/form-data" + * See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data + * See https://www.rfc-editor.org/rfc/rfc7578.html + */ + MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA = 2 + , + /** + * "text/plain" + * Introduced by HTML5 + * See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data + * @warning Format is ambiguous. Do not use unless there is a very strong reason. + */ + MHD_HTTP_POST_ENCODING_TEXT_PLAIN = 3 +}; + + +/** @} */ /* end of group postenc */ + +#define MHD_HTTP_POSTENCODING_DEFINED 1 +#endif /* ! MHD_HTTP_POSTENCODING_DEFINED */ + + +// TODO: correct and describe +struct mhd_PostProcessorActionData +{ + size_t pp_buffer_size; + size_t pp_stream_limit; // FIXME: Remove? Duplicated with pp_buffer_size + enum MHD_HTTP_PostEncoding enc; + MHD_PostDataReader reader; + void *reader_cls; + MHD_PostDataFinished done_cb; + void *done_cb_cls; +}; + +/** + * The data for the application action + */ +union mhd_ActionData +{ + /** + * The data for the action #mhd_ACTION_RESPONSE + */ + struct MHD_Response *response; + + /** + * The data for the action #mhd_ACTION_UPLOAD + */ + struct mhd_UploadCallbacks upload; + + /** + * The data for the action #mhd_ACTION_POST_PROCESS + */ + struct mhd_PostProcessorActionData post_process; +}; + + +/** + * The action provided after reporting all headers to application + */ +struct MHD_Action +{ + /** + * The action + */ + enum mhd_ActionType act; + + /** + * The data for the @a act action + */ + union mhd_ActionData data; +}; + +/** + * The type of the action requested by application + */ +enum mhd_UploadActionType +{ + /** + * Action has not been set yet. + */ + mhd_UPLOAD_ACTION_NO_ACTION = 0 + , + /** + * Start replying with the response + */ + mhd_UPLOAD_ACTION_RESPONSE + , + /** + * Suspend requests (connection) + */ + mhd_UPLOAD_ACTION_SUSPEND + , + /** + * Hard close request with no response + */ + mhd_UPLOAD_ACTION_CLOSE + , + /** + * Continue processing the upload + */ + mhd_UPLOAD_ACTION_CONTINUE +}; + +/** + * The data for the application action + */ +union mhd_UploadActionData +{ + /** + * The data for the action #mhd_ACTION_RESPONSE + */ + struct MHD_Response *response; +}; + +/** + * The action provided when consuming client's upload + */ +struct MHD_UploadAction +{ + /** + * The action + */ + enum mhd_UploadActionType act; + + /** + * The data for the @a act action + */ + union mhd_UploadActionData data; +}; + +#endif /* ! MHD_ACTION_H */ diff --git a/src/mhd2/mhd_assert.h b/src/mhd2/mhd_assert.h new file mode 100644 index 00000000..4c3c1d6c --- /dev/null +++ b/src/mhd2/mhd_assert.h @@ -0,0 +1,81 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2017-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_assert.h + * @brief macros for mhd_assert() + * @author Karlson2k (Evgeny Grin) + */ + +/* Unlike POSIX version of 'assert.h', MHD version of 'assert' header + * does not allow multiple redefinition of 'mhd_assert' macro within single + * source file. */ +#ifndef MHD_ASSERT_H +#define MHD_ASSERT_H 1 + +#include "mhd_sys_options.h" + +#if ! defined(_DEBUG) && ! defined(NDEBUG) +# ifndef DEBUG /* Used by some toolchains */ +# define NDEBUG 1 /* Use NDEBUG by default */ +# else /* DEBUG */ +# define _DEBUG 1 /* Non-standart macro */ +# endif /* DEBUG */ +#endif /* !_DEBUG && !NDEBUG */ + +#if defined(_DEBUG) && defined(NDEBUG) +#error Both _DEBUG and NDEBUG are defined +#endif /* _DEBUG && NDEBUG */ + +#ifdef NDEBUG +# define mhd_assert(ignore) ((void) 0) +#else /* ! NDEBUG */ +# ifdef HAVE_ASSERT +# include +# define mhd_assert(CHK) assert (CHK) +# else /* ! HAVE_ASSERT */ +# include +# ifdef HAVE_STDLIB_H +# include +# elif defined(HAVE_UNISTD_H) +# include +# endif +# ifdef MHD_HAVE_MHD_FUNC_ +# define mhd_assert(CHK) \ + do { \ + if (! (CHK)) { \ + fprintf (stderr, \ + "%s:%s:%u Assertion failed: %s\nProgram aborted.\n", \ + __FILE__, MHD_FUNC_, (unsigned) __LINE__, #CHK); \ + fflush (stderr); abort (); } \ + } while (0) +# else +# define mhd_assert(CHK) \ + do { \ + if (! (CHK)) { \ + fprintf (stderr, "%s:%u Assertion failed: %s\nProgram aborted.\n", \ + __FILE__, (unsigned) __LINE__, #CHK); \ + fflush (stderr); abort (); } \ + } while (0) +# endif +# endif /* ! HAVE_ASSERT */ +#endif /* NDEBUG */ + +#endif /* ! MHD_ASSERT_H */ diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h new file mode 100644 index 00000000..527b9eee --- /dev/null +++ b/src/mhd2/mhd_connection.h @@ -0,0 +1,537 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2014-2024 Evgeny Grin (Karlson2k) + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_connection.h + * @brief Definition of struct MHD_connection + * @author Karlson2k (Evgeny Grin) + * @author Daniel Pittman + * @author Christian Grothoff + * + * @warning Imported from MHD1 with minimal changes + * TODO: Rewrite + */ + +#ifndef MHD_CONNECTION_H +#define MHD_CONNECTION_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_threads.h" + +#include "mhd_tristate.h" +#include "mhd_dlinked_list.h" + +#include "mhd_request.h" +#include "mhd_reply.h" + +#include "mhd_socket_error.h" + +#include "mhd_public_api.h" + + +struct MHD_Connection; /* forward declaration */ + +/** + * What is this connection waiting for? + */ +enum MHD_FIXED_FLAGS_ENUM_ MHD_ConnectionEventLoopInfo +{ + /** + * We are waiting to be able to read. + * The same value as #mhd_SOCKET_NET_STATE_RECV_READY + */ + MHD_EVENT_LOOP_INFO_READ = 1 << 0 + , + /** + * We are waiting to be able to write. + * The same value as #mhd_SOCKET_NET_STATE_SEND_READY + */ + MHD_EVENT_LOOP_INFO_WRITE = 1 << 1 + , + /** + * We are waiting for the application to provide data. + */ + MHD_EVENT_LOOP_INFO_PROCESS = 1 << 4 + , + /** + * We are finished and are awaiting cleanup. + */ + MHD_EVENT_LOOP_INFO_CLEANUP = 1 << 5 +}; + +#define MHD_EVENT_LOOP_INFO_PROCESS_READ \ + (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_PROCESS) + + +/** + * The network states for connected sockets + * An internal version of #MHD_FdState. Keep in sync! + */ +enum MHD_FIXED_FLAGS_ENUM_ mhd_SocketNetState +{ + /** + * No active states of the socket + */ + mhd_SOCKET_NET_STATE_NOTHING = 0 + , + /** + * The socket is ready for receiving + */ + mhd_SOCKET_NET_STATE_RECV_READY = 1 << 0 + , + /** + * The socket is ready for sending + */ + mhd_SOCKET_NET_STATE_SEND_READY = 1 << 1 + , + /** + * The socket has some unrecoverable error + */ + mhd_SOCKET_NET_STATE_ERROR_READY = 1 << 2 +}; + + +/** + * States in a state machine for a connection. + * + * The main transitions are any-state to #MHD_CONNECTION_CLOSED, any + * state to state+1, #MHD_CONNECTION_FOOTERS_SENT to + * #MHD_CONNECTION_INIT. #MHD_CONNECTION_CLOSED is the terminal state + * and #MHD_CONNECTION_INIT the initial state. + * + * Note that transitions for *reading* happen only after the input has + * been processed; transitions for *writing* happen after the + * respective data has been put into the write buffer (the write does + * not have to be completed yet). A transition to + * #MHD_CONNECTION_CLOSED or #MHD_CONNECTION_INIT requires the write + * to be complete. + */ +enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE +{ + /** + * Connection just started (no headers received). + * Waiting for the line with the request type, URL and version. + */ + MHD_CONNECTION_INIT = 0, + + /** + * Part of the request line was received. + * Wait for complete line. + */ + MHD_CONNECTION_REQ_LINE_RECEIVING = MHD_CONNECTION_INIT + 1, + + /** + * We got the URL (and request type and version). Wait for a header line. + * + * A milestone state. No received data is processed in this state. + */ + MHD_CONNECTION_REQ_LINE_RECEIVED = MHD_CONNECTION_REQ_LINE_RECEIVING + 1, + + /** + * Receiving request headers. Wait for the rest of the headers. + */ + MHD_CONNECTION_REQ_HEADERS_RECEIVING = MHD_CONNECTION_REQ_LINE_RECEIVED + 1, + + /** + * We got the request headers. Process them. + */ + MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_REQ_HEADERS_RECEIVING + 1, + + /** + * We have processed the request headers. Call application callback. + */ + MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, + + /** + * We have processed the headers and need to send 100 CONTINUE. + */ + MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, + + /** + * We have sent 100 CONTINUE (or do not need to). Read the message body. + */ + MHD_CONNECTION_BODY_RECEIVING = MHD_CONNECTION_CONTINUE_SENDING + 1, + + /** + * We got the request body. + * + * A milestone state. No received data is processed in this state. + */ + MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_BODY_RECEIVING + 1, + + /** + * We are reading the request footers. + */ + MHD_CONNECTION_FOOTERS_RECEIVING = MHD_CONNECTION_BODY_RECEIVED + 1, + + /** + * We received the entire footer. + * + * A milestone state. No received data is processed in this state. + */ + MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVING + 1, + + /** + * We received the entire request. + * Wait for a response to be queued. + */ + MHD_CONNECTION_FULL_REQ_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVED + 1, + + /** + * Finished reading of the request and the response is ready. + * Switch internal logic from receiving to sending, prepare connection + * sending the reply and build the reply header. + */ + MHD_CONNECTION_START_REPLY = MHD_CONNECTION_FULL_REQ_RECEIVED + 1, + + /** + * We have prepared the response headers in the write buffer. + * Send the response headers. + */ + MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_START_REPLY + 1, + + /** + * We have sent the response headers. Get ready to send the body. + */ + MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, + + /** + * We are waiting for the client to provide more + * data of a non-chunked body. + */ + MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_HEADERS_SENT + 1, + + /** + * We are ready to send a part of a non-chunked body. Send it. + */ + MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, + + /** + * We are waiting for the client to provide a chunk of the body. + */ + MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, + + /** + * We are ready to send a chunk. + */ + MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, + + /** + * We have sent the chunked response body. Prepare the footers. + */ + MHD_CONNECTION_CHUNKED_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_READY + 1, + + /** + * We have prepared the response footer. Send it. + */ + MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_CHUNKED_BODY_SENT + 1, + + /** + * We have sent the entire reply. + * Shutdown connection or restart processing to get a new request. + */ + MHD_CONNECTION_FULL_REPLY_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, + + /** + * This connection is to be closed. + */ + MHD_CONNECTION_CLOSED = MHD_CONNECTION_FULL_REPLY_SENT + 1 + +}; + + +/** + * Ability to use same connection for next request + */ +enum MHD_FIXED_ENUM_ MHD_ConnKeepAlive +{ + /** + * Connection must be closed after sending response. + */ + MHD_CONN_MUST_CLOSE = -1, + + /** + * KeelAlive state is not yet determined + */ + MHD_CONN_KEEPALIVE_UNKOWN = 0, + + /** + * Connection can be used for serving next request + */ + MHD_CONN_USE_KEEPALIVE = 1, + + /** + * Connection will be upgraded + */ + MHD_CONN_MUST_UPGRADE = 2 +}; + +/** + * The helper struct for the connections list + */ +mhd_DLINKEDL_LINKS_DEF (MHD_Connection); + + +/** + * State kept for HTTP network connection. + */ +struct MHD_Connection +{ + + /** + * The list with all daemon's connections + */ + mhd_DLNKDL_LINKS (MHD_Connection,all_conn); + + /** + * The state of the connected socket + */ + enum mhd_SocketNetState sk_ready; + + /** + * The type of the error when disconnected early + */ + enum mhd_SocketError sk_discnt_err; + + /** + * Set to 'true' when the client shut down write/send and + * the last byte from remote has been read. + */ + bool sk_rmt_shut_wr; + + /** + * The list with all daemon's connections that ready to processing + */ + mhd_DLNKDL_LINKS (MHD_Connection,proc_ready); + + /** + * The list of connections sorted by timeout + */ + mhd_DLNKDL_LINKS (MHD_Connection,by_timeout); + + /** + * Reference to the MHD_Daemon struct. + */ + struct MHD_Daemon *daemon; + + /** + * Request-specific data + */ + struct MHD_Request rq; + + /** + * Reply-specific data + */ + struct MHD_Reply rp; + + /** + * The memory pool is created whenever we first read from the TCP + * stream and destroyed at the end of each request (and re-created + * for the next request). In the meantime, this pointer is NULL. + * The pool is used for all connection-related data except for the + * response (which maybe shared between connections) and the IP + * address (which persists across individual requests). + */ + struct MemoryPool *pool; + + /** + * We allow the main application to associate some pointer with the + * TCP connection (which may span multiple HTTP requests). Here is + * where we store it. (MHD does not know or care what it is). + * The location is given to the #MHD_NotifyConnectionCallback and + * also accessible via #MHD_CONNECTION_INFO_SOCKET_CONTEXT. + */ + void *socket_context; + + /** + * Close connection after sending response? + * Functions may change value from "Unknown" or "KeepAlive" to "Must close", + * but no functions reset value "Must Close" to any other value. + */ + enum MHD_ConnKeepAlive keepalive; + + /** + * Buffer for reading requests. Allocated in pool. Actually one + * byte larger than @e read_buffer_size (if non-NULL) to allow for + * 0-termination. + */ + char *read_buffer; + + /** + * Buffer for writing response (headers only). Allocated + * in pool. + */ + char *write_buffer; + + /** + * Foreign address (of length @e addr_len). MALLOCED (not + * in pool!). + */ + struct sockaddr_storage *addr; + +#if defined(MHD_USE_THREADS) + /** + * Thread handle for this connection (if we are using + * one thread per connection). + */ + mhd_thread_handle_ID tid; +#endif + + /** + * Size of @e read_buffer (in bytes). + * This value indicates how many bytes we're willing to read + * into the buffer. + */ + size_t read_buffer_size; + + /** + * Position where we currently append data in @e read_buffer (the + * next char after the last valid position). + */ + size_t read_buffer_offset; + + /** + * Size of @e write_buffer (in bytes). + */ + size_t write_buffer_size; + + /** + * Offset where we are with sending from @e write_buffer. + */ + size_t write_buffer_send_offset; + + /** + * Last valid location in write_buffer (where do we + * append and up to where is it safe to send?) + */ + size_t write_buffer_append_offset; + + /** + * Position in the 100 CONTINUE message that + * we need to send when receiving http 1.1 requests. + */ + size_t continue_message_write_offset; + + /** + * Length of the foreign address. + */ + size_t addr_len; + + /** + * Last time this connection had any activity + * (reading or writing). + */ + uint_fast64_t last_activity; + + /** + * After how many milliseconds of inactivity should + * this connection time out? + * Zero for no timeout. + */ + uint_fast64_t connection_timeout_ms; + + /** + * Socket for this connection. Set to #MHD_INVALID_SOCKET if + * this connection has died (daemon should clean + * up in that case). + */ + MHD_Socket socket_fd; + + /** + * The type of the socket: TCP/IP or non TCP/IP (a UNIX domain socket, a pipe) + */ + enum mhd_Tristate is_nonip; + + /** + * true if #socket_fd is non-blocking, false otherwise. + */ + bool sk_nonblck; + + /** + * true if connection socket has set SIGPIPE suppression + */ + bool sk_spipe_suppress; + + /** + * Tracks TCP_CORK / TCP_NOPUSH of the connection socket. + */ + enum mhd_Tristate sk_corked; + + /** + * Tracks TCP_NODELAY state of the connection socket. + */ + enum mhd_Tristate sk_nodelay; + + /** + * Has this socket been closed for reading (i.e. other side closed + * the connection)? If so, we must completely close the connection + * once we are done sending our response (and stop trying to read + * from this socket). + */ + bool read_closed; + + /** + * Some error happens during processing the connection therefore this + * connection must be closed. + * The error may come from the client side (like wrong request format), + * from the application side (like data callback returned error), or from + * the OS side (like out-of-memory). + */ + bool stop_with_error; + + /** + * Response queued early, before the request is fully processed, + * the client upload is rejected. + * The connection cannot be reused for additional requests as the current + * request is incompletely read and it is unclear where is the initial + * byte of the next request. + */ + bool discard_request; + +#if defined(MHD_USE_THREADS) + /** + * Set to `true` if the thread has been joined. + */ + bool thread_joined; +#endif + + /** + * Connection is in the cleanup DL-linked list. + */ + bool in_cleanup; + + /** + * State in the FSM for this connection. + */ + enum MHD_CONNECTION_STATE state; + + /** + * What is this connection waiting for? + */ + enum MHD_ConnectionEventLoopInfo event_loop_info; +}; + + +#endif /* ! MHD_CONNECTION_H */ diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h new file mode 100644 index 00000000..5aecc65f --- /dev/null +++ b/src/mhd2/mhd_daemon.h @@ -0,0 +1,900 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_daemon.h + * @brief The header for declaration of struct MHD_Daemon + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_DAEMON_H +#define MHD_DAEMON_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_socket_type.h" + +#include "mhd_public_api.h" + +#ifdef MHD_USE_THREADS +# include "mhd_threads.h" +# include "mhd_itc_types.h" +#endif + +#if defined(MHD_USE_SELECT) && defined(MHD_POSIX_SOCKETS) +# ifdef HAVE_SYS_SELECT_H +# include +# else +# ifdef HAVE_SYS_TIME_H +# include +# endif +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_UNISTD_H +# include +# else +# include +# endif +# ifdef HAVE_SELECTLIB_H +# include +# endif +# endif +#endif + +#include "sys_poll.h" + +#include "mhd_dlinked_list.h" + +struct DaemonOptions; /* Forward declaration */ +struct MHD_Connection; /* Forward declaration */ + +/** + * The helper struct for the connections list + */ +mhd_DLINKEDL_LIST_DEF (MHD_Connection); + +// #define MHD_USE_EPOLL ///////////////////////////////////////// +// #undef MHD_WINSOCK_SOCKETS +// #define MHD_POSIX_SOCKETS ///////////////////////////// + +/** + * The current phase of the daemon life + */ +enum MHD_FIXED_ENUM_ mhd_DaemonState +{ + /** + * The daemon has been created, but not yet started. + * Setting configuration options is possible. + */ + mhd_DAEMON_STATE_NOT_STARTED = 0 + , + /** + * The daemon is being started. + */ + mhd_DAEMON_STATE_STARTING + , + /** + * The daemon has been started. + * Normal operations. + */ + mhd_DAEMON_STATE_STARTED + , + /** + * The daemon has failed to start + */ + mhd_DAEMON_STATE_FAILED + , + /** + * The daemon is being stopped. + */ + mhd_DAEMON_STATE_STOPPING + , + /** + * The daemon is stopped. + * The state should rarely visible as daemon should be destroyed when stopped. + */ + mhd_DAEMON_STATE_STOPPED +}; + + +/** + * Internal version of the daemon work mode type + */ +enum MHD_FIXED_ENUM_ mhd_WorkModeIntType +{ + /** + * Network edge-triggered events are monitored and provided by application. + * Receiving, sending and processing of the network data if performed when + * special MHD function is called by application. + * No threads managed by the daemon. + */ + mhd_WM_INT_EXTERNAL_EVENTS_EDGE + , + /** + * Network level-triggered events are monitored and provided by application. + * Receiving, sending and processing of the network data if performed when + * special MHD function is called by application. + * No threads managed by the daemon. + */ + mhd_WM_INT_EXTERNAL_EVENTS_LEVEL + , + /** + * The daemon checks for the network events, receives, sends and process + * the network data when special MHD function is called by application. + * No threads managed by the daemon. + */ + mhd_WM_INT_INTERNAL_EVENTS_NO_THREADS + , + /** + * The daemon runs its own single thread, where the daemon monitors + * all network events, receives, sends and process the network data. + */ + mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD + , + /** + * The daemon runs its own single thread, where the daemon monitors + * the new incoming connections, and runs individual thread for each + * established connection, where the daemon monitors connection, receives, + * sends and process the network data. + */ + mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION + , + /** + * The daemon runs its fixed number of threads, all threads monitors the + * new incoming connections and each thread handles own subset of the network + * connections (monitors connections network events, receives, sends and + * process the network data). + */ + mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL +}; + +/** + * Check whether given mhd_WorkModeIntType value should have internal threads, + * either directly controlled or indirectly, via additional workers daemons. + */ +#define mhd_WM_INT_HAS_THREADS(wm_i) \ + (mhd_WM_INT_INTERNAL_EVENTS_ONE_THREAD <= wm_i) + +/** + * Check whether given mhd_WorkModeIntType value has external events + */ +#define mhd_WM_INT_HAS_EXT_EVENTS(wm_i) \ + (mhd_WM_INT_EXTERNAL_EVENTS_LEVEL >= wm_i) + + +/** + * Sockets polling internal syscalls used by MHD. + * + * The same value used as by #MHD_SockPollSyscall, however instead of "auto" + * method this enum uses "not yet set" and enum is extended with an additional + * "external" value. + */ +enum MHD_FIXED_ENUM_ mhd_IntPollType +{ + /** + * External sockets polling is used. + */ + mhd_POLL_TYPE_EXT = -1 + , + /** + * Internal sockets polling syscall has not been selected yet. + */ + mhd_POLL_TYPE_NOT_SET_YET = MHD_SPS_AUTO + , + /** + * Use select(). + */ + mhd_POLL_TYPE_SELECT = MHD_SPS_SELECT + , + /** + * Use poll(). + */ + mhd_POLL_TYPE_POLL = MHD_SPS_POLL + , + /** + * Use epoll. + */ + mhd_POLL_TYPE_EPOLL = MHD_SPS_EPOLL +}; + +#if defined(HAVE_UINTPTR_T) +typedef uintptr_t mhd_SockRelMarker; +#else +typedef unsigned char *mhd_SockRelMarker; +#endif + + +#define mhd_SOCKET_REL_MARKER_EMPTY ((mhd_SockRelMarker) 0) + +#define mhd_SOCKET_REL_MARKER_ITC ((mhd_SockRelMarker) - 1) + +#define mhd_SOCKET_REL_MARKER_LISTEN (mhd_SOCKET_REL_MARKER_ITC - 1) +/** + * Identifier of the FD related to event + */ +union mhd_SocketRelation +{ + /** + * Identifier of the FD. + * Only valid when it is equal to #mhd_SOCKET_REL_MARKER_EMPTY, + * #mhd_SOCKET_REL_MARKER_ITC or #mhd_SOCKET_REL_MARKER_LISTEN. + */ + mhd_SockRelMarker fd_id; + /** + * This is a connection's FD. + * This is valid only when @a fd_id is not valid. + */ + struct MHD_Connection *connection; +}; + +#ifdef MHD_USE_SELECT + +/** + * Daemon's pointers to the preallocated arrays for running sockets monitoring + * by poll(). + */ +struct mhd_DaemonEventsSelectData +{ + /** + * Set of sockets monitored for read (receive) readiness. + */ + fd_set *rfds; + /** + * Set of sockets monitored for write (send) readiness. + */ + fd_set *wfds; + /** + * Set of sockets monitored for exception (error) readiness. + */ + fd_set *efds; +}; + +#endif /* MHD_USE_SELECT */ + +#ifdef MHD_USE_POLL + +/** + * Daemon's pointers to the preallocated arrays for running sockets monitoring + * by poll(). + */ +struct mhd_DaemonEventsPollData +{ + /** + * Array of sockets monitored for read (receive) readiness. + * The size of the array is maximum number of connections per this daemon plus + * two (one for the listen socket and one for ITC). + * ITC FDs and the listening are always the first (and the second), if used. + * The number of elements is always two plus maximum number of connections + * allowed for the daemon. + */ + struct pollfd *fds; + /** + * Array of the @a fds identifications. + * Each slot matches the slot with the same number in @a fds. + * Up to two first positions reserved for the ITC and the listening. + * The number of elements is always two plus maximum number of connections + * allowed for the daemon. + */ + union mhd_SocketRelation *rel; +}; + +#endif /* MHD_USE_POLL */ + +#ifdef MHD_USE_EPOLL +/** + * Daemon's parameters and pointers to the preallocated memory for running + * sockets monitoring by epoll. + */ +struct mhd_DaemonEventsEPollData +{ + /** + * The epoll control FD. + */ + int e_fd; + /** + * The array of events reported by epoll. + */ + struct epoll_event *events; + + /** + * The number of elements in the allocated @a events arrays. + */ + size_t num_elements; +}; + +#endif + +/** + * Daemon's data for external events for sockets monitoring. + * Internal version of struct MHD_WorkModeExternalEventLoopCBParam. + */ +struct mhd_DaemonEventsExternal +{ + /** + * Socket registration callback + */ + MHD_SocketRegistrationUpdateCallback cb; + /** + * Closure for the @a cb + */ + void *cls; +}; + +/** + * Type-specific events monitoring data + */ +union mhd_DaemonEventMonitoringTypeSpecificData +{ +#ifdef MHD_USE_SELECT + /** + * Daemon's pointers to the preallocated arrays for running sockets monitoring + * by poll(). + */ + struct mhd_DaemonEventsSelectData select; +#endif /* MHD_USE_SELECT */ + +#ifdef MHD_USE_POLL + /** + * Daemon's pointers to the preallocated arrays for running sockets monitoring + * by poll(). + */ + struct mhd_DaemonEventsPollData poll; +#endif /* MHD_USE_POLL */ + +#ifdef MHD_USE_EPOLL + /** + * Daemon's parameters and pointers to the preallocated memory for running + * sockets monitoring by epoll. + */ + struct mhd_DaemonEventsEPollData epoll; +#endif + + /** + * Daemon's data for external events for sockets monitoring. + * Internal version of struct MHD_WorkModeExternalEventLoopCBParam. + */ + struct mhd_DaemonEventsExternal ext; +}; + + +/** + * The required actions for the daemon + */ +struct mhd_DaemonEventActionRequired +{ + /** + * If 'true' then connection is waiting to be accepted + */ + bool accept; +}; + + +/** + * The data for events monitoring + */ +struct mhd_DaemonEventMonitoringData +{ + /** + * Sockets polling type used by the daemon. + */ + enum mhd_IntPollType poll_type; + + /** + * Type-specific events monitoring data + */ + union mhd_DaemonEventMonitoringTypeSpecificData data; + + /** + * The required actions for the daemon. + * If daemon has internal thread, this should be changed only inside + * the daemon's thread. + */ + struct mhd_DaemonEventActionRequired act_req; + + /** + * The list of the daemon's connections that need processing + */ + mhd_DLNKDL_LIST (MHD_Connection,proc_ready); + +}; + + +/** + * The type of the socket + */ +enum MHD_FIXED_ENUM_ mhd_SocketType +{ + /** + * The socket type is some non-IP type. + */ + mhd_SOCKET_TYPE_NON_IP = -2 + , + /** + * The socket type is UNIX (LOCAL) + */ + mhd_SOCKET_TYPE_UNIX = -1 + , + /** + * The socket is unknown yet. It can be IP or non-IP. + */ + mhd_SOCKET_TYPE_UNKNOWN = 0 + , + /** + * The socket is definitely IP. + */ + mhd_SOCKET_TYPE_IP = 1 +}; + +/** + * Listen socket data + */ +struct mhd_ListenSocket +{ + /** + * The listening socket + */ + MHD_Socket fd; + /** + * The type of the listening socket @a fd + */ + enum mhd_SocketType type; + /** + * 'true' if @a fd is non-blocking + */ + bool non_block; + /** + * The port number for @a fd + * + * Zero if unknown and for non-IP socket. + */ + uint_fast16_t port; +}; + +/** + * Configured settings for the daemon's network data + */ +struct mhd_DaemonNetworkSettings +{ +#ifdef MHD_POSIX_SOCKETS + /** + * The maximum number for the network FDs. + * The valid FD number must be less then @a max_fd_num. + */ + MHD_Socket max_fd_num; +#else + int dummy; /* mute compiler warning */ +#endif +}; + +/** + * The daemon network/sockets data + */ +struct mhd_DaemonNetwork +{ + /** + * The listening socket + */ + struct mhd_ListenSocket listen; + +#ifdef HAVE_EPOLL + /** + * The epoll FD. + * Set to '-1' when epoll is not used. + */ + int epoll_fd; +#endif + /** + * Configured settings for the daemon's network data + */ + struct mhd_DaemonNetworkSettings cfg; +}; + +#ifdef MHD_USE_THREADS + +/** + * The type of the daemon + */ +enum MHD_FIXED_ENUM_ mhd_DaemonType +{ + /** + * A single daemon, performing all the work. + * + * This daemon may have a optional single thread, managed by MHD. + */ + mhd_DAEMON_TYPE_SINGLE +#ifndef NDEBUG + = 1 +#endif + , + /** + * A master daemon, only controlling worker daemons. + * + * This daemon never handle any network activity directly. + */ + mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY + , + /** + * A daemon with single internal thread for listening and multiple threads + * handling connections with the clients, one thread per connection. + */ + mhd_DAEMON_TYPE_LISTEN_ONLY + , + /** + * A worker daemon, performing the same work as a single daemon, but + * controlled by master daemon. + * + * This type of daemon always have single internal tread and never exposed + * to application directly. + */ + mhd_DAEMON_TYPE_WORKER +}; + +/** + * Check whether the daemon type is allowed to have internal thread with + * direct control + */ +#define mhd_D_TYPE_IS_VALID(t) \ + ((mhd_DAEMON_TYPE_SINGLE <= (t)) && (mhd_DAEMON_TYPE_WORKER >= (t))) + +/** + * Check whether the daemon type must not be exposed to the application + */ +#define mhd_D_TYPE_IS_INTERNAL_ONLY(t) \ + (mhd_DAEMON_TYPE_WORKER == (t)) + +/** + * Check whether the daemon type is allowed to process the network data + */ +#define mhd_D_TYPE_HAS_EVENTS_PROCESSING(t) \ + (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY != (t)) + +/** + * Check whether the daemon type must not be exposed to the application + */ +#define mhd_D_TYPE_HAS_WORKERS(t) \ + (mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY == (t)) + +/** + * Check whether the daemon type has master (controlling) daemon + */ +#define mhd_D_TYPE_HAS_MASTER_DAEMON(t) \ + (mhd_DAEMON_TYPE_WORKER == (t)) + +#else /* ! MHD_USE_THREADS */ + +/** + * Check whether the daemon type is allowed to have internal thread with + * direct control + */ +#define mhd_D_TYPE_IS_VALID(t) (! 0) + +/** + * Check whether the daemon type must not be exposed to the application + */ +#define mhd_D_TYPE_IS_INTERNAL_ONLY(t) (0) + +/** + * Check whether the daemon type is allowed to process the network data + */ +#define mhd_D_TYPE_HAS_EVENTS_PROCESSING(t) (! 0) + +/** + * Check whether the daemon type must not be exposed to the application + */ +#define mhd_D_TYPE_HAS_WORKERS(t) (0) + +/** + * Check whether the daemon type has master (controlling) daemon + */ +#define mhd_D_TYPE_HAS_MASTER_DAEMON(t) (0) + +#endif /* ! MHD_USE_THREADS */ + +#ifdef MHD_USE_THREADS + +/** + * Workers pool data + */ +struct mhd_DaemonWorkerPoolData +{ + /** + * Array of worker daemons + */ + struct MHD_Daemon *workers; + + /** + * The number of workers in the @a workers array + */ + unsigned int num; +}; + +/** + * Hierarchy data for the daemon + */ +union mhd_DeamonHierarchyData +{ + /** + * The pointer to the master daemon + * Only for #mhd_DAEMON_TYPE_WORKER daemons. + */ + struct MHD_Daemon *master; + + /** + * Workers pool data. + * Only for #mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY daemons. + */ + struct mhd_DaemonWorkerPoolData pool; +}; + +/** + * Configured settings for threading + */ +struct mhd_DaemonThreadingDataSettings +{ + /** + * The size of the stack. + * Zero to use system's defaults. + */ + size_t stack_size; +}; + +/** + * Threading and Inter-Thread Communication data + */ +struct mhd_DaemonThreadingData +{ + /** + * The type of this daemon + */ + enum mhd_DaemonType d_type; + + /** + * Inter-Thread Communication channel. + * Used to trigger processing of the command or the data provided or updated + * by the application. + */ + struct mhd_itc itc; + + /** + * 'True' if stop has been requested. + * The daemon thread should stop all connections and then close. + */ + volatile bool stop_requested; + + /** + * The handle of the daemon's thread (if managed by the daemon) + */ + mhd_thread_handle_ID tid; + + /** + * The hierarchy data for the daemon. + * Used only when @a d_type is #mhd_DAEMON_TYPE_MASTER_CONTROL_ONLY or + * #mhd_DAEMON_TYPE_WORKER. + */ + union mhd_DeamonHierarchyData hier; + + /** + * Configured settings for threading + */ + struct mhd_DaemonThreadingDataSettings cfg; +}; + +#endif /* MHD_USE_THREADS */ + +/** + * Configured settings for the daemon's connections + */ +struct mhd_DaemonConnectionsSettings +{ + /** + * The maximum number of connections handled by the daemon + */ + unsigned int count_limit; + + /** + * Connection's default timeout value (in seconds) + */ + unsigned int timeout; + + /** + * Connection's memory pool size + */ + size_t mem_pool_size; +}; + +/** + * Connections handling data + */ +struct mhd_DaemonConnections +{ + + /** + * The list of all daemon's connections. + * All connection are listed here, expect connection in @a to_clean list. + */ + mhd_DLNKDL_LIST (MHD_Connection,all_conn); + + /** + * The list of all daemon's connections + */ + mhd_DLNKDL_LIST (MHD_Connection,def_timeout); + + /** + * The list of all daemon's connections + */ + mhd_DLNKDL_LIST (MHD_Connection,to_clean); + + /** + * The current number of connections handled by the daemon + */ + unsigned int count; + + /** + * If set to 'true' then no new connection is allowed. + * New connection may be blocked because of various system limits, when + * additional connection would fail anyway. This flag should be cleared + * when any already processing connection closed. + * Can be checked from other threads + */ + volatile bool block_new; + + /** + * Configured settings for the daemon's connections + */ + struct mhd_DaemonConnectionsSettings cfg; +}; + + +#ifndef NDEBUG +/** + * Various debugging data + */ +struct mhd_daemon_debug +{ + bool net_inited; + bool net_deinited; + bool events_allocated; + bool events_fully_inited; + bool thread_pool_inited; + bool threading_inited; + bool connections_inited; + bool avoid_accept4; +}; +#endif /* NDEBUG */ + + +struct MHD_Daemon +{ + /* General data */ + + /** + * The daemon state + */ + enum mhd_DaemonState state; + + /** + * The daemon work mode (private version) + */ + enum mhd_WorkModeIntType wmode_int; + + /* Events/sockets monitoring/polling data */ + + /** + * The data for events monitoring + */ + struct mhd_DaemonEventMonitoringData events; + + /* Network/sockets data */ + + /** + * The daemon network/sockets data + */ + struct mhd_DaemonNetwork net; + +#ifdef MHD_USE_THREADS + /* Threading data */ + + /** + * The daemon threading and Inter-Thread Communication data + */ + struct mhd_DaemonThreadingData threading; +#endif + + /* Connections handling */ + + /** + * The connections handling data + */ + struct mhd_DaemonConnections conns; + + /* Request processing data */ + + /** + * Request callback. + * The main request processing callback. + */ + MHD_RequestCallback req_cb; + + /** + * The closure for @a req_cb + */ + void *req_cb_cls; + + /* Other data */ + + /** + * Daemon logging parameters + */ + struct MHD_DaemonOptionValueLog log_params; + + + /* Temporal data */ + + /** + * User settings, before applied to the daemon itself + */ + struct DaemonOptions *settings; + +#ifndef NDEBUG + /* Debug data */ + + struct mhd_daemon_debug dbg; +#endif +}; + + +#ifdef MHD_POSIX_SOCKETS +/** + * Checks whether @a fd socket number fits limitations for the @a d_ptr daemon + */ +# define mhd_FD_FITS_DAEMON(d_ptr,fd) \ + ((MHD_INVALID_SOCKET == d_ptr->net.cfg.max_fd_num) || \ + (d_ptr->net.cfg.max_fd_num > fd)) +#else +# define mhd_FD_FITS_DAEMON(d_ptr,fd) (! 0) +#endif +#endif /* ! MHD_DAEMON_H */ + +#ifdef MHD_USE_EPOLL +# define mhd_D_IS_USING_EPOLL(d) \ + (mhd_POLL_TYPE_EPOLL == ((d)->events.poll_type)) +#else +# define mhd_D_IS_USING_EPOLL(d) (0) +#endif + +#ifdef MHD_USE_THREADS +# define mhd_D_HAS_THREADS(d) mhd_WM_INT_HAS_THREADS ((d)->wmode_int) +#else +# define mhd_D_HAS_THREADS(d) (0) +#endif + +#define mhd_D_HAS_WORKERS(d) mhd_D_TYPE_HAS_WORKERS ((d)->threading.d_type) + +#define mhd_D_HAS_MASTER(d) mhd_D_TYPE_HAS_MASTER_DAEMON ((d)->threading.d_type) + +#define mhd_D_IS_INTERNAL_ONLY(d) \ + mhd_D_TYPE_IS_INTERNAL_ONLY ((d)->threading.d_type) + +#define mhd_D_IS_USING_EDGE_TRIG(d) \ + (mhd_D_IS_USING_EPOLL (d) || \ + (mhd_WM_INT_EXTERNAL_EVENTS_EDGE ==((d)->wmode_int))) diff --git a/src/mhd2/mhd_dlinked_list.h b/src/mhd2/mhd_dlinked_list.h new file mode 100644 index 00000000..266dbded --- /dev/null +++ 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 new file mode 100644 index 00000000..53614a51 --- /dev/null +++ b/src/mhd2/mhd_iovec.h @@ -0,0 +1,105 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_iovec.h + * @brief The definition of the tristate type and helper macros + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_IOVEC_H +#define MHD_IOVEC_H 1 + +#include "mhd_sys_options.h" +#include "mhd_socket_type.h" + +#if defined(HAVE_WRITEV) || defined(HAVE_SENDMSG) +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_SYS_SOCKET_H +# include +# elif defined(HAVE_UNISTD_H) +# include +# endif +# ifdef HAVE_SOCKLIB_H +# include +# endif +# ifdef HAVE_SYS_UIO_H +# include +# endif +#endif + +#if defined(MHD_WINSOCK_SOCKETS) +/** + * Internally used I/O vector type for use with winsock. + * Binary matches system "WSABUF". + */ +struct mhd_w32_iovec +{ + unsigned long iov_len; + char *iov_base; +}; +typedef struct mhd_w32_iovec mhd_iovec; +#define MHD_IOV_ELMN_MAX_SIZE ULONG_MAX +typedef unsigned long mhd_iov_size; +#elif defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) +/** + * Internally used I/O vector type for use when writev or sendmsg + * is available. Matches system "struct iovec". + */ +typedef struct iovec mhd_iovec; +#define MHD_IOV_ELMN_MAX_SIZE SIZE_MAX +typedef size_t mhd_iov_size; +#else +/** + * Internally used I/O vector type for use when writev or sendmsg + * is not available. + */ +typedef struct MHD_IoVec mhd_iovec; +#define MHD_IOV_ELMN_MAX_SIZE SIZE_MAX +typedef size_t mhd_iov_size; +#endif + + +struct mhd_iovec_track +{ + /** + * The copy of array of iovec elements. + * The copy of elements are updated during sending. + * The number of elements is not changed during lifetime. + */ + mhd_iovec *iov; + + /** + * The number of elements in @a iov. + * This value is not changed during lifetime. + */ + size_t cnt; + + /** + * The number of sent elements. + * At the same time, it is the index of the next (or current) element + * to send. + */ + size_t sent; +}; + +#endif /* ! MHD_IOVEC_H */ diff --git a/src/mhd2/mhd_itc.c b/src/mhd2/mhd_itc.c new file mode 100644 index 00000000..b5d34ced --- /dev/null +++ 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 new file mode 100644 index 00000000..669c02a5 --- /dev/null +++ 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 +# include /* for uint_fast64_t */ +# ifdef HAVE_UNISTD_H +# include /* for read(), write() */ +# else +# include +# endif /* HAVE_UNISTD_H */ +# include + +/** + * 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 /* for O_CLOEXEC, O_NONBLOCK */ +# endif /* HAVE_PIPE2_FUNC && HAVE_FCNTL_H */ +# ifdef HAVE_UNISTD_H +# include /* for read(), write() */ +# else +# include +# endif /* HAVE_UNISTD_H */ +# include +# if defined(HAVE_PIPE2_FUNC) && defined(O_CLOEXEC) && defined(O_NONBLOCK) +# define MHD_USE_PIPE2 1 +# else +# include "sys_bool_type.h" +# endif + + +/** + * Number of FDs used by every ITC. + */ +# define mhd_ITC_NUM_FDS (2) + +/** + * Set @a itc to the invalid value. + * @param pitc the pointer to the itc to set + */ +# define mhd_itc_set_invalid(pitc) ((pitc)->fd[0] = (pitc)->fd[1] = -1) + +/** + * Check whether ITC has valid value. + * + * Macro check whether @a itc value is valid (allowed), + * macro does not check whether @a itc was really initialised. + * @param itc the itc to check + * @return boolean true if @a itc has valid value, + * boolean false otherwise. + */ +# define mhd_ITC_IS_VALID(itc) (0 <= (itc).fd[0]) + +/** + * Initialise ITC by generating pipe + * @param pitc the pointer to the ITC to initialise + * @return non-zero if succeeded, zero otherwise + */ +# if defined(MHD_USE_PIPE2) +# define mhd_itc_init(pitc) (! pipe2 ((pitc)->fd, O_CLOEXEC | O_NONBLOCK)) +# else /* ! MHD_USE_PIPE2 */ +# define mhd_itc_init(pitc) \ + ( (! pipe ((pitc)->fd)) ? \ + (mhd_itc_nonblocking ((pitc)) ? \ + (! 0) : \ + (mhd_itc_destroy (*(pitc)), 0) ) \ + : (0) ) + +# define MHD_HAVE_MHD_ITC_NONBLOCKING 1 +/** + * Change itc FD options to be non-blocking. + * + * @param pitc the pointer to ITC to manipulate + * @return true if succeeded, false otherwise + */ +MHD_INTERNAL bool +mhd_itc_nonblocking (struct mhd_itc *pitc); + +# endif /* ! MHD_USE_PIPE2 */ + +/** + * Activate signal on @a itc + * @param itc the itc to use + * @return non-zero if succeeded, zero otherwise + */ +# define mhd_itc_activate(itc) \ + ((write ((itc).fd[1], (const void*) "", 1) > 0) || (EAGAIN == errno)) + +/** + * Return read FD of @a itc which can be used for poll(), select() etc. + * @param itc the itc to get FD + * @return FD of read side + */ +# define mhd_itc_r_fd(itc) ((itc).fd[0]) + +/** + * Clear signaled state on @a itc + * @param itc the itc to clear + */ +# define mhd_itc_clear(itc) do \ + { long __b; \ + while (0 < read ((itc).fd[0], (void*) &__b, sizeof(__b))) \ + {(void) 0;} } while (0) + +/** + * Destroy previously initialised ITC + * @param itc the itc to destroy + * @return non-zero if succeeded, zero otherwise + */ +# define mhd_itc_destroy(itc) \ + (0 == (close ((itc).fd[0]) + close ((itc).fd[1]))) + +#elif defined(MHD_ITC_SOCKETPAIR_) + +/* **************** ITC implementation by socket pair ********** */ + +# include "mhd_sockets_macros.h" +# if ! defined(mhd_socket_pair_nblk) +# include "mhd_sockets_funcs.h" +# endif + +/** + * Number of FDs used by every ITC. + */ +# define mhd_ITC_NUM_FDS (2) + +/** + * Set @a itc to the invalid value. + * @param pitc the pointer to the itc to set + */ +# define mhd_itc_set_invalid(pitc) \ + ((pitc)->sk[0] = (pitc)->sk[1] = MHD_INVALID_SOCKET) + +/** + * Check whether ITC has valid value. + * + * Macro check whether @a itc value is valid (allowed), + * macro does not check whether @a itc was really initialised. + * @param itc the itc to check + * @return boolean true if @a itc has valid value, + * boolean false otherwise. + */ +# define mhd_ITC_IS_VALID(itc) (MHD_INVALID_SOCKET != (itc).sk[0]) + +/** + * Initialise ITC by generating socketpair + * @param itc the itc to initialise + * @return non-zero if succeeded, zero otherwise + */ +# ifdef mhd_socket_pair_nblk +# define mhd_itc_init(pitc) mhd_socket_pair_nblk ((pitc)->sk) +# else /* ! mhd_socket_pair_nblk */ +# define mhd_itc_init(pitc) \ + ( (! mhd_socket_pair ((pitc)->sk)) ? \ + (0) : ( (! mhd_itc_nonblocking ((pitc))) ? \ + (mhd_itc_destroy (*(pitc)), 0) : (! 0) ) ) + +/** + * Change itc FD options to be non-blocking. + * + * @param pitc the pointer to ITC to manipulate + * @return true if succeeded, false otherwise + */ +# define mhd_itc_nonblocking(pitc) \ + (mhd_socket_nonblocking ((pitc)->sk[0]) && \ + mhd_socket_nonblocking ((pitc)->sk[1])) + +# endif /* ! mhd_socket_pair_nblk */ + +/** + * Activate signal on @a itc + * @param itc the itc to use + * @return non-zero if succeeded, zero otherwise + */ +# define mhd_itc_activate(itc) \ + ((0 < mhd_send ((itc).sk[1], "", 1)) || mhd_SCKT_LERR_IS_EAGAIN ()) + +/** + * Return read FD of @a itc which can be used for poll(), select() etc. + * @param itc the itc to get FD + * @return FD of read side + */ +# define mhd_itc_r_fd(itc) ((itc).sk[0]) + +/** + * Clear signaled state on @a itc + * @param itc the itc to clear + */ +# define mhd_itc_clear(itc) do \ + { long __b; \ + while (0 < recv ((itc).sk[0], (void*) &__b, sizeof(__b), 0)) \ + {(void) 0;} } while (0) + +/** + * Destroy previously initialised ITC + * @param itc the itc to destroy + * @return non-zero if succeeded, zero otherwise + */ +# define mhd_itc_destroy(itc) \ + (mhd_socket_close ((itc).sk[1]) ? \ + mhd_socket_close ((itc).sk[0]) : \ + ((void) mhd_socket_close ((itc).sk[0]), ! ! 0) ) + +#endif /* MHD_ITC_SOCKETPAIR_ */ + +/** + * Destroy previously initialised ITC and abort execution + * if error is detected. + * @param itc the itc to destroy + */ +#define mhd_itc_destroy_chk(itc) do { \ + if (! mhd_itc_destroy (itc)) \ + MHD_PANIC ("Failed to destroy ITC.\n"); \ +} while (0) + +/** + * Check whether ITC has invalid value. + * + * Macro check whether @a itc value is invalid, + * macro does not check whether @a itc was destroyed. + * @param itc the itc to check + * @return boolean true if @a itc has invalid value, + * boolean false otherwise. + */ +#define mhd_ITC_IS_INVALID(itc) (! mhd_ITC_IS_VALID (itc)) + +#endif /* ! MHD_ITC_H */ diff --git a/src/microhttpd/mhd_itc_types.h b/src/mhd2/mhd_itc_types.h similarity index 53% rename from src/microhttpd/mhd_itc_types.h rename to src/mhd2/mhd_itc_types.h index 60e7e260..47a3a3dd 100644 --- a/src/microhttpd/mhd_itc_types.h +++ b/src/mhd2/mhd_itc_types.h @@ -1,13 +1,13 @@ /* - This file is part of libmicrohttpd - Copyright (C) 2016-2020 Karlson2k (Evgeny Grin), Christian Grothoff + This file is part of GNU libmicrohttpd + Copyright (C) 2016-2024 Evgeny Grin (Karlson2k), Christian Grothoff - This library is free software; you can redistribute it and/or + GNU libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, + GNU libmicrohttpd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. @@ -19,7 +19,7 @@ */ /** - * @file microhttpd/mhd_itc_types.h + * @file src/mhd2/mhd_itc_types.h * @brief Types for platform-independent inter-thread communication * @author Karlson2k (Evgeny Grin) * @author Christian Grothoff @@ -29,66 +29,66 @@ */ #ifndef MHD_ITC_TYPES_H #define MHD_ITC_TYPES_H 1 -#include "mhd_options.h" +#include "mhd_sys_options.h" /* Force socketpair on native W32 */ -#if defined(_WIN32) && ! defined(__CYGWIN__) && ! defined(_MHD_ITC_SOCKETPAIR) -#error _MHD_ITC_SOCKETPAIR is not defined on naitive W32 platform -#endif /* _WIN32 && !__CYGWIN__ && !_MHD_ITC_SOCKETPAIR */ +#if defined(_WIN32) && ! defined(__CYGWIN__) && ! defined(MHD_ITC_SOCKETPAIR_) +#error MHD_ITC_SOCKETPAIR_ is not defined on native W32 platform +#endif /* _WIN32 && !__CYGWIN__ && !MHD_ITC_SOCKETPAIR_ */ -#if defined(_MHD_ITC_EVENTFD) -/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */ +#if defined(MHD_ITC_EVENTFD_) +/* **************** Optimised ITC implementation by eventfd ********** */ /** * Data type for a MHD ITC. */ -struct MHD_itc_ +struct mhd_itc { int fd; }; /** - * Static initialiser for struct MHD_itc_ + * Static initialiser for struct mhd_itc */ -#define MHD_ITC_STATIC_INIT_INVALID { -1 } +# define mhd_ITC_STATIC_INIT_INVALID { -1 } -#elif defined(_MHD_ITC_PIPE) +#elif defined(MHD_ITC_PIPE_) /* **************** Standard UNIX ITC implementation by pipe ********** */ /** * Data type for a MHD ITC. */ -struct MHD_itc_ +struct mhd_itc { int fd[2]; }; /** - * Static initialiser for struct MHD_itc_ + * Static initialiser for struct mhd_itc */ -#define MHD_ITC_STATIC_INIT_INVALID { { -1, -1 } } +# define mhd_ITC_STATIC_INIT_INVALID { { -1, -1 } } -#elif defined(_MHD_ITC_SOCKETPAIR) +#elif defined(MHD_ITC_SOCKETPAIR_) /* **************** ITC implementation by socket pair ********** */ -#include "mhd_sockets.h" +# include "mhd_socket_type.h" /** * Data type for a MHD ITC. */ -struct MHD_itc_ +struct mhd_itc { - MHD_socket sk[2]; + MHD_Socket sk[2]; }; /** - * Static initialiser for struct MHD_itc_ + * Static initialiser for struct mhd_itc */ -#define MHD_ITC_STATIC_INIT_INVALID \ - { { MHD_INVALID_SOCKET, MHD_INVALID_SOCKET } } +# define mhd_ITC_STATIC_INIT_INVALID \ + { { MHD_INVALID_SOCKET, MHD_INVALID_SOCKET } } -#endif /* _MHD_ITC_SOCKETPAIR */ +#endif /* MHD_ITC_SOCKETPAIR_ */ #endif /* ! MHD_ITC_TYPES_H */ diff --git a/src/mhd2/mhd_lib_init.c b/src/mhd2/mhd_lib_init.c new file mode 100644 index 00000000..17a5ae06 --- /dev/null +++ b/src/mhd2/mhd_lib_init.c @@ -0,0 +1,85 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_lib_init_impl.c + * @brief Library global initialisers and de-initialisers + * @author Karlson2k (Evgeny Grin) + */ +#include "mhd_sys_options.h" +#include "mhd_lib_init.h" +#include "mhd_panic.h" +#include "mhd_mono_clock.h" +#include "mhd_socket_type.h" +#ifdef MHD_WINSOCK_SOCKETS +# include +#endif + +void +mhd_lib_global_init (void) +{ + mhd_panic_init_default (); + +#if defined(MHD_WINSOCK_SOCKETS) + if (1) + { + WSADATA wsd; + if ((0 != WSAStartup (MAKEWORD (2, 2), &wsd)) || (MAKEWORD (2, 2) != wsd. + wVersion)) + MHD_PANIC ("Failed to initialise WinSock."); + } +#endif /* MHD_WINSOCK_SOCKETS */ +} + + +void +mhd_lib_global_deinit (void) +{ +#if defined(MHD_WINSOCK_SOCKETS) + (void) WSACleanup (); +#endif /* MHD_WINSOCK_SOCKETS */ +} + + +#ifndef _AUTOINIT_FUNCS_ARE_SUPPORTED +static volatile int mhd_lib_global_inited = 0; +static volatile int mhd_lib_global_not_inited = ! 0; + +MHD_EXTERN_ void +MHD_lib_global_check_init (void) +{ + if ((! mhd_lib_global_inited) || (mhd_lib_global_not_inited)) + mhd_lib_global_init (); + mhd_lib_global_inited = ! 0; + mhd_lib_global_not_inited = 0; +} + + +MHD_EXTERN_ void +MHD_lib_global_check_deinit (void) +{ + if ((mhd_lib_global_inited) && (! mhd_lib_global_not_inited)) + mhd_lib_global_deinit (); + mhd_lib_global_inited = 0; + mhd_lib_global_not_inited = ! 0; +} + + +#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */ diff --git a/src/mhd2/mhd_lib_init.h b/src/mhd2/mhd_lib_init.h new file mode 100644 index 00000000..9383b275 --- /dev/null +++ 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 new file mode 100644 index 00000000..fae86501 --- /dev/null +++ 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_locks.h b/src/mhd2/mhd_locks.h new file mode 100644 index 00000000..3071059a --- /dev/null +++ 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 +# else +# include +# endif +#elif defined(HAVE_PTHREAD_H) && defined(MHD_USE_POSIX_THREADS) +# define MHD_PTHREAD_MUTEX_ 1 +# include +# ifdef HAVE_STDDEF_H +# include /* 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/microhttpd/memorypool.c b/src/mhd2/mhd_mempool.c similarity index 94% rename from src/microhttpd/memorypool.c rename to src/mhd2/mhd_mempool.c index 810251eb..4fad335b 100644 --- a/src/microhttpd/memorypool.c +++ b/src/mhd2/mhd_mempool.c @@ -19,43 +19,52 @@ */ /** - * @file memorypool.c + * @file src/mhd2/mhd_mempool.h * @brief memory pool * @author Christian Grothoff * @author Karlson2k (Evgeny Grin) + * TODO: + * + Update code style + * + Detect mmap() in configure (it is purely optional!) */ -#include "memorypool.h" +#include "mhd_mempool.h" #ifdef HAVE_STDLIB_H -#include +# include #endif /* HAVE_STDLIB_H */ #include -#include #include "mhd_assert.h" #ifdef HAVE_SYS_MMAN_H -#include +# include #endif #ifdef _WIN32 -#include +# include #endif #ifdef HAVE_SYSCONF -#include -#if defined(_SC_PAGE_SIZE) -#define MHD_SC_PAGESIZE _SC_PAGE_SIZE -#elif defined(_SC_PAGESIZE) -#define MHD_SC_PAGESIZE _SC_PAGESIZE -#endif /* _SC_PAGESIZE */ +# include +# if defined(_SC_PAGE_SIZE) +# define MHD_SC_PAGESIZE _SC_PAGE_SIZE +# elif defined(_SC_PAGESIZE) +# define MHD_SC_PAGESIZE _SC_PAGESIZE +# endif /* _SC_PAGESIZE */ #endif /* HAVE_SYSCONF */ -#include "mhd_limits.h" /* for SIZE_MAX, PAGESIZE / PAGE_SIZE */ #if defined(MHD_USE_PAGESIZE_MACRO) || defined(MHD_USE_PAGE_SIZE_MACRO) -#ifndef HAVE_SYSCONF /* Avoid duplicate include */ -#include -#endif /* HAVE_SYSCONF */ -#ifdef HAVE_SYS_PARAM_H -#include -#endif /* HAVE_SYS_PARAM_H */ +# ifndef HAVE_SYSCONF /* Avoid duplicate include */ +# include +# endif /* HAVE_SYSCONF */ +# ifdef HAVE_LIMITS_H +# include +# endif +# ifdef HAVE_SYS_PARAM_H +# include +# endif /* HAVE_SYS_PARAM_H */ #endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */ +#ifndef SIZE_MAX +/* This file does not use SIZE_MAX macro with preprocessor */ +# define SIZE_MAX ((size_t) ~((size_t) 0)) +#endif + /** * Fallback value of page size */ @@ -99,40 +108,42 @@ #ifndef MHD_ASAN_POISON_ACTIVE #define _MHD_NOSANITIZE_PTRS /**/ #define _MHD_RED_ZONE_SIZE (0) -#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN(n) -#define _MHD_POISON_MEMORY(pointer, size) (void)0 -#define _MHD_UNPOISON_MEMORY(pointer, size) (void)0 +#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN (n) +#define _MHD_POISON_MEMORY(pointer, size) (void) 0 +#define _MHD_UNPOISON_MEMORY(pointer, size) (void) 0 /** * Boolean 'true' if the first pointer is less or equal the second pointer */ #define mp_ptr_le_(p1,p2) \ - (((const uint8_t*)(p1)) <= ((const uint8_t*)(p2))) + (((const uint8_t*) (p1)) <= ((const uint8_t*) (p2))) /** * The difference in bytes between positions of the first and * the second pointers */ #define mp_ptr_diff_(p1,p2) \ - ((size_t)(((const uint8_t*)(p1)) - ((const uint8_t*)(p2)))) + ((size_t) (((const uint8_t*) (p1)) - ((const uint8_t*) (p2)))) #else /* MHD_ASAN_POISON_ACTIVE */ #define _MHD_RED_ZONE_SIZE (ALIGN_SIZE) -#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE) +#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) \ + (ROUND_TO_ALIGN (n) + _MHD_RED_ZONE_SIZE) #define _MHD_POISON_MEMORY(pointer, size) \ - ASAN_POISON_MEMORY_REGION ((pointer), (size)) + ASAN_POISON_MEMORY_REGION ((pointer), (size)) #define _MHD_UNPOISON_MEMORY(pointer, size) \ - ASAN_UNPOISON_MEMORY_REGION ((pointer), (size)) + ASAN_UNPOISON_MEMORY_REGION ((pointer), (size)) #if defined(FUNC_PTRCOMPARE_CAST_WORKAROUND_WORKS) /** * Boolean 'true' if the first pointer is less or equal the second pointer */ #define mp_ptr_le_(p1,p2) \ - (((uintptr_t)((const void*)(p1))) <= ((uintptr_t)((const void*)(p2)))) + (((uintptr_t) ((const void*) (p1))) <= \ + ((uintptr_t) ((const void*) (p2)))) /** * The difference in bytes between positions of the first and * the second pointers */ #define mp_ptr_diff_(p1,p2) \ - ((size_t)(((uintptr_t)((const uint8_t*)(p1))) - \ - ((uintptr_t)((const uint8_t*)(p2))))) + ((size_t) (((uintptr_t) ((const uint8_t*) (p1))) - \ + ((uintptr_t) ((const uint8_t*) (p2))))) #elif defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \ defined(FUNC_ATTR_PTRSUBTRACT_WORKS) #ifdef _DEBUG diff --git a/src/microhttpd/memorypool.h b/src/mhd2/mhd_mempool.h similarity index 96% rename from src/microhttpd/memorypool.h rename to src/mhd2/mhd_mempool.h index b7b9e932..73ec1030 100644 --- a/src/microhttpd/memorypool.h +++ b/src/mhd2/mhd_mempool.h @@ -19,24 +19,22 @@ */ /** - * @file memorypool.h + * @file src/mhd2/mhd_mempool.h * @brief memory pool; mostly used for efficient (de)allocation * for each connection and bounding memory use for each * request * @author Christian Grothoff * @author Karlson2k (Evgeny Grin) + * + * TODO: Update code style */ -#ifndef MEMORYPOOL_H -#define MEMORYPOOL_H +#ifndef MHD_MEMPOOL_H +#define MHD_MEMPOOL_H 1 -#include "mhd_options.h" -#ifdef HAVE_STDDEF_H -#include -#endif /* HAVE_STDDEF_H */ -#ifdef HAVE_STDBOOL_H -#include -#endif +#include "mhd_sys_options.h" +#include "sys_base_types.h" +#include "sys_bool_type.h" /** * Opaque handle for a memory pool. @@ -198,4 +196,4 @@ MHD_pool_reset (struct MemoryPool *pool, size_t copy_bytes, size_t new_size); -#endif +#endif /* ! MHD_MEMPOOL_H */ diff --git a/src/microhttpd/mhd_mono_clock.c b/src/mhd2/mhd_mono_clock.c similarity index 76% rename from src/microhttpd/mhd_mono_clock.c rename to src/mhd2/mhd_mono_clock.c index 117c5c13..c536539e 100644 --- a/src/microhttpd/mhd_mono_clock.c +++ b/src/mhd2/mhd_mono_clock.c @@ -18,60 +18,53 @@ */ /** - * @file microhttpd/mhd_mono_clock.h + * @file src/mhd2/mhd_mono_clock.h * @brief internal monotonic clock functions implementations * @author Karlson2k (Evgeny Grin) + * + * TODO: update code style */ #include "mhd_mono_clock.h" #if defined(_WIN32) && ! defined(__CYGWIN__) /* Prefer native clock source over wrappers */ -#ifdef HAVE_CLOCK_GETTIME -#undef HAVE_CLOCK_GETTIME -#endif /* HAVE_CLOCK_GETTIME */ -#ifdef HAVE_GETTIMEOFDAY -#undef HAVE_GETTIMEOFDAY -#endif /* HAVE_GETTIMEOFDAY */ +# ifdef HAVE_CLOCK_GETTIME +# undef HAVE_CLOCK_GETTIME +# endif /* HAVE_CLOCK_GETTIME */ +# ifdef HAVE_GETTIMEOFDAY +# undef HAVE_GETTIMEOFDAY +# endif /* HAVE_GETTIMEOFDAY */ #endif /* _WIN32 && ! __CYGWIN__ */ #ifdef HAVE_TIME_H -#include +# include #endif /* HAVE_TIME_H */ #ifdef HAVE_SYS_TIME_H -#include +# include #endif /* HAVE_SYS_TIME_H */ #ifdef HAVE_CLOCK_GET_TIME -#include +# include /* for host_get_clock_service(), mach_host_self(), mach_task_self() */ -#include +# include /* for clock_get_time() */ -#define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) +# define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; #endif /* HAVE_CLOCK_GET_TIME */ #ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -/* Do not include unneeded parts of W32 headers. */ -#define WIN32_LEAN_AND_MEAN 1 -#endif /* !WIN32_LEAN_AND_MEAN */ -#include -#include +# include #endif /* _WIN32 */ -#ifndef NULL -#define NULL ((void*)0) -#endif /* ! NULL */ - #ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_REALTIME -#define _MHD_UNWANTED_CLOCK CLOCK_REALTIME -#else /* !CLOCK_REALTIME */ -#define _MHD_UNWANTED_CLOCK ((clockid_t) -2) -#endif /* !CLOCK_REALTIME */ +# ifdef CLOCK_REALTIME +# define _MHD_UNWANTED_CLOCK CLOCK_REALTIME +# else /* !CLOCK_REALTIME */ +# define _MHD_UNWANTED_CLOCK ((clockid_t) -2) +# endif /* !CLOCK_REALTIME */ static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; #endif /* HAVE_CLOCK_GETTIME */ @@ -81,21 +74,24 @@ static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; defined(HAVE_GETHRTIME) static time_t mono_clock_start; #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ + #if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY) /* The start value shared for timespec_get() and gettimeofday () */ static time_t gettime_start; #endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */ static time_t sys_clock_start; + #ifdef HAVE_GETHRTIME static hrtime_t hrtime_start; #endif /* HAVE_GETHRTIME */ + #ifdef _WIN32 -#if _WIN32_WINNT >= 0x0600 +# if _WIN32_WINNT >= 0x0600 static uint64_t tick_start; -#else /* _WIN32_WINNT < 0x0600 */ +# else /* _WIN32_WINNT < 0x0600 */ static uint64_t perf_freq; static uint64_t perf_start; -#endif /* _WIN32_WINNT < 0x0600 */ +# endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ @@ -137,10 +133,10 @@ enum _MHD_mono_clock_source /** - * Initialise monotonic seconds and milliseconds counters. + * Initialise milliseconds counters. */ void -MHD_monotonic_sec_counter_init (void) +MHD_monotonic_msec_counter_init (void) { #ifdef HAVE_CLOCK_GET_TIME mach_timespec_t cur_time; @@ -360,11 +356,10 @@ MHD_monotonic_sec_counter_init (void) /** - * Deinitialise monotonic seconds and milliseconds counters by freeing - * any allocated resources + * Deinitialise milliseconds counters by freeing any allocated resources */ void -MHD_monotonic_sec_counter_finish (void) +MHD_monotonic_msec_counter_finish (void) { #ifdef HAVE_CLOCK_GET_TIME if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) @@ -377,58 +372,6 @@ MHD_monotonic_sec_counter_finish (void) } -/** - * Monotonic seconds counter. - * Tries to be not affected by manually setting the system real time - * clock or adjustments by NTP synchronization. - * - * @return number of seconds from some fixed moment - */ -time_t -MHD_monotonic_sec_counter (void) -{ -#ifdef HAVE_CLOCK_GETTIME - struct timespec ts; - - if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && - (0 == clock_gettime (mono_clock_id, - &ts)) ) - return ts.tv_sec - mono_clock_start; -#endif /* HAVE_CLOCK_GETTIME */ -#ifdef HAVE_CLOCK_GET_TIME - if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) - { - mach_timespec_t cur_time; - - if (KERN_SUCCESS == clock_get_time (mono_clock_service, - &cur_time)) - return cur_time.tv_sec - mono_clock_start; - } -#endif /* HAVE_CLOCK_GET_TIME */ -#if defined(_WIN32) -#if _WIN32_WINNT >= 0x0600 - if (1) - return (time_t) (((uint64_t) (GetTickCount64 () - tick_start)) / 1000); -#else /* _WIN32_WINNT < 0x0600 */ - if (0 != perf_freq) - { - LARGE_INTEGER perf_counter; - - QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ - return (time_t) (((uint64_t) perf_counter.QuadPart - perf_start) - / perf_freq); - } -#endif /* _WIN32_WINNT < 0x0600 */ -#endif /* _WIN32 */ -#ifdef HAVE_GETHRTIME - if (1) - return (time_t) (((uint64_t) (gethrtime () - hrtime_start)) / 1000000000); -#endif /* HAVE_GETHRTIME */ - - return time (NULL) - sys_clock_start; -} - - /** * Monotonic milliseconds counter, useful for timeout calculation. * Tries to be not affected by manually setting the system real time @@ -436,7 +379,7 @@ MHD_monotonic_sec_counter (void) * * @return number of microseconds from some fixed moment */ -uint64_t +uint_fast64_t MHD_monotonic_msec_counter (void) { #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET) @@ -447,8 +390,9 @@ MHD_monotonic_msec_counter (void) if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && (0 == clock_gettime (mono_clock_id, &ts)) ) - return (uint64_t) (((uint64_t) (ts.tv_sec - mono_clock_start)) * 1000 - + (uint64_t) (ts.tv_nsec / 1000000)); + return (uint_fast64_t) (((uint_fast64_t) (ts.tv_sec - mono_clock_start)) + * 1000 + + (uint_fast64_t) (ts.tv_nsec / 1000000)); #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) @@ -457,22 +401,24 @@ MHD_monotonic_msec_counter (void) if (KERN_SUCCESS == clock_get_time (mono_clock_service, &cur_time)) - return (uint64_t) (((uint64_t) (cur_time.tv_sec - mono_clock_start)) - * 1000 + (uint64_t) (cur_time.tv_nsec / 1000000)); + return (uint_fast64_t) (((uint_fast64_t) (cur_time.tv_sec + - mono_clock_start)) + * 1000 + (uint_fast64_t) (cur_time.tv_nsec + / 1000000)); } #endif /* HAVE_CLOCK_GET_TIME */ #if defined(_WIN32) #if _WIN32_WINNT >= 0x0600 if (1) - return (uint64_t) (GetTickCount64 () - tick_start); + return (uint_fast64_t) (GetTickCount64 () - tick_start); #else /* _WIN32_WINNT < 0x0600 */ if (0 != perf_freq) { LARGE_INTEGER perf_counter; - uint64_t num_ticks; + uint_fast64_t num_ticks; QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ - num_ticks = (uint64_t) (perf_counter.QuadPart - perf_start); + num_ticks = (uint_fast64_t) (perf_counter.QuadPart - perf_start); return ((num_ticks / perf_freq) * 1000) + ((num_ticks % perf_freq) / (perf_freq / 1000)); } @@ -480,24 +426,25 @@ MHD_monotonic_msec_counter (void) #endif /* _WIN32 */ #ifdef HAVE_GETHRTIME if (1) - return ((uint64_t) (gethrtime () - hrtime_start)) / 1000000; + return ((uint_fast64_t) (gethrtime () - hrtime_start)) / 1000000; #endif /* HAVE_GETHRTIME */ /* Fallbacks, affected by system time change */ #ifdef HAVE_TIMESPEC_GET if (TIME_UTC == timespec_get (&ts, TIME_UTC)) - return (uint64_t) (((uint64_t) (ts.tv_sec - gettime_start)) * 1000 - + (uint64_t) (ts.tv_nsec / 1000000)); + return (uint_fast64_t) (((uint_fast64_t) (ts.tv_sec - gettime_start)) * 1000 + + (uint_fast64_t) (ts.tv_nsec / 1000000)); #elif defined(HAVE_GETTIMEOFDAY) if (1) { struct timeval tv; if (0 == gettimeofday (&tv, NULL)) - return (uint64_t) (((uint64_t) (tv.tv_sec - gettime_start)) * 1000 - + (uint64_t) (tv.tv_usec / 1000)); + return (uint_fast64_t) (((uint_fast64_t) (tv.tv_sec - gettime_start)) + * 1000 + + (uint_fast64_t) (tv.tv_usec / 1000)); } #endif /* HAVE_GETTIMEOFDAY */ /* The last resort fallback with very low resolution */ - return (uint64_t) (time (NULL) - sys_clock_start) * 1000; + return (uint_fast64_t) (time (NULL) - sys_clock_start) * 1000; } diff --git a/src/microhttpd/mhd_mono_clock.h b/src/mhd2/mhd_mono_clock.h similarity index 63% rename from src/microhttpd/mhd_mono_clock.h rename to src/mhd2/mhd_mono_clock.h index 9ac58d79..928199ee 100644 --- a/src/microhttpd/mhd_mono_clock.h +++ b/src/mhd2/mhd_mono_clock.h @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2015-2021 Karlson2k (Evgeny Grin) + Copyright (C) 2015-2024 Karlson2k (Evgeny Grin) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -18,46 +18,29 @@ */ /** - * @file microhttpd/mhd_mono_clock.h + * @file src/mhd2/mhd_mono_clock.h * @brief internal monotonic clock functions declarations * @author Karlson2k (Evgeny Grin) */ #ifndef MHD_MONO_CLOCK_H #define MHD_MONO_CLOCK_H 1 -#include "mhd_options.h" +#include "mhd_sys_options.h" -#if defined(HAVE_TIME_H) -#include -#elif defined(HAVE_SYS_TYPES_H) -#include -#endif -#include +#include "sys_base_types.h" /** - * Initialise monotonic seconds and milliseconds counters. + * Initialise milliseconds counters. */ void -MHD_monotonic_sec_counter_init (void); +MHD_monotonic_msec_counter_init (void); /** - * Deinitialise monotonic seconds and milliseconds counters by freeing - * any allocated resources + * Deinitialise milliseconds counters by freeing any allocated resources */ void -MHD_monotonic_sec_counter_finish (void); - - -/** - * Monotonic seconds counter. - * Tries to be not affected by manually setting the system real time - * clock or adjustments by NTP synchronization. - * - * @return number of seconds from some fixed moment - */ -time_t -MHD_monotonic_sec_counter (void); +MHD_monotonic_msec_counter_finish (void); /** @@ -67,7 +50,7 @@ MHD_monotonic_sec_counter (void); * * @return number of microseconds from some fixed moment */ -uint64_t +uint_fast64_t MHD_monotonic_msec_counter (void); #endif /* MHD_MONO_CLOCK_H */ diff --git a/src/mhd2/mhd_panic.c b/src/mhd2/mhd_panic.c new file mode 100644 index 00000000..461012ab --- /dev/null +++ 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 +#ifdef HAVE_STDLIB_H +# include +#elif defined(HAVE_UNISTD_H) +# include +#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 new file mode 100644 index 00000000..27c769be --- /dev/null +++ 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 +#ifdef HAVE_STDLIB_H +# include +#elif defined(HAVE_UNISTD_H) +# include +#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 index d9acb39a..d2ce340e 100644 --- a/src/mhd2/mhd_public_api.h +++ b/src/mhd2/mhd_public_api.h @@ -33,7 +33,8 @@ #include "mhd_sys_options.h" #include "sys_base_types.h" -#include "sys_socket_types.h" +#include "mhd_socket_type.h" +#include "sys_sockets_types.h" #include "microhttpd2.h" diff --git a/src/mhd2/mhd_recv.c b/src/mhd2/mhd_recv.c new file mode 100644 index 00000000..4a5fdaf7 --- /dev/null +++ b/src/mhd2/mhd_recv.c @@ -0,0 +1,106 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_recv.c + * @brief The implementation of the mhd_recv() function + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "mhd_recv.h" + +#include "mhd_connection.h" + +#include "mhd_socket_type.h" +#include "sys_sockets_headers.h" +#include "mhd_sockets_macros.h" + +#include + +#ifndef SSIZE_MAX +# define SSIZE_MAX ((ssize_t) ((~((size_t) 0)) >> 1)) +#endif + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) \ + MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_recv (struct MHD_Connection *restrict c, + size_t buf_size, + void *restrict buf, + size_t *restrict received) +{ + mhd_assert (MHD_INVALID_SOCKET != c->socket_fd); + mhd_assert (MHD_CONNECTION_CLOSED != c->state); + + // TODO: implement TLS support + if (1) /* Plain TCP connection */ + { + ssize_t res; + int err; + + if (MHD_SCKT_SEND_MAX_SIZE_ < buf_size) + buf_size = MHD_SCKT_SEND_MAX_SIZE_; + + res = mhd_sys_recv (c->socket_fd, buf, buf_size); + if (0 <= res) + { + *received = (size_t) res; + if (buf_size > (size_t) res) + c->sk_ready = + (enum mhd_SocketNetState) (((unsigned int) c->sk_ready) + & (~mhd_SOCKET_NET_STATE_RECV_READY)); + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + err = mhd_SCKT_GET_LERR (); + + if (mhd_SCKT_ERR_IS_EAGAIN (err)) + { + c->sk_ready = /* Clear 'recv-ready' */ + (enum mhd_SocketNetState) + (((unsigned int) c->sk_ready) + & (~mhd_SOCKET_NET_STATE_RECV_READY)); + return mhd_SOCKET_ERR_AGAIN; + } + else if (mhd_SCKT_ERR_IS_CONNRESET (err)) + return mhd_SOCKET_ERR_CONNRESET; + else if (mhd_SCKT_ERR_IS_EINTR (err)) + return mhd_SOCKET_ERR_INTR; + else if (mhd_SCKT_ERR_IS_CONN_BROKEN (err)) + return mhd_SOCKET_ERR_CONN_BROKEN; + else if (mhd_SCKT_ERR_IS_NOTCONN (err)) + return mhd_SOCKET_ERR_NOTCONN; + else if (mhd_SCKT_ERR_IS_LOW_MEM (err)) + return mhd_SOCKET_ERR_NOMEM; + else if (mhd_SCKT_ERR_IS_BADF (err)) + return mhd_SOCKET_ERR_BADF; + else if (mhd_SCKT_ERR_IS_EINVAL (err)) + return mhd_SOCKET_ERR_INVAL; + else if (mhd_SCKT_ERR_IS_OPNOTSUPP (err)) + return mhd_SOCKET_ERR_OPNOTSUPP; + else if (mhd_SCKT_ERR_IS_NOTSOCK (err)) + return mhd_SOCKET_ERR_NOTSOCK; + } + + return mhd_SOCKET_ERR_OTHER; +} diff --git a/src/mhd2/mhd_recv.h b/src/mhd2/mhd_recv.h new file mode 100644 index 00000000..8f539141 --- /dev/null +++ b/src/mhd2/mhd_recv.h @@ -0,0 +1,56 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_recv.h + * @brief The definition of the mhd_recv() function + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_RECV_H +#define MHD_RECV_H 1 + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" +#include "mhd_socket_error.h" + +struct MHD_Connection; /* forward declaration */ + +/** + * Receive the data from the network socket. + * Clear #mhd_SOCKET_NET_STATE_RECV_READY in sk_ready if necessary. + * + * @param c the connection to use + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the received data + * @param[out] received the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets + * the received size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_recv (struct MHD_Connection *restrict c, + size_t buf_size, + void *restrict buf, + size_t *restrict received) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2) MHD_FN_PAR_OUT_ (4); + +#endif /* ! MHD_RECV_H */ diff --git a/src/mhd2/mhd_reply.h b/src/mhd2/mhd_reply.h new file mode 100644 index 00000000..4b223e69 --- /dev/null +++ b/src/mhd2/mhd_reply.h @@ -0,0 +1,115 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2021-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_reply.h + * @brief The definition of the working reply data + * @author Karlson2k (Evgeny Grin) + * + * Data structures in this header are used when responding to client's request. + * Do not be confused with terms "response" and "reply" using in MHD code. + * The "MHD_Response" is an connection-independent object that have all + * data required to form a respond. + * The "MHD_Reply" is working connection-specific data used to format + * the respond based on provided data in "MHD_Response". + */ + +#ifndef MHD_REPLY_H +#define MHD_REPLY_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_iovec.h" + +struct MHD_Response; /* forward declaration */ + +/** + * Reply-specific properties. + */ +struct MHD_Reply_Properties +{ +#ifdef _DEBUG + bool set; /**< Indicates that other members are set and valid */ +#endif /* _DEBUG */ + bool use_reply_body_headers; /**< Use reply body-specific headers */ + bool send_reply_body; /**< Send reply body (can be zero-sized) */ + bool chunked; /**< Use chunked encoding for reply */ +}; + +#if defined(MHD_USE_SENDFILE) +enum MHD_resp_sender_ +{ + MHD_resp_sender_std = 0, + MHD_resp_sender_sendfile +}; +#endif /* MHD_USE_SENDFILE */ + +/** + * Reply-specific values. + * + * Meaningful for the current reply only. + */ +struct MHD_Reply +{ + /** + * Response to transmit (initially NULL). + */ + struct MHD_Response *response; + + /** + * HTTP response code. Only valid if response object + * is already set. + */ + unsigned int responseCode; + + /** + * The "ICY" response. + * Reply begins with the SHOUTcast "ICY" line instead of "HTTP". + */ + bool responseIcy; + + /** + * Current write position in the actual response + * (excluding headers, content only; should be 0 + * while sending headers). + */ + uint_fast64_t rsp_write_position; + + /** + * The copy of iov response. + * Valid if iovec response is used. + * Updated during send. + * Members are allocated in the pool. + */ + struct mhd_iovec_track resp_iov; + +#if defined(MHD_USE_SENDFILE) + enum MHD_resp_sender_ resp_sender; +#endif /* MHD_USE_SENDFILE */ + + /** + * Reply-specific properties + */ + struct MHD_Reply_Properties props; +}; + +#endif /* ! MHD_REPLY_H */ diff --git a/src/mhd2/mhd_request.h b/src/mhd2/mhd_request.h new file mode 100644 index 00000000..e7277149 --- /dev/null +++ b/src/mhd2/mhd_request.h @@ -0,0 +1,376 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_request.h + * @brief The definition of the request data structures + * @author Karlson2k (Evgeny Grin) + * + * Data structures in this header are used when parsing client's request + */ + +#ifndef MHD_REQUEST_H +#define MHD_REQUEST_H 1 + +#include "mhd_sys_options.h" +#include "sys_base_types.h" +#include "sys_bool_type.h" +#include "mhd_dlinked_list.h" + +#include "http_prot_ver.h" +#include "http_method.h" +#include "mhd_action.h" + + +/** + * The action set by the application + */ +struct mhd_ApplicationAction +{ + /** + * The action after header reporting + */ + struct MHD_Action head_act; + /** + * The action during upload processing + */ + struct MHD_UploadAction upl_act; +}; + +/** + * The request line processing data + */ +struct MHD_RequestLineProcessing +{ + /** + * The position of the next character to be processed + */ + size_t proc_pos; + /** + * The number of empty lines skipped + */ + unsigned int skipped_empty_lines; + /** + * The position of the start of the current/last found whitespace block, + * zero if not found yet. + */ + size_t last_ws_start; + /** + * The position of the next character after the last known whitespace + * character in the current/last found whitespace block, + * zero if not found yet. + */ + size_t last_ws_end; + /** + * The pointer to the request target. + * The request URI will be formed based on it. + */ + char *rq_tgt; + /** + * The pointer to the first question mark in the @a rq_tgt. + */ + char *rq_tgt_qmark; + /** + * The number of whitespace characters in the request URI + */ + size_t num_ws_in_uri; +}; + +/** + * The request header processing data + */ +struct MHD_HeaderProcessing +{ + /** + * The position of the last processed character + */ + size_t proc_pos; + + /** + * The position of the first whitespace character in current contiguous + * whitespace block. + * Zero when no whitespace found or found non-whitespace character after + * whitespace. + * Must be zero, if the current character is not whitespace. + */ + size_t ws_start; + + /** + * Indicates that end of the header (field) name found. + * Must be false until the first colon in line is found. + */ + bool name_end_found; + + /** + * The length of the header name. + * Must be zero until the first colon in line is found. + * Name always starts at zero position. + */ + size_t name_len; + + /** + * The position of the first character of the header value. + * Zero when the first character has not been found yet. + */ + size_t value_start; + + /** + * Line starts with whitespace. + * It's meaningful only for the first line, as other lines should be handled + * as "folded". + */ + bool starts_with_ws; +}; + +/** + * The union of request line and header processing data + */ +union MHD_HeadersProcessing +{ + /** + * The request line processing data + */ + struct MHD_RequestLineProcessing rq_line; + + /** + * The request header processing data + */ + struct MHD_HeaderProcessing hdr; +}; + + +/** + * The union of text staring point and the size of the text + */ +union MHD_StartOrSize +{ + /** + * The starting point of the text. + * Valid when the text is being processed and the end of the text + * is not yet determined. + */ + const char *start; + /** + * The size of the text. + * Valid when the text has been processed and the end of the text + * is known. + */ + size_t size; +}; + +struct MHD_HTTP_Req_Header; /* forward declarations */ + +mhd_DLINKEDL_LINKS_DEF (MHD_HTTP_Req_Header); + +/** + * Header, footer, or cookie for HTTP request. + */ +struct MHD_HTTP_Req_Header +{ + /** + * Headers are kept in a double-linked list. + */ + mhd_DLNKDL_LINKS (MHD_HTTP_Req_Header,list); + + /** + * The name of the header (key), without the colon. + */ + const char *header; + + /** + * The length of the @a header, not including the final zero termination. + */ + size_t header_size; + + /** + * The value of the header. + */ + const char *value; + + /** + * The length of the @a value, not including the final zero termination. + */ + size_t value_size; + + /** + * Type of the value. + */ + enum MHD_ValueKind kind; + +}; + +mhd_DLINKEDL_LIST_DEF (MHD_HTTP_Req_Header); + +/** + * Request-specific values. + * + * Meaningful for the current request only. + */ +struct MHD_Request +{ + /** + * Linked list of parsed headers. + */ + mhd_DLNKDL_LIST (MHD_HTTP_Req_Header,list); + + /** + * The action set by the application + */ + struct mhd_ApplicationAction app_act; + + /** + * HTTP version string (i.e. http/1.1). Allocated + * in pool. + */ + const char *version; + + /** + * HTTP protocol version as enum. + */ + enum MHD_HTTP_ProtocolVersion http_ver; + + /** + * Request method. Should be GET/POST/etc. Allocated in pool. + */ + const char *method; + + /** + * The request method as enum. + */ + enum mhd_HTTP_Method http_mthd; + + /** + * Requested URL, the part before '?' (excluding parameters). Allocated + * in pool. + */ + const char *url; + + /** + * The length of the @a url in characters, not including the terminating zero. + */ + size_t url_len; + + /** + * The original length of the request target. + */ + size_t req_target_len; + + /** + * Number of bytes we had in the HTTP header, set once we + * pass #MHD_CONNECTION_HEADERS_RECEIVED. + * This includes the request line, all request headers, the header section + * terminating empty line, with all CRLF (or LF) characters. + */ + size_t header_size; + + /** + * The union of the size of all request field lines (headers) and + * the starting point of the first request field line (the first header). + * Until #MHD_CONNECTION_HEADERS_RECEIVED the @a start member is valid, + * staring with #MHD_CONNECTION_HEADERS_RECEIVED the @a size member is valid. + * The size includes CRLF (or LR) characters, but does not include + * the terminating empty line. + */ + union MHD_StartOrSize field_lines; + + /** + * How many more bytes of the body do we expect + * to read? #MHD_SIZE_UNKNOWN for unknown. + */ + uint_fast64_t remaining_upload_size; + + /** + * Are we receiving with chunked encoding? + * This will be set to #MHD_YES after we parse the headers and + * are processing the body with chunks. + * After we are done with the body and we are processing the footers; + * once the footers are also done, this will be set to #MHD_NO again + * (before the final call to the handler). + * It is used only for requests, chunked encoding for response is + * indicated by @a rp_props. + */ + bool have_chunked_upload; + + /** + * If we are receiving with chunked encoding, where are we right + * now? + * Set to 0 if we are waiting to receive the chunk size; + * otherwise, this is the size of the current chunk. + * A value of zero is also used when we're at the end of the chunks. + */ + uint_fast64_t current_chunk_size; + + /** + * If we are receiving with chunked encoding, where are we currently + * with respect to the current chunk (at what offset / position)? + */ + uint_fast64_t current_chunk_offset; + + /** + * Indicate that some of the upload payload data (from the currently + * processed chunk for chunked uploads) have been processed by the + * last call of the connection handler. + * If any data have been processed, but some data left in the buffer + * for further processing, then MHD will use zero timeout before the + * next data processing round. This allow the application handler + * process the data by the fixed portions or other way suitable for + * application developer. + * If no data have been processed, than MHD will wait for more data + * to come (as it makes no sense to call the same connection handler + * under the same conditions). However this is dangerous as if buffer + * is completely used then connection is aborted. Connection + * suspension should be used in such case. + */ + bool some_payload_processed; + + /** + * We allow the main application to associate some pointer with the + * HTTP request, which is passed to each #MHD_AccessHandlerCallback + * and some other API calls. Here is where we store it. (MHD does + * not know or care what it is). + */ + void *client_context; + + /** + * Did we ever call the "default_handler" on this request? + * This flag determines if we have called the #MHD_OPTION_NOTIFY_COMPLETED + * handler when the request finishes. + */ + bool client_aware; + + /** + * Number of bare CR characters that were replaced with space characters + * in the request line or in the headers (field lines). + */ + size_t num_cr_sp_replaced; + + /** + * The number of header lines skipped because they have no colon + */ + size_t skipped_broken_lines; + + /** + * The data of the request line / request headers processing + */ + union MHD_HeadersProcessing hdrs; +}; + + +#endif /* ! MHD_REQUEST_H */ diff --git a/src/mhd2/mhd_response.h b/src/mhd2/mhd_response.h new file mode 100644 index 00000000..94e8425c --- /dev/null +++ b/src/mhd2/mhd_response.h @@ -0,0 +1,324 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_response.h + * @brief The definition of the MHD_Response type and related structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_RESPONSE_H +#define MHD_RESPONSE_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_public_api.h" + +#include "mhd_dlinked_list.h" +#include "mhd_str_types.h" + +#include "mhd_iovec.h" + +#include "mhd_locks.h" + + +struct ResponseOptions; /* forward declaration */ + +struct mhd_ResponseHeader; /* forward declaration */ + +mhd_DLINKEDL_LINKS_DEF (mhd_ResponseHeader); + +/** + * Response header / field + */ +struct mhd_ResponseHeader +{ + /** + * The name of the header / field + */ + struct MHD_String name; + + /** + * The value of the header / field + */ + struct MHD_String value; + + /** + * The links to other headers + */ + mhd_DLNKDL_LINKS (mhd_ResponseHeader,headers); +}; + +/** + * The type of content + */ +enum mhd_ResponseContentType +{ + mhd_RESPONSE_CONTENT_INVALID = 0 + , + mhd_RESPONSE_CONTENT_BUFFER + , + mhd_RESPONSE_CONTENT_IOVEC + , + mhd_RESPONSE_CONTENT_FILE + , + mhd_RESPONSE_CONTENT_CALLBACK +}; + +/** + * I/O vector response data + */ +struct mhd_ResponseIoVec +{ + /** + * The copy of array of iovec elements. + * Must be freed! + */ + mhd_iovec *iov; + + /** + * The number of elements in the @a iov array + */ + size_t cnt; +}; + +/** + * The file data for the the response + */ +struct mhd_ResponseFD +{ + /** + * The file description of the response + */ + int fd; + + /** + * The offset in the file of the response content + */ + uint_fast64_t offset; + + /** + * Indicate that @a fd is a pipe + */ + bool is_pipe; + +#ifdef MHD_USE_SENDFILE + /** + * Use 'sendfile()' function for the @a FD + * Initially 'true' (except for pipes) but can be flipped to 'false' if + * sendfile() cannot handle this file. + */ + volatile bool use_sf; +#endif +}; + +/** + * Dynamic response data + */ +struct mhd_ResponseDynamic +{ + /** + * The callback for the content data + */ + MHD_DynamicContentCreator cb; + /** + * The closure for the @a cb + */ + void *cls; +}; + +/** + * The response content data + */ +union mhd_ResponseContent +{ + /** + * The fixed unmodifiable data. + * 'unsigned char' pointer is used to simplify individual ranges addressing. + */ + const unsigned char *restrict buf; + + /** + * The + */ + struct mhd_ResponseIoVec iovec; + + /** + * The file data for the the response + */ + struct mhd_ResponseFD file; + + /** + * Dynamic response data + */ + struct mhd_ResponseDynamic dyn; +}; + +/** + * The data of the free/cleanup callback + */ +struct mhd_FreeCbData +{ + /** + * The Free/Cleanup callback + */ + MHD_FreeCallback cb; + + /** + * The closure for the @a cb + */ + void *cls; +}; + + +struct mhd_ResponseReuseData +{ + /** + * Indicate that response could be used more than one time + */ + bool reusable; + + /** + * The number of active uses of the response. + * Used only when @a reusable is 'true'. + * When number reached zero, the response is destroyed. + * Must be checked only under the @a cnt_lock + */ + volatile uint_fast64_t counter; + +#ifdef MHD_USE_THREADS + /** + * The mutex for @a counter access. + * Used only when @a reusable is 'true'. + */ + mhd_mutex cnt_lock; + + /** + * The mutex for @a settings access. + * Used only when @a reusable is 'true'. + */ + mhd_mutex settings_lock; +#endif /* MHD_USE_THREADS */ +}; + +struct mhd_ResponseConfiguration +{ + /** + * Response have undefined content + * Must be used only when response content (even zero-size) is not allowed. + */ + bool head_only; + + /** + * If set to 'true' then the chunked encoding must be used (if allowed + * by HTTP version). + * If 'false' then chunked encoding must not be used. + */ + bool chunked; + + /** + * If 'true', "Connection: close" header must be always used + */ + bool conn_close; + + /** + * Use "HTTP/1.0" in the reply header + */ + bool mode_1_0; + + /** + * The (possible incorrect) content length is provided by application + */ + bool cnt_len_by_app; +}; + +#ifndef NDEBUG +struct mhd_ResponseDebug +{ + bool is_internal; +}; +#endif + +mhd_DLINKEDL_LIST_DEF (mhd_ResponseHeader); + +struct MHD_Response +{ + /** + * The response status code + */ + enum MHD_HTTP_StatusCode sc; + + /** + * The size of the response. + * #MHD_SIZE_UNKNOWN if size is undefined + */ + uint_fast64_t size; + + /** + * The type of the content data + */ + enum mhd_ResponseContentType cntn_type; + + /** + * The data of the content of the response + */ + union mhd_ResponseContent cntn; + + /** + * The data of the free/cleanup callback + */ + struct mhd_FreeCbData free; + + /** + * Configuration data of the response + */ + struct mhd_ResponseConfiguration cfg; + + /** + * If response is "frozen" then response data cannot be changed. + * The use counter for re-usable responses is the exception and can be + * changed when "frozen". + */ + volatile bool frozen; + + /** + * The re-use parameters + */ + struct mhd_ResponseReuseData reuse; + + /** + * The settings, before the response is @a frozen + */ + struct ResponseOptions *restrict settings; + + /** + * The double linked list of the response headers + */ + mhd_DLNKDL_LIST (mhd_ResponseHeader,headers); + +#ifndef NDEBUG + struct mhd_ResponseDebug dbg; +#endif +}; + +#endif /* ! MHD_RESPONSE_H */ diff --git a/src/mhd2/mhd_socket_error.h b/src/mhd2/mhd_socket_error.h new file mode 100644 index 00000000..3d1a4b76 --- /dev/null +++ b/src/mhd2/mhd_socket_error.h @@ -0,0 +1,119 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_socket_error.h + * @brief The definition of the mhd_SocketError enum + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SOCKET_ERROR_H +#define MHD_SOCKET_ERROR_H 1 + +#include "mhd_sys_options.h" + +/** + * Recognised socket errors + */ +enum MHD_FIXED_ENUM_ mhd_SocketError +{ + /** + * No error. + */ + mhd_SOCKET_ERR_NO_ERROR = 0 + , + /** + * No more data to get / no more space to put the data. + */ + mhd_SOCKET_ERR_AGAIN + , + /** + * The process has been interrupted by external factors. + */ + mhd_SOCKET_ERR_INTR + , + /** + * "Not enough memory" / "not enough system resources" + */ + mhd_SOCKET_ERR_NOMEM + , + /** + * The connection has been hard-closed by remote peer. + */ + mhd_SOCKET_ERR_CONNRESET + , + /** + * Meta-error for any other errors indicating a broken connection. + * It can be keep-alive ping failure or timeout to get ACK for the + * transmitted data. + */ + mhd_SOCKET_ERR_CONN_BROKEN + , + /** + * Connection is not connected anymore due to network error or + * any other reason. + */ + mhd_SOCKET_ERR_NOTCONN + , + /** + * The socket FD is invalid + */ + mhd_SOCKET_ERR_BADF + , + /** + * Socket function parameters are invalid + */ + mhd_SOCKET_ERR_INVAL + , + /** + * Socket function parameters are not supported + */ + mhd_SOCKET_ERR_OPNOTSUPP + , + /** + * Used FD is not a socket + */ + mhd_SOCKET_ERR_NOTSOCK + , + /** + * The remote side shut down reading, the socket has been shut down + * for writing or no longer connected + * Only for 'send()'. + */ + mhd_SOCKET_ERR_PIPE + , + /** + * General TLS encryption or decryption error + */ + mhd_SOCKET_ERR_TLS + , + /** + * Other socket error + */ + mhd_SOCKET_ERR_OTHER + +}; + +/** + * Check whether the socket error is unrecoverable + */ +#define mhd_SOCKET_ERR_IS_HARD(err) (mhd_SOCKET_ERR_CONNRESET <= (err)) + +#endif /* ! MHD_SOCKET_ERROR_H */ diff --git a/src/mhd2/mhd_sockets_funcs.c b/src/mhd2/mhd_sockets_funcs.c new file mode 100644 index 00000000..3f98da24 --- /dev/null +++ b/src/mhd2/mhd_sockets_funcs.c @@ -0,0 +1,252 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2014-2024 Karlson2k (Evgeny Grin) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_sockets_funcs.c + * @brief Implementations of sockets manipulating functions + * @author Karlson2k (Evgeny Grin) + */ +#include "mhd_sys_options.h" +#include "mhd_sockets_funcs.h" +#include "sys_sockets_headers.h" +#include "sys_ip_headers.h" +#ifdef MHD_POSIX_SOCKETS +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_UNISTD_H +# include +# else +# include +# endif +# include +#elif defined(MHD_WINSOCK_SOCKETS) +# include +#endif +#ifndef INADDR_LOOPBACK +# include /* For memcpy() */ +#endif + +#include "mhd_sockets_macros.h" + + +MHD_INTERNAL bool +mhd_socket_nonblocking (MHD_Socket sckt) +{ +#if defined(MHD_POSIX_SOCKETS) + // TODO: detect constants in configure +#if defined(F_GETFL) && defined(O_NONBLOCK) && defined(F_SETFL) + int get_flags; + int set_flags; + + get_flags = fcntl (sckt, F_GETFL); + if (0 > get_flags) + return false; + + set_flags = (flags | O_NONBLOCK); + if (get_flags == set_flags) + return true; + + if (-1 != fcntl (sckt, F_SETFL, set_flags)) + return true; +#endif /* F_GETFL && O_NONBLOCK && F_SETFL */ +#elif defined(MHD_WINSOCK_SOCKETS) + unsigned long set_flag = 1; + + if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag)) + return true; +#endif /* MHD_WINSOCK_SOCKETS */ + + return false; +} + + +MHD_INTERNAL bool +mhd_socket_noninheritable (MHD_Socket sckt) +{ +#if defined(MHD_POSIX_SOCKETS) + // TODO: detect constants in configure +#if defined(F_GETFD) && defined(FD_CLOEXEC) && defined(F_SETFD) + int get_flags; + int set_flags; + + get_flags = fcntl (sckt, F_GETFD); + if (0 > flags) + return false; + + set_flags = (flags | FD_CLOEXEC); + if (get_flags == set_flags) + return true; + + if (-1 != fcntl (sckt, F_SETFD, set_flags)) + return true; +#endif /* F_GETFD && FD_CLOEXEC && F_SETFD */ +#elif defined(MHD_WINSOCK_SOCKETS) + if (SetHandleInformation ((HANDLE) sckt, HANDLE_FLAG_INHERIT, 0)) + return true; +#endif /* MHD_WINSOCK_SOCKETS */ + return false; +} + + +MHD_INTERNAL bool +mhd_socket_set_nodelay (MHD_Socket sckt, + bool on) +{ + // TODO: detect constants in configure +#ifdef TCP_NODELAY + mhd_SCKT_OPT_BOOL value; + + value = on ? 1 : 0; + + return 0 == setsockopt (sckt, IPPROTO_TCP, TCP_NODELAY, + (const void *) &value, sizeof (value)); +#else /* ! TCP_NODELAY */ + (void) sock; (void) on; + return false; +#endif /* ! TCP_NODELAY */ +} + + +#ifndef HAVE_SOCKETPAIR + +static bool +mhd_socket_blocking (MHD_Socket sckt) +{ +#if defined(MHD_POSIX_SOCKETS) + // TODO: detect constants in configure +#if defined(F_GETFL) && defined(O_NONBLOCK) && defined(F_SETFL) + int get_flags; + int set_flags; + + get_flags = fcntl (sckt, F_GETFL); + if (0 > get_flags) + return false; + + set_flags = (flags & ~O_NONBLOCK); + if (get_flags == set_flags) + return true; + + if (-1 != fcntl (sckt, F_SETFL, set_flags)) + return true; +#endif /* F_GETFL && O_NONBLOCK && F_SETFL */ +#elif defined(MHD_WINSOCK_SOCKETS) + unsigned long set_flag = 0; + + if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag)) + return true; +#endif /* MHD_WINSOCK_SOCKETS */ + + return false; +} + + +MHD_INTERNAL bool +mhd_socket_pair_func (MHD_Socket sckt[2], bool non_blk) +{ + int i; + +#define PAIR_MAX_TRIES 511 + for (i = 0; i < PAIR_MAX_TRIES; i++) + { + struct sockaddr_in listen_addr; + MHD_Socket listen_s; + static const socklen_t c_addinlen = sizeof(struct sockaddr_in); /* Try to help compiler to optimise */ + socklen_t addr_len = c_addinlen; + + listen_s = socket (AF_INET, + SOCK_STREAM, + IPPROTO_TCP); + if (INVALID_SOCKET == listen_s) + break; /* can't create even single socket */ + + listen_addr.sin_family = AF_INET; + listen_addr.sin_port = 0; /* same as htons(0) */ +#ifdef INADDR_LOOPBACK + listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); +#else + memcpy (&(listen_addr.sin_addr.s_addr), "\x7F\x00\x00\x01", 4); +#endif + if ( ((0 == bind (listen_s, + (struct sockaddr *) &listen_addr, + c_addinlen)) && + (0 == listen (listen_s, + 1) ) && + (0 == getsockname (listen_s, + (struct sockaddr *) &listen_addr, + &addr_len))) ) + { + MHD_Socket client_s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + struct sockaddr_in accepted_from_addr; + struct sockaddr_in client_addr; + + if (INVALID_SOCKET != client_s) + { + if (mhd_socket_nonblocking (client_s) && + ( (0 == connect (client_s, + (struct sockaddr *) &listen_addr, + c_addinlen)) || + mhd_SCKT_LERR_IS_EAGAIN () )) + { + MHD_Socket server_s; + + addr_len = c_addinlen; + server_s = accept (listen_s, + (struct sockaddr *) &accepted_from_addr, + &addr_len); + if (MHD_INVALID_SOCKET != server_s) + { + addr_len = c_addinlen; + if ( (0 == getsockname (client_s, + (struct sockaddr *) &client_addr, + &addr_len)) && + (accepted_from_addr.sin_port == client_addr.sin_port) && + (accepted_from_addr.sin_addr.s_addr == + client_addr.sin_addr.s_addr) ) + { + (void) mhd_socket_set_nodelay (server_s, true); + (void) mhd_socket_set_nodelay (client_s, true); + if (non_blk ? + mhd_socket_nonblocking (server_s) : + mhd_socket_blocking (client_s)) + { + mhd_socket_close (listen_s); + sckt[0] = server_s; + sckt[1] = client_s; + return true; + } + } + mhd_socket_close (server_s); + } + } + mhd_socket_close (client_s); + } + } + mhd_socket_close (listen_s); + } + + sckt[0] = INVALID_SOCKET; + sckt[1] = INVALID_SOCKET; + + return false; +} + + +#endif /* ! HAVE_SOCKETPAIR */ diff --git a/src/mhd2/mhd_sockets_funcs.h b/src/mhd2/mhd_sockets_funcs.h new file mode 100644 index 00000000..d8b52daa --- /dev/null +++ b/src/mhd2/mhd_sockets_funcs.h @@ -0,0 +1,87 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2014-2024 Karlson2k (Evgeny Grin) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_sockets_funcs.h + * @brief Declarations for sockets manipulating functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SOCKETS_FUNCS_H +#define MHD_SOCKETS_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "mhd_socket_type.h" + + +/** + * Change socket options to be non-blocking. + * + * @param sckt the socket to manipulate + * @return true on success, false otherwise + */ +MHD_INTERNAL bool +mhd_socket_nonblocking (MHD_Socket sckt); + +/** + * Change socket options to be non-inheritable. + * + * @param sock the socket to manipulate + * @return true on success, false otherwise + */ +MHD_INTERNAL bool +mhd_socket_noninheritable (MHD_Socket sckt); + + +/** + * Control Nagle's algorithm on @a sock. + * + * @param sckt the socket to manipulate + * @param on the value to use: true to set "no delay" (disable Nagle's + * algorithm), false to clear "no delay" (enable Nagle's algorithm) + * @return true on success, false otherwise + */ +MHD_INTERNAL bool +mhd_socket_set_nodelay (MHD_Socket sckt, + bool on); + + +#ifndef HAVE_SOCKETPAIR + +# define mhd_socket_pair(fdarr_ptr) mhd_socket_pair_func (fdarr_ptr, false) +# define mhd_socket_pair_nblk(fdarr_ptr) mhd_socket_pair_func (fdarr_ptr, true) + + +/** + * Create pair of mutually connected sockets on loopback address + * @param sockets_pair the array to receive resulted sockets + * @param non_blk if set to true, sockets created in non-blocking mode + * otherwise sockets will be in blocking mode + * @return true if succeeded, false otherwise + */ +MHD_INTERNAL bool +mhd_socket_pair_func (MHD_Socket sckt[2], bool non_blk); + +#endif /* ! HAVE_SOCKETPAIR */ + + +#endif /* ! MHD_SOCKETS_FUNCS_H */ diff --git a/src/mhd2/mhd_sockets_macros.h b/src/mhd2/mhd_sockets_macros.h new file mode 100644 index 00000000..b7703c0b --- /dev/null +++ b/src/mhd2/mhd_sockets_macros.h @@ -0,0 +1,334 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2014-2024 Karlson2k (Evgeny Grin) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_sockets_macros.h + * @brief Various helper macros functions related to sockets + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SOCKETS_MACROS_H +#define MHD_SOCKETS_MACROS_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_socket_type.h" +#include "sys_base_types.h" +#include "sys_sockets_headers.h" + +#if defined(MHD_POSIX_SOCKETS) +# include "sys_errno.h" +# ifdef HAVE_UNISTD_H +# include +# else +# include +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# include +#endif + +/** + * Close the socket FD + * @param sckt the socket to close + */ +#if defined(MHD_POSIX_SOCKETS) +# define mhd_socket_close(sckt) close (sckt) +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_socket_close(sckt) closesocket (sckt) +#endif + +/** + * mhd_send4 is a wrapper for system's send() + * @param s the socket to use + * @param b the buffer with data to send + * @param l the length of data in @a b + * @param f the additional flags + * @return ssize_t type value + */ +#define mhd_sys_send4(s,b,l,f) \ + ((ssize_t) send ((s),(const void*) (b),(mhd_SCKT_SEND_SIZE) (l), \ + ((MHD_MSG_NOSIGNAL) | (f)))) + + +/** + * mhd_send is a simple wrapper for system's send() + * @param s the socket to use + * @param b the buffer with data to send + * @param l the length of data in @a b + * @return ssize_t type value + */ +#define mhd_sys_send(s,b,l) mhd_sys_send4 ((s),(b),(l), 0) + + +/** + * mhd_recv is wrapper for system's recv() + * @param s the socket to use + * @param b the buffer for data to receive + * @param l the length of @a b + * @return ssize_t type value + */ +#define mhd_sys_recv(s,b,l) \ + ((ssize_t) recv ((s),(void*) (b),(mhd_SCKT_SEND_SIZE) (l), 0)) + +#ifdef EMFILE +# define MHD_EMFILE_OR_ZERO EMFILE +#else +# define MHD_EMFILE_OR_ZERO (0) +#endif + +#ifdef ENFILE +# define MHD_ENFILE_OR_ZERO ENFILE +#else +# define MHD_ENFILE_OR_ZERO (0) +#endif + +#ifdef ENOMEM +# define MHD_ENOMEM_OR_ZERO ENOMEM +#else +# define MHD_ENOMEM_OR_ZERO (0) +#endif + +#ifdef ENOBUFS +# define MHD_ENOBUFS_OR_ZERO ENOBUFS +#else +# define MHD_ENOBUFS_OR_ZERO (0) +#endif + +#ifdef EHOSTUNREACH +# define MHD_EHOSTUNREACH_OR_ZERO EHOSTUNREACH +#else +# define MHD_EHOSTUNREACH_OR_ZERO (0) +#endif + +#ifdef ETIMEDOUT +# define MHD_ETIMEDOUT_OR_ZERO ETIMEDOUT +#else +# define MHD_ETIMEDOUT_OR_ZERO (0) +#endif + +#ifdef ENETUNREACH +# define MHD_ENETUNREACH_OR_ZERO ENETUNREACH +#else +# define MHD_ENETUNREACH_OR_ZERO (0) +#endif + +/** + * Last socket error + */ +#if defined(MHD_POSIX_SOCKETS) +# define mhd_SCKT_GET_LERR() errno +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_GET_LERR() (WSAGetLastError ()) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# if defined(EAGAIN) && defined(EWOULDBLOCK) && \ + ((EWOULDBLOCK + 0) != (EAGAIN + 0)) +# define mhd_SCKT_ERR_IS_EAGAIN(err) \ + ((EAGAIN == (err)) || (EWOULDBLOCK == (err))) +# elif defined(EAGAIN) +# define mhd_SCKT_ERR_IS_EAGAIN(err) (EAGAIN == (err)) +# elif defined(EWOULDBLOCK) +# define mhd_SCKT_ERR_IS_EAGAIN(err) (EWOULDBLOCK == (err)) +# else +# define mhd_SCKT_ERR_IS_EAGAIN(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_EAGAIN(err) (WSAEWOULDBLOCK == (err)) +#endif + +#define mhd_SCKT_LERR_IS_EAGAIN() mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) + +#if defined(MHD_POSIX_SOCKETS) +# ifdef EAFNOSUPPORT +# define mhd_SCKT_ERR_IS_AF(err) (EAFNOSUPPORT == (err)) +# else +# define mhd_SCKT_ERR_IS_AF(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_AF(err) (WSAEAFNOSUPPORT == (err)) +#endif + +#define mhd_SCKT_LERR_IS_AF() mhd_SCKT_ERR_IS_AF (mhd_SCKT_GET_LERR ()) + +#if defined(MHD_POSIX_SOCKETS) +# ifdef EINVAL +# define mhd_SCKT_ERR_IS_EINVAL(err) (EINVAL == (err)) +# else +# define mhd_SCKT_ERR_IS_EINVAL(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_EINVAL(err) (WSAEINVAL == (err)) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# ifdef EINTR +# define mhd_SCKT_ERR_IS_EINTR(err) (EINTR == (err)) +# else +# define mhd_SCKT_ERR_IS_EINTR(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_EINTR(err) (WSAEINTR == (err)) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# ifdef ECONNRESET +# define mhd_SCKT_ERR_IS_CONNRESET(err) (ECONNRESET == (err)) +# else +# define mhd_SCKT_ERR_IS_CONNRESET(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_CONNRESET(err) (WSAECONNRESET == (err)) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# ifdef ENOTCONN +# define mhd_SCKT_ERR_IS_NOTCONN(err) (ENOTCONN == (err)) +# else +# define mhd_SCKT_ERR_IS_NOTCONNT(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_NOTCONN(err) (WSAENOTCONN == (err)) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# ifdef EOPNOTSUPP +# define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (EOPNOTSUPP == (err)) +# else +# define mhd_SCKT_ERR_IS_OPNOTSUPP(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (WSAEOPNOTSUPP == (err)) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# ifdef EBADF +# define mhd_SCKT_ERR_IS_BADF(err) (EBADF == (err)) +# else +# define mhd_SCKT_ERR_IS_BADF(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_BADF(err) ((void) (err), ! ! 0) +#endif + +#if defined(MHD_POSIX_SOCKETS) +# ifdef ENOTSOCK +# define mhd_SCKT_ERR_IS_NOTSOCK(err) (ENOTSOCK == (err)) +# else +# define mhd_SCKT_ERR_IS_NOTSOCK(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_NOTSOCK(err) (WSAENOTSOCK == (err)) +#endif + +/** + * Check whether is given socket error is type of "incoming connection + * was disconnected before 'accept()' is called". + * @return boolean true is @a err match described socket error code, + * boolean false otherwise. + */ +#if defined(MHD_POSIX_SOCKETS) +# ifdef ECONNABORTED +# define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) (ECONNABORTED == (err)) +# else +# define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) (WSAECONNRESET == (err)) +#endif + +/** + * Error for any reason when the system detects connection broke, but not + * because of the peer. + * It can be keep-alive ping failure or timeout to get ACK for the + * transmitted data. + */ +#if defined(MHD_POSIX_SOCKETS) +/* + EHOSTUNREACH: probably reported by intermediate + + ETIMEDOUT: probably keep-alive ping failure + + ENETUNREACH: probably cable physically disconnected or similar */ +# define mhd_SCKT_ERR_IS_CONN_BROKEN(err) \ + ((0 != (err)) && \ + ((MHD_EHOSTUNREACH_OR_ZERO == (err)) || \ + (MHD_ETIMEDOUT_OR_ZERO == (err)) || \ + (MHD_ENETUNREACH_OR_ZERO == (err)))) +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_CONN_BROKEN(err) \ + ( (WSAENETRESET == (err)) || (WSAECONNABORTED == (err)) || \ + (WSAETIMEDOUT == (err)) ) +#endif + +/** + * Check whether given socket error is any kind of "low resource" error. + * @return boolean true if @a err is any kind of "low resource" error, + * boolean false otherwise. + */ +#if defined(MHD_POSIX_SOCKETS) +# define mhd_SCKT_ERR_IS_LOW_RESOURCES(err) \ + ((0 != (err)) && \ + ((MHD_EMFILE_OR_ZERO == (err)) || (MHD_ENFILE_OR_ZERO == (err)) || \ + (MHD_ENOMEM_OR_ZERO == (err)) || (MHD_ENOBUFS_OR_ZERO == (err)))) +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_LOW_RESOURCES(err) \ + ( (WSAEMFILE == (err)) || (WSAENOBUFS == (err)) ) +#endif + +/** + * Check whether given socket error is any kind of "low memory" error. + * This is subset of #mhd_SCKT_ERR_IS_LOW_RESOURCES() + * @return boolean true if @a err is any kind of "low memory" error, + * boolean false otherwise. + */ +#if defined(MHD_POSIX_SOCKETS) +# define mhd_SCKT_ERR_IS_LOW_MEM(err) \ + ((0 != (err)) && \ + ((MHD_ENOMEM_OR_ZERO == (err)) || (MHD_ENOBUFS_OR_ZERO == (err)))) +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_LOW_MEM(err) (WSAENOBUFS == (err)) +#endif + + +#if defined(MHD_POSIX_SOCKETS) +# ifdef HAVE_SOCKETPAIR +# ifdef MHD_AF_UNIX +# define mhd_socket_pair(fdarr_ptr) \ + (0 != socketpair (MHD_AF_UNIX, SOCK_STREAM, 0, (fdarr_ptr))) +# else +# define mhd_socket_pair(fdarr_ptr) \ + (0 != socketpair (AF_INET, SOCK_STREAM, 0, (fdarr_ptr))) /* Fallback, could be broken on many platforms */ +# endif +# if defined(HAVE_SOCK_NONBLOCK) +# ifdef MHD_AF_UNIX +# define mhd_socket_pair_nblk(fdarr_ptr) \ + (0 != socketpair (MHD_AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, ( \ + fdarr_ptr))) +# else +# define mhd_socket_pair_nblk(fdarr_ptr) \ + (0 != socketpair (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0, (fdarr_ptr))) /* Fallback, could be broken on many platforms */ +# endif +# endif /* HAVE_SOCK_NONBLOCK*/ +# endif /* HAVE_SOCKETPAIR */ +#endif + +#ifndef mhd_socket_pair +/* mhd_socket_pair() implemented in "mhd_sockets_funcs.h" based on local function */ +#endif + +#endif /* ! MHD_SOCKETS_MACROS_H */ diff --git a/src/mhd2/mhd_str_macros.h b/src/mhd2/mhd_str_macros.h new file mode 100644 index 00000000..220b4654 --- /dev/null +++ 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 new file mode 100644 index 00000000..df4518d4 --- /dev/null +++ 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/microhttpd/mhd_threads.c b/src/mhd2/mhd_threads.c similarity index 51% rename from src/microhttpd/mhd_threads.c rename to src/mhd2/mhd_threads.c index 189704c0..66cec793 100644 --- a/src/microhttpd/mhd_threads.c +++ b/src/mhd2/mhd_threads.c @@ -1,13 +1,13 @@ /* - This file is part of libmicrohttpd - Copyright (C) 2016-2023 Karlson2k (Evgeny Grin) + This file is part of GNU libmicrohttpd + Copyright (C) 2016-2024 Evgeny Grin (Karlson2k) - This library is free software; you can redistribute it and/or + GNU libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, + GNU libmicrohttpd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. @@ -25,37 +25,36 @@ */ #include "mhd_threads.h" +#include "sys_null_macro.h" #ifdef MHD_USE_W32_THREADS -#include "mhd_limits.h" -#include +# include #endif -#ifdef MHD_USE_THREAD_NAME_ -#ifdef HAVE_STDLIB_H -#include -#endif /* HAVE_STDLIB_H */ -#ifdef HAVE_PTHREAD_NP_H -#include -#endif /* HAVE_PTHREAD_NP_H */ +#if defined(MHD_USE_THREAD_NAME_) +# if ! defined(MHD_USE_THREAD_ATTR_SETNAME) +# include "sys_malloc.h" +# endif +# ifdef HAVE_PTHREAD_NP_H +# include +# endif /* HAVE_PTHREAD_NP_H */ #endif /* MHD_USE_THREAD_NAME_ */ #include #include "mhd_assert.h" - #ifndef MHD_USE_THREAD_NAME_ -#define MHD_set_thread_name_(t, n) (void) -#define MHD_set_cur_thread_name_(n) (void) +# define mhd_set_thread_name(t, n) (void) +# define mhd_set_cur_thread_name(n) (void) #else /* MHD_USE_THREAD_NAME_ */ -#if defined(MHD_USE_POSIX_THREADS) -#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ +# if defined(MHD_USE_POSIX_THREADS) +# if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) -# define MHD_USE_THREAD_ATTR_SETNAME 1 -#endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \ +# define MHD_USE_THREAD_ATTR_SETNAME 1 +# endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \ HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */ -#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ +# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \ || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) @@ -64,67 +63,68 @@ * * @param thread_id ID of thread * @param thread_name name to set - * @return non-zero on success, zero otherwise + * @return true on success, false otherwise */ -static int -MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id, - const char *thread_name) +static bool +mhd_set_thread_name (const mhd_thread_ID_native thread_id, + const char *thread_name) { if (NULL == thread_name) - return 0; + return false; -#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) - return ! pthread_setname_np (thread_id, thread_name); -#elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) +# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) + return 0 == pthread_setname_np (thread_id, thread_name); +# elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) /* FreeBSD and OpenBSD use different function name and void return type */ pthread_set_name_np (thread_id, thread_name); - return ! 0; -#elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) - /* NetBSD use 3 arguments: second argument is string in printf-like format, - * third argument is a single argument for printf(); + return true; +# elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) + /* NetBSD uses 3 arguments: second argument is string in printf-like format, + * third argument is a single argument for printf(); * OSF1 use 3 arguments too, but last one always must be zero (NULL). * MHD doesn't use '%' in thread names, so both form are used in same way. */ - return ! pthread_setname_np (thread_id, thread_name, 0); -#endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */ + return 0 == pthread_setname_np (thread_id, thread_name, 0); +# endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */ } -#ifndef __QNXNTO__ +# ifndef __QNXNTO__ /** * Set current thread name * @param n name to set * @return non-zero on success, zero otherwise */ -#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (pthread_self (),(n)) -#else /* __QNXNTO__ */ +# define mhd_set_cur_thread_name(n) \ + mhd_set_thread_name (pthread_self (),(n)) +# else /* __QNXNTO__ */ /* Special case for QNX Neutrino - using zero for thread ID sets name faster. */ -#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (0,(n)) -#endif /* __QNXNTO__ */ -#elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) +# define mhd_set_cur_thread_name(n) mhd_set_thread_name (0,(n)) +# endif /* __QNXNTO__ */ +# elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) /** * Set current thread name * @param n name to set * @return non-zero on success, zero otherwise */ -#define MHD_set_cur_thread_name_(n) (! (pthread_setname_np ((n)))) -#endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */ +# define mhd_set_cur_thread_name(n) (! (pthread_setname_np ((n)))) +# endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */ -#elif defined(MHD_USE_W32_THREADS) -#ifndef _MSC_FULL_VER -/* Thread name available only for VC-compiler */ -#else /* _MSC_FULL_VER */ +# elif defined(MHD_USE_W32_THREADS) +# ifndef _MSC_FULL_VER +#error Thread name available only for VC-compiler +# else /* _MSC_FULL_VER */ /** * Set thread name * * @param thread_id ID of thread, -1 for current thread * @param thread_name name to set - * @return non-zero on success, zero otherwise + * @return true on success, false otherwise */ -static int -MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id, - const char *thread_name) +static bool +mhd_set_thread_name (const mhd_thread_ID_native thread_id, + const char *thread_name) { static const DWORD VC_SETNAME_EXC = 0x406D1388; #pragma pack(push,8) @@ -138,7 +138,7 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id, #pragma pack(pop) if (NULL == thread_name) - return 0; + return false; thread_info.type = 0x1000; thread_info.name = thread_name; @@ -155,19 +155,19 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id, __except (EXCEPTION_EXECUTE_HANDLER) {} - return ! 0; + return true; } /** * Set current thread name * @param n name to set - * @return non-zero on success, zero otherwise + * @return true on success, false otherwise */ -#define MHD_set_cur_thread_name_(n) \ - MHD_set_thread_name_ ((MHD_thread_ID_native_) -1,(n)) -#endif /* _MSC_FULL_VER */ -#endif /* MHD_USE_W32_THREADS */ +# define mhd_set_cur_thread_name(n) \ + mhd_set_thread_name ((mhd_thread_ID_native) (-1),(n)) +# endif /* _MSC_FULL_VER */ +# endif /* MHD_USE_W32_THREADS */ #endif /* MHD_USE_THREAD_NAME_ */ @@ -175,7 +175,7 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id, /** * Create a thread and set the attributes according to our options. * - * If thread is created, thread handle must be freed by MHD_join_thread_(). + * If thread is created, thread handle must be freed by mhd_join_thread(). * * @param handle_id handle to initialise * @param stack_size size of stack for new thread, 0 for default @@ -183,23 +183,23 @@ MHD_set_thread_name_ (const MHD_thread_ID_native_ thread_id, * @param arg argument for start_routine * @return non-zero on success; zero otherwise (with errno set) */ -int -MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg) +bool +mhd_create_thread (mhd_thread_handle_ID *handle_id, + size_t stack_size, + mhd_THREAD_START_ROUTINE start_routine, + void *arg) { #if defined(MHD_USE_POSIX_THREADS) int res; -#if defined(MHD_thread_handle_ID_get_native_handle_ptr_) +# if defined(mhd_thread_handle_ID_get_native_handle_ptr) pthread_t *const new_tid_ptr = - MHD_thread_handle_ID_get_native_handle_ptr_ (handle_id); -#else /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */ + mhd_thread_handle_ID_get_native_handle_ptr (handle_id); +# else /* ! mhd_thread_handle_ID_get_native_handle_ptr */ pthread_t new_tid; pthread_t *const new_tid_ptr = &new_tid; -#endif /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */ +# endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */ - mhd_assert (! MHD_thread_handle_ID_is_valid_handle_ (*handle_id)); + mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id)); if (0 != stack_size) { @@ -226,53 +226,55 @@ MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id, if (0 != res) { errno = res; - MHD_thread_handle_ID_set_invalid_ (handle_id); + mhd_thread_handle_ID_set_invalid (handle_id); } -#if ! defined(MHD_thread_handle_ID_get_native_handle_ptr_) +# if ! defined(mhd_thread_handle_ID_get_native_handle_ptr) else - MHD_thread_handle_ID_set_native_handle_ (handle_id, new_tid); -#endif /* ! MHD_thread_handle_ID_set_current_thread_ID_ */ + mhd_thread_handle_ID_set_native_handle (handle_id, new_tid); +# endif /* ! mhd_thread_handle_ID_set_current_thread_ID */ - return ! res; + return 0 == res; #elif defined(MHD_USE_W32_THREADS) uintptr_t thr_handle; -#if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT + unsigned int stack_size_w32; +# if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT - mhd_assert (! MHD_thread_handle_ID_is_valid_handle_ (*handle_id)); + mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id)); - if (stack_size > UINT_MAX) + stack_size_w32 = (unsigned int) stack_size; + if (stack_size != stack_size_w32) { errno = EINVAL; - return 0; + return false; } #endif /* SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT */ - thr_handle = (uintptr_t) _beginthreadex (NULL, - (unsigned int) stack_size, - start_routine, - arg, - 0, - NULL); - if ((MHD_thread_handle_native_) 0 == (MHD_thread_handle_native_) thr_handle) - return 0; + 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); + mhd_thread_handle_ID_set_native_handle (handle_id, \ + (mhd_thread_handle_native) \ + thr_handle); - return ! 0; -#endif + return true; +#endif /* MHD_USE_W32_THREADS */ } #ifdef MHD_USE_THREAD_NAME_ -#ifndef MHD_USE_THREAD_ATTR_SETNAME -struct MHD_named_helper_param_ +# ifndef MHD_USE_THREAD_ATTR_SETNAME +struct mhd_named_helper_param { /** * Real thread start routine */ - MHD_THREAD_START_ROUTINE_ start_routine; + mhd_THREAD_START_ROUTINE start_routine; /** * Argument for thread start routine @@ -286,18 +288,18 @@ struct MHD_named_helper_param_ }; -static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ +static mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC named_thread_starter (void *data) { - struct MHD_named_helper_param_ *const param = - (struct MHD_named_helper_param_ *) data; + struct mhd_named_helper_param *const param = + (struct mhd_named_helper_param *) data; void *arg; - MHD_THREAD_START_ROUTINE_ thr_func; + mhd_THREAD_START_ROUTINE thr_func; if (NULL == data) - return (MHD_THRD_RTRN_TYPE_) 0; + return (mhd_THRD_RTRN_TYPE) 0; - MHD_set_cur_thread_name_ (param->name); + mhd_set_cur_thread_name (param->name); arg = param->arg; thr_func = param->start_routine; @@ -307,7 +309,7 @@ named_thread_starter (void *data) } -#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ +# endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ /** @@ -320,28 +322,28 @@ named_thread_starter (void *data) * @param arg argument for start_routine * @return non-zero on success; zero otherwise (with errno set) */ -int -MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id, - const char *thread_name, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg) +bool +mhd_create_named_thread (mhd_thread_handle_ID *handle_id, + const char *thread_name, + size_t stack_size, + mhd_THREAD_START_ROUTINE start_routine, + void *arg) { -#if defined(MHD_USE_THREAD_ATTR_SETNAME) +# if defined(MHD_USE_THREAD_ATTR_SETNAME) int res; pthread_attr_t attr; -#if defined(MHD_thread_handle_ID_get_native_handle_ptr_) +# if defined(mhd_thread_handle_ID_get_native_handle_ptr) pthread_t *const new_tid_ptr = - MHD_thread_handle_ID_get_native_handle_ptr_ (handle_id); -#else /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */ + mhd_thread_handle_ID_get_native_handle_ptr (handle_id); +# else /* ! mhd_thread_handle_ID_get_native_handle_ptr */ pthread_t new_tid; pthread_t *const new_tid_ptr = &new_tid; -#endif /* ! MHD_thread_handle_ID_get_native_handle_ptr_ */ +# endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */ res = pthread_attr_init (&attr); if (0 == res) { -#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) +# if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) /* NetBSD uses 3 arguments: second argument is string in printf-like format, * third argument is single argument for printf; * OSF1 uses 3 arguments too, but last one always must be zero (NULL). @@ -350,12 +352,12 @@ MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id, res = pthread_attr_setname_np (&attr, thread_name, 0); -#elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) +# elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) res = pthread_attr_setname_np (&attr, thread_name); -#else +# else #error No pthread_attr_setname_np() function. -#endif +# endif if ((res == 0) && (0 != stack_size) ) res = pthread_attr_setstacksize (&attr, stack_size); @@ -369,49 +371,49 @@ MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id, if (0 != res) { errno = res; - MHD_thread_handle_ID_set_invalid_ (handle_id); + mhd_thread_handle_ID_set_invalid (handle_id); } -#if ! defined(MHD_thread_handle_ID_get_native_handle_ptr_) +# if ! defined(mhd_thread_handle_ID_get_native_handle_ptr) else - MHD_thread_handle_ID_set_native_handle_ (handle_id, new_tid); -#endif /* ! MHD_thread_handle_ID_set_current_thread_ID_ */ + mhd_thread_handle_ID_set_native_handle (handle_id, new_tid); +# endif /* ! mhd_thread_handle_ID_set_current_thread_ID */ - return ! res; -#else /* ! MHD_USE_THREAD_ATTR_SETNAME */ - struct MHD_named_helper_param_ *param; + return 0 == res; +# else /* ! MHD_USE_THREAD_ATTR_SETNAME */ + struct mhd_named_helper_param *param; if (NULL == thread_name) { errno = EINVAL; - return 0; + return false; } - param = malloc (sizeof (struct MHD_named_helper_param_)); + param = malloc (sizeof (struct mhd_named_helper_param)); if (NULL == param) - return 0; + return false; param->start_routine = start_routine; param->arg = arg; param->name = thread_name; - /* Set thread name in thread itself to avoid problems with - * threads which terminated before name is set in other thread. + /* Set the thread name in the thread itself to avoid problems with + * threads which terminated before the name is set in other thread. */ - if (! MHD_create_thread_ (handle_id, - stack_size, - &named_thread_starter, - (void *) param)) + if (! mhd_create_thread (handle_id, + stack_size, + &named_thread_starter, + (void *) param)) { int err_num; err_num = errno; free (param); errno = err_num; - return 0; + return false; } - return ! 0; -#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ + return true; +# endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ } diff --git a/src/mhd2/mhd_threads.h b/src/mhd2/mhd_threads.h new file mode 100644 index 00000000..7c23edb1 --- /dev/null +++ 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 +# ifndef MHD_USE_THREADS +# define MHD_USE_THREADS 1 +# endif +#elif defined(MHD_USE_W32_THREADS) +# include +# ifndef MHD_USE_THREADS +# define MHD_USE_THREADS 1 +# endif +#else +# error No threading API is available. +#endif + +#include "sys_bool_type.h" +#include "sys_base_types.h" +#include "sys_thread_entry_type.h" + +#if defined(MHD_USE_POSIX_THREADS) && defined(MHD_USE_W32_THREADS) +# error Both MHD_USE_POSIX_THREADS and MHD_USE_W32_THREADS are defined +#endif /* MHD_USE_POSIX_THREADS && MHD_USE_W32_THREADS */ + +#ifndef MHD_NO_THREAD_NAMES +# if defined(MHD_USE_POSIX_THREADS) +# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ + defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \ + defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \ + defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \ + defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ + defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) +# define MHD_USE_THREAD_NAME_ +# endif /* HAVE_PTHREAD_SETNAME_NP */ +# elif defined(MHD_USE_W32_THREADS) +# ifdef _MSC_FULL_VER +/* Thread names only available with VC compiler */ +# define MHD_USE_THREAD_NAME_ +# endif /* _MSC_FULL_VER */ +# endif +#endif + +/* ** Thread handle - used to control the thread ** */ + +#if defined(MHD_USE_POSIX_THREADS) +/** + * The native type to control the thread from other threads + */ +typedef pthread_t mhd_thread_handle_native; +#elif defined(MHD_USE_W32_THREADS) +/** + * The native type to control the thread from other threads + */ +typedef HANDLE mhd_thread_handle_native; +#endif + +#if defined(MHD_USE_POSIX_THREADS) +# if defined(__gnu_linux__) || \ + (defined(__linux__) && defined(__GLIBC__)) +/* The next part of code is disabled because it relies on undocumented + behaviour (while the thread ID cannot be zero with GNU C Library, it is + not specified anywhere officially). + It could be enabled for neglectable performance and size improvements. */ +# if 0 /* Disabled code */ +/** + * The native invalid value for native thread handle + */ +# define mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID \ + ((mhd_thread_handle_native) 0) +# endif /* Disabled code */ +# endif /* __gnu_linux__ || (__linux__ && __GLIBC__) */ +#elif defined(MHD_USE_W32_THREADS) +/* On W32 the invalid value for thread handle is described directly in + the official documentation. */ +/** + * The native invalid value for native thread handle + */ +# define mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID \ + ((mhd_thread_handle_native) NULL) +#endif /* MHD_USE_W32_THREADS */ + +#if defined(MHD_USE_POSIX_THREADS) +/** + * Wait until specified thread is ended and free the thread handle on success. + * @param native_handle the handle to watch + * @return nonzero on success, zero otherwise + */ +# define mhd_join_thread(native_handle) \ + (! pthread_join ((native_handle), NULL)) +#elif defined(MHD_USE_W32_THREADS) +/** + * Wait until specified thread is ended and the free thread handle on success. + * @param native_handle the handle to watch + * @return nonzero on success, zero otherwise + */ +# define mhd_join_thread(native_handle) \ + ( (WAIT_OBJECT_0 == WaitForSingleObject ( (native_handle), INFINITE)) ? \ + (CloseHandle ( (native_handle)), ! 0) : 0) +#endif + +#if ! defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) +/** + * Structure with thread handle and validity flag + */ +struct mhd_thread_handle_struct +{ + bool valid; /**< true if native handle is set */ + mhd_thread_handle_native native; /**< the native thread handle */ +}; +/** + * Type with thread handle that can be set to invalid value + */ +typedef struct mhd_thread_handle_struct mhd_thread_handle; + +/** + * Set variable pointed by @a handle_ptr to invalid (unset) value + */ +# define mhd_thread_handle_set_invalid(handle_ptr) \ + ((handle_ptr)->valid = false) +/** + * Set the native handle in the variable pointed by the @a handle_ptr + * to the @a native_val value + */ +# define mhd_thread_handle_set_native(handle_ptr,native_val) \ + ((handle_ptr)->valid = true, (handle_ptr)->native = native_val) +/** + * Check whether the native handle value is set in the @a handle_var variable + */ +# define mhd_thread_handle_is_valid(handle_var) \ + ((handle_var).valid) +/** + * Get the native handle value from the @a handle_var variable + */ +# define mhd_thread_handle_get_native(handle_var) \ + ((handle_var).native) +#else /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */ +/** + * Type with thread handle that can be set to invalid value + */ +typedef mhd_thread_handle_native mhd_thread_handle; + +/** + * Set the variable pointed by the @a handle_ptr to the invalid (unset) value + */ +# define mhd_thread_handle_set_invalid(handle_ptr) \ + ((*(handle_ptr)) = mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) +/** + * Set the native handle in the variable pointed by the @a handle_ptr + * to the @a native_val value + */ +# define mhd_thread_handle_set_native(handle_ptr,native_val) \ + ((*(handle_ptr)) = (native_val)) +/** + * Check whether the native handle value is set in the @a handle_var variable + */ +# define mhd_thread_handle_is_valid(handle_var) \ + (mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID != (handle_var)) +/** + * Get the native handle value from the @a handle_var variable + */ +# define mhd_thread_handle_get_native(handle_var) \ + (handle_var) +/** + * Get the pointer to the native handle stored the variable pointed by + * the @a handle_ptr pointer + * @note This macro could be not available if direct manipulation of + * the native handle is not possible + */ +# define mhd_thread_handle_get_native_ptr(handle_ptr) \ + (handle_ptr) +#endif /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */ + + +/* ** Thread ID - used to check threads match ** */ + +#if defined(MHD_USE_POSIX_THREADS) +/** + * The native type used to check whether the current thread matches + * the expected thread + */ +typedef pthread_t mhd_thread_ID_native; + +/** + * Function to get the current thread native ID. + */ +# define mhd_thread_ID_native_current pthread_self + +/** + * Check whether two native thread IDs are equal. + * @return non-zero if equal, zero if not equal + */ +# define mhd_thread_ID_native_equal(id1,id2) \ + (pthread_equal ((id1),(id2))) +#elif defined(MHD_USE_W32_THREADS) +/** + * The native type used to check whether the current thread matches + * the expected thread + */ +typedef DWORD mhd_thread_ID_native; + +/** + * Function to get the current thread native ID. + */ +# define mhd_thread_ID_native_current GetCurrentThreadId + +/** + * Check whether two native thread IDs are equal. + * @return non-zero if equal, zero if not equal + */ +# define mhd_thread_ID_native_equal(id1,id2) \ + ((id1) == (id2)) +#endif + +/** + * Check whether the specified thread ID matches current thread. + * @param id the thread ID to match + * @return nonzero on match, zero otherwise + */ +#define mhd_thread_ID_native_is_current_thread(id) \ + mhd_thread_ID_native_equal ((id), mhd_thread_ID_native_current ()) + + +#if defined(MHD_USE_POSIX_THREADS) +# if defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) +/** + * The native invalid value for native thread ID + */ +# define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ + mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID +# endif /* mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID */ +#elif defined(MHD_USE_W32_THREADS) +/** + * The native invalid value for native thread ID + */ + # define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ + ((mhd_thread_ID_native) 0) +#endif /* MHD_USE_W32_THREADS */ + +#if ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) +/** + * Structure with thread id and validity flag + */ +struct mhd_thread_ID_struct +{ + bool valid; /**< true if native ID is set */ + mhd_thread_ID_native native; /**< the native thread ID */ +}; +/** + * Type with thread ID that can be set to the invalid value + */ +typedef struct mhd_thread_ID_struct mhd_thread_ID; + +/** + * Set variable pointed by the @a ID_ptr to the invalid (unset) value + */ +# define mhd_thread_ID_set_invalid(ID_ptr) \ + ((ID_ptr)->valid = false) +/** + * Set the native ID in the variable pointed by the @a ID_ptr + * to the @a native_val value + */ +# define mhd_thread_ID_set_native(ID_ptr,native_val) \ + ((ID_ptr)->valid = true, (ID_ptr)->native = (native_val)) +/** + * Check whether the native ID value is set in the @a ID_var variable + */ +# define mhd_thread_ID_is_valid(ID_var) \ + ((ID_var).valid) +/** + * Get the native ID value from the @a ID_var variable + */ +# define mhd_thread_ID_get_native(ID_var) \ + ((ID_var).native) +/** + * Check whether the @a ID_var variable is equal current thread + */ +# define mhd_thread_ID_is_current_thread(ID_var) \ + (mhd_thread_ID_is_valid (ID_var) && \ + mhd_thread_ID_native_is_current_thread ((ID_var).native)) +#else /* MHD_THREAD_ID_NATIVE_INVALID_ */ +/** + * Type with thread ID that can be set to the invalid value + */ +typedef mhd_thread_ID_native mhd_thread_ID; + +/** + * Set variable pointed by the @a ID_ptr to the invalid (unset) value + */ +# define mhd_thread_ID_set_invalid(ID_ptr) \ + ((*(ID_ptr)) = MHD_THREAD_ID_NATIVE_VALUE_INVALID_) +/** + * Set the native ID in the variable pointed by the @a ID_ptr + * to the @a native_val value + */ +# define mhd_thread_ID_set_native(ID_ptr,native_val) \ + ((*(ID_ptr)) = (native_val)) +/** + * Check whether the native ID value is set in the @a ID_var variable + */ +# define mhd_thread_ID_is_valid(ID_var) \ + (MHD_THREAD_ID_NATIVE_VALUE_INVALID_ != (ID_var)) +/** + * Get the native ID value from the @a ID_var variable + */ +# define mhd_thread_ID_get_native(ID_var) \ + (ID_var) +/** + * Check whether the @a ID_var variable is equal current thread + */ +# define mhd_thread_ID_is_current_thread(ID_var) \ + mhd_thread_ID_native_is_current_thread (ID_var) +#endif /* MHD_THREAD_ID_NATIVE_INVALID_ */ + +/** + * Set current thread ID in the variable pointed by the @a ID_ptr + */ +# define mhd_thread_ID_set_current_thread(ID_ptr) \ + mhd_thread_ID_set_native ((ID_ptr),mhd_thread_ID_native_current ()) + + +#if defined(MHD_USE_POSIX_THREADS) +# if defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \ + ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) +# error \ + MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined +# elif ! defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \ + defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) +# error \ + MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined +# endif +#endif /* MHD_USE_POSIX_THREADS */ + +/* When staring a new thread, the kernel (and thread implementation) may + * pause the calling (initial) thread and start the new thread. + * If thread identifier is assigned to variable in the initial thread then + * the value of the identifier variable will be undefined in the new thread + * until the initial thread continue processing. + * However, it is also possible that the new thread created, but not executed + * for some time while the initial thread continue execution. In this case any + * variable assigned in the new thread will be undefined for some time until + * they really processed by the new thread. + * To avoid data races, a special structure mhd_thread_handle_ID is used. + * The "handle" is assigned by calling (initial) thread and should be always + * defined when checked in the initial thread. + * The "ID" is assigned by the new thread and should be always defined when + * checked inside the new thread. + */ +/* Depending on implementation, pthread_create() MAY set thread ID into + * provided pointer and after it start thread OR start thread and after + * it set thread ID. In the latter case, to avoid data races, additional + * pthread_self() call is required in thread routine. If some platform + * is known for setting thread ID BEFORE starting thread macro + * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined + * to save some resources. */ + +/* #define MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 1 */ + +/* * handle - must be valid when other thread knows that particular thread + is started. + * ID - must be valid when code is executed inside thread */ +#if defined(MHD_USE_POSIX_THREADS) && \ + defined(MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD) && \ + defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \ + defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) && \ + defined(mhd_thread_handle_get_native_ptr) +union mhd_thread_handle_ID_ +{ + mhd_thread_handle handle; /**< To be used in other threads */ + mhd_thread_ID ID; /**< To be used in the thread itself */ +}; +typedef union mhd_thread_handle_ID_ mhd_thread_handle_ID; +# define MHD_THREAD_HANDLE_ID_IS_UNION 1 +#else /* !MHD_USE_POSIX_THREADS + || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD + || !mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID + || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ + || !mhd_thread_handle_get_native_ptr */ +struct mhd_thread_handle_ID_ +{ + mhd_thread_handle handle; /**< To be used in other threads */ + mhd_thread_ID ID; /**< To be used in the thread itself */ +}; +typedef struct mhd_thread_handle_ID_ mhd_thread_handle_ID; +#endif /* !MHD_USE_POSIX_THREADS + || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD + || !mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID + || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ + || !mhd_thread_handle_get_native_ptr */ + +/** + * Set the mhd_thread_handle_ID to the invalid value + */ +#define mhd_thread_handle_ID_set_invalid(hndl_id_ptr) \ + (mhd_thread_handle_set_invalid (&((hndl_id_ptr)->handle)), \ + mhd_thread_ID_set_invalid (&((hndl_id_ptr)->ID))) + +/** + * Check whether the thread handle is valid. + * To be used in threads other then the thread specified by the @a hndl_id. + */ +#define mhd_thread_handle_ID_is_valid_handle(hndl_id) \ + mhd_thread_handle_is_valid ((hndl_id).handle) + +/** + * Set the native handle in the variable pointed by the @a hndl_id_ptr + * to the @a native_val value + */ +#define mhd_thread_handle_ID_set_native_handle(hndl_id_ptr,native_val) \ + mhd_thread_handle_set_native (&((hndl_id_ptr)->handle),native_val) + +#if defined(mhd_thread_handle_get_native_ptr) +/** + * Get the pointer to the native handle stored the variable pointed by + * the @a hndl_id_ptr + * @note This macro could not available if direct manipulation of + * the native handle is not possible + */ +# define mhd_thread_handle_ID_get_native_handle_ptr(hndl_id_ptr) \ + mhd_thread_handle_get_native_ptr (&((hndl_id_ptr)->handle)) +#endif /* mhd_thread_handle_get_native_ptr */ + +/** + * Get the native thread handle from the mhd_thread_handle_ID variable. + */ +#define mhd_thread_handle_ID_get_native_handle(hndl_id) \ + mhd_thread_handle_get_native ((hndl_id).handle) + +/** + * Check whether the thread ID is valid. + * To be used in the thread itself. + */ +#define mhd_thread_handle_ID_is_valid_ID(hndl_id) \ + mhd_thread_ID_is_valid ((hndl_id).ID) + +#if defined(MHD_THREAD_HANDLE_ID_IS_UNION) +# if defined(MHD_USE_W32_THREADS) +# error mhd_thread_handle_ID cannot be a union with W32 threads +# endif /* MHD_USE_W32_THREADS */ +/** + * Set current thread ID in the variable pointed by the @a hndl_id_ptr + */ +# define mhd_thread_handle_ID_set_current_thread_ID(hndl_id_ptr) (void) 0 +#else /* ! MHD_THREAD_HANDLE_ID_IS_UNION */ +/** + * Set current thread ID in the variable pointed by the @a hndl_id_ptr + */ +# define mhd_thread_handle_ID_set_current_thread_ID(hndl_id_ptr) \ + mhd_thread_ID_set_current_thread (&((hndl_id_ptr)->ID)) +#endif /* ! MHD_THREAD_HANDLE_ID_IS_UNION */ + +/** + * Check whether provided thread ID matches current thread. + * @param ID thread ID to match + * @return nonzero on match, zero otherwise + */ +#define mhd_thread_handle_ID_is_current_thread(hndl_id) \ + mhd_thread_ID_is_current_thread ((hndl_id).ID) + +/** + * Wait until specified thread is ended and free thread handle on success. + * @param hndl_id_ handle with ID to watch + * @return nonzero on success, zero otherwise + */ +#define mhd_thread_handle_ID_join_thread(hndl_id) \ + mhd_join_thread (mhd_thread_handle_ID_get_native_handle (hndl_id)) + + +/** + * Create a thread and set the attributes according to our options. + * + * If thread is created, thread handle must be freed by mhd_join_thread(). + * + * @param handle_id handle to initialise + * @param stack_size size of stack for new thread, 0 for default + * @param start_routine main function of thread + * @param arg argument for start_routine + * @return non-zero on success; zero otherwise (with errno set) + */ +bool +mhd_create_thread (mhd_thread_handle_ID *handle_id, + size_t stack_size, + mhd_THREAD_START_ROUTINE start_routine, + void *arg); + +#ifndef MHD_USE_THREAD_NAME_ +#define mhd_create_named_thread(t,n,s,r,a) mhd_create_thread ((t),(s),(r),(a)) +#else /* MHD_USE_THREAD_NAME_ */ +/** + * Create a named thread and set the attributes according to our options. + * + * @param handle_id handle to initialise + * @param thread_name name for new thread + * @param stack_size size of stack for new thread, 0 for default + * @param start_routine main function of thread + * @param arg argument for start_routine + * @return non-zero on success; zero otherwise + */ +int +mhd_create_named_thread (mhd_thread_handle_ID *handle_id, + const char *thread_name, + size_t stack_size, + mhd_THREAD_START_ROUTINE start_routine, + void *arg); + +#endif /* MHD_USE_THREAD_NAME_ */ + +#endif /* ! MHD_THREADS_H */ diff --git a/src/mhd2/mhd_tristate.h b/src/mhd2/mhd_tristate.h new file mode 100644 index 00000000..4dae8c91 --- /dev/null +++ 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/response_add_header.c b/src/mhd2/response_add_header.c new file mode 100644 index 00000000..94c4372b --- /dev/null +++ b/src/mhd2/response_add_header.c @@ -0,0 +1,124 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2021-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/response_add_header.c + * @brief The definitions of MHD_response_add_*header() functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "mhd_response.h" +#include "mhd_locks.h" + +#include + +#include "mhd_public_api.h" + + +static enum MHD_StatusCode +response_add_header_int (struct MHD_Response *response, + const char *name, + const char *value) +{ + const size_t name_len = strlen (name); + const size_t value_len = strlen (name); + char *buf; + struct mhd_ResponseHeader *new_hdr; + + if (response->frozen) /* Re-check with the lock held */ + return MHD_SC_TOO_LATE; + + if ((NULL != memchr (name, ' ', name_len)) || + (NULL != memchr (name, '\t', name_len)) || + (NULL != memchr (name, ':', name_len)) || + (NULL != memchr (name, '\n', name_len)) || + (NULL != memchr (name, '\r', name_len))) + return MHD_SC_RESP_HEADER_NAME_INVALID; + if ((NULL != memchr (value, '\n', value_len)) || + (NULL != memchr (value, '\r', value_len))) + return MHD_SC_RESP_HEADER_VALUE_INVALID; + + buf = malloc (sizeof(struct mhd_ResponseHeader) + name_len + value_len + 2); + if (NULL == buf) + return MHD_SC_RESPONSE_HEADER_MALLOC_FAILED; + + new_hdr = (struct mhd_ResponseHeader *) buf; + buf += sizeof(struct mhd_ResponseHeader); + memcpy (buf, name, name_len + 1); + new_hdr->name.cstr = buf; + new_hdr->name.len = name_len; + buf += name_len + 1; + memcpy (buf, value, value_len + 1); + new_hdr->value.cstr = buf; + new_hdr->value.len = value_len; + + mhd_DLINKEDL_INS_LAST (response, new_hdr, headers); + + return MHD_SC_OK; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) +MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode +MHD_response_add_header (struct MHD_Response *response, + const char *name, + const char *value) +{ + bool need_lock; + enum MHD_StatusCode res; + + if (response->frozen) + return MHD_SC_TOO_LATE; + + if (response->reuse.reusable) + { + need_lock = true; + if (! mhd_mutex_lock (&(response->reuse.settings_lock))) + return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED; + mhd_assert (1 == response->reuse.counter); + } + else + need_lock = false; + + // TODO: add special processing for "Date", "Connection", "Content-Length" + + res = response_add_header_int (response, name, value); + + if (need_lock) + mhd_mutex_unlock_chk (&(response->reuse.settings_lock)); + + return res; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode +MHD_response_add_predef_header (struct MHD_Response *response, + enum MHD_PredefinedHeader stk, + const char *content) +{ + (void) response; (void) stk; (void) content; + return MHD_SC_FEATURE_DISABLED; +} diff --git a/src/mhd2/response_from.c b/src/mhd2/response_from.c new file mode 100644 index 00000000..1502dbac --- /dev/null +++ b/src/mhd2/response_from.c @@ -0,0 +1,368 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2021-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/response_from.c + * @brief The definitions of MHD_response_from_X() functions and related + * internal functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "response_from.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "compat_calloc.h" +#include "sys_malloc.h" + +#include "mhd_public_api.h" + +#include "mhd_locks.h" +#include "mhd_response.h" +#include "response_options.h" + +#include "mhd_assert.h" + +#include + +#ifndef ULONG_MAX +# define ULONG_MAX ((unsigned long) ~((unsigned long) 0)) +#endif + +#ifndef SIZE_MAX +# define ULONG_MAX ((unsigned long) ~((unsigned long) 0)) +#endif + +static struct MHD_Response * +response_create_basic (enum MHD_HTTP_StatusCode sc, + uint_fast64_t cntn_size, + MHD_FreeCallback free_cb, + void *free_cb_cls) +{ + struct MHD_Response *restrict r; + struct ResponseOptions *restrict s; + + r = mhd_calloc (1, sizeof(struct MHD_Response)); + if (NULL != r) + { + s = mhd_calloc (1, sizeof(struct ResponseOptions)); + if (NULL != s) + { +#ifndef HAVE_NULL_PTR_ALL_ZEROS + mhd_DLINKEDL_INIT_LIST (r, headers); + + s->termination_callback.v_term_cb = NULL; + s->termination_callback.v_term_cb_cls = NULL; +#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ + + r->sc = sc; + r->size = cntn_size; + r->free.cb = free_cb; + r->free.cls = free_cb_cls; + r->settings = s; + + return r; /* Success exit point */ + } + free (r); + } + return NULL; /* Failure exit point */ +} + + +static void +response_destroy_unfrozen (struct MHD_Response *restrict r) +{ + mhd_assert (NULL != r->settings); + mhd_assert (! r->frozen); + + // TODO: remove headers + + if (mhd_RESPONSE_CONTENT_INVALID != r->cntn_type) + { /* Free callback has been set correctly already */ + if (mhd_RESPONSE_CONTENT_IOVEC == r->cntn_type) + free (r->cntn.iovec.iov); + else if (mhd_RESPONSE_CONTENT_FILE == r->cntn_type) + close (r->cntn.file.fd); + /* For #mhd_RESPONSE_CONTENT_IOVEC clean-up performed by callback + for both modes: internal copy and external cleanup */ + if (NULL != r->free.cb) + r->free.cb (r->free.cls); + } + free (r->settings); + free (r); +} + + +MHD_INTERNAL void +mhd_response_dec_use_count (struct MHD_Response *restrict response) +{ + (void) response; + // TODO: implement + return; +} + + +MHD_EXTERN_ struct MHD_Response * +MHD_response_from_callback (enum MHD_HTTP_StatusCode sc, + uint_fast64_t size, + MHD_DynamicContentCreator dyn_cont, + void *dyn_cont_cls, + MHD_FreeCallback dyn_cont_fc) +{ + struct MHD_Response *restrict res; + res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls); + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_CALLBACK; + res->cntn.dyn.cb = dyn_cont; + res->cntn.dyn.cls = dyn_cont_cls; + } + return res; +} + + +static const unsigned char empty_buf[1] = { 0 }; + +MHD_EXTERN_ +MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response * +MHD_response_from_buffer ( + enum MHD_HTTP_StatusCode sc, + size_t buffer_size, + const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)], + MHD_FreeCallback free_cb, + void *free_cb_cls) +{ + struct MHD_Response *restrict res; + + if (MHD_SIZE_UNKNOWN == buffer_size) + return NULL; + + res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls); + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER; + res->cntn.buf = (0 != buffer_size) ? + (const unsigned char *) buffer : empty_buf; + } + return res; +} + + +static void +response_cntn_free_buf (void *ptr) +{ + free (ptr); +} + + +MHD_EXTERN_ +MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response * +MHD_response_from_buffer_copy ( + enum MHD_HTTP_StatusCode sc, + size_t buffer_size, + const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)]) +{ + struct MHD_Response *restrict res; + const unsigned char *buf_copy; + + if (MHD_SIZE_UNKNOWN == buffer_size) + return NULL; + + if (0 != buffer_size) + { + unsigned char *new_buf; + new_buf = (unsigned char *) malloc (buffer_size); + if (NULL == new_buf) + return NULL; + memcpy (new_buf, buffer, buffer_size); + res = response_create_basic (sc, buffer_size, + response_cntn_free_buf, new_buf); + buf_copy = new_buf; + } + else + { + buf_copy = empty_buf; + res = response_create_basic (sc, 0, NULL, NULL); + } + + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER; + res->cntn.buf = buf_copy; + } + return res; +} + + +MHD_EXTERN_ struct MHD_Response * +MHD_response_from_iovec ( + enum MHD_HTTP_StatusCode sc, + unsigned int iov_count, + const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)], + MHD_FreeCallback free_cb, + void *free_cb_cls) +{ + unsigned int i; + size_t i_cp = 0; /**< Index in the copy of iov */ + uint_fast64_t total_size = 0; + +#if 0 + response = mhd_calloc (1, sizeof (struct MHD_Response)); + if (NULL == response) + return NULL; + if (! MHD_mutex_init_ (&response->mutex)) + { + free (response); + return NULL; + } +#endif + /* Calculate final size, number of valid elements, and check 'iov' */ + for (i = 0; i < iov_count; ++i) + { + if (0 == iov[i].iov_len) + continue; /* skip zero-sized elements */ + if (NULL == iov[i].iov_base) + return NULL; /* NULL pointer with non-zero size */ + + total_size += iov[i].iov_len; + if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size) + || (((size_t) total_size) != total_size)) + return NULL; /* Larger than send function may report as success */ +#if defined(MHD_POSIX_SOCKETS) || ! defined(_WIN64) + i_cp++; +#else /* ! MHD_POSIX_SOCKETS && _WIN64 */ + if (1) + { + size_t i_add; + + i_add = (size_t) (iov[i].iov_len / MHD_IOV_ELMN_MAX_SIZE); + if (0 != iov[i].iov_len % MHD_IOV_ELMN_MAX_SIZE) + i_add++; + i_cp += i_add; + if (i_cp < i_add) + return NULL; /* Counter overflow */ + } +#endif /* ! MHD_POSIX_SOCKETS && _WIN64 */ + } + if (0 == total_size) + { + struct MHD_Response *restrict res; + + res = response_create_basic (sc, 0, free_cb, free_cb_cls); + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER; + res->cntn.buf = empty_buf; + } + return res; + } + if (MHD_SIZE_UNKNOWN == total_size) + return NULL; + + mhd_assert (0 < i_cp); + if (1) + { /* for local variables local scope only */ + struct MHD_Response *restrict res; + mhd_iovec *iov_copy; + size_t num_copy_elements = i_cp; + + iov_copy = mhd_calloc (num_copy_elements, sizeof(mhd_iovec)); + if (NULL == iov_copy) + return NULL; + + i_cp = 0; + for (i = 0; i < iov_count; ++i) + { + size_t element_size = iov[i].iov_len; + const unsigned char *buf = (const unsigned char *) iov[i].iov_base; + + if (0 == element_size) + continue; /* skip zero-sized elements */ +#if defined(MHD_WINSOCK_SOCKETS) && defined(_WIN64) + while (MHD_IOV_ELMN_MAX_SIZE < element_size) + { + iov_copy[i_cp].iov_base = (char *) mhd_DROP_CONST (buf); + iov_copy[i_cp].iov_len = MHD_IOV_ELMN_MAX_SIZE; + buf += MHD_IOV_ELMN_MAX_SIZE; + element_size -= MHD_IOV_ELMN_MAX_SIZE; + i_cp++; + } +#endif /* MHD_WINSOCK_SOCKETS && _WIN64 */ + iov_copy[i_cp].iov_base = mhd_DROP_CONST (buf); + iov_copy[i_cp].iov_len = (mhd_iov_size) element_size; + i_cp++; + } + mhd_assert (num_copy_elements == i_cp); + mhd_assert (0 < i_cp); + + res = response_create_basic (sc, total_size, free_cb, free_cb_cls); + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_IOVEC; + res->cntn.iovec.iov = iov_copy; + res->cntn.iovec.cnt = i_cp; + return res; + } + free (iov_copy); + } + return NULL; +} + + +MHD_EXTERN_ +MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * +MHD_response_from_fd (enum MHD_HTTP_StatusCode sc, + int fd, + uint_fast64_t offset, + uint_fast64_t size) +{ + struct MHD_Response *restrict res; + res = response_create_basic (sc, size, NULL, NULL); + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_FILE; + res->cntn.file.fd = fd; + res->cntn.file.offset = offset; + res->cntn.file.is_pipe = false; /* Not necessary */ + } + return res; +} + + +MHD_EXTERN_ +MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * +MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc, + int fd) +{ + struct MHD_Response *restrict res; + res = response_create_basic (sc, MHD_SIZE_UNKNOWN, NULL, NULL); + if (NULL != res) + { + res->cntn_type = mhd_RESPONSE_CONTENT_FILE; + res->cntn.file.fd = fd; + res->cntn.file.offset = 0; /* Not necessary */ + res->cntn.file.is_pipe = true; + } + return res; + +} diff --git a/src/mhd2/response_from.h b/src/mhd2/response_from.h new file mode 100644 index 00000000..fe5e97ae --- /dev/null +++ b/src/mhd2/response_from.h @@ -0,0 +1,36 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/response_from.h + * @brief The declarations of internal functions for response creation and + * deletion + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_RESPONSE_FROM_H +#define MHD_RESPONSE_FROM_H 1 + +#include "mhd_sys_options.h" + +struct MHD_Response; /* forward declaration */ + + +#endif /* ! MHD_RESPONSE_FROM_H */ diff --git a/src/mhd2/response_funcs.c b/src/mhd2/response_funcs.c new file mode 100644 index 00000000..0ec5c534 --- /dev/null +++ b/src/mhd2/response_funcs.c @@ -0,0 +1,85 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/response_funcs.с + * @brief The definition of the internal response helper functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_null_macro.h" +#include "mhd_response.h" +#include "response_funcs.h" +#include "mhd_locks.h" + +static void +response_set_properties (struct MHD_Response *restrict r) +{ + struct ResponseOptions *restrict const s = r->settings; + mhd_assert (NULL != s); + r->frozen = true; + + r->cfg.head_only = s->head_only_response; + if (s->http_1_0_compatible_strict) + { + r->cfg.conn_close = true; + r->cfg.chunked = false; + } + else + { + r->cfg.conn_close = s->conn_close; + r->cfg.chunked = s->chunked_enc || (MHD_SIZE_UNKNOWN == r->size); + } + r->cfg.mode_1_0 = s->http_1_0_server; + r->cfg.cnt_len_by_app = s->insanity_header_content_length; // TODO: set only if "content-lengh" header is used + + r->settings = NULL; +} + + +/** + * Check whether response is "frozen" (modifications blocked) and "freeze" + * it if it was not frozen before + * @param response the response to manipulate + */ +MHD_INTERNAL void +mhd_response_check_frozen_freeze (struct MHD_Response *restrict response) +{ + bool need_lock; + if (response->frozen) + return; + + if (response->reuse.reusable) + { + need_lock = true; + mhd_mutex_lock_chk (&(response->reuse.settings_lock)); + mhd_assert (1 == response->reuse.counter); + } + else + need_lock = false; + + if (! response->frozen)/* Re-check under the lock */ + response_set_properties (response); + + if (need_lock) + mhd_mutex_unlock_chk (&(response->reuse.settings_lock)); +} diff --git a/src/mhd2/response_funcs.h b/src/mhd2/response_funcs.h new file mode 100644 index 00000000..e72aff19 --- /dev/null +++ b/src/mhd2/response_funcs.h @@ -0,0 +1,51 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/response_funcs.h + * @brief The declarations of the internal response helper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_RESPONSE_FUNCS_H +#define MHD_RESPONSE_FUNCS_H 1 + +#include "mhd_sys_options.h" + +struct MHD_Response; /* forward declaration */ + +/** + * Check whether response is "frozen" (modifications blocked) and "freeze" + * it if not frozen before + * @param response the response to manipulate + */ +MHD_INTERNAL void +mhd_response_check_frozen_freeze (struct MHD_Response *restrict response); + +/** + * Free/destroy non-reusable response, decrement use count for reusable + * response and free/destroy if it is not used any more. + * @param response the response to manipulate + */ +MHD_INTERNAL void +mhd_response_dec_use_count (struct MHD_Response *restrict response); + + +#endif /* ! MHD_RESPONSE_FUNCS_H */ diff --git a/src/mhd2/response_options.h b/src/mhd2/response_options.h index d3c55558..85120146 100644 --- a/src/mhd2/response_options.h +++ b/src/mhd2/response_options.h @@ -16,11 +16,12 @@ #include "mhd_public_api.h" -struct ResponseOptions { +struct ResponseOptions +{ /** * Value for #MHD_R_O_REUSABLE. */ - enum MHD_Bool reusable; + enum MHD_Bool reusable; /* Not used in this struct. FIXME: remove? */ /** diff --git a/src/mhd2/response_set_options.c b/src/mhd2/response_set_options.c index e251a7a6..7dc2a8d1 100644 --- a/src/mhd2/response_set_options.c +++ b/src/mhd2/response_set_options.c @@ -11,51 +11,86 @@ #include "mhd_sys_options.h" #include "sys_base_types.h" -#include "mhd_response.h" #include "response_options.h" +#include "mhd_response.h" #include "mhd_public_api.h" +#include "sys_bool_type.h" +#include "mhd_locks.h" -MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_ enum MHD_StatusCode -MHD_response_set_options (struct MHD_Response *response, - const struct MHD_ResponseOptionAndValue *options, - size_t options_max_num) +/** + * Internal version of the #MHD_response_set_options() + * Assuming that settings lock is held by the MHD_response_set_options() + * @param response the response to change + * @param options the options to use + * @param options_max_num the maximum number of @a options to use + * @return #MHD_SC_OK on success, error code otherwise + */ +static enum MHD_StatusCode +response_set_options_int ( + struct MHD_Response *volatile response, + const struct MHD_ResponseOptionAndValue *volatile options, + size_t options_max_num) { + struct ResponseOptions *volatile settings; size_t i; if (response->frozen) - return MHD_SC_TOO_LATE; + return MHD_SC_TOO_LATE; /* Re-check under the lock (if any) */ - for (i=0;isettings; + + for (i = 0; i < options_max_num; i++) { - const struct MHD_ResponseOptionAndValue *const option = options + i; - switch (option->opt) { + const struct MHD_ResponseOptionAndValue *const volatile option = + options + i; + + switch (option->opt) + { case MHD_R_O_END: return MHD_SC_OK; case MHD_R_O_REUSABLE: - response->settings.reusable = option->val.reusable; + if (response->reuse.reusable && !option->val.reusable) + return MHD_SC_RESPONSE_CANNOT_CLEAR_REUSE; + else if (response->reuse.reusable) + continue; /* This flag has been set before */ + else + { + if (mhd_mutex_init(&(response->reuse.settings_lock))) + { + if (mhd_mutex_init_short(&(response->reuse.cnt_lock))) + { + response->reuse.counter = 1; + response->reuse.reusable = true; + continue; + } + mhd_mutex_destroy_chk(&(response->reuse.settings_lock)); + } + return MHD_SC_RESPONSE_MUTEX_INIT_FAILED; + } + settings->reusable = option->val.reusable; continue; case MHD_R_O_HEAD_ONLY_RESPONSE: - response->settings.head_only_response = option->val.head_only_response; + settings->head_only_response = option->val.head_only_response; continue; case MHD_R_O_CHUNKED_ENC: - response->settings.chunked_enc = option->val.chunked_enc; + settings->chunked_enc = option->val.chunked_enc; continue; case MHD_R_O_CONN_CLOSE: - response->settings.conn_close = option->val.conn_close; + settings->conn_close = option->val.conn_close; continue; case MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT: - response->settings.http_1_0_compatible_strict = option->val.http_1_0_compatible_strict; + settings->http_1_0_compatible_strict = option->val.http_1_0_compatible_strict; continue; case MHD_R_O_HTTP_1_0_SERVER: - response->settings.http_1_0_server = option->val.http_1_0_server; + settings->http_1_0_server = option->val.http_1_0_server; continue; case MHD_R_O_INSANITY_HEADER_CONTENT_LENGTH: - response->settings.insanity_header_content_length = option->val.insanity_header_content_length; + settings->insanity_header_content_length = option->val.insanity_header_content_length; continue; case MHD_R_O_TERMINATION_CALLBACK: - response->settings.termination_callback.v_term_cb = option->val.termination_callback.v_term_cb; - response->settings.termination_callback.v_term_cb_cls = option->val.termination_callback.v_term_cb_cls; + settings->termination_callback.v_term_cb = option->val.termination_callback.v_term_cb; + settings->termination_callback.v_term_cb_cls = option->val.termination_callback.v_term_cb_cls; continue; case MHD_R_O_SENTINEL: default: @@ -65,3 +100,32 @@ MHD_response_set_options (struct MHD_Response *response, } return MHD_SC_OK; } + +MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_ enum MHD_StatusCode +MHD_response_set_options (struct MHD_Response *response, + const struct MHD_ResponseOptionAndValue *options, + size_t options_max_num) +{ + bool need_lock; + enum MHD_StatusCode res; + + if (response->frozen) + return MHD_SC_TOO_LATE; + + if (response->reuse.reusable) + { + need_lock = true; + if (! mhd_mutex_lock(&(response->reuse.settings_lock))) + return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED; + mhd_assert (1 == response->reuse.counter); + } + else + need_lock = false; + + res = response_set_options_int(response, options, options_max_num); + + if (need_lock) + mhd_mutex_unlock_chk(&(response->reuse.settings_lock)); + + return res; +} diff --git a/src/mhd2/sys_base_types.h b/src/mhd2/sys_base_types.h index c19cfe36..41e71e5d 100644 --- a/src/mhd2/sys_base_types.h +++ b/src/mhd2/sys_base_types.h @@ -32,6 +32,11 @@ #include "mhd_sys_options.h" +#if defined(HAVE_SYS_TYPES_H) +# include /* ssize_t */ +#elif defined(HAVE_UNISTD_H) +# include /* should provide ssize_t */ +#endif #include /* uint_fast_XXt, int_fast_XXt */ #if defined(HAVE_STDDEF_H) # include /* size_t, NULL */ @@ -40,11 +45,6 @@ #else # include /* should provide size_t, NULL */ #endif -#if defined(HAVE_SYS_TYPES_H) -# include /* ssize_t */ -#elif defined(HAVE_UNISTD_H) -# include /* should provide ssize_t */ -#endif #ifdef HAVE_CRTDEFS_H # include /* W32-specific header */ #endif diff --git a/src/mhd2/sys_errno.h b/src/mhd2/sys_errno.h new file mode 100644 index 00000000..ab9b957f --- /dev/null +++ b/src/mhd2/sys_errno.h @@ -0,0 +1,52 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/sys_errno.h + * @brief The wrapper for system . 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 + +#ifdef EFAULT +# define MHD_EFAULT_OR_ZERO EFAULT +#else +# define MHD_EFAULT_OR_ZERO (0) +#endif + +#ifdef EINVAL +# define MHD_EINVAL_OR_ZERO EINVAL +#else +# define MHD_EINVAL_OR_ZERO (0) +#endif + +#ifdef EINTR +# define MHD_EINTR_OR_ZERO EINTR +#else +# define MHD_EINTR_OR_ZERO (0) +#endif + + +#endif /* ! MHD_SYS_ERRNO_H */ diff --git a/src/mhd2/sys_file_fd.h b/src/mhd2/sys_file_fd.h new file mode 100644 index 00000000..dbd67720 --- /dev/null +++ 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 +#endif +#ifdef HAVE_UNISTD_H +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +# include +#endif + + +#endif /* ! SYS_FILE_FD_H */ diff --git a/src/mhd2/sys_ip_headers.h b/src/mhd2/sys_ip_headers.h new file mode 100644 index 00000000..9d659429 --- /dev/null +++ b/src/mhd2/sys_ip_headers.h @@ -0,0 +1,56 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/sys_ip_headers.h + * @brief The header for system headers related to TCP/IP + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SYS_IP_HEADERS_H +#define MHD_SYS_IP_HEADERS_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_socket_type.h" +#include "sys_sockets_headers.h" + +#ifdef MHD_POSIX_SOCKETS +# ifdef HAVE_INETLIB_H +# include +# endif /* HAVE_INETLIB_H */ +# ifdef HAVE_NETINET_IN_H +# include +# endif /* HAVE_NETINET_IN_H */ +# ifdef HAVE_ARPA_INET_H +# include +# endif +# if ! defined(HAVE_NETINET_IN_H) && ! defined(HAVE_ARPA_INET_H) \ + && defined(HAVE_NETDB_H) +# include +# endif +# ifdef HAVE_NETINET_TCP_H +# include +# endif +#else +# include +#endif + +#endif /* ! MHD_SYS_IP_HEADERS_H */ diff --git a/src/mhd2/sys_null_macro.h b/src/mhd2/sys_null_macro.h new file mode 100644 index 00000000..9294380c --- /dev/null +++ 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 /* NULL */ +#else +# include /* should provide NULL */ +#endif + +#endif /* ! MHD_SYS_NULL_MACRO_H */ diff --git a/src/mhd2/sys_poll.h b/src/mhd2/sys_poll.h new file mode 100644 index 00000000..f6b98c62 --- /dev/null +++ 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 +# define mhd_poll poll +# elif defined(MHD_WINSOCK_SOCKETS) +# include +# 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_sendfile.h b/src/mhd2/sys_sendfile.h new file mode 100644 index 00000000..50a9734d --- /dev/null +++ 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 +#elif defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE) +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_UNISTD_H +# include +# endif +# ifdef HAVE_SYS_SOCKET_H +# include +# endif +# include +#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 new file mode 100644 index 00000000..03488a9d --- /dev/null +++ b/src/mhd2/sys_sockets_headers.h @@ -0,0 +1,131 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/sys_sockets_headers.h + * @brief The header for system headers for the sockets and some basic macros + * @author Karlson2k (Evgeny Grin) + * + * The macros are limited to simple local constants definitions + */ + +#ifndef MHD_SYS_SOCKETS_HEADERS_H +#define MHD_SYS_SOCKETS_HEADERS_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_socket_type.h" + +#ifdef MHD_POSIX_SOCKETS +# include "sys_base_types.h" /* required on old platforms */ +# ifdef HAVE_SYS_SOCKET_H +# include +# endif +# ifdef HAVE_SOCKLIB_H +# include +# endif /* HAVE_SOCKLIB_H */ +#elif defined(MHD_WINSOCK_SOCKETS) +# include +#endif +#ifdef HAVE_SYS_UN_H +# include +#endif + +#if defined(HAVE_SOCK_NONBLOCK) && ! defined(MHD_WINSOCK_SOCKETS) +# define MHD_SOCK_NONBLOCK SOCK_NONBLOCK +#else +# define MHD_SOCK_NONBLOCK (0) +#endif + +#if defined(SOCK_CLOEXEC) && ! defined(MHD_WINSOCK_SOCKETS) +# define MHD_SOCK_CLOEXEC SOCK_CLOEXEC +#else +# define MHD_SOCK_CLOEXEC (0) +#endif + +#if defined(SOCK_NOSIGPIPE) && ! defined(MHD_WINSOCK_SOCKETS) +# define MHD_SOCK_NOSIGPIPE SOCK_NOSIGPIPE +#else +# define MHD_SOCK_NOSIGPIPE (0) +#endif + +#if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS) +# define MHD_MSG_NOSIGNAL MSG_NOSIGNAL +#else +# define MHD_MSG_NOSIGNAL (0) +#endif + +#if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS) +# define MHD_MSG_NOSIGNAL MSG_MORE +#else +# define MHD_MSG_NOSIGNAL (0) +#endif + + +/** + * mhd_SCKT_OPT_BOOL is the type for bool parameters + * for setsockopt()/getsockopt() functions + */ +#if defined(MHD_POSIX_SOCKETS) +# define mhd_SCKT_OPT_BOOL int +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_OPT_BOOL BOOL +#endif /* MHD_WINSOCK_SOCKETS */ + +/** + * mhd_SCKT_SEND_SIZE is type used to specify size for send() and recv() + * functions + */ +#if defined(MHD_POSIX_SOCKETS) +typedef size_t mhd_SCKT_SEND_SIZE; +#elif defined(MHD_WINSOCK_SOCKETS) +typedef int mhd_SCKT_SEND_SIZE; +#endif + +/** + * MHD_SCKT_SEND_MAX_SIZE_ is maximum send()/recv() size value. + */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_SCKT_SEND_MAX_SIZE_ SSIZE_MAX +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_SCKT_SEND_MAX_SIZE_ (0x7FFFFFFF) /* INT_MAX */ +#endif + + +#if defined(AF_UNIX) || \ + (defined(HAVE_DECL_AF_UNIX) && (HAVE_DECL_AF_UNIX + 0 != 0)) +# define MHD_AF_UNIX AF_UNIX +#elif defined(AF_LOCAL) || \ + (defined(HAVE_DECL_AF_LOCAL) && (HAVE_DECL_AF_LOCAL + 0 != 0)) +# define MHD_AF_UNIX AF_LOCAL +#endif /* AF_UNIX */ + + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(MHD_WINSOCK_SOCKETS) || defined(__MACH__) || defined(__sun) || \ + defined(SOMEBSD) +/* Most of the OSes inherit nonblocking setting from the listen socket */ +# define MHD_ACCEPTED_INHERITS_NONBLOCK 1 +#elif defined(__gnu_linux__) || defined(__linux__) +# define MHD_ACCEPTED_DOES_NOT_INHERIT_NONBLOCK 1 +#endif + +#endif /* ! MHD_SYS_SOCKETS_HEADERS_H */ diff --git a/src/mhd2/sys_sockets_types.h b/src/mhd2/sys_sockets_types.h index 0a2535f0..b2ffb799 100644 --- a/src/mhd2/sys_sockets_types.h +++ b/src/mhd2/sys_sockets_types.h @@ -23,7 +23,8 @@ * @brief The header for system types related to sockets * @author Karlson2k (Evgeny Grin) * - * This header should provide declaration of 'struct sockaddr'. + * This header should provide declaration of 'struct sockaddr' and + * socklen_t (if it is present in headers). */ #ifndef MHD_SYS_SOCKETS_TYPES_H diff --git a/src/mhd2/sys_thread_entry_type.h b/src/mhd2/sys_thread_entry_type.h new file mode 100644 index 00000000..f0c2cbc5 --- /dev/null +++ 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 b/src/mhd2/w32_lib_res.rc new file mode 100644 index 00000000..56af3aa2 --- /dev/null +++ b/src/mhd2/w32_lib_res.rc @@ -0,0 +1,51 @@ +/* W32 resources for .dll */ + +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,99,0,0 + PRODUCTVERSION 1,99,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#if defined(_DEBUG) + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS_NT_WINDOWS32 +#ifdef DLL_EXPORT + FILETYPE VFT_DLL +#else + FILETYPE VFT_STATIC_LIB +#endif + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" /* Lang = US English, Charset = ASCII */ + BEGIN + VALUE "ProductName", "GNU libmicrohttpd\0" + VALUE "ProductVersion", "1.99.0\0" + VALUE "FileVersion", "1.99.0\0" +#ifdef DLL_EXPORT + VALUE "FileDescription", "GNU libmicrohttpd2 DLL for Windows (MinGW build, MSVCRT run-time lib)\0" +#else + VALUE "FileDescription", "GNU libmicrohttpd2 static library for Windows (MinGW build, MSVCRT run-time lib)\0" +#endif + VALUE "InternalName", "libmicrohttpd2\0" +#ifdef DLL_EXPORT + VALUE "OriginalFilename", "libmicrohttpd2-12.dll\0" +#else + VALUE "OriginalFilename", "libmicrohttpd2.lib\0" +#endif + VALUE "CompanyName", "Free Software Foundation\0" + VALUE "LegalCopyright", "Copyright (C) 2007-2024 Christian Grothoff, Evgeny Grin, and project contributors\0" + VALUE "Comments", "http://www.gnu.org/software/libmicrohttpd/\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0 /* US English, ASCII */ + END +END + diff --git a/src/mhd2/w32_lib_res.rc.in b/src/mhd2/w32_lib_res.rc.in new file mode 100644 index 00000000..4a7192a3 --- /dev/null +++ b/src/mhd2/w32_lib_res.rc.in @@ -0,0 +1,51 @@ +/* W32 resources for .dll */ + +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +VS_VERSION_INFO VERSIONINFO + FILEVERSION @PACKAGE_VERSION_MAJOR@,@PACKAGE_VERSION_MINOR@,@PACKAGE_VERSION_SUBMINOR@,0 + PRODUCTVERSION @PACKAGE_VERSION_MAJOR@,@PACKAGE_VERSION_MINOR@,@PACKAGE_VERSION_SUBMINOR@,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#if defined(_DEBUG) + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS_NT_WINDOWS32 +#ifdef DLL_EXPORT + FILETYPE VFT_DLL +#else + FILETYPE VFT_STATIC_LIB +#endif + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" /* Lang = US English, Charset = ASCII */ + BEGIN + VALUE "ProductName", "GNU libmicrohttpd\0" + VALUE "ProductVersion", "@PACKAGE_VERSION@\0" + VALUE "FileVersion", "@PACKAGE_VERSION@\0" +#ifdef DLL_EXPORT + VALUE "FileDescription", "GNU libmicrohttpd2 DLL for Windows (MinGW build, @W32CRT@ run-time lib)\0" +#else + VALUE "FileDescription", "GNU libmicrohttpd2 static library for Windows (MinGW build, MSVCRT run-time lib)\0" +#endif + VALUE "InternalName", "libmicrohttpd2\0" +#ifdef DLL_EXPORT + VALUE "OriginalFilename", "libmicrohttpd2-@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/microhttpd/mhd_itc.c b/src/microhttpd/mhd_itc.c deleted file mode 100644 index ef5e49bd..00000000 --- a/src/microhttpd/mhd_itc.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -/** - * @file microhttpd/mhd_itc.c - * @brief Implementation of inter-thread communication functions - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - */ - -#include "mhd_itc.h" -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#include -#include "internal.h" - - -#if defined(_MHD_ITC_PIPE) -#if ! defined(_WIN32) || defined(__CYGWIN__) - -#ifndef HAVE_PIPE2_FUNC -/** - * Change itc FD options to be non-blocking. - * - * @param itc the inter-thread communication primitive to manipulate - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_itc_nonblocking_ (struct MHD_itc_ itc) -{ - unsigned int i; - - for (i = 0; i<2; i++) - { - int flags; - - flags = fcntl (itc.fd[i], - F_GETFL); - if (-1 == flags) - return 0; - - if ( ((flags | O_NONBLOCK) != flags) && - (0 != fcntl (itc.fd[i], - F_SETFL, - flags | O_NONBLOCK)) ) - return 0; - } - return ! 0; -} - - -#endif /* ! HAVE_PIPE2_FUNC */ -#endif /* !_WIN32 || __CYGWIN__ */ -#endif /* _MHD_ITC_EVENTFD || _MHD_ITC_PIPE */ diff --git a/src/microhttpd/mhd_itc.h b/src/microhttpd/mhd_itc.h deleted file mode 100644 index 9d609d5d..00000000 --- a/src/microhttpd/mhd_itc.h +++ /dev/null @@ -1,387 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016-2023 Karlson2k (Evgeny Grin) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -/** - * @file microhttpd/mhd_itc.h - * @brief Header for platform-independent inter-thread communication - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - * - * Provides basic abstraction for inter-thread communication. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - */ -#ifndef MHD_ITC_H -#define MHD_ITC_H 1 -#include "mhd_itc_types.h" - -#include - -#ifndef MHD_PANIC -# include -# ifdef HAVE_STDLIB_H -# include -# endif /* HAVE_STDLIB_H */ -/* Simple implementation of MHD_PANIC, to be used outside lib */ -# define MHD_PANIC(msg) \ - do { fprintf (stderr, \ - "Abnormal termination at %d line in file %s: %s\n", \ - (int) __LINE__, __FILE__, msg); abort (); \ - } while (0) -#endif /* ! MHD_PANIC */ - -#if defined(_MHD_ITC_EVENTFD) - -/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */ -#include -#include /* for uint64_t */ -#ifdef HAVE_UNISTD_H -#include /* for read(), write() */ -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_STRING_H -#include /* for strerror() */ -#include -#endif - -/** - * Number of FDs used by every ITC. - */ -#define MHD_ITC_NUM_FDS_ (1) - -/** - * Initialise ITC by generating eventFD - * @param itc the itc to initialise - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_init_(itc) \ - (-1 != ((itc).fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK))) - -/** - * Get description string of last errno for itc operations. - */ -#define MHD_itc_last_strerror_() strerror (errno) - -/** - * Internal static const helper for MHD_itc_activate_() - */ -static const uint64_t _MHD_itc_wr_data = 1; - -/** - * Activate signal on @a itc - * @param itc the itc to use - * @param str ignored - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_activate_(itc, str) \ - ((write ((itc).fd, (const void*) &_MHD_itc_wr_data, \ - sizeof(_MHD_itc_wr_data)) > 0) || (EAGAIN == errno)) - -/** - * Return read FD of @a itc which can be used for poll(), select() etc. - * @param itc the itc to get FD - * @return FD of read side - */ -#define MHD_itc_r_fd_(itc) ((itc).fd) - -/** - * Return write FD of @a itc - * @param itc the itc to get FD - * @return FD of write side - */ -#define MHD_itc_w_fd_(itc) ((itc).fd) - -/** - * Clear signaled state on @a itc - * @param itc the itc to clear - */ -#define MHD_itc_clear_(itc) \ - do { uint64_t __b; \ - (void) read ((itc).fd, (void*)&__b, sizeof(__b)); \ - } while (0) - -/** - * Destroy previously initialised ITC. Note that close() - * on some platforms returns odd errors, so we ONLY fail - * if the errno is EBADF. - * @param itc the itc to destroy - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_destroy_(itc) \ - ((0 == close ((itc).fd)) || (EBADF != errno)) - -/** - * Check whether ITC has valid value. - * - * Macro check whether @a itc value is valid (allowed), - * macro does not check whether @a itc was really initialised. - * @param itc the itc to check - * @return boolean true if @a itc has valid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_VALID_(itc) (-1 != ((itc).fd)) - -/** - * Set @a itc to invalid value. - * @param itc the itc to set - */ -#define MHD_itc_set_invalid_(itc) ((itc).fd = -1) - - -#elif defined(_MHD_ITC_PIPE) - -/* **************** Standard UNIX ITC implementation by pipe ********** */ - -#if defined(HAVE_PIPE2_FUNC) && defined(HAVE_FCNTL_H) -# include /* for O_CLOEXEC, O_NONBLOCK */ -#endif /* HAVE_PIPE2_FUNC && HAVE_FCNTL_H */ -#ifdef HAVE_UNISTD_H -#include /* for read(), write(), errno */ -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_STRING_H -#include /* for strerror() */ -#endif - - -/** - * Number of FDs used by every ITC. - */ -#define MHD_ITC_NUM_FDS_ (2) - -/** - * Initialise ITC by generating pipe - * @param itc the itc to initialise - * @return non-zero if succeeded, zero otherwise - */ -#ifdef HAVE_PIPE2_FUNC -# define MHD_itc_init_(itc) (! pipe2 ((itc).fd, O_CLOEXEC | O_NONBLOCK)) -#else /* ! HAVE_PIPE2_FUNC */ -# define MHD_itc_init_(itc) \ - ( (! pipe ((itc).fd)) ? \ - (MHD_itc_nonblocking_ ((itc)) ? \ - (! 0) : \ - (MHD_itc_destroy_ ((itc)), 0) ) \ - : (0) ) -#endif /* ! HAVE_PIPE2_FUNC */ - -/** - * Get description string of last errno for itc operations. - */ -#define MHD_itc_last_strerror_() strerror (errno) - -/** - * Activate signal on @a itc - * @param itc the itc to use - * @param str one-symbol string, useful only for strace debug - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_activate_(itc, str) \ - ((write ((itc).fd[1], (const void*) (str), 1) > 0) || (EAGAIN == errno)) - - -/** - * Return read FD of @a itc which can be used for poll(), select() etc. - * @param itc the itc to get FD - * @return FD of read side - */ -#define MHD_itc_r_fd_(itc) ((itc).fd[0]) - -/** - * Return write FD of @a itc - * @param itc the itc to get FD - * @return FD of write side - */ -#define MHD_itc_w_fd_(itc) ((itc).fd[1]) - -/** - * Clear signaled state on @a itc - * @param itc the itc to clear - */ -#define MHD_itc_clear_(itc) do \ - { long __b; \ - while (0 < read ((itc).fd[0], (void*) &__b, sizeof(__b))) \ - {(void)0;} } while (0) - -/** - * Destroy previously initialised ITC - * @param itc the itc to destroy - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_destroy_(itc) \ - ( (0 == close ((itc).fd[0])) ? \ - (0 == close ((itc).fd[1])) : \ - ((close ((itc).fd[1])), 0) ) - -/** - * Check whether ITC has valid value. - * - * Macro check whether @a itc value is valid (allowed), - * macro does not check whether @a itc was really initialised. - * @param itc the itc to check - * @return boolean true if @a itc has valid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_VALID_(itc) (-1 != (itc).fd[0]) - -/** - * Set @a itc to invalid value. - * @param itc the itc to set - */ -#define MHD_itc_set_invalid_(itc) ((itc).fd[0] = (itc).fd[1] = -1) - -#ifndef HAVE_PIPE2_FUNC -/** - * Change itc FD options to be non-blocking. - * - * @param fd the FD to manipulate - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_itc_nonblocking_ (struct MHD_itc_ itc); - -#endif /* ! HAVE_PIPE2_FUNC */ - - -#elif defined(_MHD_ITC_SOCKETPAIR) - -/* **************** ITC implementation by socket pair ********** */ - -#include "mhd_sockets.h" - - -/** - * Number of FDs used by every ITC. - */ -#define MHD_ITC_NUM_FDS_ (2) - -/** - * Initialise ITC by generating socketpair - * @param itc the itc to initialise - * @return non-zero if succeeded, zero otherwise - */ -#ifdef MHD_socket_pair_nblk_ -# define MHD_itc_init_(itc) MHD_socket_pair_nblk_ ((itc).sk) -#else /* ! MHD_socket_pair_nblk_ */ -# define MHD_itc_init_(itc) \ - (MHD_socket_pair_ ((itc).sk) ? \ - (MHD_itc_nonblocking_ ((itc)) ? \ - (! 0) : \ - (MHD_itc_destroy_ ((itc)), 0) ) \ - : (0)) -#endif /* ! MHD_socket_pair_nblk_ */ - -/** - * Get description string of last error for itc operations. - */ -#define MHD_itc_last_strerror_() MHD_socket_last_strerr_ () - -/** - * Activate signal on @a itc - * @param itc the itc to use - * @param str one-symbol string, useful only for strace debug - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_activate_(itc, str) \ - ((MHD_send_ ((itc).sk[1], (str), 1) > 0) || \ - (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))) - -/** - * Return read FD of @a itc which can be used for poll(), select() etc. - * @param itc the itc to get FD - * @return FD of read side - */ -#define MHD_itc_r_fd_(itc) ((itc).sk[0]) - -/** - * Return write FD of @a itc - * @param itc the itc to get FD - * @return FD of write side - */ -#define MHD_itc_w_fd_(itc) ((itc).sk[1]) - -/** - * Clear signaled state on @a itc - * @param itc the itc to clear - */ -#define MHD_itc_clear_(itc) do \ - { long __b; \ - while (0 < recv ((itc).sk[0], (void*) &__b, sizeof(__b), 0)) \ - {(void)0;} } while (0) - -/** - * Destroy previously initialised ITC - * @param itc the itc to destroy - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_destroy_(itc) \ - (MHD_socket_close_ ((itc).sk[0]) ? \ - MHD_socket_close_ ((itc).sk[1]) : \ - ((void) MHD_socket_close_ ((itc).sk[1]), 0) ) - - -/** - * Check whether ITC has valid value. - * - * Macro check whether @a itc value is valid (allowed), - * macro does not check whether @a itc was really initialised. - * @param itc the itc to check - * @return boolean true if @a itc has valid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_VALID_(itc) (MHD_INVALID_SOCKET != (itc).sk[0]) - -/** - * Set @a itc to invalid value. - * @param itc the itc to set - */ -#define MHD_itc_set_invalid_(itc) \ - ((itc).sk[0] = (itc).sk[1] = MHD_INVALID_SOCKET) - -#ifndef MHD_socket_pair_nblk_ -# define MHD_itc_nonblocking_(pip) \ - (MHD_socket_nonblocking_ ((pip).sk[0]) && \ - MHD_socket_nonblocking_ ((pip).sk[1])) -#endif /* ! MHD_socket_pair_nblk_ */ - -#endif /* _MHD_ITC_SOCKETPAIR */ - -/** - * Destroy previously initialised ITC and abort execution - * if error is detected. - * @param itc the itc to destroy - */ -#define MHD_itc_destroy_chk_(itc) do { \ - if (! MHD_itc_destroy_ (itc)) \ - MHD_PANIC (_ ("Failed to destroy ITC.\n")); \ -} while (0) - -/** - * Check whether ITC has invalid value. - * - * Macro check whether @a itc value is invalid, - * macro does not check whether @a itc was destroyed. - * @param itc the itc to check - * @return boolean true if @a itc has invalid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_INVALID_(itc) (! MHD_ITC_IS_VALID_ (itc)) - -#endif /* MHD_ITC_H */ diff --git a/src/microhttpd/mhd_threads.h b/src/microhttpd/mhd_threads.h deleted file mode 100644 index c0bf3f13..00000000 --- a/src/microhttpd/mhd_threads.h +++ /dev/null @@ -1,566 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016-2023 Karlson2k (Evgeny Grin) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -/** - * @file microhttpd/mhd_threads.h - * @brief Header for platform-independent threads abstraction - * @author Karlson2k (Evgeny Grin) - * - * Provides basic abstraction for threads. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - * - * @warning Unlike pthread functions, most of functions return - * nonzero on success. - */ - -#ifndef MHD_THREADS_H -#define MHD_THREADS_H 1 - -#include "mhd_options.h" -#ifdef HAVE_STDDEF_H -# include /* for size_t */ -#elif defined(HAVE_STDLIB_H) -# include /* for size_t */ -#else /* ! HAVE_STDLIB_H */ -# include /* for size_t */ -#endif /* ! HAVE_STDLIB_H */ - -#if defined(MHD_USE_POSIX_THREADS) -# undef HAVE_CONFIG_H -# include -# define HAVE_CONFIG_H 1 -# ifndef MHD_USE_THREADS -# define MHD_USE_THREADS 1 -# endif -#elif defined(MHD_USE_W32_THREADS) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN 1 -# endif /* !WIN32_LEAN_AND_MEAN */ -# include -# ifndef MHD_USE_THREADS -# define MHD_USE_THREADS 1 -# endif -#else -# error No threading API is available. -#endif - -#ifdef HAVE_STDBOOL_H -# include -#endif /* HAVE_STDBOOL_H */ - -#if defined(MHD_USE_POSIX_THREADS) && defined(MHD_USE_W32_THREADS) -# error Both MHD_USE_POSIX_THREADS and MHD_USE_W32_THREADS are defined -#endif /* MHD_USE_POSIX_THREADS && MHD_USE_W32_THREADS */ - -#ifndef MHD_NO_THREAD_NAMES -# if defined(MHD_USE_POSIX_THREADS) -# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ - defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \ - defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \ - defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \ - defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ - defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) -# define MHD_USE_THREAD_NAME_ -# endif /* HAVE_PTHREAD_SETNAME_NP */ -# elif defined(MHD_USE_W32_THREADS) -# ifdef _MSC_FULL_VER -/* Thread names only available with VC compiler */ -# define MHD_USE_THREAD_NAME_ -# endif /* _MSC_FULL_VER */ -# endif -#endif - -/* ** Thread handle - used to control the thread ** */ - -#if defined(MHD_USE_POSIX_THREADS) -/** - * Wait until specified thread is ended and free thread handle on success. - * @param thread handle to watch - * @return nonzero on success, zero otherwise - */ -# define MHD_join_thread_(native_handle) \ - (! pthread_join ((native_handle), NULL)) -#elif defined(MHD_USE_W32_THREADS) -/** - * Wait until specified thread is ended and free thread handle on success. - * @param thread handle to watch - * @return nonzero on success, zero otherwise - */ -# define MHD_join_thread_(native_handle) \ - ( (WAIT_OBJECT_0 == WaitForSingleObject ( (native_handle), INFINITE)) ? \ - (CloseHandle ( (native_handle)), ! 0) : 0 ) -#endif - -#if defined(MHD_USE_POSIX_THREADS) -/** - * The native type to control the thread from other threads - */ -typedef pthread_t MHD_thread_handle_native_; -#elif defined(MHD_USE_W32_THREADS) -/** - * The native type to control the thread from other threads - */ -typedef HANDLE MHD_thread_handle_native_; -#endif - -#if defined(MHD_USE_POSIX_THREADS) -# if defined(__gnu_linux__) || \ - (defined(__linux__) && defined(__GLIBC__)) -/* The next part of code is disabled because it relies on undocumented - behaviour. - It could be enabled for neglectable performance and size improvements. */ -# if 0 /* Disabled code */ -/** - * The native invalid value for native thread handle - */ -# define MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ \ - ((MHD_thread_handle_native_) 0) -# endif /* Disabled code */ -# endif /* __gnu_linux__ || (__linux__ && __GLIBC__) */ -#elif defined(MHD_USE_W32_THREADS) -/** - * The native invalid value for native thread handle - */ -# define MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ \ - ((MHD_thread_handle_native_) NULL) -#endif /* MHD_USE_W32_THREADS */ - -#if ! defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) -/** - * Structure with thread handle and validity flag - */ -struct MHD_thread_handle_struct_ -{ - bool valid; /**< true if native handle is set */ - MHD_thread_handle_native_ native; /**< the native thread handle */ -}; -/** - * Type with thread handle that can be set to invalid value - */ -typedef struct MHD_thread_handle_struct_ MHD_thread_handle_; - -/** - * Set variable pointed by @a handle_ptr to invalid (unset) value - */ -# define MHD_thread_handle_set_invalid_(handle_ptr) \ - ((handle_ptr)->valid = false) -/** - * Set native handle in variable pointed by @a handle_ptr - * to @a native_val value - */ -# define MHD_thread_handle_set_native_(handle_ptr,native_val) \ - ((handle_ptr)->valid = true, (handle_ptr)->native = native_val) -/** - * Check whether native handle value is set in @a handle_var variable - */ -# define MHD_thread_handle_is_valid_(handle_var) \ - ((handle_var).valid) -/** - * Get native handle value from @a handle_var variable - */ -# define MHD_thread_handle_get_native_(handle_var) \ - ((handle_var).native) -#else /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */ -/** - * Type with thread handle that can be set to invalid value - */ -typedef MHD_thread_handle_native_ MHD_thread_handle_; - -/** - * Set variable pointed by @a handle_ptr to invalid (unset) value - */ -# define MHD_thread_handle_set_invalid_(handle_ptr) \ - ((*(handle_ptr)) = MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) -/** - * Set native handle in the variable pointed by @a handle_ptr - * to @a native_val value - */ -# define MHD_thread_handle_set_native_(handle_ptr,native_val) \ - ((*(handle_ptr)) = native_val) -/** - * Check whether native handle value is set in @a handle_var variable - */ -# define MHD_thread_handle_is_valid_(handle_var) \ - (MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ != handle_var) -/** - * Get native handle value from @a handle_var variable - */ -# define MHD_thread_handle_get_native_(handle_var) \ - (handle_var) -/** - * Get pointer to native handle stored the variable pointed by @a handle_ptr - * @note This macro could not available if direct manipulation of - * the native handle is not possible - */ -# define MHD_thread_handle_get_native_ptr_(handle_ptr) \ - (handle_ptr) -#endif /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */ - - -/* ** Thread ID - used to check threads match ** */ - -#if defined(MHD_USE_POSIX_THREADS) -/** - * The native type used to check whether current thread matches expected thread - */ -typedef pthread_t MHD_thread_ID_native_; - -/** - * Function to get the current thread native ID. - */ -# define MHD_thread_ID_native_current_ pthread_self - -/** - * Check whether two native thread IDs are equal. - * @return non-zero if equal, zero if not equal - */ -# define MHD_thread_ID_native_equal_(id1,id2) \ - (pthread_equal(id1,id2)) -#elif defined(MHD_USE_W32_THREADS) -/** - * The native type used to check whether current thread matches expected thread - */ -typedef DWORD MHD_thread_ID_native_; - -/** - * Function to get the current thread native ID. - */ -# define MHD_thread_ID_native_current_ GetCurrentThreadId - -/** - * Check whether two native thread IDs are equal. - * @return non-zero if equal, zero if not equal - */ -# define MHD_thread_ID_native_equal_(id1,id2) \ - ((id1) == (id2)) -#endif - -/** - * Check whether specified thread ID matches current thread. - * @param id the thread ID to match - * @return nonzero on match, zero otherwise - */ -#define MHD_thread_ID_native_is_current_thread_(id) \ - MHD_thread_ID_native_equal_(id, MHD_thread_ID_native_current_()) - - -#if defined(MHD_USE_POSIX_THREADS) -# if defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) -/** - * The native invalid value for native thread ID - */ -# define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ - MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ -# endif /* MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ */ -#elif defined(MHD_USE_W32_THREADS) -/** - * The native invalid value for native thread ID - */ - # define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ - ((MHD_thread_ID_native_) 0) -#endif /* MHD_USE_W32_THREADS */ - -#if ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) -/** - * Structure with thread id and validity flag - */ -struct MHD_thread_ID_struct_ -{ - bool valid; /**< true if native ID is set */ - MHD_thread_ID_native_ native; /**< the native thread ID */ -}; -/** - * Type with thread ID that can be set to invalid value - */ -typedef struct MHD_thread_ID_struct_ MHD_thread_ID_; - -/** - * Set variable pointed by @a ID_ptr to invalid (unset) value - */ -# define MHD_thread_ID_set_invalid_(ID_ptr) \ - ((ID_ptr)->valid = false) -/** - * Set native ID in variable pointed by @a ID_ptr - * to @a native_val value - */ -# define MHD_thread_ID_set_native_(ID_ptr,native_val) \ - ((ID_ptr)->valid = true, (ID_ptr)->native = native_val) -/** - * Check whether native ID value is set in @a ID_var variable - */ -# define MHD_thread_ID_is_valid_(ID_var) \ - ((ID_var).valid) -/** - * Get native ID value from @a ID_var variable - */ -# define MHD_thread_ID_get_native_(ID_var) \ - ((ID_var).native) -/** - * Check whether @a ID_var variable is equal current thread - */ -# define MHD_thread_ID_is_current_thread_(ID_var) \ - (MHD_thread_ID_is_valid_(ID_var) && \ - MHD_thread_ID_native_is_current_thread_((ID_var).native)) -#else /* MHD_THREAD_ID_NATIVE_INVALID_ */ -/** - * Type with thread ID that can be set to invalid value - */ -typedef MHD_thread_ID_native_ MHD_thread_ID_; - -/** - * Set variable pointed by @a ID_ptr to invalid (unset) value - */ -# define MHD_thread_ID_set_invalid_(ID_ptr) \ - ((*(ID_ptr)) = MHD_THREAD_ID_NATIVE_VALUE_INVALID_) -/** - * Set native ID in variable pointed by @a ID_ptr - * to @a native_val value - */ -# define MHD_thread_ID_set_native_(ID_ptr,native_val) \ - ((*(ID_ptr)) = native_val) -/** - * Check whether native ID value is set in @a ID_var variable - */ -# define MHD_thread_ID_is_valid_(ID_var) \ - (MHD_THREAD_ID_NATIVE_VALUE_INVALID_ != ID_var) -/** - * Get native ID value from @a ID_var variable - */ -# define MHD_thread_ID_get_native_(ID_var) \ - (ID_var) -/** - * Check whether @a ID_var variable is equal current thread - */ -# define MHD_thread_ID_is_current_thread_(ID_var) \ - MHD_thread_ID_native_is_current_thread_(ID_var) -#endif /* MHD_THREAD_ID_NATIVE_INVALID_ */ - -/** - * Set current thread ID in variable pointed by @a ID_ptr - */ -# define MHD_thread_ID_set_current_thread_(ID_ptr) \ - MHD_thread_ID_set_native_(ID_ptr,MHD_thread_ID_native_current_()) - - -#if defined(MHD_USE_POSIX_THREADS) -# if defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \ - ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) -# error \ - MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined -# elif ! defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \ - defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) -# error \ - MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined -# endif -#endif /* MHD_USE_POSIX_THREADS */ - -/* When staring a new thread, the kernel (and thread implementation) may - * pause the calling (initial) thread and start the new thread. - * If thread identifier is assigned to variable in the initial thread then - * the value of the identifier variable will be undefined in the new thread - * until the initial thread continue processing. - * However, it is also possible that the new thread created, but not executed - * for some time while the initial thread continue execution. In this case any - * variable assigned in the new thread will be undefined for some time until - * they really processed by the new thread. - * To avoid data races, a special structure MHD_thread_handle_ID_ is used. - * The "handle" is assigned by calling (initial) thread and should be always - * defined when checked in the initial thread. - * The "ID" is assigned by the new thread and should be always defined when - * checked inside the new thread. - */ -/* Depending on implementation, pthread_create() MAY set thread ID into - * provided pointer and after it start thread OR start thread and after - * it set thread ID. In the latter case, to avoid data races, additional - * pthread_self() call is required in thread routine. If some platform - * is known for setting thread ID BEFORE starting thread macro - * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined - * to save some resources. */ -/* #define MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 1 */ - -/* * handle - must be valid when other thread knows that particular thread - is started. - * ID - must be valid when code is executed inside thread */ -#if defined(MHD_USE_POSIX_THREADS) && \ - defined(MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD) && \ - defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \ - defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) && \ - defined(MHD_thread_handle_get_native_ptr_) -union _MHD_thread_handle_ID_ -{ - MHD_thread_handle_ handle; /**< To be used in other threads */ - MHD_thread_ID_ ID; /**< To be used in the thread itself */ -}; -typedef union _MHD_thread_handle_ID_ MHD_thread_handle_ID_; -# define MHD_THREAD_HANDLE_ID_IS_UNION 1 -#else /* !MHD_USE_POSIX_THREADS - || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD - || !MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ - || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ - || !MHD_thread_handle_get_native_ptr_ */ -struct _MHD_thread_handle_ID_ -{ - MHD_thread_handle_ handle; /**< To be used in other threads */ - MHD_thread_ID_ ID; /**< To be used in the thread itself */ -}; -typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_; -#endif /* !MHD_USE_POSIX_THREADS - || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD - || !MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ - || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ - || !MHD_thread_handle_get_native_ptr_ */ - -/** - * Set MHD_thread_handle_ID_ to invalid value - */ -#define MHD_thread_handle_ID_set_invalid_(hndl_id_ptr) \ - (MHD_thread_handle_set_invalid_(&((hndl_id_ptr)->handle)), \ - MHD_thread_ID_set_invalid_(&((hndl_id_ptr)->ID))) - -/** - * Check whether thread handle is valid. - * To be used in threads other then the thread specified by @a hndl_id. - */ -#define MHD_thread_handle_ID_is_valid_handle_(hndl_id) \ - MHD_thread_handle_is_valid_((hndl_id).handle) - -/** - * Set native handle in variable pointed by @a hndl_id_ptr - * to @a native_val value - */ -#define MHD_thread_handle_ID_set_native_handle_(hndl_id_ptr,native_val) \ - MHD_thread_handle_set_native_(&((hndl_id_ptr)->handle),native_val) - -#if defined(MHD_thread_handle_get_native_ptr_) -/** - * Get pointer to native handle stored the variable pointed by @a hndl_id_ptr - * @note This macro could not available if direct manipulation of - * the native handle is not possible - */ -# define MHD_thread_handle_ID_get_native_handle_ptr_(hndl_id_ptr) \ - MHD_thread_handle_get_native_ptr_(&((hndl_id_ptr)->handle)) -#endif /* MHD_thread_handle_get_native_ptr_ */ - -/** - * Get native thread handle from MHD_thread_handle_ID_ variable. - */ -#define MHD_thread_handle_ID_get_native_handle_(hndl_id) \ - MHD_thread_handle_get_native_((hndl_id).handle) - -/** - * Check whether thread ID is valid. - * To be used in the thread itself. - */ -#define MHD_thread_handle_ID_is_valid_ID_(hndl_id) \ - MHD_thread_ID_is_valid_((hndl_id).ID) - -#if defined(MHD_THREAD_HANDLE_ID_IS_UNION) -# if defined(MHD_USE_W32_THREADS) -# error MHD_thread_handle_ID_ cannot be a union with W32 threads -# endif /* MHD_USE_W32_THREADS */ -/** - * Set current thread ID in the variable pointed by @a hndl_id_ptr - */ -# define MHD_thread_handle_ID_set_current_thread_ID_(hndl_id_ptr) (void) 0 -#else /* ! MHD_THREAD_HANDLE_ID_IS_UNION */ -/** - * Set current thread ID in the variable pointed by @a hndl_id_ptr - */ -# define MHD_thread_handle_ID_set_current_thread_ID_(hndl_id_ptr) \ - MHD_thread_ID_set_current_thread_(&((hndl_id_ptr)->ID)) -#endif /* ! MHD_THREAD_HANDLE_ID_IS_UNION */ - -/** - * Check whether provided thread ID matches current thread. - * @param ID thread ID to match - * @return nonzero on match, zero otherwise - */ -#define MHD_thread_handle_ID_is_current_thread_(hndl_id) \ - MHD_thread_ID_is_current_thread_((hndl_id).ID) - -/** - * Wait until specified thread is ended and free thread handle on success. - * @param hndl_id_ handle with ID to watch - * @return nonzero on success, zero otherwise - */ -#define MHD_thread_handle_ID_join_thread_(hndl_id) \ - MHD_join_thread_(MHD_thread_handle_ID_get_native_handle_(hndl_id)) - -#if defined(MHD_USE_POSIX_THREADS) -# define MHD_THRD_RTRN_TYPE_ void* -# define MHD_THRD_CALL_SPEC_ -#elif defined(MHD_USE_W32_THREADS) -# define MHD_THRD_RTRN_TYPE_ unsigned -# define MHD_THRD_CALL_SPEC_ __stdcall -#endif - -/** - * Signature of main function for a thread. - * - * @param cls closure argument for the function - * @return termination code from the thread - */ -typedef MHD_THRD_RTRN_TYPE_ -(MHD_THRD_CALL_SPEC_ *MHD_THREAD_START_ROUTINE_)(void *cls); - - -/** - * Create a thread and set the attributes according to our options. - * - * If thread is created, thread handle must be freed by MHD_join_thread_(). - * - * @param handle_id handle to initialise - * @param stack_size size of stack for new thread, 0 for default - * @param start_routine main function of thread - * @param arg argument for start_routine - * @return non-zero on success; zero otherwise (with errno set) - */ -int -MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg); - -#ifndef MHD_USE_THREAD_NAME_ -#define MHD_create_named_thread_(t,n,s,r,a) MHD_create_thread_ ((t),(s),(r),(a)) -#else /* MHD_USE_THREAD_NAME_ */ -/** - * Create a named thread and set the attributes according to our options. - * - * @param handle_id handle to initialise - * @param thread_name name for new thread - * @param stack_size size of stack for new thread, 0 for default - * @param start_routine main function of thread - * @param arg argument for start_routine - * @return non-zero on success; zero otherwise - */ -int -MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id, - const char *thread_name, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg); - -#endif /* MHD_USE_THREAD_NAME_ */ - -#endif /* ! MHD_THREADS_H */ diff --git a/src/testcurl/https/Makefile.am b/src/testcurl/https/Makefile.am index 5f620d06..a90d3fbf 100644 --- a/src/testcurl/https/Makefile.am +++ b/src/testcurl/https/Makefile.am @@ -57,7 +57,7 @@ THREAD_ONLY_TESTS = \ check_PROGRAMS = \ test_https_get_select -if USE_THREADS +if MHD_USE_THREADS check_PROGRAMS += \ $(THREAD_ONLY_TESTS) endif diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am new file mode 100644 index 00000000..61a9c0d1 --- /dev/null +++ b/src/tests/Makefile.am @@ -0,0 +1,6 @@ +# 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 new file mode 100644 index 00000000..eee19e17 --- /dev/null +++ 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 \ + -DMHD_CPU_COUNT=$(CPU_COUNT) \ + $(CPPFLAGS_ac) + +AM_CFLAGS = $(CFLAGS_ac) + +AM_LDFLAGS = $(LDFLAGS_ac) + +AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) + +if USE_COVERAGE + AM_CFLAGS += -fprofile-arcs -ftest-coverage +endif + +LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la + +$(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile + @echo ' cd $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la'; \ + $(am__cd) $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la + +check_PROGRAMS = \ + test_create_destroy \ + test_create_start_destroy \ + test_create_destroy_ipv4 \ + test_create_start_destroy_ipv4 \ + test_create_destroy_ipv6 \ + test_create_start_destroy_ipv6_ipv4 \ + test_create_destroy_ipbest \ + test_create_start_destroy_ipbest \ + $(EMPTY_ITEM) + +if MHD_USE_SELECT +check_PROGRAMS += \ + test_create_destroy_select \ + test_create_start_destroy_select \ + test_create_destroy_select_ipv4 \ + test_create_start_destroy_select_ipv4 \ + $(EMPTY_ITEM) +endif + +if MHD_USE_POLL +check_PROGRAMS += \ + test_create_destroy_poll \ + test_create_start_destroy_poll \ + test_create_destroy_poll_ipv4 \ + test_create_start_destroy_poll_ipv4 \ + $(EMPTY_ITEM) +endif + +if MHD_USE_EPOLL +check_PROGRAMS += \ + test_create_destroy_epoll \ + test_create_start_destroy_epoll \ + test_create_destroy_epoll_ipv4 \ + test_create_start_destroy_epoll_ipv4 \ + $(EMPTY_ITEM) +endif + +if MHD_USE_THREADS +check_PROGRAMS += \ + test_create_destroy_int_thread \ + test_create_start_destroy_int_thread \ + test_create_start_destroy_int_thread_ipv4 \ + test_create_destroy_thread_per_conn \ + test_create_start_destroy_thread_per_conn \ + test_create_start_destroy_thread_per_conn_ipv4 \ + test_create_destroy_thread_pool \ + test_create_start_destroy_thread_pool \ + test_create_start_destroy_thread_pool_ipv4 \ + $(EMPTY_ITEM) + +if MHD_USE_SELECT +check_PROGRAMS += \ + test_create_start_destroy_select_int_thread_ipv4 \ + test_create_start_destroy_select_thread_per_conn_ipv4 \ + $(EMPTY_ITEM) +endif + +if MHD_USE_POLL +check_PROGRAMS += \ + test_create_start_destroy_poll_int_thread_ipv4 \ + test_create_start_destroy_poll_thread_per_conn_ipv4 \ + $(EMPTY_ITEM) +endif + +if MHD_USE_EPOLL +check_PROGRAMS += \ + test_create_start_destroy_epoll_int_thread_ipv4 \ + test_create_start_destroy_epoll_thread_per_conn_ipv4 \ + $(EMPTY_ITEM) +endif + +endif + +if USE_IPV6_TESTING +check_PROGRAMS += \ + test_create_start_destroy_ipv6 +endif + +TESTS = $(check_PROGRAMS) + +# The universal sources used in all tests +basic_test_sources = test_basic_checks.c + +test_create_destroy_SOURCES = $(basic_test_sources) + +test_create_start_destroy_SOURCES = $(basic_test_sources) + +test_create_destroy_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_ipv6_SOURCES = $(basic_test_sources) + +test_create_start_destroy_ipv6_SOURCES = $(basic_test_sources) + +test_create_start_destroy_ipv6_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_ipbest_SOURCES = $(basic_test_sources) + +test_create_start_destroy_ipbest_SOURCES = $(basic_test_sources) + +test_create_destroy_select_SOURCES = $(basic_test_sources) + +test_create_start_destroy_select_SOURCES = $(basic_test_sources) + +test_create_destroy_select_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_select_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_poll_SOURCES = $(basic_test_sources) + +test_create_start_destroy_poll_SOURCES = $(basic_test_sources) + +test_create_destroy_poll_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_poll_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_epoll_SOURCES = $(basic_test_sources) + +test_create_start_destroy_epoll_SOURCES = $(basic_test_sources) + +test_create_destroy_epoll_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_epoll_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_int_thread_SOURCES = $(basic_test_sources) + +test_create_start_destroy_int_thread_SOURCES = $(basic_test_sources) + +test_create_start_destroy_int_thread_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_thread_per_conn_SOURCES = $(basic_test_sources) + +test_create_start_destroy_thread_per_conn_SOURCES = $(basic_test_sources) + +test_create_start_destroy_thread_per_conn_ipv4_SOURCES = $(basic_test_sources) + +test_create_destroy_thread_pool_SOURCES = $(basic_test_sources) + +test_create_start_destroy_thread_pool_SOURCES = $(basic_test_sources) + +test_create_start_destroy_thread_pool_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_select_int_thread_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_select_thread_per_conn_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_poll_int_thread_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_poll_thread_per_conn_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_epoll_int_thread_ipv4_SOURCES = $(basic_test_sources) + +test_create_start_destroy_epoll_thread_per_conn_ipv4_SOURCES = $(basic_test_sources) + diff --git a/src/tests/basic/test_basic_checks.c b/src/tests/basic/test_basic_checks.c new file mode 100644 index 00000000..9b5549c8 --- /dev/null +++ b/src/tests/basic/test_basic_checks.c @@ -0,0 +1,363 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2016, 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file test_start_destroy.c + * @brief test for create, start and destroy + * @author Karlson2k (Evgeny Grin) + */ +#include +#include +#include +#include + +/* Helper macros */ + +#define ERR_PRINT_LINE() \ + ((void) fprintf (stderr, "At the line number %u: ", \ + (unsigned) __LINE__)) + +/** + * Check whether SC code is OK, print error if not. + * @warning Do not use function call in the argument + * @param sc the status code to check + */ +#define tst_EXPECT_OK(sc) \ + ( (MHD_SC_OK == (sc)) ? (! 0) : \ + (ERR_PRINT_LINE (), \ + ((void) fprintf (stderr, "MHD function failed, returned: %u\n", \ + (unsigned int) (sc))), (0)) ) +#if 0 +#define tst_EXPECT_OK(sc) \ + ( (MHD_SC_OK == (sc)) ? (! 0) : \ + (ERR_PRINT_LINE (), \ + ((void) fprintf (stderr, \ + "MHD function failed, returned: %s\n", \ + MHD_status_code_to_string_lazy (sc))), (0)) ) +#endif + +/** + * Check whether SC code is OK, print error if not. + * @warning Do not use function call in the argument + * @param sc the status code to check + */ +#define tst_EXPECT_FAIL(sc) \ + ( (MHD_SC_OK != (sc)) ? (! 0) : \ + (ERR_PRINT_LINE (), \ + ((void) fprintf (stderr, "MHD function unexpectedly succeed.\n")), \ + (0)) ) + +/** + * Check whether SC code is success/failure as expected, print error if not. + * @warning Do not use function call in the argument + * @param sc the status code to check + * @param expect_ok non-zero if SC should be OK, zero is SC should NOT be OK + */ +#define tst_EXPECT_CHECK(sc,expect_ok) \ + ((expect_ok) ? tst_EXPECT_OK ((sc)) : tst_EXPECT_FAIL ((sc))) + +/* Helper functions */ + +/** + * Check whether program name contains specific @a marker string. + * Only last component in pathname is checked for marker presence, + * all leading directories names (if any) are ignored. Directories + * separators are handled correctly on both non-W32 and W32 + * platforms. + * @param prog_name the program name, may include path + * @param marker the marker to look for + * @return zero if any parameter is NULL or empty string or + * @a prog_name ends with slash or @a marker is not found in + * program name, non-zero if @a maker is found in program + * name. + */ +static int +has_in_name (const char *prog_name, const char *marker) +{ + const char *s; + const char *basename; + + if (! prog_name || ! marker || ! prog_name[0] || ! marker[0]) + return 0; + + basename = prog_name; + for (s = prog_name; *s; ++s) + { + if ('/' == *s) + basename = s + 1; +#if defined(_WIN32) || defined(__CYGWIN__) + else if ('\\' == *s) + basename = s + 1; +#endif /* _WIN32 || __CYGWIN__ */ + } + + return strstr (basename, marker) != NULL; +} + + +/* The test */ + +static int use_start = 0; + +static int use_ipv4 = 0; + +static int use_ipv6 = 0; + +static int use_ip_best = 0; + +static int use_select = 0; + +static int use_poll = 0; + +static int use_epoll = 0; + +static int use_int_thread = 0; + +static int use_thread_per_conn = 0; + +static int use_thread_pool = 0; + +/* Dynamic run-time variables */ + +static int err_flag = 0; + +MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) static const struct MHD_Action * +my_req_process (void *cls, + struct MHD_Request *request, + const struct MHD_String *path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size) +{ + (void) cls; (void) request; (void) path; (void) method; (void) upload_size; + // MHD_lib_global_fake(); + fprintf (stderr, "Unexpected call of the request callback.\n"); + err_flag = ! 0; + return NULL; +} + + +static struct MHD_Daemon * +test_daemon_create (void) +{ + struct MHD_Daemon *d; + + d = MHD_daemon_create (my_req_process, NULL); + if (NULL == d) + { + err_flag = ! 0; + ERR_PRINT_LINE (); + fprintf (stderr, "MHD_daemon_create() failed, NULL returned.\n"); + return NULL; + } + return d; +} + + +static int +test_daemon_setup (struct MHD_Daemon *d, + int should_succeed) +{ + enum MHD_StatusCode sc; + int ret = ! 0; + + if (use_ipv6) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL_v4_OPTIONAL, 0)); + if (! tst_EXPECT_CHECK (sc,should_succeed)) + ret = 0; + } + + if (use_ipv4) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL_v6_OPTIONAL, 0)); + if (! tst_EXPECT_CHECK (sc,should_succeed)) + ret = 0; + } + + if (use_ip_best) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, 0)); + if (! tst_EXPECT_CHECK (sc,should_succeed)) + ret = 0; + } + + if (use_select) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_SELECT)); + if (! tst_EXPECT_CHECK (sc,should_succeed)) + ret = 0; + } + + if (use_poll) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_POLL)); + if (! tst_EXPECT_CHECK (sc, should_succeed)) + ret = 0; + } + + if (use_epoll) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_EPOLL)); + if (! tst_EXPECT_CHECK (sc,should_succeed)) + ret = 0; + } + + if (use_int_thread) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_WORKER_THREADS (1))); + if (! tst_EXPECT_CHECK (sc, should_succeed)) + ret = 0; + } + + if (use_thread_per_conn) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_THREAD_PER_CONNECTION ())); + if (! tst_EXPECT_CHECK (sc, should_succeed)) + ret = 0; + } + + if (use_thread_pool) + { + sc = MHD_DAEMON_SET_OPTIONS ( \ + d, MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_WORKER_THREADS (4))); + if (! tst_EXPECT_CHECK (sc, should_succeed)) + ret = 0; + } + + if (! ret) + err_flag = ! 0; + + return ret; +} + + +static int +test_daemon_start (struct MHD_Daemon *d, + int should_succeed) +{ + enum MHD_StatusCode sc; + + sc = MHD_daemon_start (d); + if (! tst_EXPECT_CHECK (sc,should_succeed)) + { + err_flag = ! 0; + return 0; + } + + return ! 0; +} + + +static int +test_simple (void) +{ + struct MHD_Daemon *d; + int ret = ! 0; + + err_flag = 0; + + d = test_daemon_create (); + if (NULL == d) + return (ret && ! err_flag); + + test_daemon_setup (d, ! 0); + if (use_start) + test_daemon_start (d, ! 0); + + test_daemon_setup (d, ! use_start); + + if (use_start) + test_daemon_start (d, 0); /* Second "start" should fail */ + + MHD_daemon_destroy (d); + + return (ret && ! err_flag); +} + + +/** + * Initialise the test data + * @param prog_name the name of the this program + * @return non-zero if succeed, + * zero if failed + */ +static int +init_test (const char *prog_name) +{ + if (has_in_name (prog_name, "_start")) + use_start = ! 0; + + if (has_in_name (prog_name, "_ipv4")) + use_ipv4 = ! 0; + + if (has_in_name (prog_name, "_ipv6")) + use_ipv6 = ! 0; + + if (has_in_name (prog_name, "_ipbest")) + use_ip_best = ! 0; + + use_select = has_in_name (prog_name, "_select"); + + use_poll = has_in_name (prog_name, "_poll"); + + use_epoll = has_in_name (prog_name, "_epoll"); + + use_int_thread = has_in_name (prog_name, "_int_thread"); + + use_thread_per_conn = has_in_name (prog_name, "_thread_per_conn"); + + use_thread_pool = has_in_name (prog_name, "_thread_pool"); + + return ! 0; +} + + +int +main (int argc, char *argv[]) +{ + unsigned int num_err = 0; + (void) argc; /* Unused. Silence compiler warning. */ + + if (! init_test (argv[0])) + { + fprintf (stderr, "Failed to initialise the test!\n"); + return 77; + } + + if (! test_simple ()) + ++num_err; + + if (0 != num_err) + { + fprintf (stderr, "Number of failed checks: %u\n", num_err); + return 2; + } + + printf ("All checks succeed.\n"); + return 0; +} diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index da776afe..4795f6bb 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -29,7 +29,7 @@ $(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/ # Tools noinst_PROGRAMS = -if USE_THREADS +if MHD_USE_THREADS noinst_PROGRAMS += \ perf_replies endif