commit bff98a30e524ffbbc98e8a662b181b134b8aa974
parent c4c60ec518bf08206a591e98e032e7cbbefc0ac7
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Tue, 3 Sep 2024 17:51:21 +0200
Implemented POST parser + some fixes
Diffstat:
64 files changed, 7048 insertions(+), 1994 deletions(-)
diff --git a/Makefile.am b/Makefile.am
@@ -188,8 +188,8 @@ dist-po: po/Makefile $(PO_MAIN_FILES)
$(am__cd) po && $(MAKE) $(AM_MAKEFLAGS) distdir="$$rel_distsubdir" distdir
$(srcdir_po)/POTFILES.in: $(top_srcdir)/src/microhttpd/Makefile.am
- @echo "cd $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) update-po-POTFILES.in" && \
- $(am__cd) '$(top_builddir)/src/microhttpd' && $(MAKE) $(AM_MAKEFLAGS) update-po-POTFILES.in
+ @echo "cd $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) update-po-POTFILES.in" && \
+ $(am__cd) '$(top_builddir)/src/mhd2' && $(MAKE) $(AM_MAKEFLAGS) update-po-POTFILES.in
AUTOPOINT = autopoint
AUTOPOINT_FLAGS =
diff --git a/configure.ac b/configure.ac
@@ -48,6 +48,8 @@ AC_SUBST([PACKAGE_VERSION_SUBMINOR])
AC_SUBST([MHD_W32_DLL_SUFF])
AC_CONFIG_FILES([src/mhd2/w32_lib_res.rc])
+CONF_FINAL_WARNS=""
+
MHD_LIB_CPPFLAGS=""
MHD_LIB_CFLAGS=""
MHD_LIB_LDFLAGS=""
@@ -159,6 +161,15 @@ CPPFLAGS_ac=""
MHD_SYS_EXT([CPPFLAGS_ac])
CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}"
+m4_version_prereq([2.72],
+ [
+AC_SYS_YEAR2038
+ ],
+ [
+AC_SYS_LARGEFILE
+ ]
+)
+AC_FUNC_FSEEKO
LT_INIT([win32-dll])
LT_LANG([Windows Resource])
@@ -819,102 +830,7 @@ AS_VAR_IF([enable_linker_hardening],["yes"],
]
)
-
-AH_TEMPLATE([[HAVE_STDBOOL_H]], [Define to 1 if you have the <stdbool.h> header file and <stdbool.h> defines 'bool' type.])
-AH_TEMPLATE([[HAVE_BUILTIN_TYPE_BOOL]], [Define to 1 if you have the real boolean type.])
-AH_TEMPLATE([[bool]], [Define to type name which will be used as boolean type.])
-AC_CHECK_HEADER([stdbool.h],
- [
- AC_CHECK_TYPE([bool],
- [
- AC_DEFINE([[HAVE_STDBOOL_H]], [[1]])
- AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]], [[1]])
- ],
- [
- AC_MSG_WARN([[Header <stdbool.h> is present, but "bool" type cannot be detected. Check compiler flags.]])
- AC_DEFINE([[bool]], [[int]])
- ], [
-#include <stdbool.h>
- ]
- )
- ],
- [
- AC_CHECK_TYPE([bool],
- [AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]], [[1]])],
- [
- AC_CHECK_TYPE([_Bool],
- [
- AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]], [[1]])
- AC_DEFINE([[bool]], [[_Bool]])
- ],
- [
- AC_DEFINE([[bool]], [[int]])
- ], []
- )
- ], []
- )
- ],
- [AC_INCLUDES_DEFAULT]
-)
-
-AC_CACHE_CHECK([[whether "true" is defined or builtin]], [[mhd_cv_macro_true_defined]],
- [AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [[
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif
- ]], [[
-#if defined(true)
- /* dummy */
-#else
- (void)true;
-#endif
- ]])
- ], [[mhd_cv_macro_true_defined='yes']], [[mhd_cv_macro_true_defined='no']])
- ])
-AS_VAR_IF([[mhd_cv_macro_true_defined]], [["yes"]], [[:]],
- [AC_DEFINE([[true]],[[(!0)]], [Define to value interpreted by compiler as boolean "true", if "true" is not defined by system headers.])])
-
-AC_CACHE_CHECK([[whether "false" is defined or builtin]], [[mhd_cv_macro_false_defined]],
- [AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [[
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif
- ]], [[
-#if !defined(false)
- (void)false;
-#else
- /* dummy */
-#endif
- ]])
- ], [[mhd_cv_macro_false_defined='yes']], [[mhd_cv_macro_false_defined='no']])
- ])
-AS_VAR_IF([[mhd_cv_macro_false_defined]], [["yes"]], [[:]],
- [AC_DEFINE([[false]],[[0]], [Define to value interpreted by compiler as boolean "false", if "false" is not defined by system headers.])])
-
-AC_CACHE_CHECK([[whether "true" and "false" could be used]], [[mhd_cv_macro_true_false_valid]],
- [AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [[
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif
- ]], [[
- int var1[true ? 1 : -1] = { 1 };
- int var2[false ? -1 : 1] = { 2 };
- int var3[!true ? -1 : 1] = { 3 };
- int var4[!false ? 1 : -1] = { 4 };
- if (var1[0] == var2[0] || var3[0] == var4[0])
- return 1;
- ]])
- ], [[mhd_cv_macro_true_false_valid='yes']], [[mhd_cv_macro_true_false_valid='no']])
- ])
-AS_VAR_IF([[mhd_cv_macro_true_false_valid]], [["yes"]], [[:]],
- [AC_MSG_ERROR([[Value of "true" or value of "false" is not valid. Check config.log for details.]])])
-
+MHD_BOOL
AC_CACHE_CHECK([whether the NULL pointer has all zero bits],
[mhd_cv_ptr_null_all_zeros],
@@ -1201,7 +1117,6 @@ static int test_strct_func(struct test_strct *strc)
int main(void)
{
- struct test_strct *strct_ptr;
int int_var;
int *int_ptr;
@@ -1447,6 +1362,14 @@ AS_CASE(["$host_os"],
AC_DEFINE([_REENTRANT],[1],[Need with solaris or errno does not work])
mhd_host_os='Solaris'
AC_MSG_RESULT([[$mhd_host_os]])],
+ [linux-gnu],
+ [AC_DEFINE([LINUX],[1],[This is a Linux kernel])
+ mhd_host_os='GNU/Linux'
+ AC_MSG_RESULT([[$mhd_host_os]])],
+ [linux-android*],
+ [AC_DEFINE([LINUX],[1],[This is a Linux kernel])
+ mhd_host_os='Android'
+ AC_MSG_RESULT([[$mhd_host_os]])],
[*linux*],
[AC_DEFINE([LINUX],[1],[This is a Linux kernel])
mhd_host_os='Linux'
@@ -1735,24 +1658,21 @@ AX_PTHREAD(
[
mhd_have_posix_threads='yes'
AC_DEFINE([[HAVE_PTHREAD_H]],[[1]],[Define to 1 if you have the <pthread.h> header file.])
- AC_CACHE_CHECK([[whether pthread_sigmask(3) is available]],
- [[mhd_cv_func_pthread_sigmask]], [dnl
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- CFLAGS="${CFLAGS_ac} ${PTHREAD_CFLAGS} ${user_CFLAGS}"
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <signal.h>]],
- [[
- sigset_t nset, oset;
- sigemptyset (&nset);
- sigaddset (&nset, SIGPIPE);
- if (0 != pthread_sigmask(SIG_BLOCK, &nset, &oset)) return 1;
- ]])],
- [[mhd_cv_func_pthread_sigmask="yes"]],[[mhd_cv_func_pthread_sigmask="no"]])
- LIBS="${save_LIBS}"
- CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
- ])
- AS_VAR_IF([mhd_cv_func_pthread_sigmask],["yes"],
- [AC_DEFINE([[HAVE_PTHREAD_SIGMASK]],[[1]],[Define to 1 if you have the pthread_sigmask(3) function.])])
+ CFLAGS="${CFLAGS_ac} ${PTHREAD_CFLAGS} ${user_CFLAGS}"
+ MHD_CHECK_FUNC([pthread_sigmask],
+ [[
+#include <pthread.h>
+#include <signal.h>
+ ]],
+ [[
+ sigset_t nset, oset;
+ sigemptyset (&nset);
+ sigaddset (&nset, SIGPIPE);
+ i][f (0 != pthread_sigmask(SIG_BLOCK, &nset, &oset)) return 1;
+ ]],
+ [AC_DEFINE([[HAVE_PTHREAD_SIGMASK]],[[1]],[Define to 1 if you have the pthread_sigmask(3) function.])]
+ )
+ CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
],[[mhd_have_posix_threads='no']])
AM_CONDITIONAL([HAVE_POSIX_THREADS],[test "x$mhd_have_posix_threads" = "xyes"])
@@ -2804,8 +2724,7 @@ AM_CONDITIONAL([VHEAVY_TESTS],[test "x$use_vheavy_tests" = "xyes"])
AM_CONDITIONAL([TESTS_STRESS_OS],[false])
AC_ARG_ENABLE([[select]],
- [
- AS_HELP_STRING([[--enable-select[=ARG]]], [enable 'select()' support (yes, no, auto) [auto]])
+ [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],
@@ -2886,8 +2805,7 @@ AS_VAR_IF([mhd_cv_func_select],["yes"],
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]])
+ [AS_HELP_STRING([[--enable-poll[=ARG]]], [enable 'poll()' support (yes, no, auto) [auto]])
],[],[enable_poll='auto']
)
AS_IF([test "$os_is_native_w32" != "yes"],
@@ -2952,14 +2870,23 @@ AS_VAR_IF([have_poll],["yes"],
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]])
+ [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"],
+ MHD_CHECK_FUNC_RUN([epoll_create],
+ [[
+#include <sys/epoll.h>
+#include <unistd.h>
+ ]],
+ [[
+ int epfd = epoll_create(64);
+ i][f (0 > epfd) return -epfd;
+ (void) close(epfd);
+ return 0;
+ ]],
+ [cacheVar="assuming yes"],
[
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])
@@ -2972,6 +2899,23 @@ AS_IF([test "$enable_epoll" != "no"],
enable_epoll='no'
]
)
+ AS_UNSET([warn_msg])
+ AS_VAR_IF([mhd_cv_works_func_epoll_create],["assuming yes"],
+ [[warn_msg="When cross-compiling it is not possible to check whether 'epoll_create()' really works on the host (final) platform.
+'epoll' is enabled as most probably the host kernel supports it (CONFIG_EPOLL option enabled in case of Linux kernel).
+Use './configure mhd_cv_works_func_epoll_create=yes' to mute this warning."]]
+ )
+ AS_VAR_SET_IF([warn_msg],[AC_MSG_WARN([$warn_msg])
+ AS_IF([test -n "${CONF_FINAL_WARNS}" ],
+ [
+ CONF_FINAL_WARNS="${CONF_FINAL_WARNS}
+
+WARNING: "
+ ]
+ )
+ CONF_FINAL_WARNS="${CONF_FINAL_WARNS}${warn_msg}"
+ ]
+ )
]
)
AM_CONDITIONAL([MHD_USE_EPOLL], [[test "x${enable_epoll}" = "xyes"]])
@@ -3052,7 +2996,8 @@ AC_CACHE_CHECK([size of tv_sec member of struct timeval], [mhd_cv_size_timeval_t
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif /* HAVE_SYS_TYPES_H */
-static struct timeval test_var;
+extern struct timeval test_var; /* Declaration */
+struct timeval test_var = {0, 0}; /* Definition */
]],
[
# The size is used only to exclude additional checks/comparison in code
@@ -3132,14 +3077,12 @@ AC_CHECK_MEMBERS([struct sockaddr.sa_len, struct sockaddr_storage.ss_len,
#endif
])
-MHD_CHECK_LINK_RUN([[f][or working getsockname()]],[[mhd_cv_getsockname_usable]],
- [[mhd_cv_getsockname_usable='assuming yes']],
- [
- AC_LANG_SOURCE(
- [[
+MHD_CHECK_FUNC_RUN([getsockname],
+ [[
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#include <string.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
@@ -3162,12 +3105,8 @@ MHD_CHECK_LINK_RUN([[f][or working getsockname()]],[[mhd_cv_getsockname_usable]]
#include <arpa/inet.h>
#endif
-
-static void zr_mem(void *ptr, socklen_t size)
-{ char *mem = ptr; while(size--) {mem[0] = 0; mem++;} }
-
-int main(void)
-{
+ ]],
+ [[
const socklen_t c_addr_size = (socklen_t)sizeof(struct sockaddr_in);
struct sockaddr_in sa;
socklen_t addr_size;
@@ -3187,7 +3126,7 @@ int main(void)
sckt = socket (PF_INET, SOCK_STREAM, 0);
if (invld_sckt != sckt)
{
- zr_mem(&sa, c_addr_size);
+ memset(&sa, 0, c_addr_size);
sa.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
sa.sin_len = c_addr_size;
@@ -3216,10 +3155,8 @@ int main(void)
WSACleanup();
#endif
return ret;
-}
- ]]
- )
- ],
+ ]],
+ [cacheVar='assuming yes'],
[AC_DEFINE([[MHD_USE_GETSOCKNAME]], [[1]], [Define if you have usable `getsockname' function.])]
)
@@ -3456,15 +3393,12 @@ AS_UNSET([[use_itc]])
AS_IF([[test "x$enable_itc" = "xeventfd" || test "x$enable_itc" = "xauto"]],
[
- MHD_CHECK_LINK_RUN([[f][or working eventfd(2)]],[[mhd_cv_eventfd_usable]],[[mhd_cv_eventfd_usable='assuming no']],
- [
- AC_LANG_SOURCE([[
+ MHD_CHECK_FUNC_RUN([eventfd],[[
#include <sys/eventfd.h>
#include <unistd.h>
-
-int main(void)
-{
- unsigned char buf[8];
+ ]],
+ [[
+ static unsigned char buf[8];
int ret;
int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (0 > efd)
@@ -3480,8 +3414,10 @@ int main(void)
}
close(efd);
return ret;
-}
- ]]
+ ]],
+ [
+ AS_VAR_IF([enable_itc],["eventfd"],
+ [cacheVar="assuming yes"],[cacheVar="assuming no"]
)
],
[
@@ -3493,8 +3429,27 @@ int main(void)
AS_VAR_IF([[enable_itc]], [["eventfd"]], [AC_MSG_ERROR([[eventfd(2) is not usable, consider using other type of inter-thread communication]])])
]
)
- AS_VAR_IF([mhd_cv_eventfd_usable],["assuming no"],
- [AC_MSG_WARN([if you have 'eventfd' support enabled on your target system consider overriding test result by "mhd_cv_eventfd_usable=yes" configure parameter])]
+ AS_UNSET([warn_msg])
+ AS_VAR_IF([mhd_cv_works_func_eventfd],["assuming yes"],
+ [[warn_msg="When cross-compiling it is not possible to check whether 'eventfd()' really works on the host (final) platform.
+'eventfd' is enabled as requested by configure parameters.
+Use './configure mhd_cv_works_func_eventfd=yes' to mute this warning."]]
+ )
+ AS_VAR_IF([mhd_cv_works_func_eventfd],["assuming no"],
+ [[warn_msg="When cross-compiling it is not possible to check whether 'eventfd()' really works on the host (final) platform.
+'eventfd()' is disabled. If it is available use './configure mhd_cv_works_func_eventfd=yes' to enable.
+Use './configure mhd_cv_works_func_eventfd=no' to mute this warning."]]
+ )
+ AS_VAR_SET_IF([warn_msg],[AC_MSG_WARN([$warn_msg])
+ AS_IF([test -n "${CONF_FINAL_WARNS}" ],
+ [
+ CONF_FINAL_WARNS="${CONF_FINAL_WARNS}
+
+WARNING: "
+ ]
+ )
+ CONF_FINAL_WARNS="${CONF_FINAL_WARNS}${warn_msg}"
+ ]
)
]
)
@@ -3522,30 +3477,27 @@ AC_INCLUDES_DEFAULT
use_itc='pipe'
enable_itc="$use_itc"
AC_DEFINE([[MHD_ITC_PIPE_]], [[1]], [Define to use pipe for inter-thread communication])
- MHD_CHECK_LINK_RUN([[whether pipe2(2) is usable]],[[mhd_cv_pipe2_usable]],
- [
- # Cross-compiling
- AS_CASE([${host_os}], [kfreebsd*-gnu], [[mhd_cv_pipe2_usable='assuming no']],
- [[mhd_cv_pipe2_usable='assuming yes']])
- ],
- [
- AC_LANG_PROGRAM([
+ MHD_CHECK_FUNC_RUN([pipe2],[
AC_INCLUDES_DEFAULT
+[
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
- ], [[
- int arr[2];
- int res;
- res = pipe2(arr, O_CLOEXEC | O_NONBLOCK);
- if (res != 0) return 33;
- close (arr[0]);
- close (arr[1]);
- ]]
- )
+ ]],[[
+ int arr[2];
+ int res;
+ res = pipe2(arr, O_CLOEXEC | O_NONBLOCK);
+ i][f (res != 0) return 33;
+ close (arr[0]);
+ close (arr[1]);
+ return 0;
+ ]],[
+ # Cross-compiling
+ AS_CASE([${host_os}], [kfreebsd*-gnu], [cacheVar='assuming no'],
+ [cacheVar='assuming yes'])
],
[AC_DEFINE([[HAVE_PIPE2_FUNC]], [[1]], [Define if you have usable pipe2(2) function])]
)
@@ -4047,6 +3999,30 @@ int main(void) {return test_intern_func();}
]
)
+ AC_CACHE_CHECK([whether $CC supports __attribute__((visibility("hidden")))],[mhd_cv_cc_attr_visibility_hidden],
+ [
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+__attribute__((visibility("hidden"))) int
+test_hidden_func(void);
+
+__attribute__((visibility("hidden"))) int
+test_hidden_func(void) {return 0;}
+
+int main(void) {return test_hidden_func();}
+ ]])
+ ],
+ [mhd_cv_cc_attr_visibility_hidden="yes"],[mhd_cv_cc_attr_visibility_hidden="no"]
+ )
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_attr_visibility_hidden],["yes"],
+ [
+ AC_DEFINE([HAVE_ATTR_VISIBILITY_HIDDEN],[1],
+ [Define to '1' if your compiler supports __attribute__((visibility("hidden")))]
+ )
+ ]
+ )
+
CFLAGS="${user_CFLAGS}"
MHD_CHECK_CC_CFLAG([-fvisibility=hidden],[CFLAGS_ac],
[
@@ -4515,11 +4491,9 @@ MHD_CHECK_FUNC([magic_open],
[],
[-lmagic]
)
-AM_CONDITIONAL([MHD_HAVE_LIBMAGIC], [[test "x$mhd_cv_func_magic_open" = "xyes"]])
+AM_CONDITIONAL([MHD_HAVE_LIBMAGIC], [[test "x$mhd_cv_have_func_magic_open" = "xyes"]])
# large file support (> 4 GB)
-AC_SYS_LARGEFILE
-AC_FUNC_FSEEKO
MHD_CHECK_FUNC([lseek64],
[[
#if defined(HAVE_SYS_TYPES_H)
@@ -4792,8 +4766,7 @@ AS_IF([[test "x$found_sendfile" = "xno" && test "x$enable_sendfile" = "xyes"]],
# optional: disable log and HTTP automatic messages
AC_ARG_ENABLE([messages],
- [
- AS_HELP_STRING([--disable-messages],
+ [AS_HELP_STRING([--disable-messages],
[disable log messages and text bodies for ]
[automatic HTTP responses (to reduce the binary size)]
)
@@ -4803,8 +4776,7 @@ AC_ARG_ENABLE([messages],
AC_MSG_CHECKING([[whether to support internal logging functionality and build messages for log]])
AC_ARG_ENABLE([log-messages],
- [
- AS_HELP_STRING([--disable-log-messages],
+ [AS_HELP_STRING([--disable-log-messages],
[disable logger functionality and exclude log mesages from binary]
)
],
@@ -4819,8 +4791,7 @@ AM_CONDITIONAL([HAVE_LOG_FUNCTIONALITY], [test "x$enable_log_messages" != "xno"]
AC_MSG_CHECKING([[whether to build text bodies for automatic HTTP response messages]])
AC_ARG_ENABLE([http-messages],
- [
- AS_HELP_STRING([--disable-http-messages],
+ [AS_HELP_STRING([--disable-http-messages],
[use empty bodies for automatic HTTP responses (less verbose for clients)]
)
],
@@ -4835,17 +4806,17 @@ AM_CONDITIONAL([HAVE_HTTP_AUTO_MESSAGES_BODIES], [test "x$enable_http_messages"
# optional: have postprocessor?
-AC_MSG_CHECKING([[whether to enable postprocessor]])
-AC_ARG_ENABLE([postprocessor],
- [AS_HELP_STRING([--disable-postprocessor],
- [disable MHD PostProcessor functionality])],
- [enable_postprocessor=${enableval}],
- [enable_postprocessor=yes])
-AS_IF([[test "x$enable_postprocessor" != "xno"]],
- [ enable_postprocessor=yes
- AC_DEFINE([HAVE_POSTPROCESSOR],[1],[Define to 1 if libmicrohttpd is compiled with postprocessor support.]) ])
-AM_CONDITIONAL([HAVE_POSTPROCESSOR], [test "x$enable_postprocessor" != "xno"])
-AC_MSG_RESULT([[$enable_postprocessor]])
+AC_MSG_CHECKING([[whether to enable POST parser]])
+AC_ARG_ENABLE([postparser],
+ [AS_HELP_STRING([--disable-postparser],
+ [disable MHD POST parser functionality])],
+ [enable_postparser=${enableval}],
+ [enable_postparser=yes])
+AS_IF([[test "x$enable_postparser" != "xno"]],
+ [ enable_postparser=yes
+ AC_DEFINE([HAVE_POST_PARSER],[1],[Define to 1 if libmicrohttpd is compiled with POST parser support.]) ])
+AM_CONDITIONAL([HAVE_POST_PARSER], [test "x$enable_postparser" != "xno"])
+AC_MSG_RESULT([[$enable_postparser]])
have_gnutls=no
have_gnutls_sni=no
@@ -5343,7 +5314,7 @@ AC_MSG_RESULT([[$enable_httpupgrade]])
# optional: HTTP cookie parsing support. Enabled by default
AC_MSG_CHECKING([[whether to support HTTP cookie parsing]])
AC_ARG_ENABLE([[cookie]],
- [AS_HELP_STRING([[--disable-cookie]], [disable HTTP cookie parsing support])],
+ [AS_HELP_STRING([[--disable-cookie]], [disable HTTP cookie parser support])],
[AS_VAR_IF([[enable_cookie]],[["no"]],[],[[enable_cookie='yes']])],
[[enable_cookie='yes']])
AS_VAR_IF([[enable_cookie]],[["yes"]],
@@ -5952,7 +5923,7 @@ AS_VAR_IF([enable_tools],["yes"],
)
]
)
- AS_VAR_IF([mhd_cv_func_CPU_COUNT_S],["yes"],
+ AS_VAR_IF([mhd_cv_have_func_CPU_COUNT_S],["yes"],
[
AC_CACHE_CHECK([whether CPU_COUNT_S() expects max CPU number as 'size' parameter],[mhd_cv_func_cpu_count_s_cpus],
[
@@ -7269,8 +7240,8 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary:
HTTPS support: ${MSG_HTTPS}
Logging support: ${enable_log_messages}
Verbose auto replies: ${enable_http_messages}
- Cookie parsing: ${enable_cookie}
- Postproc: ${enable_postprocessor}
+ Cookie parser: ${enable_cookie}
+ POST parser: ${enable_postparser}
Basic auth.: ${enable_bauth}
Digest auth.: ${enable_dauth}
Digest auth. defaults: ${dauth_defs_MSG}
@@ -7309,6 +7280,7 @@ AS_IF([test "x$enable_bauth" != "xyes" || \
test "x$enable_httpupgrade" != "xyes" || \
test "x$enable_cookie" != "xyes" || \
test "x$enable_httpupgrade" != "xyes" || \
- test "x$enable_postprocessor" != "xyes"],
+ test "x$enable_postparser" != "xyes"],
[AC_MSG_WARN([This will be a custom build with missing symbols. Do NOT use this build in a distribution. Building with these kinds of configure options is only for custom builds for embedded systems.])]
)
+AS_IF([test -n "${CONF_FINAL_WARNS}"],[AC_MSG_WARN([${CONF_FINAL_WARNS}])])
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
@@ -29,6 +29,6 @@ noinst_PROGRAMS +=
endif
endif
-if HAVE_POSTPROCESSOR
+if HAVE_POST_PARSER
noinst_PROGRAMS +=
endif
diff --git a/m4/mhd_bool.m4 b/m4/mhd_bool.m4
@@ -0,0 +1,135 @@
+# SYNOPSIS
+#
+# MHD_BOOL
+#
+# DESCRIPTION
+#
+# This macro checks whether the boolean keywords "bool", "true", "false"
+# are supported by compiler. If they are not supported, suitable replacement
+# macros are defined.
+#
+# Example usage:
+#
+# MHD_BOOL
+#
+# The cache variables are used in the checks so if any test will not work
+# correctly on some platform, user may simply fix it by giving cache
+# variable in configure parameters, for example:
+#
+# ./configure mhd_cv_bool_native=no
+#
+# This simplify building from source on exotic platforms as patching
+# of configure.ac is not required to change results of tests.
+#
+# LICENSE
+#
+# Copyright (c) 2024 Karlson2k (Evgeny Grin) <k2k@narod.ru>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 2
+
+AC_DEFUN([MHD_BOOL],[dnl
+AC_PREREQ([2.64])dnl for AS_VAR_IF
+m4_newline([[# Expansion of macro $0 starts here]])
+AC_LANG_ASSERT([C])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AH_TEMPLATE([[HAVE_STDBOOL_H]], [Define to 1 i][f you have the <stdbool.h> header file and <stdbool.h> is required to use 'bool' type.])dnl
+AH_TEMPLATE([[HAVE_BUILTIN_TYPE_BOOL]], [Define to 1 i][f you have the boolean type that takes only 'true' and 'false'.])dnl
+AH_TEMPLATE([[bool]], [Define to the name of the type which is used as a replacemnt f][or boolean type.])dnl
+[mhd_bool_test_code='
+static bool get_false(void)
+{
+ static bool test_arr[sizeof(bool)*2 - 1] = {false};
+ return test_arr[0];
+}
+
+int main(void)
+{
+ static bool var1 = true;
+ static bool var2 = false;
+ static int int_arr[2 - 3 * ((int) (false))] = {(int) true, (int) false};
+ static bool bool_arr[2 - 3 * ((int) (!true))] = {false, true};
+ i][f(!var1 || var2 || !int_arr[0] || int_arr[1] || bool_arr[0] ||
+ !bool_arr[1] || get_false() || 0 == sizeof(bool) || false || !true)
+ return 5;
+ return 0;
+}
+']
+AC_CACHE_CHECK([[whether "bool", "true" and "false" work natively]],[[mhd_cv_bool_native]],
+[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[${mhd_bool_test_code}]])],[mhd_cv_bool_native="yes"],
+[mhd_cv_bool_native="no"])dnl AC_COMPILE_IFELSE
+])
+AS_VAR_IF([mhd_cv_bool_native],["yes"],[
+AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]],[1])],[dnl mhd_cv_bool_native "yes"
+AC_CACHE_CHECK([[whether <stdbool.h> is present and defines proper "bool", "true" and "false"]],[[mhd_cv_bool_stdbool]],
+[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#include <stdbool.h>
+
+${mhd_bool_test_code}
+]])],[mhd_cv_bool_stdbool="yes"],[mhd_cv_bool_stdbool="no"])dnl AC_COMPILE_IFELSE
+])
+dnl end of AC_CACHE_CHECK
+AS_VAR_IF([mhd_cv_bool_stdbool],["yes"],[
+AC_DEFINE([[HAVE_STDBOOL_H]],[1])
+AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]],[1])],[dnl mhd_cv_bool_stdbool "yes"
+# Need 'bool', 'false' and 'true'.
+AC_CHECK_TYPE([bool],[AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]], [[1]])],[dnl AC_CHECK_TYPE bool
+AC_CHECK_TYPE([_Bool],[AC_DEFINE([[HAVE_BUILTIN_TYPE_BOOL]], [[1]])
+AC_DEFINE([[bool]], [[_Bool]])],[AC_DEFINE([[bool]], [[int]])
+],[[]])dnl AC_CHECK_TYPE _Bool
+],[[]])dnl AC_CHECK_TYPE bool
+# Have 'bool'. Need 'false' and 'true'.
+AC_CACHE_CHECK([[whether "false" keyword available and works]],[[mhd_cv_keyword_false_works]],
+[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+int main(void)
+{
+ static bool var1 = false || false;
+ static bool var2 = false;
+ static bool bool_arr[2 - 3 * ((int) (false))] = {false, !false};
+ i][f(var1 || var2 || bool_arr[0] || !bool_arr[1] || false)
+ return 5;
+ return 0;
+}
+]])], [[mhd_cv_keyword_false_works='yes']], [[mhd_cv_keyword_false_works='no']])])
+dnl end of AC_CACHE_CHECK
+AS_VAR_IF([[mhd_cv_keyword_false_works]], [["no"]],[dnl
+AC_DEFINE([[false]],[[(0)]], [Define to value interpreted by compiler as boolean "false", i][f "false" is not a keyword and not defined by headers.])])
+dnl AS_VAR_IF mhd_cv_keyword_false_works
+# Have 'bool' and 'false'. Need 'true'.
+AC_CACHE_CHECK([[whether "true" keyword available and works]],[[mhd_cv_keyword_true_works]],
+[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+int main(void)
+{
+ static bool var1 = true && true;
+ static bool var2 = true;
+ static bool bool_arr[2 - 3 * ((int) (!true))] = {true, !true};
+ i][f(!var1 || !var2 || !bool_arr[0] || bool_arr[1] || !true)
+ return 5;
+ return 0;
+}
+]])], [[mhd_cv_keyword_true_works='yes']], [[mhd_cv_keyword_true_works='no']])])
+dnl end of AC_CACHE_CHECK
+AS_VAR_IF([[mhd_cv_keyword_true_works]], [["no"]],[dnl
+AC_DEFINE([[true]],[[(!false)]], [Define to value interpreted by compiler as boolean "true", i][f "true" is not a keyword and not defined by headers.])])
+dnl AS_VAR_IF mhd_cv_keyword_false_works
+# Have 'bool', 'false' and 'true'.
+AC_CACHE_CHECK([[whether the defined "bool", "true" and "false" can work together]],[[mhd_cv_bool_true_false_work]],
+[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[${mhd_bool_test_code}]])],[mhd_cv_bool_true_false_work="yes"],
+[mhd_cv_bool_true_false_work="no"])dnl AC_COMPILE_IFELSE
+])
+dnl end of AC_CACHE_CHECK
+AS_VAR_IF([[mhd_cv_bool_true_false_work]], [["yes"]],[[:]],[dnl
+AC_MSG_FAILURE([cannot find suitable replacements for "bool", "true" and "false"])
+])
+dnl end of AS_VAR_IF mhd_cv_bool_true_false_work "yes"
+])
+dnl end of AS_VAR_IF mhd_cv_bool_stdbool "yes"
+])
+dnl end of AS_VAR_IF mhd_cv_bool_native "yes"
+AS_UNSET([mhd_bool_test_code])
+m4_newline([[# Expansion of macro $0 ends here]])
+])dnl AC_DEFUN MHD_BOOL
diff --git a/m4/mhd_check_func.m4 b/m4/mhd_check_func.m4
@@ -8,16 +8,17 @@
# DESCRIPTION
#
# This macro checks for presence of specific function by including
-# specified headers and compiling and linking CHECK_CODE.
+# specified headers, checking for declaration of the function and then
+# compiling and linking CHECK_CODE.
# This checks both declaration and presence in library.
# If function is available then macro HAVE_function_name (the name
-# of the function convetedd to all uppercase characters) is defined
+# of the function conveted to all uppercase characters) is defined
# automatically.
# Unlike AC_CHECK_FUNCS macro, this macro do not produce false
# negative result if function is declared with specific calling
# conventions like __stdcall' or attribute like
# __attribute__((__dllimport__)) and linker failing to build test
-# program due to the different calling conventions.
+# program due to the different calling conventions.
# By using definition from provided headers, this macro ensures that
# correct calling convention is used for detection.
#
@@ -32,10 +33,10 @@
# correctly on some platform, user may simply fix it by giving cache
# variable in configure parameters, for example:
#
-# ./configure mhd_cv_func_memmem_have=no
+# ./configure mhd_cv_have_func_memmem=no
#
# This simplifies building from source on exotic platforms as patching
-# of configure.ac is not required to change results of tests.
+# of configure.ac is not required to change the results of the tests.
#
# LICENSE
#
@@ -46,7 +47,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 5
+#serial 6
AC_DEFUN([MHD_CHECK_FUNC],[dnl
AC_PREREQ([2.64])dnl for AS_VAR_IF, m4_ifblank, m4_ifnblank
@@ -59,18 +60,16 @@ m4_bmatch(m4_normalize([$1]), [\s],dnl
m4_if(m4_index([$3], m4_normalize(m4_translit([$1],[()],[ ]))), [-1], dnl
[m4_fatal([CHECK_CODE parameter (third macro argument) does not contain ']m4_normalize([$1])[' token])])dnl
AS_VAR_PUSHDEF([decl_cv_Var],[ac_cv_have_decl_]m4_bpatsubst(_mhd_norm_expd(m4_translit([$1],[()],[ ])),[[^a-zA-Z0-9]],[_]))dnl
-AS_VAR_PUSHDEF([cv_Var],[mhd_cv_func_]m4_bpatsubst(_mhd_norm_expd(m4_translit([$1],[()],[ ])),[[^a-zA-Z0-9]],[_]))dnl
-AS_VAR_SET_IF([cv_Var],[],[AC_CHECK_DECL(_mhd_norm_expd([$1]),[],[],[$2])])
-AC_CACHE_CHECK([for function $1], [cv_Var],dnl
+AS_VAR_PUSHDEF([cv_Var],[mhd_cv_have_func_]m4_bpatsubst(_mhd_norm_expd(m4_translit([$1],[()],[ ])),[[^a-zA-Z0-9]],[_]))dnl
+AS_VAR_SET_IF([cv_Var],[],[AC_CHECK_DECL(_mhd_norm_expd([$1]),[],[cv_Var="no"],[$2])])
+AC_CACHE_CHECK([f][or function $1], [cv_Var],dnl
[dnl
- AS_VAR_IF([decl_cv_Var],["yes"],dnl
- [dnl
- m4_ifnblank([$6],[dnl
- mhd_check_func_SAVE_LIBS="$LIBS"
- LIBS="_mhd_norm_expd([$6]) $LIBS"
- ])dnl
- AC_LINK_IFELSE(
- [AC_LANG_SOURCE([
+ m4_ifnblank([$6],[dnl
+ mhd_check_func_SAVE_LIBS="$LIBS"
+ LIBS="_mhd_norm_expd([$6]) $LIBS"
+ ])dnl
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([
m4_default_nblank([$2],[AC_INCLUDES_DEFAULT])
[int main(void)
@@ -80,18 +79,16 @@ m4_default_nblank([$2],[AC_INCLUDES_DEFAULT])
return 0;
}
- ]])
- ],
- [AS_VAR_SET([cv_Var],["yes"])],
- [AS_VAR_SET([cv_Var],["no"])]dnl
- )
- m4_ifnblank([$6],[dnl
- LIBS="${mhd_check_func_SAVE_LIBS}"
- AS_UNSET([mhd_check_func_SAVE_LIBS])
- ])dnl
- ],[AS_VAR_SET([cv_Var],["no"])]dnl
- )dnl
- ]dnl
+ ]])
+ ],
+ [AS_VAR_SET([cv_Var],["yes"])],
+ [AS_VAR_SET([cv_Var],["no"])]dnl
+ )
+ m4_ifnblank([$6],[dnl
+ LIBS="${mhd_check_func_SAVE_LIBS}"
+ AS_UNSET([mhd_check_func_SAVE_LIBS])
+ ])dnl
+ ],[AS_VAR_SET([cv_Var],["no"])]dnl
)
AS_VAR_IF([cv_Var], ["yes"],
[AC_DEFINE([[HAVE_]]m4_bpatsubst(m4_toupper(_mhd_norm_expd(m4_translit([$1],[()],[ ]))),[[^A-Z0-9]],[_]),
diff --git a/m4/mhd_check_func_run.m4 b/m4/mhd_check_func_run.m4
@@ -0,0 +1,120 @@
+# SYNOPSIS
+#
+# MHD_CHECK_FUNC_RUN(FUNCTION_NAME,
+# [INCLUDES=AC_INCLUDES_DEFAULT], CHECK_CODE
+# COMMAND_IF_CROSS_COMPILING,
+# [ACTION_IF_AVAILABLE], [ACTION_IF_NOT_AVAILABLE],
+# [ADDITIONAL_LIBS])
+#
+# DESCRIPTION
+#
+# This macro checks for presence of specific function by including
+# specified headers, checking for declaration of the function and then
+# compiling and linking CHECK_CODE. If all previous steps succeed,
+# the binary is executed.
+# The CHECK_CODE is full body of the "main" function and must include
+# the final "return" keyword.
+# This checks the declaration, the presence in library and the functionality.
+# If function is available then macro HAVE_function_name (the name
+# of the function conveted to all uppercase characters) is defined
+# automatically.
+# By using definition from provided headers, this macro ensures that
+# correct calling convention is used for detection.
+# The compilation and linking is tried even if cross-compiling. If they
+# failed then the result is "no".
+# The COMMAND_IF_CROSS_COMPILING commands are executed if and only if
+# the compilation and linking succeed. These commands must set 'cacheVar'
+# variable to "yes", "no", "assuming yes" or "assuming no".
+# ACTION_IF_SUCCEED is executed if result is "yes" or "assuming yes".
+# ACTION_IF_FAILED is executed if result is "no" or "assuming no".
+#
+# Example usage:
+#
+# MHD_CHECK_FUNC_RUN([memmem],
+# [[#include <string.h>]],
+# [[const void *ptr = memmem("aa", 2, "a", 1);
+# if (((void*)0) == ptr) return 1;
+# return 0;]],
+# [cacheVar="assuming yes"],
+# [var_use_memmem='yes'], [var_use_memmem='no'])
+#
+# The cache variable used in check so if any test will not work
+# correctly on some platform, user may simply fix it by giving cache
+# variable in configure parameters, for example:
+#
+# ./configure mhd_cv_works_func_memmem=no
+#
+# This simplifies building from source on exotic platforms as patching
+# of configure.ac is not required to change the results of the tests.
+#
+# LICENSE
+#
+# Copyright (c) 2024 Karlson2k (Evgeny Grin) <k2k@narod.ru>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 1
+
+dnl $1: FUNCTION_NAME
+dnl $2: INCLUDES
+dnl $3: CHECK_CODE
+dnl $4: COMMAND_IF_CROSS_COMPILING
+dnl $5: ACTION_IF_AVAILABLE
+dnl $6: ACTION_IF_NOT_AVAILABLE
+dnl $7: ADDITIONAL_LIBS
+AC_DEFUN([MHD_CHECK_FUNC_RUN],[dnl
+AC_PREREQ([2.64])dnl for AS_VAR_SET_IF, m4_ifblank, m4_ifnblank, m4_default_nblank
+m4_newline([[# Expansion of $0 macro starts here]])
+AC_LANG_ASSERT([C])dnl
+m4_ifblank(m4_translit([$1],[()],[ ]), [m4_fatal([$0: First macro argument ("FUNCTION_NAME") must not be empty])])dnl
+m4_ifblank([$3], [m4_fatal([$0: Third macro argument ("CHECK_CODE") must not be empty])])dnl
+m4_bmatch(m4_normalize([$1]), [\s],dnl
+ [m4_fatal([$0: First macro argument ("FUNCTION_NAME") must not contain whitespaces])])dnl
+m4_if(m4_index([$3], m4_normalize(m4_translit([$1],[()],[ ]))), [-1], dnl
+ [m4_fatal([$0: "CHECK_CODE" macro parameter (third argument) does not contain ']m4_normalize([$1])[' token])])dnl
+m4_if(m4_index([$3],return), [-1], dnl
+ [m4_fatal([$0: "CHECK_CODE" macro parameter (third argument) does not contain 'return' token])])dnl
+m4_bmatch(_mhd_norm_expd([$4]),[\<]cacheVar[\>],[],dnl
+[m4_fatal([$0: The fourth macro argument ("COMMAND_IF_CROSS_COMPILING") must assign ]dnl
+[a value to the cache variable 'cacheVar'. The variable must be not overquoted.])])dnl
+AS_VAR_PUSHDEF([decl_cv_Var],[ac_cv_have_decl_]m4_bpatsubst(_mhd_norm_expd(m4_translit([$1],[()],[ ])),[[^a-zA-Z0-9]],[_]))dnl
+AS_VAR_PUSHDEF([cacheVar],[mhd_cv_works_func_]m4_bpatsubst(_mhd_norm_expd(m4_translit([$1],[()],[ ])),[[^a-zA-Z0-9]],[_]))dnl
+AS_VAR_SET_IF([cacheVar],[],[AC_CHECK_DECL(_mhd_norm_expd([$1]),[],[cacheVar="no"],[$2])])
+AC_CACHE_CHECK([whether '$1' available and works],[cacheVar],dnl
+[
+AC_LANG_CONFTEST([AC_LANG_SOURCE([
+m4_default_nblank([$2],[AC_INCLUDES_DEFAULT])
+
+[int main(void)
+{
+ ]$3[
+}
+]])])
+m4_ifnblank([$7],[dnl
+mhd_check_func_SAVE_LIBS="$LIBS"
+LIBS="_mhd_norm_expd([$7]) $LIBS"
+])dnl m4_ifnblank
+AS_VAR_IF([cross_compiling],["yes"],
+[AC_LINK_IFELSE([],[
+$4
+],[cacheVar='no'])dnl AC_LINK_IFELSE
+],dnl
+[AC_RUN_IFELSE([],[cacheVar='yes'],[cacheVar='no'],[[# Dummy placeholder]])
+])
+rm -f conftest.$ac_ext
+m4_ifnblank([$7],[dnl
+ LIBS="${mhd_check_func_SAVE_LIBS}"
+ AS_UNSET([mhd_check_func_SAVE_LIBS])
+])dnl m4_ifnblank
+])dnl AC_CACHE_CHECK
+m4_ifnblank([$5],[
+AS_IF([test "x$cacheVar" = "xyes" || test "x$cacheVar" = "xassuming yes"],[$5])dnl AS_IF
+])dnl m4_ifnblank
+m4_ifnblank([$6],[
+AS_IF([test "x$cacheVar" = "xno" || test "x$cacheVar" = "xassuming no"],[$6])dnl AS_IF
+])dnl m4_ifnblank
+m4_newline([[# Expansion of $0 macro ends here]])
+])dnl AC_DEFUN MHD_CHECK_FUNC_RUN
diff --git a/m4/mhd_check_link_run.m4 b/m4/mhd_check_link_run.m4
@@ -17,10 +17,10 @@
# Example usage:
#
# MHD_CHECK_LINK_RUN([for valid snprintf()], [mhd_cv_snprintf_valid],
+# [mhd_cv_snprintf_valid='assuming no'],
# [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
# [if (4 != snprintf(NULL, 0, "test"))
-# return 2;])],
-# [mhd_cv_snprintf_valid='assuming no'])
+# return 2;])])
#
#
# LICENSE
@@ -32,7 +32,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 2
+#serial 3
AC_DEFUN([MHD_CHECK_LINK_RUN],[dnl
m4_ifblank([$1],[m4_fatal([$0: The first macro argument ("MESSAGE") must not be empty])])dnl
diff --git a/m4/mhd_sys_extentions.m4 b/m4/mhd_sys_extentions.m4
@@ -40,18 +40,23 @@
#
# LICENSE
#
-# Copyright (c) 2016, 2017 Karlson2k (Evgeny Grin) <k2k@narod.ru>
+# Copyright (c) 2016, 2017, 2024 Karlson2k (Evgeny Grin) <k2k@narod.ru>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 2
+#serial 5
AC_DEFUN([MHD_SYS_EXT],[dnl
AC_PREREQ([2.64])dnl for AS_VAR_IF, AS_VAR_SET_IF, m4_ifnblank
- AC_LANG_PUSH([C])dnl Use C language for simplicity
+ AC_LANG_ASSERT([C])dnl Only C is supported
+m4_ifdef([_AC_UNDECLARED_BUILTIN],
+[AC_DEFUN_ONCE([_AC_UNDECLARED_BUILTIN_]_AC_LANG_ABBREV,
+ [_AC_UNDECLARED_BUILTIN])]dnl
+[AC_REQUIRE([_AC_UNDECLARED_BUILTIN_]_AC_LANG_ABBREV)]dnl
+)dnl m4_ifdef _AC_UNDECLARED_BUILTIN
mhd_mse_sys_ext_defines=""
mhd_mse_sys_ext_flags=""
@@ -277,7 +282,9 @@ int main()
}
"
AC_CACHE_CHECK([[for useful system-specific features]],
- [[mhd_cv_headers_useful_features_present]], [dnl
+ [[mhd_cv_headers_useful_features_present]], [
+ mhd_SYS_EXT_SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $ac_[]_AC_LANG_ABBREV[]_undeclared_builtin_options"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
${mhd_mse_sys_ext_defines}
${mhd_mse_sys_features_src}
@@ -302,7 +309,9 @@ ${mhd_mse_sys_features_src}
)dnl
])dnl
]
- )dnl
+ )
+ CFLAGS="$mhd_SYS_EXT_SAVE_CFLAGS"
+ AS_UNSET([mhd_SYS_EXT_SAVE_CFLAGS])
]
)
@@ -366,7 +375,6 @@ ${mhd_mse_sys_features_src}
AS_UNSET([[mhd_mse_result]])
AS_UNSET([[mhd_mse_xopen_flags]])
AS_UNSET([[mhd_mse_sys_ext_flags]])
- AC_LANG_POP([C])
])
@@ -761,6 +769,8 @@ $2
dnl Check whether features test works without _XOPEN_SOURCE and
dnl with disabled extensions (undefined most of
dnl predefined macros for specific requested mode).
+ mhd_SYS_EXT_SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $ac_[]_AC_LANG_ABBREV[]_undeclared_builtin_options"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
_MHD_UNDEF_ALL_EXT
$src_Var
@@ -947,6 +957,8 @@ $src_Var
)
]
)
+ CFLAGS="$mhd_SYS_EXT_SAVE_CFLAGS"
+ AS_UNSET([mhd_SYS_EXT_SAVE_CFLAGS])
AS_UNSET([defs_Var])
AS_UNSET([src_Var])
AS_VAR_POPDEF([defs_Var])dnl
@@ -991,18 +1003,6 @@ AC_DEFUN([MHD_CHECK_HEADERS_PRESENCE], [dnl
#
-# MHD_CHECK_HEADERS_PRESENCE_COMPACT(oneheader.h otherheader.h ...)
-#
-# Same as MHD_CHECK_HEADERS_PRESENCE, but a bit slower and produce more compact 'configure'.
-
-AC_DEFUN([MHD_CHECK_HEADERS_PRESENCE_COMPACT], [dnl
- for mhd_chk_Header in $1 ; do
- MHD_CHECK_HEADER_PRESENCE([[${mhd_chk_Header}]])
- done
-])
-
-
-#
# MHD_CHECK_BASIC_HEADERS_PRESENCE
#
# Check basic headers for presence.
diff --git a/src/incl_priv/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h
@@ -85,12 +85,22 @@
# endif /* ! BUILDING_MHD_LIB */
#endif /* ! MHD_EXTERN_ */
+#ifdef HAVE_ATTR_VISIBILITY_HIDDEN
+/* To be used with internal non-static functions, that can be potentially used
+ * externally via function pointers */
+# define MHD_VISIBILITY_HIDDEN __attribute__((visibility ("hidden")))
+#else
+/* To be used with internal non-static functions, that can be potentially used
+ * externally via function pointers */
+# define MHD_VISIBILITY_HIDDEN /* empty */
+#endif
+
#ifdef HAVE_ATTR_VISIBILITY_INTERNAL
/* To be used with internal non-static functions */
# define MHD_VISIBILITY_INTERNAL __attribute__((visibility ("internal")))
#else
/* To be used with internal non-static functions */
-# define MHD_VISIBILITY_INTERNAL /* empty */
+# define MHD_VISIBILITY_INTERNAL MHD_VISIBILITY_HIDDEN
#endif
/* To be used with internal non-static functions */
diff --git a/src/include/.gitignore b/src/include/.gitignore
@@ -1 +1,5 @@
+microhttpd2_inline_daemon_documentation.h.in
+microhttpd2_inline_response_documentation.h.in
+d_options.json
options-generator
+r_options.json
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
@@ -1,21 +1,42 @@
# 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 $@
+MHD2DIR = $(srcdir)/../mhd2
+
+DAEMON_GEN_FILES = \
+ $(srcdir)/microhttpd2_inline_daemon_documentation.h.in \
+ $(srcdir)/microhttpd2_generated_daemon_options.h \
+ $(MHD2DIR)/daemon_options.h \
+ $(MHD2DIR)/daemon_set_options.c
+
+RESPONSE_GEN_FILES = \
+ $(srcdir)/microhttpd2_inline_response_documentation.h.in \
+ $(srcdir)/microhttpd2_generated_response_options.h \
+ $(MHD2DIR)/response_options.h \
+ $(MHD2DIR)/response_set_options.c
+
+update-daemon-gen-files: $(DAEMON_GEN_FILES)
+
+$(DAEMON_GEN_FILES): d_options.rec options-generator.c
+ -for f in $(DAEMON_GEN_FILES); do if test -f $$f; then chmod +w $$f; else :; fi; done
+ $(MAKE) $(AM_MAKEFLAGS) options-generator$(EXEEXT)
+ ./options-generator$(EXEEXT) daemon > $(srcdir)/microhttpd2_generated_daemon_options.h
+ -chmod -w $(DAEMON_GEN_FILES)
+
+update-response-gen-files: $(RESPONSE_GEN_FILES)
+
+$(RESPONSE_GEN_FILES): r_options.rec options-generator.c
+ -for f in $(RESPONSE_GEN_FILES); do if test -f $$f; then chmod +w $$f && rm -f $$f; else :; fi; done
+ $(MAKE) $(AM_MAKEFLAGS) options-generator$(EXEEXT)
+ ./options-generator$(EXEEXT) response > $(srcdir)/microhttpd2_generated_response_options.h
+ -chmod -w $(RESPONSE_GEN_FILES)
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 $@
+ -if test -f $@; then chmod +w $@; rm -f $@; fi
cat $^ >$@
- chmod -w $@
+ -chmod -w $@
-noinst_PROGRAMS = \
+EXTRA_PROGRAMS = \
options-generator
options_generator_SOURCES = \
@@ -23,21 +44,16 @@ options_generator_SOURCES = \
mhd2includedir = $(includedir)/mhd2
-mhd2include_HEADERS = \
+include_HEADERS = \
microhttpd2.h \
microhttpd2_generated_daemon_options.h \
microhttpd2_generated_response_options.h \
microhttpd2_portability.h
EXTRA_DIST = \
- d_options.tmpl \
- d_options.rec \
- microhttpd2_generated_daemon_options.h \
- microhttpd2_generated_response_options.h \
- microhttpd2_portability.h \
- microhttpd2.h
+ $(DAEMON_GEN_FILES) d_options.rec \
+ $(RESPONSE_GEN_FILES) r_options.rec
+
+.NOTPARALLEL:
-.NOTPARALLEL: \
- microhttpd2_inline_daemon_documentation.h.in microhttpd2_generated_daemon_options.h \
- microhttpd2_inline_response_documentation.h.in microhttpd2_generated_response_options.h \
- microhttpd2.h
-\ No newline at end of file
+.PHONY: update-daemon-gen-files update-response-gen-files
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
@@ -1,21 +1,21 @@
/*
- This file is part of libmicrohttpd
- Copyright (C) 2006-2024 Christian Grothoff, Karlson2k (Evgeny Grin)
- (and other contributing authors)
-
- 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
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2006-2024 Christian Grothoff, Karlson2k (Evgeny Grin)
+ (and other contributing authors)
+
+ 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
*/
/*
@@ -147,6 +147,8 @@
*/
#define MHD_VERSION 0x01990001
+#ifndef MHD_BOOL_DEFINED
+
/**
* Representation of 'bool' in the public API as stdbool.h may not
* always be available and presence of 'bool' keyword may depend on
@@ -171,6 +173,10 @@ enum MHD_Bool
MHD_YES = 1
};
+
+#define MHD_BOOL_DEFINED 1
+#endif /* ! MHD_BOOL_DEFINED */
+
#ifndef MHD_STRINGS_DEFINED
@@ -574,7 +580,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Transfer encoding in request is unsupported or invalid.
*/
- MHD_SC_CHUNKED_ENCODING_UNSUPPORTED = 40067
+ MHD_SC_TRANSFER_ENCODING_UNSUPPORTED = 40067
+ ,
+ /**
+ * "Expect:" value in request is unsupported or invalid.
+ */
+ MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED = 40068
,
/**
* The given uploaded, chunked-encoded body was malformed.
@@ -639,9 +650,52 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_REQ_COOKIE_INVALID = 40163
,
/**
+ * The POST data parsed successfully, but has missing or incorrect
+ * termination.
+ * The last parsed field may have incorrect data.
+ */
+ MHD_SC_REQ_POST_PARSE_OK_BAD_TERMINATION = 40202
+ ,
+ /**
+ * Parsing of the POST data is incomplete because client used incorrect
+ * format of POST encoding.
+ * Some POST data is available or has been provided via callback.
+ */
+ MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT = 40203
+ ,
+ /**
+ * The request does not have "Content-Type:" header and POST data cannot
+ * be parsed
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE = 40280
+ ,
+ /**
+ * The request has unknown POST encoding specified by "Content-Type:" header
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE = 40281
+ ,
+ /**
+ * The request has "Content-Type: multipart/form-data" header without
+ * "boundary" parameter
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY = 40282
+ ,
+ /**
+ * The request has "Content-Type: multipart/form-data" header with misformed
+ * data
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED = 40283
+ ,
+ /**
+ * The POST data cannot be parsed because client used incorrect format
+ * of POST encoding.
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT = 40290
+ ,
+ /**
* The request cannot be processed. Sending error reply.
*/
- MHD_SC_REQ_PROCCESSING_ERR_REPLY = 40200
+ MHD_SC_REQ_PROCCESSING_ERR_REPLY = 41000
,
/* 50000-level errors are because of an error internal
@@ -1104,6 +1158,26 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
,
/**
+ * The request POST data cannot be parsed because stream has not enough
+ * pool memory free.
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM = 50260
+ ,
+ /**
+ * The POST data cannot be parsed completely because no "large shared buffer"
+ * space is available.
+ * Some POST data may be parsed.
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM = 50261
+ ,
+ /**
+ * The application set POST encoding to "multipart/form-data", but the request
+ * has no "Content-Type: multipart/form-data" header which is required
+ * to find "boundary" used in this encoding
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART = 50284
+ ,
+ /**
* The feature is not supported by this MHD build (either
* disabled by configure parameters or build platform
* did not support it, because headers are missing or
@@ -1577,7 +1651,6 @@ MHD_FN_CONST_;
* See also: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2
* @{
*/
-
enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_HTTP_PostEncoding
{
/**
@@ -3341,35 +3414,43 @@ struct MHD_EarlyUriCbData
struct MHD_Request *request;
/**
- * Pointer to the application context for the request.
- * Modifiable. Initially to NULL.
+ * The full URI ("request target") from the HTTP request, including URI
+ * parameters (the part after '?')
*/
- void *request_app_context;
+ struct MHD_String full_uri;
+
+ /**
+ * The request HTTP method
+ */
+ enum MHD_HTTP_Method method;
};
/**
* Function called by MHD to allow the application to log the @a full_uri
* of the new request.
- * If this callback is set then it is the first application function called
- * for the new request.
* This is the only moment when unmodified URI is provided.
* After this callback MHD parses the URI and modifies it by extracting
* GET parameters in-place.
- * If #MHD_RequestTerminationCallback is set then it is guaranteed that
- * #MHD_RequestTerminationCallback is called for the same request. Application
+ *
+ * If this callback is set then it is the first application function called
+ * for the new request.
+ *
+ * If #MHD_RequestEndedCallback is also set then it is guaranteed that
+ * #MHD_RequestEndedCallback is called for the same request. Application
* may allocate request specific data in this callback and de-allocate
- * the data in #MHD_RequestTerminationCallback.
+ * the data in #MHD_RequestEndedCallback.
*
* @param cls client-defined closure
- * @param full_uri the full URI ("request target") from the HTTP request
- * including parameters (the part after '?')
- * @param[in,out] req_data the request data
+ * @param req_data the request data
+ * @param request_app_context_ptr the pointer to variable that can be set to
+ * the application context for the request;
+ * initially the variable set to NULL
*/
typedef void
-(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_INOUT_ (3)
+(MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (3)
*MHD_EarlyUriLogCallback)(void *cls,
- const struct MHD_String *full_uri,
- struct MHD_EarlyUriCbData *req_data);
+ const struct MHD_EarlyUriCbData *req_data,
+ void **request_app_context_ptr);
/**
@@ -3531,31 +3612,31 @@ typedef void
/**
- * The `enum MHD_RequestTerminationCode` specifies reasons
- * why a request has been terminated (or completed).
+ * The `enum MHD_RequestEndedCode` specifies reasons
+ * why a request has been ended.
* @ingroup request
*/
-enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestTerminationCode
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
{
/**
* The response was successfully sent.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_COMPLETED_OK = 0
+ MHD_REQUEST_ENDED_COMPLETED_OK = 0
,
/**
* No activity on the connection for the number of seconds specified using
* #MHD_C_OPTION_TIMEOUT().
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_TIMEOUT_REACHED = 10
+ MHD_REQUEST_ENDED_TIMEOUT_REACHED = 10
,
/**
* The connection was broken or TLS protocol error.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_CONNECTION_ERROR = 20
+ MHD_REQUEST_ENDED_CONNECTION_ERROR = 20
,
/**
* The client terminated the connection by closing the socket either
@@ -3563,44 +3644,44 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestTerminationCode
* request.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_CLIENT_ABORT = 30
+ MHD_REQUEST_ENDED_CLIENT_ABORT = 30
,
/**
* The request is not valid according to
* HTTP specifications.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR = 31
+ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR = 31
,
/**
* The application aborted request without response.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_BY_APP_ABORT = 40
+ MHD_REQUEST_ENDED_BY_APP_ABORT = 40
,
/**
* The application aborted request without response.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_BY_APP_ERROR = 41
+ MHD_REQUEST_ENDED_BY_APP_ERROR = 41
,
/**
* Error handling the connection due to resources exhausted.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_NO_RESOURCES = 50
+ MHD_REQUEST_ENDED_NO_RESOURCES = 50
,
/**
* Closing the session since MHD is being shut down.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN = 60
+ MHD_REQUEST_ENDED_DAEMON_SHUTDOWN = 60
};
/**
- * Additional information about request termination
+ * Additional information about request ending
*/
-union MHD_RequestTerminationDetail
+union MHD_RequestEndedDetail
{
/**
* Reserved member.
@@ -3612,21 +3693,21 @@ union MHD_RequestTerminationDetail
/**
* Request termination data structure
*/
-struct MHD_RequestTerminationData
+struct MHD_RequestEndedData
{
/**
- * The code of the event
+ * The request handle.
+ * Note that most of the request data may be already unvailable.
*/
- enum MHD_RequestTerminationCode code;
+ struct MHD_Request *req;
/**
- * Detailed information about termination event
+ * The code of the event
*/
- union MHD_RequestTerminationDetail details;
+ enum MHD_RequestEndedCode code;
/**
- * Pointer to the application context for the request.
- * NULL unless other value set by application when processing the request.
+ * Detailed information about the event
*/
- void *request_app_context;
+ union MHD_RequestEndedDetail details;
};
@@ -3634,16 +3715,20 @@ struct MHD_RequestTerminationData
* Signature of the callback used by MHD to notify the application
* about completed requests.
*
+ * This is the last callback called for any request (if provided by
+ * the application).
+ *
* @param cls client-defined closure
* @param data the details about the event
- * @param request_context request context value, as originally
- * returned by the #MHD_EarlyUriLogCallback
+ * @param request_app_context the application request context, as possibly set
+ by the #MHD_EarlyUriLogCallback
* @see #MHD_R_OPTION_TERMINATION_CALLBACK()
* @ingroup request
*/
typedef void
-(*MHD_RequestTerminationCallback) (void *cls,
- struct MHD_RequestTerminationData *data);
+(*MHD_RequestEndedCallback) (void *cls,
+ const struct MHD_RequestEndedData *data,
+ void *request_app_context);
#include "microhttpd2_generated_response_options.h"
@@ -3788,11 +3873,11 @@ MHD_D_OPTION_LISTEN_BACKLOG (
* Inform that SIGPIPE is suppressed or handled by application.
* If suppressed/handled, MHD uses network functions that could generate SIGPIPE, like `sendfile()`.
* Silently ignored when MHD creates internal threads as for them SIGPIPE is suppressed automatically.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_SIGPIPE_SUPPRESSED (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -3849,11 +3934,11 @@ MHD_D_OPTION_TLS_PSK_CALLBACK (
* Control ALPN for TLS connection.
* Silently ignored for non-TLS.
* By default ALPN is automatically used for TLS connections.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_NO_ALPN (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -3934,32 +4019,32 @@ MHD_D_OPTION_EARLY_URI_LOGGER (
* Disable converting plus ('+') character to space in GET parameters (URI part after '?').
* Plus conversion is not required by HTTP RFCs, however it required by HTML specifications, see https://url.spec.whatwg.org/#application/x-www-form-urlencoded for details.
* By default plus is converted to space in the query part of URI.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISABLE_URI_QUERY_PLUS_AS_SPACE (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
* Suppresse use of 'Date:' header.
* According to RFC should be suppressed only if the system has no RTC.
* The 'Date:' is not suppressed (the header is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_SUPPRESS_DATE_HEADER (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
* Use SHOUTcast for responses.
* This will cause *all* responses to begin with the SHOUTcast 'ICY' line instead of 'HTTP'.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_ENABLE_SHOUTCAST (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -3967,11 +4052,11 @@ MHD_D_OPTION_ENABLE_SHOUTCAST (
* Default is 32kb.
* Values above 128kb are unlikely to result in much performance benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems.
* The size should be large enough to fit all request headers (together with internal parsing information).
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_CONN_MEMORY_LIMIT (
- size_t val
+ size_t value
);
/**
@@ -3979,22 +4064,22 @@ MHD_D_OPTION_CONN_MEMORY_LIMIT (
* The same large pool is shared for all connections server by MHD and used when application requests avoiding of incremental upload processing to accamulate complete content upload before giving it to the application.
* Default is 8Mb.
* Can be set to zero to disable share pool.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_LARGE_POOL_SIZE (
- size_t val
+ size_t value
);
/**
* Desired size of the stack for the threads started by MHD.
* Use 0 for system default, which is also MHD default.
* Works only with #MHD_D_OPTION_WM_WORKER_THREADS() or #MHD_D_OPTION_WM_THREAD_PER_CONNECTION().
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_STACK_SIZE (
- size_t val
+ size_t value
);
/**
@@ -4017,11 +4102,11 @@ MHD_D_OPTION_FD_NUMBER_LIMIT (
* Disables certain calls to `shutdown()`, enables aggressive non-blocking optimistic reads and other potentially unsafe optimisations.
* Most effects only happen with internal threads with epoll.
* The 'turbo' mode is not enabled (mode is disabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_TURBO (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4031,11 +4116,11 @@ MHD_D_OPTION_TURBO (
* Not compatible with any internal threads modes.
* If MHD is compiled with custom configuration for embedded projects without threads support, this option is mandatory.
* Thread safety is not disabled (safety is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISABLE_THREAD_SAFETY (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4043,11 +4128,11 @@ MHD_D_OPTION_DISABLE_THREAD_SAFETY (
* Upgrade may require usage of additional internal resources, which we can avoid providing if they will not be used.
* You should only use this option if you do not use upgrade functionality and need a generally minor boost in performance and resources saving.
* The upgrade is not disallowed (upgrade is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISALLOW_UPGRADE (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4055,11 +4140,11 @@ MHD_D_OPTION_DISALLOW_UPGRADE (
*
You should only use this function if you do not use suspend functionality and need a generally minor boost in performance.
* The suspend is not disallowed (suspend is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISALLOW_SUSPEND_RESUME (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4176,14 +4261,14 @@ MHD_D_OPTION_DAUTH_DEF_MAX_NC (
#if 0
/**
- * Make the response object re-usable. (FIXME: not used in struct ResponseOptions; remove!?)
+ * Make the response object re-usable.
* The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy().
* Useful if the same response is often used to reply.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_REUSABLE (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4192,30 +4277,30 @@ MHD_R_OPTION_REUSABLE (
* This flag value can be used only with responses created without body (zero-size body).
* Responses with this flag enabled cannot be used in situations where reply body must be sent to the client.
* This flag is primarily intended to be used when automatic 'Content-Length' header is undesirable in response to HEAD requests.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_HEAD_ONLY_RESPONSE (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
* Force use of chunked encoding even if the response content size is known.
* Ignored when the reply cannot have body/content.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_CHUNKED_ENC (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
* Force close connection after sending the response, prevents keep-alive connections and adds 'Connection: close' header.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_CONN_CLOSE (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4229,11 +4314,11 @@ MHD_R_OPTION_CONN_CLOSE (
* + chunked: no
*
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.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
@@ -4249,34 +4334,34 @@ MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT (
* + chunked: no
*
With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections).
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_HTTP_1_0_SERVER (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
* Disable sanity check preventing clients from manually setting the HTTP content length option.
* Allow to set several 'Content-Length' headers. These headers will be used even with replies without body.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH (
- enum MHD_Bool val
+ enum MHD_Bool value
);
/**
* Set a function to be called once MHD is finished with the request.
- * @param term_cb the function to call,
+ * @param ended_cb the function to call,
* NULL to not use the callback
- * @param term_cb_cls the closure for the callback
+ * @param ended_cb_cls the closure for the callback
* @return structure with the requested setting
*/
struct MHD_ResponseOptionAndValue
MHD_R_OPTION_TERMINATION_CALLBACK (
- MHD_RequestTerminationCallback term_cb,
- void *term_cb_cls
+ MHD_RequestEndedCallback ended_cb,
+ void *ended_cb_cls
);
/* End of generated code documenting how to use options */
@@ -4823,9 +4908,9 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind
,
/**
* POST data.
- * This is available only if a content encoding
- * supported by MHD is used, and only if the posted content
- * fits within the available memory pool.
+ * This is available only if #MHD_action_parse_post() action is used,
+ * a content encoding is supported by MHD, and only if the posted content
+ * fits within the specified memory buffers.
*
* @warning The encoding "multipart/form-data" has more fields than just
* "name" and "value". See #MHD_request_get_post_data_cb() and
@@ -4887,23 +4972,30 @@ struct MHD_NameValueKind
/**
* Iterator over name-value pairs. This iterator can be used to
- * iterate over all of the cookies, headers, or POST-data fields of a
- * request, and also to iterate over the headers that have been added
- * to a response.
+ * iterate over all of the cookies, headers, footers or POST-data fields
+ * of a request.
*
- * The pointers to the strings in @a nvt are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The @a nv pointer is valid only until return from this function.
+ *
+ * For @a kind other then #MHD_VK_POSTDATA the pointers to the strings in @a nv
+ * are valid until the response is queued.
+ * For the #MHD_VK_POSTDATA @a kind the pointers to the strings in @a nv
+ * are valid until any MHD_UploadAction is provided.
+ * If the data is needed beyond this point, it should be copied.
*
* @param cls closure
- * @param nvt the name, the value and the kind of the element
+ * @param nv the name and the value of the element, the pointer is valid only until
+ * return from this function
+ * @param kind the type (kind) of the element
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
* @ingroup request
*/
typedef enum MHD_Bool
-(MHD_FN_PAR_NONNULL_ (2)
+(MHD_FN_PAR_NONNULL_ (3)
*MHD_NameValueIterator)(void *cls,
- const struct MHD_NameValueKind *nvt);
+ enum MHD_ValueKind kind,
+ const struct MHD_NameAndValue *nv);
/**
@@ -4928,8 +5020,9 @@ MHD_FN_PAR_NONNULL_ (1);
/**
* Get all of the headers (or other kind of request data) from the request.
*
- * The pointers to the strings in @a elements are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The pointers to the strings in @a elements are valid until any
+ * MHD_Action or MHD_UploadAction is provided. If the data is needed beyond
+ * this point, it should be copied.
*
* @param[in] request request to get values from
* @param kind the types of values to get, can be a bitmask
@@ -4956,8 +5049,8 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
* Get a particular header (or other kind of request data) value.
* If multiple values match the kind, return any one of them.
*
- * The returned pointer is valid until the response is queued.
- * If the data is needed beyond this point, it should be copied.
+ * The returned pointer is valid until any MHD_Action or MHD_UploadAction is
+ * provided. If the data is needed beyond this point, it should be copied.
*
* @param request request to get values from
* @param kind what kind of value are we looking for
@@ -5679,8 +5772,7 @@ MHD_FN_PAR_NONNULL_ (1);
*/
MHD_EXTERN_ const struct MHD_DynamicContentCreatorAction *
MHD_DCC_action_suspend (struct MHD_DynamicContentCreatorContext *ctx)
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_RETURNS_NONNULL_;
+MHD_FN_PAR_NONNULL_ (1);
/**
* Create "stop with error" action.
@@ -5763,7 +5855,7 @@ MHD_EXTERN_ 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)],
+ const char *buffer,
MHD_FreeCallback free_cb,
void *free_cb_cls)
MHD_FN_PAR_IN_SIZE_ (3,2);
@@ -5995,7 +6087,7 @@ MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3);
*/
MHD_EXTERN_ const struct MHD_UploadAction *
MHD_upload_action_suspend (struct MHD_Request *request)
-MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_;
+MHD_FN_PAR_NONNULL_ALL_;
/**
* Converts a @a response to an action. If #MHD_R_O_REUSABLE
@@ -6040,7 +6132,7 @@ MHD_FN_PAR_NONNULL_ (1);
*/
MHD_EXTERN_ const struct MHD_UploadAction *
MHD_upload_action_continue (struct MHD_Request *request)
-MHD_FN_RETURNS_NONNULL_;
+MHD_FN_PAR_NONNULL_ (1);
/**
@@ -6156,24 +6248,123 @@ MHD_FN_PAR_NONNULL_ (1);
#define MHD_action_process_upload_inc(request,uc,uc_cls) \
MHD_action_process_upload (request, 0, NULL, NULL, uc, uc_cls)
+#ifndef MHD_POST_PARSE_RESULT_DEFINED
+
+/**
+ * The result of POST data parsing
+ */
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_PostParseResult
+{
+ /**
+ * The POST data parsed successfully and completely.
+ */
+ MHD_POST_PARSE_RES_OK = 0
+ ,
+ /**
+ * The POST request has no content or zero-length content.
+ */
+ MHD_POST_PARSE_RES_REQUEST_EMPTY = 1
+ ,
+ /**
+ * The POST data parsed successfully, but has missing or incorrect
+ * termination.
+ * The last parsed field may have incorrect data.
+ */
+ MHD_POST_PARSE_RES_OK_BAD_TERMINATION = 2
+ ,
+ /**
+ * Parsing of the POST data is incomplete because client used incorrect
+ * format of POST encoding.
+ * The last parsed field may have incorrect data.
+ * Some POST data is available or has been provided via callback.
+ */
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT = 3
+ ,
+ /**
+ * The POST data cannot be parsed completely because the stream has
+ * no free pool memory.
+ * Some POST data may be parsed.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM = 60
+ ,
+ /**
+ * The POST data cannot be parsed completely because no "large shared buffer"
+ * space is available.
+ * Some POST data may be parsed.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM = 61
+ ,
+ /**
+ * The POST data cannot be parsed because 'Content-Type:' is unknown.
+ */
+ MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE = 80
+ ,
+ /**
+ * The POST data cannot be parsed because 'Content-Type:' header is not set.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE = 81
+ ,
+ /**
+ * The POST data cannot be parsed because "Content-Type:" request header has
+ * no "boundary" parameter for "multipart/form-data"
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY = 82
+ ,
+ /**
+ * The POST data cannot be parsed because "Content-Type: multipart/form-data"
+ * request header is misformed
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED = 83
+ ,
+ /**
+ * The application set POST encoding to "multipart/form-data", but the request
+ * has no "Content-Type: multipart/form-data" header which is required
+ * to find "boundary" used in this encoding
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART = 84
+ ,
+ /**
+ * The POST data cannot be parsed because client used incorrect format
+ * of POST encoding.
+ */
+ MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT = 90
+
+};
+
+#define MHD_POST_PARSE_RESULT_DEFINED 1
+#endif /* ! MHD_POST_PARSE_RESULT_DEFINED */
+
#ifndef MHD_POST_DATA_READER_DEFINED
/**
- * Iterator over key-value pairs where the value maybe made available
- * in increments and/or may not be zero-terminated. Used for
- * MHD parsing POST data. To access "raw" data from POST or PUT
- * requests, use #MHD_action_process_upload() instead.
+ * "Stream" reader for POST data.
+ * This callback is called to incrementally process parsed POST data sent by
+ * the client.
+ * The pointers to the MHD_String and MHD_StringNullable are valid only until
+ * return from this callback.
+ * The pointers to the strings and the @a data are valid only until return from
+ * this callback.
*
+ * @param req the request
* @param cls user-specified closure
- * @param name 0-terminated key for the value
- * @param filename name of the uploaded file, NULL if not known
- * @param content_type mime-type of the data, NULL if not known
- * @param encoding the encoding of the data
- * @param data pointer to @a size bytes of data at the
- * specified @a off offset,
- * NOT zero-terminated
- * @param off offset of data in the overall value
- * @param size number of bytes in @a data available
+ * @param name the name of the POST field
+ * @param filename the name of the uploaded file, @a cstr member is NULL if not
+ * known / not provided
+ * @param content_type the mime-type of the data, cstr member is NULL if not
+ * known / not provided
+ * @param encoding the encoding of the data, cstr member is NULL if not known /
+ * not provided
+ * @param size the number of bytes in @a data available, may be zero if
+ * the @a final_data is #MHD_YES
+ * @param data the pointer to @a size bytes of data at the specified
+ * @a off offset, NOT zero-terminated
+ * @param off the offset of @a data in the overall value, always equal to
+ * the sum of sizes of previous calls for the same field / file;
+ * client may provide more than one field with the same name and
+ * the same filename, the new filed (or file) is indicated by zero
+ * value of @a off (and the end is indicated by @a final_data)
+ * @param final_data if set to #MHD_YES then full field data is provided,
+ * if set to #MHD_NO then more field data may be provided
* @return action specifying how to proceed:
* #MHD_upload_action_continue() if all is well,
* #MHD_upload_action_suspend() to stop reading the upload until
@@ -6184,14 +6375,18 @@ MHD_FN_PAR_NONNULL_ (1);
* @ingroup action
*/
typedef const struct MHD_UploadAction *
-(*MHD_PostDataReader) (void *cls,
+(MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_NONNULL_ (4)
+ MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_NONNULL_ (6)
+ *MHD_PostDataReader) (struct MHD_Request *req,
+ void *cls,
const struct MHD_String *name,
- const struct MHD_String *filename,
- const struct MHD_String *content_type,
- const struct MHD_String *encoding,
+ const struct MHD_StringNullable *filename,
+ const struct MHD_StringNullable *content_type,
+ const struct MHD_StringNullable *encoding,
+ size_t size,
const void *data,
uint_fast64_t off,
- size_t size);
+ enum MHD_Bool final_data);
/**
@@ -6199,37 +6394,55 @@ typedef const struct MHD_UploadAction *
* of the postprocessor upload data.
* @param req the request
* @param cls the closure
+ * @param parsing_result the result of POST data parsing
* @return the action to proceed
*/
typedef const struct MHD_UploadAction *
-(*MHD_PostDataFinished) (struct MHD_Request *req,
- void *cls);
+(MHD_FN_PAR_NONNULL_ (1)
+ *MHD_PostDataFinished) (struct MHD_Request *req,
+ void *cls,
+ enum MHD_PostParseResult parsing_result);
#define MHD_POST_DATA_READER_DEFINED 1
#endif /* ! MHD_POST_DATA_READER_DEFINED */
/**
- * Create an action to parse the POSTed body from the client.
+ * Create an action to parse the POSTed content from the client.
+ *
+ * The action starts parsing of the POST data. Any value that does not fit
+ * @a buffer_size or larger that @a auto_stream_size is given to
+ * @a stream_reader (if it is not NULL).
+ *
+ * If @a buffer_size is zero, then buffers will be limited to the connection's
+ * memory pool. To force all POST data process via @a stream_reader
+ * set @a auto_stream_size to zero.
*
* At most one action can be created for any request.
*
* @param request the request to create action for
- * @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 // FIXME: Remove? Duplicated with pp_buffer_size
- * given to @a iter for stream processing // FIXME: iter??
+ * @param buffer_size the maximum size allowed for the buffers to parse this
+ * request POST data. Within the set limit the buffer is
+ * allocated automatically from the "large" shared memory
+ * pool if necessary.
+ * @param max_nonstream_size the size of the field (in encoded form) above which
+ * values are not buffered and provided for
+ * the @a steam_reader automatically;
+ * useful to have large data (like file uploads)
+ * processed incrementally, while keeping buffer space
+ * for small fields only;
+ * ignored if @a stream_reader is NULL
* @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,
- * can be NULL
- * @param reader_cls closure for @a reader
+ * use #MHD_HTTP_POST_ENCODING_OTHER to detect automatically
+ * @param stream_reader the function to call for "oversize" values in
+ * the stream; can be NULL if @a auto_stream_size is
+ * not zero
+ * @param reader_cls the closure for the @a stream_reader
* @param done_cb called once all data has been processed for
- * the final action; values smaller than @a pp_stream_limit that
- * fit into @a pp_buffer_size will be available via
+ * the final action; values smaller than @a auto_stream_size that
+ * fit into @a buffer_size will be available via
* #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
+ * @param done_cb_cls the closure for the @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.
@@ -6237,63 +6450,76 @@ typedef const struct MHD_UploadAction *
* @ingroup action
*/
MHD_EXTERN_ const struct MHD_Action *
-MHD_action_post_processor (struct MHD_Request *request,
- 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)
+MHD_action_parse_post (struct MHD_Request *request,
+ size_t buffer_size,
+ size_t max_nonstream_size,
+ enum MHD_HTTP_PostEncoding enc,
+ MHD_PostDataReader stream_reader,
+ void *reader_cls,
+ MHD_PostDataFinished done_cb,
+ void *done_cb_cls)
MHD_FN_PAR_NONNULL_ (1);
+#ifndef MHD_POSTFILED_DEFINED
+
/**
* Post data element.
* If any member is not provided/set then pointer to C string is NULL.
* If any member is set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
-struct MHD_PostData
+struct MHD_PostField
{
/**
* The name of the field
*/
struct MHD_String name;
/**
+ * The field data
+ * If not set or defined then to C string is NULL.
+ * If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
+ */
+ struct MHD_StringNullable value;
+ /**
* The filename if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
*/
struct MHD_StringNullable filename;
/**
* The Content-Type if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
*/
struct MHD_StringNullable content_type;
/**
* The Transfer-Encoding if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
*/
struct MHD_StringNullable transfer_encoding;
- /**
- * The field data
- * If not set or defined then to C string is NULL.
- * If set to empty string then pointer to C string not NULL,
- */
- struct MHD_StringNullable value;
};
+#define MHD_POSTFILED_DEFINED 1
+#endif /* ! MHD_POSTFILED_DEFINED */
+
+
/**
* Iterator over POST data.
*
- * The pointers to the strings in @a data are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The @a data pointer is valid only until return from this function.
+ *
+ * The pointers to the strings in @a data are valid until any MHD_UploadAction
+ * is provided. If the data is needed beyond this point, it should be copied.
*
* @param cls closure
- * @param data the element of the post data
+ * @param data the element of the post data, the pointer is valid only until
+ * return from this function
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
* @ingroup request
@@ -6301,13 +6527,11 @@ struct MHD_PostData
typedef enum MHD_Bool
(MHD_FN_PAR_NONNULL_ (2)
*MHD_PostDataIterator)(void *cls,
- const struct MHD_PostData *data);
+ const struct MHD_PostField *data);
/**
* Get all of the post data from the request via request.
*
- * The pointers to the strings in @a elements are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
* @param request the request to get data for
* @param iterator callback to call on each header;
* maybe NULL (then just count headers)
@@ -6324,8 +6548,9 @@ MHD_FN_PAR_NONNULL_ (1);
/**
* Get all of the post data from the request.
*
- * The pointers to the strings in @a elements are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The pointers to the strings in @a elements are valid until any
+ * MHD_UploadAction is provided. If the data is needed beyond this point,
+ * it should be copied.
* @param request the request to get data for
* @param num_elements the number of elements in @a elements array
* @param[out] elements the array of @a num_elements to get the data
@@ -6337,7 +6562,7 @@ MHD_EXTERN_ size_t
MHD_request_get_post_data_list (
struct MHD_Request *request,
size_t num_elements,
- struct MHD_PostData elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
+ struct MHD_PostField elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_SIZE_ (3,2);
@@ -7590,14 +7815,14 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoFixed
* If disabled, no #MHD_VK_COOKIE will be generated by MHD.
* The result is placed in @a v_bool member.
*/
- MHD_LIB_INFO_FIXED_HAS_COOKIE_PARSING = 14
+ MHD_LIB_INFO_FIXED_HAS_COOKIE_PARSER = 14
,
/**
* Get whether postprocessor is supported. If supported then
* #MHD_action_post_processor() can be used.
* The result is placed in @a v_bool member.
*/
- MHD_LIB_INFO_FIXED_HAS_POSTPROCESSOR = 15
+ MHD_LIB_INFO_FIXED_HAS_POST_PARSER = 15
,
/**
* Get whether HTTP "Upgrade" is supported.
diff --git a/src/include/microhttpd2_generated_daemon_options.h b/src/include/microhttpd2_generated_daemon_options.h
@@ -996,14 +996,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Inform that SIGPIPE is suppressed or handled by application.
* If suppressed/handled, MHD uses network functions that could generate SIGPIPE, like `sendfile()`.
* Silently ignored when MHD creates internal threads as for them SIGPIPE is suppressed automatically.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_SIGPIPE_SUPPRESSED(val) \
+# define MHD_D_OPTION_SIGPIPE_SUPPRESSED(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_SIGPIPE_SUPPRESSED, \
- .val.sigpipe_suppressed = (val) \
+ .val.sigpipe_suppressed = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1072,14 +1072,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Control ALPN for TLS connection.
* Silently ignored for non-TLS.
* By default ALPN is automatically used for TLS connections.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_NO_ALPN(val) \
+# define MHD_D_OPTION_NO_ALPN(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_NO_ALPN, \
- .val.no_alpn = (val) \
+ .val.no_alpn = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1178,41 +1178,41 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Disable converting plus ('+') character to space in GET parameters (URI part after '?').
* Plus conversion is not required by HTTP RFCs, however it required by HTML specifications, see https://url.spec.whatwg.org/#application/x-www-form-urlencoded for details.
* By default plus is converted to space in the query part of URI.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_DISABLE_URI_QUERY_PLUS_AS_SPACE(val) \
+# define MHD_D_OPTION_DISABLE_URI_QUERY_PLUS_AS_SPACE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_DISABLE_URI_QUERY_PLUS_AS_SPACE, \
- .val.disable_uri_query_plus_as_space = (val) \
+ .val.disable_uri_query_plus_as_space = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Suppresse use of 'Date:' header.
* According to RFC should be suppressed only if the system has no RTC.
* The 'Date:' is not suppressed (the header is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_SUPPRESS_DATE_HEADER(val) \
+# define MHD_D_OPTION_SUPPRESS_DATE_HEADER(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_SUPPRESS_DATE_HEADER, \
- .val.suppress_date_header = (val) \
+ .val.suppress_date_header = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Use SHOUTcast for responses.
* This will cause *all* responses to begin with the SHOUTcast 'ICY' line instead of 'HTTP'.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_ENABLE_SHOUTCAST(val) \
+# define MHD_D_OPTION_ENABLE_SHOUTCAST(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_ENABLE_SHOUTCAST, \
- .val.enable_shoutcast = (val) \
+ .val.enable_shoutcast = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1220,14 +1220,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Default is 32kb.
* Values above 128kb are unlikely to result in much performance benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems.
* The size should be large enough to fit all request headers (together with internal parsing information).
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_CONN_MEMORY_LIMIT(val) \
+# define MHD_D_OPTION_CONN_MEMORY_LIMIT(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_CONN_MEMORY_LIMIT, \
- .val.conn_memory_limit = (val) \
+ .val.conn_memory_limit = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1235,28 +1235,28 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* The same large pool is shared for all connections server by MHD and used when application requests avoiding of incremental upload processing to accamulate complete content upload before giving it to the application.
* Default is 8Mb.
* Can be set to zero to disable share pool.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_LARGE_POOL_SIZE(val) \
+# define MHD_D_OPTION_LARGE_POOL_SIZE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_LARGE_POOL_SIZE, \
- .val.large_pool_size = (val) \
+ .val.large_pool_size = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Desired size of the stack for the threads started by MHD.
* Use 0 for system default, which is also MHD default.
* Works only with #MHD_D_OPTION_WM_WORKER_THREADS() or #MHD_D_OPTION_WM_THREAD_PER_CONNECTION().
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_STACK_SIZE(val) \
+# define MHD_D_OPTION_STACK_SIZE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_STACK_SIZE, \
- .val.stack_size = (val) \
+ .val.stack_size = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1282,14 +1282,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Disables certain calls to `shutdown()`, enables aggressive non-blocking optimistic reads and other potentially unsafe optimisations.
* Most effects only happen with internal threads with epoll.
* The 'turbo' mode is not enabled (mode is disabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_TURBO(val) \
+# define MHD_D_OPTION_TURBO(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_TURBO, \
- .val.turbo = (val) \
+ .val.turbo = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1299,14 +1299,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Not compatible with any internal threads modes.
* If MHD is compiled with custom configuration for embedded projects without threads support, this option is mandatory.
* Thread safety is not disabled (safety is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_DISABLE_THREAD_SAFETY(val) \
+# define MHD_D_OPTION_DISABLE_THREAD_SAFETY(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_DISABLE_THREAD_SAFETY, \
- .val.disable_thread_safety = (val) \
+ .val.disable_thread_safety = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1314,14 +1314,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
* Upgrade may require usage of additional internal resources, which we can avoid providing if they will not be used.
* You should only use this option if you do not use upgrade functionality and need a generally minor boost in performance and resources saving.
* The upgrade is not disallowed (upgrade is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_DISALLOW_UPGRADE(val) \
+# define MHD_D_OPTION_DISALLOW_UPGRADE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_DISALLOW_UPGRADE, \
- .val.disallow_upgrade = (val) \
+ .val.disallow_upgrade = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1329,14 +1329,14 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
*
You should only use this function if you do not use suspend functionality and need a generally minor boost in performance.
* The suspend is not disallowed (suspend is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_D_OPTION_DISALLOW_SUSPEND_RESUME(val) \
+# define MHD_D_OPTION_DISALLOW_SUSPEND_RESUME(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
.opt = MHD_D_O_DISALLOW_SUSPEND_RESUME, \
- .val.disallow_suspend_resume = (val) \
+ .val.disallow_suspend_resume = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -1701,17 +1701,17 @@ MHD_D_OPTION_LISTEN_BACKLOG (
* Inform that SIGPIPE is suppressed or handled by application.
* If suppressed/handled, MHD uses network functions that could generate SIGPIPE, like `sendfile()`.
* Silently ignored when MHD creates internal threads as for them SIGPIPE is suppressed automatically.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_SIGPIPE_SUPPRESSED (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_SIGPIPE_SUPPRESSED;
- opt_val.val.sigpipe_suppressed = (val); \
+ opt_val.val.sigpipe_suppressed = (value); \
return opt_val;
}
@@ -1810,17 +1810,17 @@ MHD_D_OPTION_TLS_PSK_CALLBACK (
* Control ALPN for TLS connection.
* Silently ignored for non-TLS.
* By default ALPN is automatically used for TLS connections.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_NO_ALPN (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_NO_ALPN;
- opt_val.val.no_alpn = (val); \
+ opt_val.val.no_alpn = (value); \
return opt_val;
}
@@ -1961,17 +1961,17 @@ MHD_D_OPTION_EARLY_URI_LOGGER (
* Disable converting plus ('+') character to space in GET parameters (URI part after '?').
* Plus conversion is not required by HTTP RFCs, however it required by HTML specifications, see https://url.spec.whatwg.org/#application/x-www-form-urlencoded for details.
* By default plus is converted to space in the query part of URI.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISABLE_URI_QUERY_PLUS_AS_SPACE (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_DISABLE_URI_QUERY_PLUS_AS_SPACE;
- opt_val.val.disable_uri_query_plus_as_space = (val); \
+ opt_val.val.disable_uri_query_plus_as_space = (value); \
return opt_val;
}
@@ -1981,17 +1981,17 @@ MHD_D_OPTION_DISABLE_URI_QUERY_PLUS_AS_SPACE (
* Suppresse use of 'Date:' header.
* According to RFC should be suppressed only if the system has no RTC.
* The 'Date:' is not suppressed (the header is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_SUPPRESS_DATE_HEADER (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_SUPPRESS_DATE_HEADER;
- opt_val.val.suppress_date_header = (val); \
+ opt_val.val.suppress_date_header = (value); \
return opt_val;
}
@@ -2000,17 +2000,17 @@ MHD_D_OPTION_SUPPRESS_DATE_HEADER (
/**
* Use SHOUTcast for responses.
* This will cause *all* responses to begin with the SHOUTcast 'ICY' line instead of 'HTTP'.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_ENABLE_SHOUTCAST (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_ENABLE_SHOUTCAST;
- opt_val.val.enable_shoutcast = (val); \
+ opt_val.val.enable_shoutcast = (value); \
return opt_val;
}
@@ -2021,17 +2021,17 @@ MHD_D_OPTION_ENABLE_SHOUTCAST (
* Default is 32kb.
* Values above 128kb are unlikely to result in much performance benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems.
* The size should be large enough to fit all request headers (together with internal parsing information).
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_CONN_MEMORY_LIMIT (
- size_t val
+ size_t value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_CONN_MEMORY_LIMIT;
- opt_val.val.conn_memory_limit = (val); \
+ opt_val.val.conn_memory_limit = (value); \
return opt_val;
}
@@ -2042,17 +2042,17 @@ MHD_D_OPTION_CONN_MEMORY_LIMIT (
* The same large pool is shared for all connections server by MHD and used when application requests avoiding of incremental upload processing to accamulate complete content upload before giving it to the application.
* Default is 8Mb.
* Can be set to zero to disable share pool.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_LARGE_POOL_SIZE (
- size_t val
+ size_t value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_LARGE_POOL_SIZE;
- opt_val.val.large_pool_size = (val); \
+ opt_val.val.large_pool_size = (value); \
return opt_val;
}
@@ -2062,17 +2062,17 @@ MHD_D_OPTION_LARGE_POOL_SIZE (
* Desired size of the stack for the threads started by MHD.
* Use 0 for system default, which is also MHD default.
* Works only with #MHD_D_OPTION_WM_WORKER_THREADS() or #MHD_D_OPTION_WM_THREAD_PER_CONNECTION().
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_STACK_SIZE (
- size_t val
+ size_t value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_STACK_SIZE;
- opt_val.val.stack_size = (val); \
+ opt_val.val.stack_size = (value); \
return opt_val;
}
@@ -2107,17 +2107,17 @@ MHD_D_OPTION_FD_NUMBER_LIMIT (
* Disables certain calls to `shutdown()`, enables aggressive non-blocking optimistic reads and other potentially unsafe optimisations.
* Most effects only happen with internal threads with epoll.
* The 'turbo' mode is not enabled (mode is disabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_TURBO (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_TURBO;
- opt_val.val.turbo = (val); \
+ opt_val.val.turbo = (value); \
return opt_val;
}
@@ -2130,17 +2130,17 @@ MHD_D_OPTION_TURBO (
* Not compatible with any internal threads modes.
* If MHD is compiled with custom configuration for embedded projects without threads support, this option is mandatory.
* Thread safety is not disabled (safety is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISABLE_THREAD_SAFETY (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_DISABLE_THREAD_SAFETY;
- opt_val.val.disable_thread_safety = (val); \
+ opt_val.val.disable_thread_safety = (value); \
return opt_val;
}
@@ -2151,17 +2151,17 @@ MHD_D_OPTION_DISABLE_THREAD_SAFETY (
* Upgrade may require usage of additional internal resources, which we can avoid providing if they will not be used.
* You should only use this option if you do not use upgrade functionality and need a generally minor boost in performance and resources saving.
* The upgrade is not disallowed (upgrade is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISALLOW_UPGRADE (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_DISALLOW_UPGRADE;
- opt_val.val.disallow_upgrade = (val); \
+ opt_val.val.disallow_upgrade = (value); \
return opt_val;
}
@@ -2172,17 +2172,17 @@ MHD_D_OPTION_DISALLOW_UPGRADE (
*
You should only use this function if you do not use suspend functionality and need a generally minor boost in performance.
* The suspend is not disallowed (suspend is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
MHD_D_OPTION_DISALLOW_SUSPEND_RESUME (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_DaemonOptionAndValue opt_val;
opt_val.opt = MHD_D_O_DISALLOW_SUSPEND_RESUME;
- opt_val.val.disallow_suspend_resume = (val); \
+ opt_val.val.disallow_suspend_resume = (value); \
return opt_val;
}
diff --git a/src/include/microhttpd2_generated_response_options.h b/src/include/microhttpd2_generated_response_options.h
@@ -11,7 +11,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption
,
/**
- * Make the response object re-usable. (FIXME: not used in struct ResponseOptions; remove!?)
+ * Make the response object re-usable.
* The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy().
* Useful if the same response is often used to reply.
*/
@@ -98,18 +98,18 @@ With this option HTTP/1.0 server is emulated (with support for 'keep-alive' conn
/**
* Data for #MHD_R_O_TERMINATION_CALLBACK
*/
-struct MHD_ResponeOptionValueTermCB
+struct MHD_ResponeOptionValueEndedCB
{
/**
* the function to call,
* NULL to not use the callback
*/
- MHD_RequestTerminationCallback v_term_cb;
+ MHD_RequestEndedCallback v_ended_cb;
/**
* the closure for the callback
*/
- void *v_term_cb_cls;
+ void *v_ended_cb_cls;
};
@@ -158,7 +158,7 @@ union MHD_ResponseOptionValue
* the function to call,
* NULL to not use the callback
*/
- struct MHD_ResponeOptionValueTermCB termination_callback;
+ struct MHD_ResponeOptionValueEndedCB termination_callback;
};
@@ -178,17 +178,17 @@ struct MHD_ResponseOptionAndValue
#if defined(MHD_USE_COMPOUND_LITERALS) && defined(MHD_USE_DESIG_NEST_INIT)
/**
- * Make the response object re-usable. (FIXME: not used in struct ResponseOptions; remove!?)
+ * Make the response object re-usable.
* The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy().
* Useful if the same response is often used to reply.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_REUSABLE(val) \
+# define MHD_R_OPTION_REUSABLE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_REUSABLE, \
- .val.reusable = (val) \
+ .val.reusable = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -197,39 +197,39 @@ struct MHD_ResponseOptionAndValue
* This flag value can be used only with responses created without body (zero-size body).
* Responses with this flag enabled cannot be used in situations where reply body must be sent to the client.
* This flag is primarily intended to be used when automatic 'Content-Length' header is undesirable in response to HEAD requests.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_HEAD_ONLY_RESPONSE(val) \
+# define MHD_R_OPTION_HEAD_ONLY_RESPONSE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_HEAD_ONLY_RESPONSE, \
- .val.head_only_response = (val) \
+ .val.head_only_response = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Force use of chunked encoding even if the response content size is known.
* Ignored when the reply cannot have body/content.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_CHUNKED_ENC(val) \
+# define MHD_R_OPTION_CHUNKED_ENC(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_CHUNKED_ENC, \
- .val.chunked_enc = (val) \
+ .val.chunked_enc = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Force close connection after sending the response, prevents keep-alive connections and adds 'Connection: close' header.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_CONN_CLOSE(val) \
+# define MHD_R_OPTION_CONN_CLOSE(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_CONN_CLOSE, \
- .val.conn_close = (val) \
+ .val.conn_close = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -243,14 +243,14 @@ struct MHD_ResponseOptionAndValue
* + chunked: no
*
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.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT(val) \
+# define MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT, \
- .val.http_1_0_compatible_strict = (val) \
+ .val.http_1_0_compatible_strict = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
@@ -266,43 +266,43 @@ This option can be used to communicate with some broken client, which does not i
* + chunked: no
*
With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections).
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_HTTP_1_0_SERVER(val) \
+# define MHD_R_OPTION_HTTP_1_0_SERVER(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_HTTP_1_0_SERVER, \
- .val.http_1_0_server = (val) \
+ .val.http_1_0_server = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Disable sanity check preventing clients from manually setting the HTTP content length option.
* Allow to set several 'Content-Length' headers. These headers will be used even with replies without body.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
-# define MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH(val) \
+# define MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH(value) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_INSANITY_HEADER_CONTENT_LENGTH, \
- .val.insanity_header_content_length = (val) \
+ .val.insanity_header_content_length = (value) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Set a function to be called once MHD is finished with the request.
- * @param term_cb the function to call,
+ * @param ended_cb the function to call,
* NULL to not use the callback
- * @param term_cb_cls the closure for the callback
+ * @param ended_cb_cls the closure for the callback
* @return structure with the requested setting
*/
-# define MHD_R_OPTION_TERMINATION_CALLBACK(term_cb,term_cb_cls) \
+# define MHD_R_OPTION_TERMINATION_CALLBACK(ended_cb,ended_cb_cls) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_ResponseOptionAndValue) \
{ \
.opt = MHD_R_O_TERMINATION_CALLBACK, \
- .val.termination_callback.v_term_cb = (term_cb), \
- .val.termination_callback.v_term_cb_cls = (term_cb_cls) \
+ .val.termination_callback.v_ended_cb = (ended_cb), \
+ .val.termination_callback.v_ended_cb_cls = (ended_cb_cls) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
@@ -321,20 +321,20 @@ With this option HTTP/1.0 server is emulated (with support for 'keep-alive' conn
#else /* !MHD_USE_COMPOUND_LITERALS || !MHD_USE_DESIG_NEST_INIT */
MHD_NOWARN_UNUSED_FUNC_
/**
- * Make the response object re-usable. (FIXME: not used in struct ResponseOptions; remove!?)
+ * Make the response object re-usable.
* The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy().
* Useful if the same response is often used to reply.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_REUSABLE (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_REUSABLE;
- opt_val.val.reusable = (val); \
+ opt_val.val.reusable = (value); \
return opt_val;
}
@@ -346,17 +346,17 @@ MHD_R_OPTION_REUSABLE (
* This flag value can be used only with responses created without body (zero-size body).
* Responses with this flag enabled cannot be used in situations where reply body must be sent to the client.
* This flag is primarily intended to be used when automatic 'Content-Length' header is undesirable in response to HEAD requests.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_HEAD_ONLY_RESPONSE (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_HEAD_ONLY_RESPONSE;
- opt_val.val.head_only_response = (val); \
+ opt_val.val.head_only_response = (value); \
return opt_val;
}
@@ -365,17 +365,17 @@ MHD_R_OPTION_HEAD_ONLY_RESPONSE (
/**
* Force use of chunked encoding even if the response content size is known.
* Ignored when the reply cannot have body/content.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_CHUNKED_ENC (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_CHUNKED_ENC;
- opt_val.val.chunked_enc = (val); \
+ opt_val.val.chunked_enc = (value); \
return opt_val;
}
@@ -383,17 +383,17 @@ MHD_R_OPTION_CHUNKED_ENC (
/**
* Force close connection after sending the response, prevents keep-alive connections and adds 'Connection: close' header.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_CONN_CLOSE (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_CONN_CLOSE;
- opt_val.val.conn_close = (val); \
+ opt_val.val.conn_close = (value); \
return opt_val;
}
@@ -410,17 +410,17 @@ MHD_R_OPTION_CONN_CLOSE (
* + chunked: no
*
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.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT;
- opt_val.val.http_1_0_compatible_strict = (val); \
+ opt_val.val.http_1_0_compatible_strict = (value); \
return opt_val;
}
@@ -439,17 +439,17 @@ MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT (
* + chunked: no
*
With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections).
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_HTTP_1_0_SERVER (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_HTTP_1_0_SERVER;
- opt_val.val.http_1_0_server = (val); \
+ opt_val.val.http_1_0_server = (value); \
return opt_val;
}
@@ -458,17 +458,17 @@ MHD_R_OPTION_HTTP_1_0_SERVER (
/**
* Disable sanity check preventing clients from manually setting the HTTP content length option.
* Allow to set several 'Content-Length' headers. These headers will be used even with replies without body.
- * @param val the value of the parameter * @return structure with the requested setting
+ * @param value the value of the parameter * @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH (
- enum MHD_Bool val
+ enum MHD_Bool value
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_INSANITY_HEADER_CONTENT_LENGTH;
- opt_val.val.insanity_header_content_length = (val); \
+ opt_val.val.insanity_header_content_length = (value); \
return opt_val;
}
@@ -476,22 +476,22 @@ MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH (
/**
* Set a function to be called once MHD is finished with the request.
- * @param term_cb the function to call,
+ * @param ended_cb the function to call,
* NULL to not use the callback
- * @param term_cb_cls the closure for the callback
+ * @param ended_cb_cls the closure for the callback
* @return structure with the requested setting
*/
static MHD_INLINE struct MHD_ResponseOptionAndValue
MHD_R_OPTION_TERMINATION_CALLBACK (
- MHD_RequestTerminationCallback term_cb,
- void *term_cb_cls
+ MHD_RequestEndedCallback ended_cb,
+ void *ended_cb_cls
)
{
struct MHD_ResponseOptionAndValue opt_val;
opt_val.opt = MHD_R_O_TERMINATION_CALLBACK;
- opt_val.val.termination_callback.v_term_cb = term_cb;
- opt_val.val.termination_callback.v_term_cb_cls = term_cb_cls;
+ opt_val.val.termination_callback.v_ended_cb = ended_cb;
+ opt_val.val.termination_callback.v_ended_cb_cls = ended_cb_cls;
return opt_val;
}
diff --git a/src/include/microhttpd2_inline_daemon_documentation.h.in b/src/include/microhttpd2_inline_daemon_documentation.h.in
@@ -1,521 +0,0 @@
-/* Beginning of generated code documenting how to use options.
- You should treat the following functions *as if* they were
- part of the header/API. The actual declarations are more
- complex, so these here are just for documentation!
- We do not actually *build* this code... */
-#if 0
-
-/**
- * Set MHD work (threading and polling) mode.
- * Consider use of #MHD_D_OPTION_WM_EXTERNAL_PERIODIC(), #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL(), #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_EDGE(), #MHD_D_OPTION_WM_EXTERNAL_SINGLE_FD_WATCH(), #MHD_D_OPTION_WM_WORKER_THREADS() or #MHD_D_OPTION_WM_THREAD_PER_CONNECTION() instead of direct use of this parameter.
- * @param wmp the object created by one of the next functions/macros: #MHD_WM_OPTION_EXTERNAL_PERIODIC(), #MHD_WM_OPTION_EXTERNAL_EVENT_LOOP_CB_LEVEL(), #MHD_WM_OPTION_EXTERNAL_EVENT_LOOP_CB_EDGE(), #MHD_WM_OPTION_EXTERNAL_SINGLE_FD_WATCH(), #MHD_WM_OPTION_WORKER_THREADS(), #MHD_WM_OPTION_THREAD_PER_CONNECTION()
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_WORK_MODE (
- struct MHD_WorkModeWithParam wmp
- );
-
-/**
- * Select a sockets watch system call used for internal polling.
- * @param els FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_POLL_SYSCALL (
- enum MHD_SockPollSyscall els
- );
-
-/**
- * Set a callback to use for logging
- * @param log_cb the callback to use for logging,
- * NULL to disable logging
- * @param log_cb_cls the closure for the logging callback
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_LOG_CALLBACK (
- MHD_LoggingCallback log_cb,
- void *log_cb_cls
- );
-
-/**
- * Bind to the given TCP port and address family.
- *
-Does not work with #MHD_D_OPTION_BIND_SA() or #MHD_D_OPTION_LISTEN_SOCKET().
- *
-If no listen socket optins (#MHD_D_OPTION_BIND_PORT(), #MHD_D_OPTION_BIND_SA(), #MHD_D_OPTION_LISTEN_SOCKET()) are used, MHD does not listen for incoming connection.
- * @param af the address family to use,
- * the #MHD_AF_NONE to disable listen socket (the same effect as if this option is not used)
- * @param port port to use, 0 to let system assign any free port,
- * ignored if @a af is #MHD_AF_NONE
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_BIND_PORT (
- enum MHD_AddressFamily af,
- uint_least16_t port
- );
-
-/**
- * Bind to the given socket address.
- *
-Does not work with #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_LISTEN_SOCKET().
- *
-If no listen socket optins (#MHD_D_OPTION_BIND_PORT(), #MHD_D_OPTION_BIND_SA(), #MHD_D_OPTION_LISTEN_SOCKET()) are used, MHD does not listen for incoming connection.
- * @param sa_len the size of the socket address pointed by @a sa.
- * @param sa the address to bind to; can be IPv4 (AF_INET), IPv6 (AF_INET6) or even a UNIX domain socket (AF_UNIX)
- * @param dual When a previous version of the protocol exist (like IPv4 when @a v_sa is IPv6) bind to both protocols (IPv6 and IPv4).
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_BIND_SA (
- size_t sa_len,
- /* const */ struct sockaddr *sa,
- enum MHD_Bool dual
- );
-
-/**
- * Accept connections from the given socket. Socket
- * must be a TCP or UNIX domain (SOCK_STREAM) socket.
- *
-Does not work with #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA().
- *
-If no listen socket optins (#MHD_D_OPTION_BIND_PORT(), #MHD_D_OPTION_BIND_SA(), #MHD_D_OPTION_LISTEN_SOCKET()) are used, MHD does not listen for incoming connection.
- * @param listen_fd the listen socket to use, ignored if set to #MHD_INVALID_SOCKET
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_LISTEN_SOCKET (
- MHD_Socket listen_fd
- );
-
-/**
- * Select mode of reusing address:port listen address.
- *
-Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
- * @param reuse_type FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_LISTEN_ADDR_REUSE (
- enum MHD_DaemonOptionBindType reuse_type
- );
-
-/**
- * Configure TCP_FASTOPEN option, including setting a
- * custom @a queue_length.
- *
-Note that having a larger queue size can cause resource exhaustion
- * attack as the TCP stack has to now allocate resources for the SYN
- * packet along with its DATA.
- *
-Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
- * @param option the type use of of TCP FastOpen
- * @param queue_length the length of the queue, zero to use system or MHD default,
- * silently ignored on platforms without support for custom queue size
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_TCP_FASTOPEN (
- enum MHD_TCPFastOpenType option,
- unsigned int queue_length
- );
-
-/**
- * Use the given backlog for the listen() call.
- *
-Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used.
- * Zero parameter treated as MHD/system default.
- * @param backlog_size FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_LISTEN_BACKLOG (
- unsigned int backlog_size
- );
-
-/**
- * Inform that SIGPIPE is suppressed or handled by application.
- * If suppressed/handled, MHD uses network functions that could generate SIGPIPE, like `sendfile()`.
- * Silently ignored when MHD creates internal threads as for them SIGPIPE is suppressed automatically.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_SIGPIPE_SUPPRESSED (
- enum MHD_Bool val
- );
-
-/**
- * Enable TLS (HTTPS) and select TLS backend
- * @param backend the TLS backend to use,
- * #MHD_TLS_BACKEND_NONE for non-TLS (plain TCP) connections
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_TLS (
- enum MHD_TlsBackend backend
- );
-
-/**
- * Provide TLS key and certificate data in-memory.
- * Works only if TLS mode is enabled.
- * @param mem_key the private key loaded into memory (not a filename)
- * @param mem_cert the certificate loaded into memory (not a filename)
- * @param mem_pass the option passphrase phrase to decrypt the private key,
- * could be NULL is private does not need a password
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_TLS_KEY_CERT (
- const char *mem_key,
- const char *mem_cert,
- const char *mem_pass
- );
-
-/**
- * Provide the certificate of the certificate authority (CA) to be used by the MHD daemon for client authentication.
- * Works only if TLS mode is enabled.
- * @param mem_client_ca the CA certificate in memory (not a filename)
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_TLS_CLIENT_CA (
- const char *mem_client_ca
- );
-
-/**
- * Configure PSK to use for the TLS key exchange.
- * @param psk_cb the function to call to obtain pre-shared key
- * @param psk_cb_cls the closure for @a psk_cb
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_TLS_PSK_CALLBACK (
- MHD_PskServerCredentialsCallback psk_cb,
- void *psk_cb_cls
- );
-
-/**
- * Control ALPN for TLS connection.
- * Silently ignored for non-TLS.
- * By default ALPN is automatically used for TLS connections.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_NO_ALPN (
- enum MHD_Bool val
- );
-
-/**
- * Specify inactivity timeout for connection.
- * When no activity for specified time on connection, it is closed automatically.
- * Use zero for no timeout, which is also the (unsafe!) default.
- * @param timeout the in seconds, zero for no timeout
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DEFAULT_TIMEOUT (
- unsigned int timeout
- );
-
-/**
- * Maximum number of (concurrent) network connections served by daemon.
- * @note The real maximum number of network connections could be smaller
- * than requested due to the system limitations, like FD_SETSIZE when
- * polling by select() is used.
- * @param glob_limit FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_GLOBAL_CONNECTION_LIMIT (
- unsigned int glob_limit
- );
-
-/**
- * Limit on the number of (concurrent) network connections made to the server from the same IP address.
- * Can be used to prevent one IP from taking over all of the allowed connections. If the same IP tries to establish more than the specified number of connections, they will be immediately rejected.
- * @param limit FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_PER_IP_LIMIT (
- unsigned int limit
- );
-
-/**
- * Set a policy callback that accepts/rejects connections based on the client's IP address. The callbeck function will be called before servicing any new incoming connection.
- * @param apc the accept policy callback
- * @param apc_cls the closure for the callback
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_ACCEPT_POLICY (
- MHD_AcceptPolicyCallback apc,
- void *apc_cls
- );
-
-/**
- * Set how strictly MHD will enforce the HTTP protocol.
- * @param sl the level of strictness
- * @param how the way how to use the requested level
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_PROTOCOL_STRICT_LEVEL (
- enum MHD_ProtocolStrictLevel sl,
- enum MHD_UseStictLevel how
- );
-
-/**
- * Set a callback to be called first for every request when the request line is received (before any parsing of the header).
- * This callback is the only way to get raw (unmodified) request URI as URI is parsed and modified by MHD in-place.
- * Mandatory URI modification may apply before this call, like binary zero replacement, as required by RFCs.
- * @param cb the early URI callback
- * @param cls the closure for the callback
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_EARLY_URI_LOGGER (
- MHD_EarlyUriLogCallback cb,
- void *cls
- );
-
-/**
- * Disable converting plus ('+') character to space in GET parameters (URI part after '?').
- * Plus conversion is not required by HTTP RFCs, however it required by HTML specifications, see https://url.spec.whatwg.org/#application/x-www-form-urlencoded for details.
- * By default plus is converted to space in the query part of URI.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DISABLE_URI_QUERY_PLUS_AS_SPACE (
- enum MHD_Bool val
- );
-
-/**
- * Suppresse use of 'Date:' header.
- * According to RFC should be suppressed only if the system has no RTC.
- * The 'Date:' is not suppressed (the header is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_SUPPRESS_DATE_HEADER (
- enum MHD_Bool val
- );
-
-/**
- * Use SHOUTcast for responses.
- * This will cause *all* responses to begin with the SHOUTcast 'ICY' line instead of 'HTTP'.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_ENABLE_SHOUTCAST (
- enum MHD_Bool val
- );
-
-/**
- * Maximum memory size per connection.
- * Default is 32kb.
- * Values above 128kb are unlikely to result in much performance benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems.
- * The size should be large enough to fit all request headers (together with internal parsing information).
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_CONN_MEMORY_LIMIT (
- size_t val
- );
-
-/**
- * The size of the shared memory pool for accamulated upload processing.
- * The same large pool is shared for all connections server by MHD and used when application requests avoiding of incremental upload processing to accamulate complete content upload before giving it to the application.
- * Default is 8Mb.
- * Can be set to zero to disable share pool.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_LARGE_POOL_SIZE (
- size_t val
- );
-
-/**
- * Desired size of the stack for the threads started by MHD.
- * Use 0 for system default, which is also MHD default.
- * Works only with #MHD_D_OPTION_WM_WORKER_THREADS() or #MHD_D_OPTION_WM_THREAD_PER_CONNECTION().
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_STACK_SIZE (
- size_t val
- );
-
-/**
- * The the maximum FD value.
- * The limit is applied to all sockets used by MHD.
- * If listen socket FD is equal or higher that specified value, the daemon fail to start.
- * If new connection FD is equal or higher that specified value, the connection is rejected.
- * Useful if application uses select() for polling the sockets, system FD_SETSIZE is good value for this option in such case.
- * Silently ignored on W32 (WinSock sockets).
- * @param max_fd FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_FD_NUMBER_LIMIT (
- MHD_Socket max_fd
- );
-
-/**
- * Enable `turbo`.
- * Disables certain calls to `shutdown()`, enables aggressive non-blocking optimistic reads and other potentially unsafe optimisations.
- * Most effects only happen with internal threads with epoll.
- * The 'turbo' mode is not enabled (mode is disabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_TURBO (
- enum MHD_Bool val
- );
-
-/**
- * Disable some internal thread safety.
- * Indicates that MHD daemon will be used by application in single-threaded mode only. When this flag is set then application must call any MHD function only within a single thread.
- * This flag turns off some internal thread-safety and allows MHD making some of the internal optimisations suitable only for single-threaded environment.
- * Not compatible with any internal threads modes.
- * If MHD is compiled with custom configuration for embedded projects without threads support, this option is mandatory.
- * Thread safety is not disabled (safety is enabled) by default.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DISABLE_THREAD_SAFETY (
- enum MHD_Bool val
- );
-
-/**
- * You need to set this option if you want to disable use of HTTP Upgrade.
- * Upgrade may require usage of additional internal resources, which we can avoid providing if they will not be used.
- * You should only use this option if you do not use upgrade functionality and need a generally minor boost in performance and resources saving.
- * The upgrade is not disallowed (upgrade is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DISALLOW_UPGRADE (
- enum MHD_Bool val
- );
-
-/**
- * Disable #MHD_action_suspend() functionality.
- *
-You should only use this function if you do not use suspend functionality and need a generally minor boost in performance.
- * The suspend is not disallowed (suspend is allowed) by default.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DISALLOW_SUSPEND_RESUME (
- enum MHD_Bool val
- );
-
-/**
- * Set a callback to be called for pre-start finalisation.
- *
-The specified callback will be called one time, after network initialisation, TLS pre-initialisation, but before the start of the internal threads (if allowed)
- * @param cb the pre-start callback
- * @param cb_cls the closure for the callback
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAEMON_READY_CALLBACK (
- MHD_DaemonReadyCallback cb,
- void *cb_cls
- );
-
-/**
- * Set a function that should be called whenever a connection is started or closed.
- * @param ncc the callback for notifications
- * @param cls the closure for the callback
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_NOTIFY_CONNECTION (
- MHD_NotifyConnectionCallback ncc,
- void *cls
- );
-
-/**
- * Register a function that should be called whenever a stream is started or closed.
- * For HTTP/1.1 this callback is called one time for every connection.
- * @param nsc the callback for notifications
- * @param cls the closure for the callback
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_NOTIFY_STREAM (
- MHD_NotifyStreamCallback nsc,
- void *cls
- );
-
-/**
- * Set strong random data to be used by MHD.
- * Currently the data is only needed for Digest Auth module.
- * The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
- * Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
- * @param buf_size the size of the buffer
- * @param buf the buffer with strong random data, the content will be copied by MHD
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_RANDOM_ENTROPY (
- size_t buf_size,
- /* const */ void *buf
- );
-
-/**
- * Specify the size of the internal hash map array that tracks generated digest nonces usage.
- * When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
- * @param size the size of the map array
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_MAP_SIZE (
- size_t size
- );
-
-/**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE (
- enum MHD_DaemonOptionValueDAuthBindNonce bind_type
- );
-
-/**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
- * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
- * @param timeout FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
- unsigned int timeout
- );
-
-/**
- * Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
- * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
- * @param max_nc FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_MAX_NC (
- uint_fast32_t max_nc
- );
-
-/* End of generated code documenting how to use options */
-#endif
-
diff --git a/src/include/microhttpd2_inline_response_documentation.h.in b/src/include/microhttpd2_inline_response_documentation.h.in
@@ -1,114 +0,0 @@
-/* Beginning of generated code documenting how to use options.
- You should treat the following functions *as if* they were
- part of the header/API. The actual declarations are more
- complex, so these here are just for documentation!
- We do not actually *build* this code... */
-#if 0
-
-/**
- * Make the response object re-usable. (FIXME: not used in struct ResponseOptions; remove!?)
- * The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy().
- * Useful if the same response is often used to reply.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_REUSABLE (
- enum MHD_Bool val
- );
-
-/**
- * Enable special processing of the response as body-less (with undefined body size). No automatic 'Content-Length' or 'Transfer-Encoding: chunked' headers are added when the response is used with #MHD_HTTP_STATUS_NOT_MODIFIED code or to respond to HEAD request.
- * The flag also allow to set arbitrary 'Content-Length' by #MHD_response_add_header() function.
- * This flag value can be used only with responses created without body (zero-size body).
- * Responses with this flag enabled cannot be used in situations where reply body must be sent to the client.
- * This flag is primarily intended to be used when automatic 'Content-Length' header is undesirable in response to HEAD requests.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_HEAD_ONLY_RESPONSE (
- enum MHD_Bool val
- );
-
-/**
- * Force use of chunked encoding even if the response content size is known.
- * Ignored when the reply cannot have body/content.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_CHUNKED_ENC (
- enum MHD_Bool val
- );
-
-/**
- * Force close connection after sending the response, prevents keep-alive connections and adds 'Connection: close' header.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_CONN_CLOSE (
- enum MHD_Bool val
- );
-
-/**
- * Only respond in conservative (dumb) HTTP/1.0-compatible mode.
- * 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.
- * Summary:
- * + declared reply version: HTTP/1.1
- * + keep-alive: no
- * + chunked: no
- *
-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.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT (
- enum MHD_Bool val
- );
-
-/**
- * Only respond in HTTP/1.0-mode.
- * Contrary to the #MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT flag, the response's HTTP version will always be set to 1.0 and keep-alive connections will be used if explicitly requested by the client.
- * The 'Connection:' header will be added for both 'close' and 'keep-alive' connections.
- * 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).
- * Summary:
- * + declared reply version: HTTP/1.0
- * + keep-alive: possible
- * + chunked: no
- *
-With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections).
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_HTTP_1_0_SERVER (
- enum MHD_Bool val
- );
-
-/**
- * Disable sanity check preventing clients from manually setting the HTTP content length option.
- * Allow to set several 'Content-Length' headers. These headers will be used even with replies without body.
- * @param val the value of the parameter * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH (
- enum MHD_Bool val
- );
-
-/**
- * Set a function to be called once MHD is finished with the request.
- * @param term_cb the function to call,
- * NULL to not use the callback
- * @param term_cb_cls the closure for the callback
- * @return structure with the requested setting
- */
-struct MHD_ResponseOptionAndValue
-MHD_R_OPTION_TERMINATION_CALLBACK (
- MHD_RequestTerminationCallback term_cb,
- void *term_cb_cls
- );
-
-/* End of generated code documenting how to use options */
-#endif
-
diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in
@@ -539,9 +539,9 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind
,
/**
* POST data.
- * This is available only if a content encoding
- * supported by MHD is used, and only if the posted content
- * fits within the available memory pool.
+ * This is available only if #MHD_action_parse_post() action is used,
+ * a content encoding is supported by MHD, and only if the posted content
+ * fits within the specified memory buffers.
*
* @warning The encoding "multipart/form-data" has more fields than just
* "name" and "value". See #MHD_request_get_post_data_cb() and
@@ -603,23 +603,30 @@ struct MHD_NameValueKind
/**
* Iterator over name-value pairs. This iterator can be used to
- * iterate over all of the cookies, headers, or POST-data fields of a
- * request, and also to iterate over the headers that have been added
- * to a response.
+ * iterate over all of the cookies, headers, footers or POST-data fields
+ * of a request.
*
- * The pointers to the strings in @a nvt are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The @a nv pointer is valid only until return from this function.
+ *
+ * For @a kind other then #MHD_VK_POSTDATA the pointers to the strings in @a nv
+ * are valid until the response is queued.
+ * For the #MHD_VK_POSTDATA @a kind the pointers to the strings in @a nv
+ * are valid until any MHD_UploadAction is provided.
+ * If the data is needed beyond this point, it should be copied.
*
* @param cls closure
- * @param nvt the name, the value and the kind of the element
+ * @param nv the name and the value of the element, the pointer is valid only until
+ * return from this function
+ * @param kind the type (kind) of the element
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
* @ingroup request
*/
typedef enum MHD_Bool
-(MHD_FN_PAR_NONNULL_ (2)
+(MHD_FN_PAR_NONNULL_ (3)
*MHD_NameValueIterator)(void *cls,
- const struct MHD_NameValueKind *nvt);
+ enum MHD_ValueKind kind,
+ const struct MHD_NameAndValue *nv);
/**
@@ -644,8 +651,9 @@ MHD_FN_PAR_NONNULL_ (1);
/**
* Get all of the headers (or other kind of request data) from the request.
*
- * The pointers to the strings in @a elements are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The pointers to the strings in @a elements are valid until any
+ * MHD_Action or MHD_UploadAction is provided. If the data is needed beyond
+ * this point, it should be copied.
*
* @param[in] request request to get values from
* @param kind the types of values to get, can be a bitmask
@@ -672,8 +680,8 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
* Get a particular header (or other kind of request data) value.
* If multiple values match the kind, return any one of them.
*
- * The returned pointer is valid until the response is queued.
- * If the data is needed beyond this point, it should be copied.
+ * The returned pointer is valid until any MHD_Action or MHD_UploadAction is
+ * provided. If the data is needed beyond this point, it should be copied.
*
* @param request request to get values from
* @param kind what kind of value are we looking for
@@ -1395,8 +1403,7 @@ MHD_FN_PAR_NONNULL_ (1);
*/
MHD_EXTERN_ const struct MHD_DynamicContentCreatorAction *
MHD_DCC_action_suspend (struct MHD_DynamicContentCreatorContext *ctx)
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_RETURNS_NONNULL_;
+MHD_FN_PAR_NONNULL_ (1);
/**
* Create "stop with error" action.
@@ -1479,7 +1486,7 @@ MHD_EXTERN_ 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)],
+ const char *buffer,
MHD_FreeCallback free_cb,
void *free_cb_cls)
MHD_FN_PAR_IN_SIZE_ (3,2);
@@ -1711,7 +1718,7 @@ MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3);
*/
MHD_EXTERN_ const struct MHD_UploadAction *
MHD_upload_action_suspend (struct MHD_Request *request)
-MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_;
+MHD_FN_PAR_NONNULL_ALL_;
/**
* Converts a @a response to an action. If #MHD_R_O_REUSABLE
@@ -1756,7 +1763,7 @@ MHD_FN_PAR_NONNULL_ (1);
*/
MHD_EXTERN_ const struct MHD_UploadAction *
MHD_upload_action_continue (struct MHD_Request *request)
-MHD_FN_RETURNS_NONNULL_;
+MHD_FN_PAR_NONNULL_ (1);
/**
@@ -1872,24 +1879,123 @@ MHD_FN_PAR_NONNULL_ (1);
#define MHD_action_process_upload_inc(request,uc,uc_cls) \
MHD_action_process_upload (request, 0, NULL, NULL, uc, uc_cls)
+#ifndef MHD_POST_PARSE_RESULT_DEFINED
+
+/**
+ * The result of POST data parsing
+ */
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_PostParseResult
+{
+ /**
+ * The POST data parsed successfully and completely.
+ */
+ MHD_POST_PARSE_RES_OK = 0
+ ,
+ /**
+ * The POST request has no content or zero-length content.
+ */
+ MHD_POST_PARSE_RES_REQUEST_EMPTY = 1
+ ,
+ /**
+ * The POST data parsed successfully, but has missing or incorrect
+ * termination.
+ * The last parsed field may have incorrect data.
+ */
+ MHD_POST_PARSE_RES_OK_BAD_TERMINATION = 2
+ ,
+ /**
+ * Parsing of the POST data is incomplete because client used incorrect
+ * format of POST encoding.
+ * The last parsed field may have incorrect data.
+ * Some POST data is available or has been provided via callback.
+ */
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT = 3
+ ,
+ /**
+ * The POST data cannot be parsed completely because the stream has
+ * no free pool memory.
+ * Some POST data may be parsed.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM = 60
+ ,
+ /**
+ * The POST data cannot be parsed completely because no "large shared buffer"
+ * space is available.
+ * Some POST data may be parsed.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM = 61
+ ,
+ /**
+ * The POST data cannot be parsed because 'Content-Type:' is unknown.
+ */
+ MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE = 80
+ ,
+ /**
+ * The POST data cannot be parsed because 'Content-Type:' header is not set.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE = 81
+ ,
+ /**
+ * The POST data cannot be parsed because "Content-Type:" request header has
+ * no "boundary" parameter for "multipart/form-data"
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY = 82
+ ,
+ /**
+ * The POST data cannot be parsed because "Content-Type: multipart/form-data"
+ * request header is misformed
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED = 83
+ ,
+ /**
+ * The application set POST encoding to "multipart/form-data", but the request
+ * has no "Content-Type: multipart/form-data" header which is required
+ * to find "boundary" used in this encoding
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART = 84
+ ,
+ /**
+ * The POST data cannot be parsed because client used incorrect format
+ * of POST encoding.
+ */
+ MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT = 90
+
+};
+
+#define MHD_POST_PARSE_RESULT_DEFINED 1
+#endif /* ! MHD_POST_PARSE_RESULT_DEFINED */
+
#ifndef MHD_POST_DATA_READER_DEFINED
/**
- * Iterator over key-value pairs where the value maybe made available
- * in increments and/or may not be zero-terminated. Used for
- * MHD parsing POST data. To access "raw" data from POST or PUT
- * requests, use #MHD_action_process_upload() instead.
+ * "Stream" reader for POST data.
+ * This callback is called to incrementally process parsed POST data sent by
+ * the client.
+ * The pointers to the MHD_String and MHD_StringNullable are valid only until
+ * return from this callback.
+ * The pointers to the strings and the @a data are valid only until return from
+ * this callback.
*
+ * @param req the request
* @param cls user-specified closure
- * @param name 0-terminated key for the value
- * @param filename name of the uploaded file, NULL if not known
- * @param content_type mime-type of the data, NULL if not known
- * @param encoding the encoding of the data
- * @param data pointer to @a size bytes of data at the
- * specified @a off offset,
- * NOT zero-terminated
- * @param off offset of data in the overall value
- * @param size number of bytes in @a data available
+ * @param name the name of the POST field
+ * @param filename the name of the uploaded file, @a cstr member is NULL if not
+ * known / not provided
+ * @param content_type the mime-type of the data, cstr member is NULL if not
+ * known / not provided
+ * @param encoding the encoding of the data, cstr member is NULL if not known /
+ * not provided
+ * @param size the number of bytes in @a data available, may be zero if
+ * the @a final_data is #MHD_YES
+ * @param data the pointer to @a size bytes of data at the specified
+ * @a off offset, NOT zero-terminated
+ * @param off the offset of @a data in the overall value, always equal to
+ * the sum of sizes of previous calls for the same field / file;
+ * client may provide more than one field with the same name and
+ * the same filename, the new filed (or file) is indicated by zero
+ * value of @a off (and the end is indicated by @a final_data)
+ * @param final_data if set to #MHD_YES then full field data is provided,
+ * if set to #MHD_NO then more field data may be provided
* @return action specifying how to proceed:
* #MHD_upload_action_continue() if all is well,
* #MHD_upload_action_suspend() to stop reading the upload until
@@ -1900,14 +2006,18 @@ MHD_FN_PAR_NONNULL_ (1);
* @ingroup action
*/
typedef const struct MHD_UploadAction *
-(*MHD_PostDataReader) (void *cls,
+(MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_NONNULL_ (4)
+ MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_NONNULL_ (6)
+ *MHD_PostDataReader) (struct MHD_Request *req,
+ void *cls,
const struct MHD_String *name,
- const struct MHD_String *filename,
- const struct MHD_String *content_type,
- const struct MHD_String *encoding,
+ const struct MHD_StringNullable *filename,
+ const struct MHD_StringNullable *content_type,
+ const struct MHD_StringNullable *encoding,
+ size_t size,
const void *data,
uint_fast64_t off,
- size_t size);
+ enum MHD_Bool final_data);
/**
@@ -1915,37 +2025,55 @@ typedef const struct MHD_UploadAction *
* of the postprocessor upload data.
* @param req the request
* @param cls the closure
+ * @param parsing_result the result of POST data parsing
* @return the action to proceed
*/
typedef const struct MHD_UploadAction *
-(*MHD_PostDataFinished) (struct MHD_Request *req,
- void *cls);
+(MHD_FN_PAR_NONNULL_ (1)
+ *MHD_PostDataFinished) (struct MHD_Request *req,
+ void *cls,
+ enum MHD_PostParseResult parsing_result);
#define MHD_POST_DATA_READER_DEFINED 1
#endif /* ! MHD_POST_DATA_READER_DEFINED */
/**
- * Create an action to parse the POSTed body from the client.
+ * Create an action to parse the POSTed content from the client.
+ *
+ * The action starts parsing of the POST data. Any value that does not fit
+ * @a buffer_size or larger that @a auto_stream_size is given to
+ * @a stream_reader (if it is not NULL).
+ *
+ * If @a buffer_size is zero, then buffers will be limited to the connection's
+ * memory pool. To force all POST data process via @a stream_reader
+ * set @a auto_stream_size to zero.
*
* At most one action can be created for any request.
*
* @param request the request to create action for
- * @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 // FIXME: Remove? Duplicated with pp_buffer_size
- * given to @a iter for stream processing // FIXME: iter??
+ * @param buffer_size the maximum size allowed for the buffers to parse this
+ * request POST data. Within the set limit the buffer is
+ * allocated automatically from the "large" shared memory
+ * pool if necessary.
+ * @param max_nonstream_size the size of the field (in encoded form) above which
+ * values are not buffered and provided for
+ * the @a steam_reader automatically;
+ * useful to have large data (like file uploads)
+ * processed incrementally, while keeping buffer space
+ * for small fields only;
+ * ignored if @a stream_reader is NULL
* @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,
- * can be NULL
- * @param reader_cls closure for @a reader
+ * use #MHD_HTTP_POST_ENCODING_OTHER to detect automatically
+ * @param stream_reader the function to call for "oversize" values in
+ * the stream; can be NULL if @a auto_stream_size is
+ * not zero
+ * @param reader_cls the closure for the @a stream_reader
* @param done_cb called once all data has been processed for
- * the final action; values smaller than @a pp_stream_limit that
- * fit into @a pp_buffer_size will be available via
+ * the final action; values smaller than @a auto_stream_size that
+ * fit into @a buffer_size will be available via
* #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
+ * @param done_cb_cls the closure for the @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.
@@ -1953,63 +2081,76 @@ typedef const struct MHD_UploadAction *
* @ingroup action
*/
MHD_EXTERN_ const struct MHD_Action *
-MHD_action_post_processor (struct MHD_Request *request,
- 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)
+MHD_action_parse_post (struct MHD_Request *request,
+ size_t buffer_size,
+ size_t max_nonstream_size,
+ enum MHD_HTTP_PostEncoding enc,
+ MHD_PostDataReader stream_reader,
+ void *reader_cls,
+ MHD_PostDataFinished done_cb,
+ void *done_cb_cls)
MHD_FN_PAR_NONNULL_ (1);
+#ifndef MHD_POSTFILED_DEFINED
+
/**
* Post data element.
* If any member is not provided/set then pointer to C string is NULL.
* If any member is set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
-struct MHD_PostData
+struct MHD_PostField
{
/**
* The name of the field
*/
struct MHD_String name;
/**
+ * The field data
+ * If not set or defined then to C string is NULL.
+ * If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
+ */
+ struct MHD_StringNullable value;
+ /**
* The filename if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
*/
struct MHD_StringNullable filename;
/**
* The Content-Type if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
*/
struct MHD_StringNullable content_type;
/**
* The Transfer-Encoding if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
+ * but the length is zero.
*/
struct MHD_StringNullable transfer_encoding;
- /**
- * The field data
- * If not set or defined then to C string is NULL.
- * If set to empty string then pointer to C string not NULL,
- */
- struct MHD_StringNullable value;
};
+#define MHD_POSTFILED_DEFINED 1
+#endif /* ! MHD_POSTFILED_DEFINED */
+
+
/**
* Iterator over POST data.
*
- * The pointers to the strings in @a data are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The @a data pointer is valid only until return from this function.
+ *
+ * The pointers to the strings in @a data are valid until any MHD_UploadAction
+ * is provided. If the data is needed beyond this point, it should be copied.
*
* @param cls closure
- * @param data the element of the post data
+ * @param data the element of the post data, the pointer is valid only until
+ * return from this function
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
* @ingroup request
@@ -2017,13 +2158,11 @@ struct MHD_PostData
typedef enum MHD_Bool
(MHD_FN_PAR_NONNULL_ (2)
*MHD_PostDataIterator)(void *cls,
- const struct MHD_PostData *data);
+ const struct MHD_PostField *data);
/**
* Get all of the post data from the request via request.
*
- * The pointers to the strings in @a elements are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
* @param request the request to get data for
* @param iterator callback to call on each header;
* maybe NULL (then just count headers)
@@ -2040,8 +2179,9 @@ MHD_FN_PAR_NONNULL_ (1);
/**
* Get all of the post data from the request.
*
- * The pointers to the strings in @a elements are valid until the response
- * is queued. If the data is needed beyond this point, it should be copied.
+ * The pointers to the strings in @a elements are valid until any
+ * MHD_UploadAction is provided. If the data is needed beyond this point,
+ * it should be copied.
* @param request the request to get data for
* @param num_elements the number of elements in @a elements array
* @param[out] elements the array of @a num_elements to get the data
@@ -2053,7 +2193,7 @@ MHD_EXTERN_ size_t
MHD_request_get_post_data_list (
struct MHD_Request *request,
size_t num_elements,
- struct MHD_PostData elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
+ struct MHD_PostField elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_SIZE_ (3,2);
@@ -3306,14 +3446,14 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoFixed
* If disabled, no #MHD_VK_COOKIE will be generated by MHD.
* The result is placed in @a v_bool member.
*/
- MHD_LIB_INFO_FIXED_HAS_COOKIE_PARSING = 14
+ MHD_LIB_INFO_FIXED_HAS_COOKIE_PARSER = 14
,
/**
* Get whether postprocessor is supported. If supported then
* #MHD_action_post_processor() can be used.
* The result is placed in @a v_bool member.
*/
- MHD_LIB_INFO_FIXED_HAS_POSTPROCESSOR = 15
+ MHD_LIB_INFO_FIXED_HAS_POST_PARSER = 15
,
/**
* Get whether HTTP "Upgrade" is supported.
diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in
@@ -1,21 +1,21 @@
/*
- This file is part of libmicrohttpd
- Copyright (C) 2006-2024 Christian Grothoff, Karlson2k (Evgeny Grin)
- (and other contributing authors)
-
- 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
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2006-2024 Christian Grothoff, Karlson2k (Evgeny Grin)
+ (and other contributing authors)
+
+ 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
*/
/*
@@ -147,6 +147,8 @@
*/
#define MHD_VERSION 0x01990001
+#ifndef MHD_BOOL_DEFINED
+
/**
* Representation of 'bool' in the public API as stdbool.h may not
* always be available and presence of 'bool' keyword may depend on
@@ -171,6 +173,10 @@ enum MHD_Bool
MHD_YES = 1
};
+
+#define MHD_BOOL_DEFINED 1
+#endif /* ! MHD_BOOL_DEFINED */
+
#ifndef MHD_STRINGS_DEFINED
@@ -574,7 +580,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Transfer encoding in request is unsupported or invalid.
*/
- MHD_SC_CHUNKED_ENCODING_UNSUPPORTED = 40067
+ MHD_SC_TRANSFER_ENCODING_UNSUPPORTED = 40067
+ ,
+ /**
+ * "Expect:" value in request is unsupported or invalid.
+ */
+ MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED = 40068
,
/**
* The given uploaded, chunked-encoded body was malformed.
@@ -639,9 +650,52 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_REQ_COOKIE_INVALID = 40163
,
/**
+ * The POST data parsed successfully, but has missing or incorrect
+ * termination.
+ * The last parsed field may have incorrect data.
+ */
+ MHD_SC_REQ_POST_PARSE_OK_BAD_TERMINATION = 40202
+ ,
+ /**
+ * Parsing of the POST data is incomplete because client used incorrect
+ * format of POST encoding.
+ * Some POST data is available or has been provided via callback.
+ */
+ MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT = 40203
+ ,
+ /**
+ * The request does not have "Content-Type:" header and POST data cannot
+ * be parsed
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE = 40280
+ ,
+ /**
+ * The request has unknown POST encoding specified by "Content-Type:" header
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE = 40281
+ ,
+ /**
+ * The request has "Content-Type: multipart/form-data" header without
+ * "boundary" parameter
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY = 40282
+ ,
+ /**
+ * The request has "Content-Type: multipart/form-data" header with misformed
+ * data
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED = 40283
+ ,
+ /**
+ * The POST data cannot be parsed because client used incorrect format
+ * of POST encoding.
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT = 40290
+ ,
+ /**
* The request cannot be processed. Sending error reply.
*/
- MHD_SC_REQ_PROCCESSING_ERR_REPLY = 40200
+ MHD_SC_REQ_PROCCESSING_ERR_REPLY = 41000
,
/* 50000-level errors are because of an error internal
@@ -1104,6 +1158,26 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
,
/**
+ * The request POST data cannot be parsed because stream has not enough
+ * pool memory free.
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM = 50260
+ ,
+ /**
+ * The POST data cannot be parsed completely because no "large shared buffer"
+ * space is available.
+ * Some POST data may be parsed.
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM = 50261
+ ,
+ /**
+ * The application set POST encoding to "multipart/form-data", but the request
+ * has no "Content-Type: multipart/form-data" header which is required
+ * to find "boundary" used in this encoding
+ */
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART = 50284
+ ,
+ /**
* The feature is not supported by this MHD build (either
* disabled by configure parameters or build platform
* did not support it, because headers are missing or
@@ -1577,7 +1651,6 @@ MHD_FN_CONST_;
* See also: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2
* @{
*/
-
enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_HTTP_PostEncoding
{
/**
@@ -3341,35 +3414,43 @@ struct MHD_EarlyUriCbData
struct MHD_Request *request;
/**
- * Pointer to the application context for the request.
- * Modifiable. Initially to NULL.
+ * The full URI ("request target") from the HTTP request, including URI
+ * parameters (the part after '?')
*/
- void *request_app_context;
+ struct MHD_String full_uri;
+
+ /**
+ * The request HTTP method
+ */
+ enum MHD_HTTP_Method method;
};
/**
* Function called by MHD to allow the application to log the @a full_uri
* of the new request.
- * If this callback is set then it is the first application function called
- * for the new request.
* This is the only moment when unmodified URI is provided.
* After this callback MHD parses the URI and modifies it by extracting
* GET parameters in-place.
- * If #MHD_RequestTerminationCallback is set then it is guaranteed that
- * #MHD_RequestTerminationCallback is called for the same request. Application
+ *
+ * If this callback is set then it is the first application function called
+ * for the new request.
+ *
+ * If #MHD_RequestEndedCallback is also set then it is guaranteed that
+ * #MHD_RequestEndedCallback is called for the same request. Application
* may allocate request specific data in this callback and de-allocate
- * the data in #MHD_RequestTerminationCallback.
+ * the data in #MHD_RequestEndedCallback.
*
* @param cls client-defined closure
- * @param full_uri the full URI ("request target") from the HTTP request
- * including parameters (the part after '?')
- * @param[in,out] req_data the request data
+ * @param req_data the request data
+ * @param request_app_context_ptr the pointer to variable that can be set to
+ * the application context for the request;
+ * initially the variable set to NULL
*/
typedef void
-(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_INOUT_ (3)
+(MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (3)
*MHD_EarlyUriLogCallback)(void *cls,
- const struct MHD_String *full_uri,
- struct MHD_EarlyUriCbData *req_data);
+ const struct MHD_EarlyUriCbData *req_data,
+ void **request_app_context_ptr);
/**
@@ -3531,31 +3612,31 @@ typedef void
/**
- * The `enum MHD_RequestTerminationCode` specifies reasons
- * why a request has been terminated (or completed).
+ * The `enum MHD_RequestEndedCode` specifies reasons
+ * why a request has been ended.
* @ingroup request
*/
-enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestTerminationCode
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
{
/**
* The response was successfully sent.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_COMPLETED_OK = 0
+ MHD_REQUEST_ENDED_COMPLETED_OK = 0
,
/**
* No activity on the connection for the number of seconds specified using
* #MHD_C_OPTION_TIMEOUT().
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_TIMEOUT_REACHED = 10
+ MHD_REQUEST_ENDED_TIMEOUT_REACHED = 10
,
/**
* The connection was broken or TLS protocol error.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_CONNECTION_ERROR = 20
+ MHD_REQUEST_ENDED_CONNECTION_ERROR = 20
,
/**
* The client terminated the connection by closing the socket either
@@ -3563,44 +3644,44 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestTerminationCode
* request.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_CLIENT_ABORT = 30
+ MHD_REQUEST_ENDED_CLIENT_ABORT = 30
,
/**
* The request is not valid according to
* HTTP specifications.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR = 31
+ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR = 31
,
/**
* The application aborted request without response.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_BY_APP_ABORT = 40
+ MHD_REQUEST_ENDED_BY_APP_ABORT = 40
,
/**
* The application aborted request without response.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_BY_APP_ERROR = 41
+ MHD_REQUEST_ENDED_BY_APP_ERROR = 41
,
/**
* Error handling the connection due to resources exhausted.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_NO_RESOURCES = 50
+ MHD_REQUEST_ENDED_NO_RESOURCES = 50
,
/**
* Closing the session since MHD is being shut down.
* @ingroup request
*/
- MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN = 60
+ MHD_REQUEST_ENDED_DAEMON_SHUTDOWN = 60
};
/**
- * Additional information about request termination
+ * Additional information about request ending
*/
-union MHD_RequestTerminationDetail
+union MHD_RequestEndedDetail
{
/**
* Reserved member.
@@ -3612,21 +3693,21 @@ union MHD_RequestTerminationDetail
/**
* Request termination data structure
*/
-struct MHD_RequestTerminationData
+struct MHD_RequestEndedData
{
/**
- * The code of the event
+ * The request handle.
+ * Note that most of the request data may be already unvailable.
*/
- enum MHD_RequestTerminationCode code;
+ struct MHD_Request *req;
/**
- * Detailed information about termination event
+ * The code of the event
*/
- union MHD_RequestTerminationDetail details;
+ enum MHD_RequestEndedCode code;
/**
- * Pointer to the application context for the request.
- * NULL unless other value set by application when processing the request.
+ * Detailed information about the event
*/
- void *request_app_context;
+ union MHD_RequestEndedDetail details;
};
@@ -3634,16 +3715,20 @@ struct MHD_RequestTerminationData
* Signature of the callback used by MHD to notify the application
* about completed requests.
*
+ * This is the last callback called for any request (if provided by
+ * the application).
+ *
* @param cls client-defined closure
* @param data the details about the event
- * @param request_context request context value, as originally
- * returned by the #MHD_EarlyUriLogCallback
+ * @param request_app_context the application request context, as possibly set
+ by the #MHD_EarlyUriLogCallback
* @see #MHD_R_OPTION_TERMINATION_CALLBACK()
* @ingroup request
*/
typedef void
-(*MHD_RequestTerminationCallback) (void *cls,
- struct MHD_RequestTerminationData *data);
+(*MHD_RequestEndedCallback) (void *cls,
+ const struct MHD_RequestEndedData *data,
+ void *request_app_context);
#include "microhttpd2_generated_response_options.h"
diff --git a/src/include/r_options.rec b/src/include/r_options.rec
@@ -20,9 +20,26 @@
Name: REUSABLE
Value: 20
Type: enum MHD_Bool
-Comment: Make the response object re-usable. (FIXME: not used in struct ResponseOptions; remove!?)
+Comment: Make the response object re-usable.
+ The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy().
+ Useful if the same response is often used to reply.
+CustomSetter: /* custom setter */
++ if (response->reuse.reusable)
++ {
++ if (MHD_NO == option->val.reusable)
++ {
++ res = MHD_SC_RESPONSE_CANNOT_CLEAR_REUSE;
++ i = options_max_num - 1;
++ break;
++ }
++ }
++ else if ((MHD_NO != option->val.reusable) &&
++ (! response_make_reusable(response)))
++ {
++ res = MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
++ i = options_max_num - 1;
++ break;
++ }
# Content control
@@ -92,10 +109,10 @@ Comment: Disable sanity check preventing clients from manually setting the HTTP
Name: termination_callback
Value: 121
-Type: struct MHD_ResponeOptionValueTermCB
+Type: struct MHD_ResponeOptionValueEndedCB
Comment: Set a function to be called once MHD is finished with the request.
-Argument1: MHD_RequestTerminationCallback term_cb
+Argument1: MHD_RequestEndedCallback ended_cb
Description1: the function to call,
+ NULL to not use the callback
-Argument2: void *term_cb_cls
+Argument2: void *ended_cb_cls
Description2: the closure for the callback
diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am
@@ -36,6 +36,7 @@ libmicrohttpd2_la_SOURCES = \
mhd_sockets_funcs.c mhd_sockets_funcs.h \
mhd_socket_error.c mhd_socket_error.h \
mhd_atomic_counter.c mhd_atomic_counter.h \
+ mhd_bool.h \
mhd_str.c mhd_str.h \
mhd_str_macros.h mhd_str_types.h \
mhd_buffer.h \
@@ -90,6 +91,15 @@ libmicrohttpd2_la_SOURCES += \
compat_calloc.c
endif
+post_parser_files = \
+ http_post_enc.h \
+ mhd_post_parser.h mhd_post_result.h mhd_postfield_int.h \
+ post_parser_funcs.c post_parser_funcs.h
+
+if HAVE_POST_PARSER
+ libmicrohttpd2_la_SOURCES += $(post_parser_files)
+endif
+
libmicrohttpd2_la_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \
@@ -105,6 +115,18 @@ libmicrohttpd2_la_LIBADD = \
$(MHD_LIBDEPS) $(MHD_TLS_LIBDEPS)
+INCL_SRC_DIR = $(srcdir)/../include
+
+# $(srcdir)/daemon_options.h - excluded to allow parallel builds in this dir
+$(srcdir)/daemon_set_options.c: $(INCL_SRC_DIR)/d_options.rec $(INCL_SRC_DIR)/options-generator.c
+ @echo "cd $(INCL_SRC_DIR) && $(MAKE) $(AM_MAKEFLAGS) update-daemon-gen-files" && \
+ $(am__cd) $(INCL_SRC_DIR) && $(MAKE) $(AM_MAKEFLAGS) update-daemon-gen-files
+
+# $(srcdir)/response_options.h - excluded to allow parallel builds in this dir
+$(srcdir)/response_set_options.c: $(INCL_SRC_DIR)/r_options.rec $(INCL_SRC_DIR)/options-generator.c
+ @echo "cd $(INCL_SRC_DIR) && $(MAKE) $(AM_MAKEFLAGS) update-response-gen-files" && \
+ $(am__cd) $(INCL_SRC_DIR) && $(MAKE) $(AM_MAKEFLAGS) update-response-gen-files
+
AM_V_RC = $(am__v_RC_@AM_V@)
am__v_RC_ = $(am__v_RC_@AM_DEFAULT_V@)
am__v_RC_0 = @echo " RC " $@;
@@ -132,8 +154,6 @@ 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
@@ -151,6 +171,8 @@ EXTRA_DIST = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libmicrohttpd2.pc
+.NOTPARALLEL: $(srcdir)/daemon_options.h $(srcdir)/daemon_set_options.c $(srcdir)/response_options.h $(srcdir)/response_set_options.c
+
$(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
diff --git a/src/mhd2/action.c b/src/mhd2/action.c
@@ -32,6 +32,7 @@
#include "daemon_logger.h"
#include "response_funcs.h"
+#include "response_destroy.h"
#include "mhd_public_api.h"
@@ -61,6 +62,7 @@ MHD_action_from_response (struct MHD_Request *request,
return (const struct MHD_Action *) NULL;
mhd_response_check_frozen_freeze (response);
+ mhd_response_inc_use_count (response);
head_act->act = mhd_ACTION_RESPONSE;
head_act->data.response = response;
@@ -107,15 +109,16 @@ MHD_action_process_upload (struct MHD_Request *request,
MHD_EXTERN_
MHD_FN_PAR_NONNULL_ (1) const struct MHD_Action *
-MHD_action_post_processor (struct MHD_Request *request,
- size_t pp_buffer_size,
- size_t pp_stream_limit,
- enum MHD_HTTP_PostEncoding enc,
- MHD_PostDataReader reader,
- void *reader_cls,
- MHD_PostDataFinished done_cb,
- void *done_cb_cls)
+MHD_action_parse_post (struct MHD_Request *request,
+ size_t buffer_size,
+ size_t max_nonstream_size,
+ enum MHD_HTTP_PostEncoding enc,
+ MHD_PostDataReader stream_reader,
+ void *reader_cls,
+ MHD_PostDataFinished done_cb,
+ void *done_cb_cls)
{
+#ifdef HAVE_POST_PARSER
struct MHD_Action *const restrict head_act =
&(request->app_act.head_act);
if (mhd_ACTION_NO_ACTION != head_act->act)
@@ -123,20 +126,26 @@ MHD_action_post_processor (struct MHD_Request *request,
if (NULL == done_cb)
return (const struct MHD_Action *) NULL;
- head_act->act = mhd_ACTION_POST_PROCESS;
- head_act->data.post_process.pp_buffer_size = pp_buffer_size;
- head_act->data.post_process.pp_stream_limit = pp_stream_limit;
- head_act->data.post_process.enc = enc;
- head_act->data.post_process.reader = reader;
- head_act->data.post_process.reader_cls = reader_cls;
- head_act->data.post_process.done_cb = done_cb;
- head_act->data.post_process.done_cb_cls = done_cb_cls;
+ head_act->act = mhd_ACTION_POST_PARSE;
+ head_act->data.post_parse.buffer_size = buffer_size;
+ head_act->data.post_parse.max_nonstream_size = max_nonstream_size;
+ head_act->data.post_parse.enc = enc;
+ head_act->data.post_parse.stream_reader = stream_reader;
+ head_act->data.post_parse.reader_cls = reader_cls;
+ head_act->data.post_parse.done_cb = done_cb;
+ head_act->data.post_parse.done_cb_cls = done_cb_cls;
return head_act;
+#else /* ! HAVE_POST_PARSER */
+ (void) request; (void) buffer_size; (void) max_nonstream_size;
+ (void) enc; (void) stream_reader; (void) reader_cls;
+ (void) done_cb; (void) done_cb_cls;
+ return NULL;
+#endif /* ! HAVE_POST_PARSER */
}
-MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
const struct MHD_UploadAction *
MHD_upload_action_suspend (struct MHD_Request *request)
{
@@ -158,10 +167,13 @@ MHD_upload_action_from_response (struct MHD_Request *request,
{
struct MHD_UploadAction *const restrict upl_act =
&(request->app_act.upl_act);
+ if (NULL == response)
+ return (const struct MHD_UploadAction *) NULL;
if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
return (const struct MHD_UploadAction *) NULL;
mhd_response_check_frozen_freeze (response);
+ mhd_response_inc_use_count (response);
upl_act->act = mhd_UPLOAD_ACTION_RESPONSE;
upl_act->data.response = response;
@@ -170,7 +182,8 @@ MHD_upload_action_from_response (struct MHD_Request *request,
}
-MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ const struct MHD_UploadAction *
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) const struct MHD_UploadAction *
MHD_upload_action_continue (struct MHD_Request *request)
{
struct MHD_UploadAction *const restrict upl_act =
diff --git a/src/mhd2/conn_data_process.c b/src/mhd2/conn_data_process.c
@@ -49,16 +49,20 @@
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
{
+ /* The "send-ready" state is known if system polling call is edge-triggered
+ (it always checks for both send- and recv-ready) or if connection needs
+ sending (therefore "send-ready" was explicitly checked by sockets polling
+ call). */
const bool send_ready_state_known =
((mhd_D_IS_USING_EDGE_TRIG (c->daemon)) ||
- (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)));
+ (0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info)));
const bool has_sock_err =
(0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready));
bool data_processed;
data_processed = false;
- if (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
+ if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
{
bool use_recv;
use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready));
@@ -74,7 +78,7 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
}
}
- if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))
+ if (0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info))
{
bool use_send;
/* Perform sending if:
@@ -107,8 +111,8 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
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) &&
+ * cannot be in MHD_EVENT_LOOP_INFO_SEND state. */
+ if ( (MHD_EVENT_LOOP_INFO_SEND == c->event_loop_info) &&
write_ready)
{
MHD_connection_handle_write (c);
@@ -172,7 +176,7 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
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)) )
+ (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)) )
c->daemon->data_already_pending = true;
#endif /* HTTPS_SUPPORT */
}
diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c
@@ -83,7 +83,7 @@ connection_set_initial_state (struct MHD_Connection *restrict c)
mhd_assert (MHD_CONNECTION_INIT == c->state);
c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
- c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
memset (&c->rq, 0, sizeof(c->rq));
memset (&c->rp, 0, sizeof(c->rp));
@@ -203,7 +203,7 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
#endif /* MHD_USE_THREADS */
connection->daemon = daemon;
connection->connection_timeout_ms = daemon->conns.cfg.timeout;
- connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
if (0 != connection->connection_timeout_ms)
connection->last_activity = MHD_monotonic_msec_counter ();
@@ -918,7 +918,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_conn_close_final (struct MHD_Connection *restrict c)
{
- mhd_assert (c->dbg.pre_closed);
+ mhd_assert (c->dbg.closing_started);
mhd_assert (c->dbg.pre_cleaned);
mhd_assert (NULL == c->rp.response);
mhd_assert (! c->rq.app_aware);
diff --git a/src/mhd2/daemon_create.c b/src/mhd2/daemon_create.c
@@ -40,6 +40,9 @@
#include "mhd_lib_init.h"
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) (~((size_t) 0)))
+#endif
MHD_FN_MUST_CHECK_RESULT_ MHD_EXTERN_ struct MHD_Daemon *
MHD_daemon_create (MHD_RequestCallback req_cb,
@@ -89,6 +92,8 @@ MHD_daemon_create (MHD_RequestCallback req_cb,
d->log_params.v_log_cb = NULL; /* optional */
#endif /* !HAVE_NULL_PTR_ALL_ZEROS */
+ s->large_pool_size = SIZE_MAX; /* The impossible value */
+
s->listen_socket = MHD_INVALID_SOCKET;
s->fd_number_limit = MHD_INVALID_SOCKET;
diff --git a/src/mhd2/daemon_funcs.c b/src/mhd2/daemon_funcs.c
@@ -99,6 +99,33 @@ mhd_daemon_claim_lbuf (struct MHD_Daemon *d,
}
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_MUST_CHECK_RESULT_ size_t
+mhd_daemon_claim_lbuf_up_to (struct MHD_Daemon *d,
+ size_t requested_size)
+{
+ size_t ret;
+ struct MHD_Daemon *const masterd = mhd_daemon_get_master_daemon (d);
+ mhd_assert (0 != requested_size);
+ if (0 == masterd->req_cfg.large_buf.space_left)
+ return false; /* Shortcut for typical use without large buffer */
+
+ mhd_mutex_lock_chk (&(masterd->req_cfg.large_buf.lock));
+ if (masterd->req_cfg.large_buf.space_left >= requested_size)
+ {
+ ret = requested_size;
+ masterd->req_cfg.large_buf.space_left -= requested_size;
+ }
+ else
+ {
+ ret = masterd->req_cfg.large_buf.space_left;
+ masterd->req_cfg.large_buf.space_left = 0;
+ }
+ mhd_mutex_unlock_chk (&(masterd->req_cfg.large_buf.lock));
+ return ret;
+}
+
+
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_daemon_reclaim_lbuf (struct MHD_Daemon *d,
size_t reclaimed_size)
@@ -120,11 +147,11 @@ mhd_daemon_get_lbuf (struct MHD_Daemon *restrict d,
if (! mhd_daemon_claim_lbuf (d, requested_size))
{
buf->size = 0;
- buf->buf = NULL;
+ buf->data = NULL;
return false;
}
- buf->buf = (char *) malloc (requested_size);
- if (NULL == buf->buf)
+ buf->data = (char *) malloc (requested_size);
+ if (NULL == buf->data)
{
buf->size = 0;
mhd_daemon_reclaim_lbuf (d, requested_size);
@@ -142,29 +169,83 @@ mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
struct mhd_Buffer *restrict buf)
{
void *new_alloc;
- mhd_assert (NULL != buf->buf || 0 == buf->size);
- mhd_assert (0 != buf->size || NULL == buf->buf);
+ mhd_assert (NULL != buf->data || 0 == buf->size);
+ mhd_assert (0 != buf->size || NULL == buf->data);
if (! mhd_daemon_claim_lbuf (d, grow_size))
return false;
- if (NULL == buf->buf)
+ if (NULL == buf->data)
new_alloc = malloc (grow_size);
else
- new_alloc = realloc (buf->buf, buf->size + grow_size);
+ new_alloc = realloc (buf->data, buf->size + grow_size);
if (NULL == new_alloc)
{
mhd_daemon_reclaim_lbuf (d, grow_size);
return false;
}
- buf->buf = (char *) new_alloc;
+ buf->data = (char *) new_alloc;
buf->size += grow_size;
return true;
}
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+MHD_FN_PAR_INOUT_ (3) size_t
+mhd_daemon_extend_lbuf_up_to (struct MHD_Daemon *restrict d,
+ size_t desired_grow_size,
+ struct mhd_Buffer *restrict buf)
+{
+ void *new_alloc;
+ size_t grow_size;
+ mhd_assert (NULL != buf->data || 0 == buf->size);
+ mhd_assert (0 != buf->size || NULL == buf->data);
+ mhd_assert (0 != desired_grow_size);
+
+ grow_size = mhd_daemon_claim_lbuf_up_to (d, desired_grow_size);
+
+ new_alloc = NULL;
+ if (NULL == buf->data)
+ {
+ while ((0 != grow_size) &&
+ (NULL == (new_alloc = malloc (grow_size))))
+ {
+ size_t reduce = grow_size / 2;
+ if (sizeof(void *) >= (grow_size - reduce))
+ reduce = grow_size;
+ mhd_daemon_reclaim_lbuf (d,
+ reduce);
+ grow_size -= reduce;
+ }
+ }
+ else
+ {
+ while ((0 != grow_size) &&
+ (NULL == (new_alloc = realloc (buf->data,
+ buf->size + grow_size))))
+ {
+ size_t reduce = grow_size / 2;
+ if (sizeof(void *) >= (grow_size - reduce))
+ reduce = grow_size;
+ mhd_daemon_reclaim_lbuf (d,
+ reduce);
+ grow_size -= reduce;
+ }
+ }
+
+ if (NULL != new_alloc)
+ {
+ mhd_assert (0 != grow_size);
+ buf->data = new_alloc;
+ buf->size += grow_size;
+ }
+
+ return grow_size;
+}
+
+
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_INOUT_ (2) void
mhd_daemon_free_lbuf (struct MHD_Daemon *restrict d,
@@ -172,11 +253,11 @@ mhd_daemon_free_lbuf (struct MHD_Daemon *restrict d,
{
if (0 == buf->size)
{
- mhd_assert (NULL == buf->buf);
+ mhd_assert (NULL == buf->data);
return;
}
- free (buf->buf);
- buf->buf = NULL;
+ free (buf->data);
+ buf->data = NULL;
mhd_daemon_reclaim_lbuf (d, buf->size);
buf->size = 0;
}
diff --git a/src/mhd2/daemon_funcs.h b/src/mhd2/daemon_funcs.h
@@ -123,6 +123,22 @@ mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
MHD_FN_PAR_INOUT_ (3);
+/**
+ * Grow or allocate the large buffer up to specified grow size.
+ * If the requested grow size is not possible, grow up to max possible size.
+ * @param d the daemon to use
+ * @param desired_grow_size the desired size of grow
+ * @param[in,out] buf the buffer to grow, must be allocated previously or
+ * zero-initialised
+ * @return the resulting grow size
+ */
+MHD_INTERNAL size_t
+mhd_daemon_extend_lbuf_up_to (struct MHD_Daemon *restrict d,
+ size_t desired_grow_size,
+ struct mhd_Buffer *restrict buf)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+ MHD_FN_PAR_INOUT_ (3);
+
/**
* Free large buffer.
diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c
@@ -3,11 +3,10 @@
/* *INDENT-OFF* */
/**
* @file daemon_set_options.c
- * @author daemon-options-generator.c
+ * @author options-generator.c
*/
#include "mhd_sys_options.h"
-#include "sys_bool_type.h"
#include "sys_base_types.h"
#include "sys_malloc.h"
#include <string.h>
@@ -15,6 +14,7 @@
#include "daemon_options.h"
#include "mhd_public_api.h"
+
MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_
enum MHD_StatusCode
MHD_daemon_set_options (
diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c
@@ -1448,9 +1448,18 @@ daemon_init_large_buf (struct MHD_Daemon *restrict d,
struct DaemonOptions *restrict s)
{
mhd_assert (! mhd_D_HAS_MASTER (d));
+ mhd_assert (0 != d->conns.cfg.count_limit);
+ mhd_assert (0 != d->conns.cfg.mem_pool_size);
+
d->req_cfg.large_buf.space_left = s->large_pool_size;
- if (0 == d->req_cfg.large_buf.space_left) // TODO: USE SETTINGS!
- d->req_cfg.large_buf.space_left = 1024 * 1024U; // TODO: USE SETTINGS!
+ if (SIZE_MAX == d->req_cfg.large_buf.space_left)
+ d->req_cfg.large_buf.space_left =
+ (d->conns.cfg.count_limit * d->conns.cfg.mem_pool_size) / 32; /* Use ~3% of the maximum memory used by connections */
+
+#ifndef NDEBUG
+ d->dbg.initial_lbuf_size = d->req_cfg.large_buf.space_left;
+#endif
+
if (! mhd_mutex_init_short (&(d->req_cfg.large_buf.lock)))
{
mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
@@ -1471,6 +1480,8 @@ daemon_init_large_buf (struct MHD_Daemon *restrict d,
static MHD_FN_PAR_NONNULL_ (1) void
daemon_deinit_large_buf (struct MHD_Daemon *restrict d)
{
+ /* All large buffer allocations must be freed / deallocated earlier */
+ mhd_assert (d->dbg.initial_lbuf_size == d->req_cfg.large_buf.space_left);
mhd_mutex_destroy_chk (&(d->req_cfg.large_buf.lock));
}
@@ -2415,16 +2426,18 @@ daemon_init_threading_and_conn (struct MHD_Daemon *restrict d,
if (! mhd_D_TYPE_HAS_WORKERS (d->threading.d_type))
res = init_individual_thread_data_events_conns (d, s);
+#ifdef MHD_USE_THREADS
else
{
-#ifdef MHD_USE_THREADS
res = init_workers_pool (d, s);
-#else /* ! MHD_USE_THREADS */
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- return MHD_SC_INTERNAL_ERROR;
-#endif /* ! MHD_USE_THREADS */
+ if (MHD_SC_OK == res)
+ {
+ /* Copy some settings to the master daemon */
+ d->conns.cfg.mem_pool_size =
+ d->threading.hier.pool.workers[0].conns.cfg.mem_pool_size;
+ }
}
+#endif /* ! MHD_USE_THREADS */
if (MHD_SC_OK == res)
{
mhd_assert (d->dbg.events_allocated || \
@@ -2847,7 +2860,6 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon)
{
mhd_assert (NULL != daemon->settings);
dsettings_release (daemon->settings);
- return;
}
else if (! has_failed)
{
diff --git a/src/mhd2/dcc_action.c b/src/mhd2/dcc_action.c
@@ -132,8 +132,7 @@ MHD_DCC_action_finish_with_footer (
MHD_EXTERN_
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_RETURNS_NONNULL_ const struct MHD_DynamicContentCreatorAction *
+MHD_FN_PAR_NONNULL_ (1) const struct MHD_DynamicContentCreatorAction *
MHD_DCC_action_suspend (struct MHD_DynamicContentCreatorContext *ctx)
{
struct MHD_DynamicContentCreatorAction *ret;
diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c
@@ -99,7 +99,7 @@ update_conn_net_status (struct MHD_Daemon *restrict d,
if ((0 !=
(((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info)
- & (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_WRITE)))
+ & (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_SEND)))
|| err_state)
mhd_conn_mark_ready (c, d);
else
@@ -246,7 +246,10 @@ daemon_process_all_active_conns (struct MHD_Daemon *restrict d)
struct MHD_Connection *next;
next = mhd_DLINKEDL_GET_NEXT (c, proc_ready); /* The current connection can be closed */
if (! mhd_conn_process_recv_send_data (c))
+ {
+ mhd_conn_pre_clean (c);
mhd_conn_close_final (c);
+ }
c = next;
}
@@ -259,13 +262,19 @@ close_all_daemon_conns (struct MHD_Daemon *d)
{
struct MHD_Connection *c;
- for (c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn);
- NULL != c;
- c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn))
+ if (! mhd_D_HAS_THR_PER_CONN (d))
{
- mhd_conn_pre_close_d_shutdown (c);
- mhd_conn_close_final (c);
+ for (c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn);
+ NULL != c;
+ c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn))
+ {
+ mhd_conn_pre_close_d_shutdown (c);
+ mhd_conn_pre_clean (c);
+ mhd_conn_close_final (c);
+ }
}
+ else
+ mhd_assert (0 && "Not implemented yet");
}
@@ -369,12 +378,12 @@ select_update_fdsets (struct MHD_Daemon *restrict d,
{
mhd_assert (MHD_CONNECTION_CLOSED != c->state);
- if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
+ if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_RECV))
fd_set_wrap (c->socket_fd,
rfds,
&ret,
d);
- if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE))
+ if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_SEND))
fd_set_wrap (c->socket_fd,
wfds,
&ret,
@@ -619,9 +628,9 @@ poll_update_fds (struct MHD_Daemon *restrict d,
d->events.data.poll.fds[i_c].fd = c->socket_fd;
d->events.data.poll.rel[i_c].connection = c;
events = 0;
- if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
+ if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_RECV))
events |= MHD_POLL_IN;
- if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE))
+ if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_SEND))
events |= MHD_POLL_OUT;
d->events.data.poll.fds[i_c].events = (short) events;
@@ -735,20 +744,20 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
if (0 != (revents & POLLHUP))
{ /* This can be a disconnect OR remote side set SHUT_WR */
recv_ready = true; /* Check the socket by reading */
- if (0 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
+ if (0 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_RECV))
err_state = true; /* The socket will not be checked by reading, the only way to avoid spinning */
}
#endif
if (0 != (revents & (MHD_POLLPRI | MHD_POLLRDBAND)))
{ /* Statuses were not requested, but returned */
if (! recv_ready ||
- (0 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ)))
+ (0 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_RECV)))
err_state = true; /* The socket will not be read, the only way to avoid spinning */
}
if (0 != (revents & MHD_POLLWRBAND))
{ /* Status was not requested, but returned */
if (! send_ready ||
- (0 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE)))
+ (0 == (c->event_loop_info & MHD_EVENT_LOOP_INFO_SEND)))
err_state = true; /* The socket will not be written, the only way to avoid spinning */
}
diff --git a/src/mhd2/http_post_enc.h b/src/mhd2/http_post_enc.h
@@ -0,0 +1,81 @@
+/*
+ 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_post_enc.h
+ * @brief The definition of the enum for POST encoding types
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_HTTP_POST_ENC_H
+#define MHD_HTTP_POST_ENC_H 1
+
+#include "mhd_sys_options.h"
+
+
+#ifndef MHD_HTTP_POSTENCODING_DEFINED
+
+/**
+ * @brief Possible encodings for HTML forms submitted as HTTP POST requests
+ *
+ * @defgroup postenc HTTP POST encodings
+ * See also: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2
+ * @{
+ */
+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 */
+
+
+
+#endif /* ! MHD_HTTP_POST_ENC_H */
diff --git a/src/mhd2/mhd_action.h b/src/mhd2/mhd_action.h
@@ -33,6 +33,12 @@
#include "mhd_str_types.h"
+#ifdef HAVE_POST_PARSER
+# include "http_post_enc.h"
+# include "mhd_bool.h"
+# include "mhd_post_result.h"
+#endif
+
/**
* The type of the action requested by application
@@ -53,11 +59,13 @@ enum mhd_ActionType
* Process clients upload by application callback
*/
mhd_ACTION_UPLOAD
+#ifdef HAVE_POST_PARSER
,
/**
- * Process clients upload by POST processor
+ * Process POST data clients upload by POST parser
*/
- mhd_ACTION_POST_PROCESS
+ mhd_ACTION_POST_PARSE
+#endif /* HAVE_POST_PARSER */
,
/**
* Suspend requests (connection)
@@ -129,78 +137,73 @@ struct mhd_UploadCallbacks
struct mhd_UploadCallbackData inc;
};
+#ifdef HAVE_POST_PARSER
#ifndef MHD_POST_DATA_READER_DEFINED
typedef const struct MHD_UploadAction *
-(*MHD_PostDataReader) (void *cls,
+(MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_NONNULL_ (4)
+ MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_NONNULL_ (6)
+ *MHD_PostDataReader) (struct MHD_Request *req,
+ void *cls,
const struct MHD_String *name,
- const struct MHD_String *filename,
- const struct MHD_String *content_type,
- const struct MHD_String *encoding,
+ const struct MHD_StringNullable *filename,
+ const struct MHD_StringNullable *content_type,
+ const struct MHD_StringNullable *encoding,
+ size_t size,
const void *data,
uint_fast64_t off,
- size_t size);
-
+ enum MHD_Bool final_data);
typedef const struct MHD_UploadAction *
-(*MHD_PostDataFinished) (struct MHD_Request *req,
- void *cls);
+(MHD_FN_PAR_NONNULL_ (1)
+ *MHD_PostDataFinished) (struct MHD_Request *req,
+ void *cls,
+ enum MHD_PostParseResult parsing_result);
#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
+/**
+ * The data for performing POST action
+ */
+struct mhd_PostParseActionData
{
/**
- * No post encoding / broken data / unknown encoding
+ * The maximum size allowed for the buffers to parse the POST data.
*/
- MHD_HTTP_POST_ENCODING_OTHER = 0
- ,
+ size_t buffer_size;
/**
- * "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
+ * The size of the field (in encoded form) above which values are not
+ * buffered and incrementally "streamed"
*/
- MHD_HTTP_POST_ENCODING_FORM_URLENCODED = 1
- ,
+ size_t max_nonstream_size;
/**
- * "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
+ * The data encoding to use,
+ * #MHD_HTTP_POST_ENCODING_OTHER indicates automatic detection
*/
- MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA = 2
- ,
+ enum MHD_HTTP_PostEncoding enc;
/**
- * "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.
+ * The callback function which process values in "streaming" way.
+ * Can be NULL.
+ */
+ MHD_PostDataReader stream_reader;
+ /**
+ * The closure for the @a stream_reader
*/
- 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;
+ /**
+ * The "final" callback, called after all POST data has been parsed.
+ */
MHD_PostDataFinished done_cb;
+ /**
+ * The closure for the @a done_cb
+ */
void *done_cb_cls;
};
+#endif /* HAVE_POST_PARSER */
+
/**
* The data for the application action
*/
@@ -216,10 +219,12 @@ union mhd_ActionData
*/
struct mhd_UploadCallbacks upload;
+#ifdef HAVE_POST_PARSER
/**
- * The data for the action #mhd_ACTION_POST_PROCESS
+ * The data for the action #mhd_ACTION_POST_PARSE
*/
- struct mhd_PostProcessorActionData post_process;
+ struct mhd_PostParseActionData post_parse;
+#endif /* HAVE_POST_PARSER */
};
diff --git a/src/mhd2/mhd_bool.h b/src/mhd2/mhd_bool.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Christian Grothoff
+ 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_bool.h
+ * @brief The definition of the enum MHD_Bool, which is used in public API
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_BOOL_H
+#define MHD_BOOL_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_BOOL_DEFINED
+
+enum MHD_Bool
+{
+ /**
+ * MHD-internal return code for "NO".
+ */
+ MHD_NO = 0
+ ,
+ /**
+ * MHD-internal return code for "YES". All non-zero values
+ * will be interpreted as "YES", but MHD will only ever
+ * return #MHD_YES or #MHD_NO.
+ */
+ MHD_YES = 1
+};
+
+
+#define MHD_BOOL_DEFINED 1
+#endif /* ! MHD_BOOL_DEFINED */
+
+#endif /* ! MHD_BOOL_H */
diff --git a/src/mhd2/mhd_buffer.h b/src/mhd2/mhd_buffer.h
@@ -20,7 +20,7 @@
/**
* @file src/mhd2/mhd_buffer.h
- * @brief The definition of the MHD_Buffer type
+ * @brief The definition of the struct mhd_Buffer
* @author Karlson2k (Evgeny Grin)
*/
@@ -31,19 +31,35 @@
#include "sys_base_types.h"
/**
- * The buffer with size
+ * The buffer with the size
*/
struct mhd_Buffer
{
/**
- * The size of the allocated @a buf buffer
+ * The size of the data or the allocation pointed by @a buf buffer
*/
size_t size;
/**
- * The pointer to the allocation
+ * The pointer to the data or the allocation
*/
- char *buf;
+ char *data;
+};
+
+/**
+ * The data with the size
+ */
+struct mhd_BufferConst
+{
+ /**
+ * The size of the data pointed by @a buf buffer
+ */
+ size_t size;
+
+ /**
+ * The pointer to the data
+ */
+ const char *data;
};
#endif /* ! MHD_BUFFER_H */
diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h
@@ -79,13 +79,13 @@ 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
+ MHD_EVENT_LOOP_INFO_RECV = 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
+ MHD_EVENT_LOOP_INFO_SEND = 1 << 1
,
/**
* We are waiting for the application to provide data.
@@ -99,7 +99,7 @@ enum MHD_FIXED_FLAGS_ENUM_ MHD_ConnectionEventLoopInfo
};
#define MHD_EVENT_LOOP_INFO_PROCESS_READ \
- (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_PROCESS)
+ (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_PROCESS)
/**
@@ -201,133 +201,139 @@ 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,
-
+ MHD_CONNECTION_INIT = 0
+ ,
/**
* Part of the request line was received.
* Wait for complete line.
*/
- MHD_CONNECTION_REQ_LINE_RECEIVING,
-
+ MHD_CONNECTION_REQ_LINE_RECEIVING
+ ,
/**
* We got the URL (and request type and version). Wait for a header line.
*
* A milestone state. No received data is processed in this state.
*/
- MHD_CONNECTION_REQ_LINE_RECEIVED,
-
+ MHD_CONNECTION_REQ_LINE_RECEIVED
+ ,
/**
* Receiving request headers. Wait for the rest of the headers.
*/
- MHD_CONNECTION_REQ_HEADERS_RECEIVING,
-
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING
+ ,
/**
* We got the request headers. Process them.
*/
- MHD_CONNECTION_HEADERS_RECEIVED,
-
+ MHD_CONNECTION_HEADERS_RECEIVED
+ ,
/**
* We have processed the request headers. Call application callback.
*/
- MHD_CONNECTION_HEADERS_PROCESSED,
-
+ MHD_CONNECTION_HEADERS_PROCESSED
+ ,
/**
* We have processed the headers and need to send 100 CONTINUE.
*/
- MHD_CONNECTION_CONTINUE_SENDING,
-
+ MHD_CONNECTION_CONTINUE_SENDING
+ ,
/**
* We have sent 100 CONTINUE (or do not need to). Read the message body.
*/
- MHD_CONNECTION_BODY_RECEIVING,
-
+ MHD_CONNECTION_BODY_RECEIVING
+ ,
/**
* We got the request body.
*
* A milestone state. No received data is processed in this state.
*/
- MHD_CONNECTION_BODY_RECEIVED,
-
+ MHD_CONNECTION_BODY_RECEIVED
+ ,
/**
* We are reading the request footers.
*/
- MHD_CONNECTION_FOOTERS_RECEIVING,
-
+ MHD_CONNECTION_FOOTERS_RECEIVING
+ ,
/**
* We received the entire footer.
*
* A milestone state. No data is receiving in this state.
*/
- MHD_CONNECTION_FOOTERS_RECEIVED,
-
+ MHD_CONNECTION_FOOTERS_RECEIVED
+ ,
/**
* We received the entire request.
*
* A milestone state. No data is receiving in this state.
*/
- MHD_CONNECTION_FULL_REQ_RECEIVED,
-
+ MHD_CONNECTION_FULL_REQ_RECEIVED
+ ,
/**
* Finished receiving request data: either complete request received or
* MHD is going to send reply early, without getting full request.
*/
- MHD_CONNECTION_REQ_RECV_FINISHED,
-
+ MHD_CONNECTION_REQ_RECV_FINISHED
+ ,
/**
* Finished reading of the request and the response is ready.
* Switch internal logic from receiving to sending, prepare connection
* sending the reply and build the reply header.
*/
- MHD_CONNECTION_START_REPLY,
-
+ MHD_CONNECTION_START_REPLY
+ ,
/**
* We have prepared the response headers in the write buffer.
* Send the response headers.
*/
- MHD_CONNECTION_HEADERS_SENDING,
-
+ MHD_CONNECTION_HEADERS_SENDING
+ ,
/**
* We have sent the response headers. Get ready to send the body.
*/
- MHD_CONNECTION_HEADERS_SENT,
-
+ MHD_CONNECTION_HEADERS_SENT
+ ,
/**
* We are waiting for the client to provide more
* data of a non-chunked body.
*/
- MHD_CONNECTION_UNCHUNKED_BODY_UNREADY,
-
+ MHD_CONNECTION_UNCHUNKED_BODY_UNREADY
+ ,
/**
* We are ready to send a part of a non-chunked body. Send it.
*/
- MHD_CONNECTION_UNCHUNKED_BODY_READY,
-
+ MHD_CONNECTION_UNCHUNKED_BODY_READY
+ ,
/**
* We are waiting for the client to provide a chunk of the body.
*/
- MHD_CONNECTION_CHUNKED_BODY_UNREADY,
-
+ MHD_CONNECTION_CHUNKED_BODY_UNREADY
+ ,
/**
* We are ready to send a chunk.
*/
- MHD_CONNECTION_CHUNKED_BODY_READY,
-
+ MHD_CONNECTION_CHUNKED_BODY_READY
+ ,
/**
* We have sent the chunked response body. Prepare the footers.
*/
- MHD_CONNECTION_CHUNKED_BODY_SENT,
-
+ MHD_CONNECTION_CHUNKED_BODY_SENT
+ ,
/**
* We have prepared the response footer. Send it.
*/
- MHD_CONNECTION_FOOTERS_SENDING,
-
+ MHD_CONNECTION_FOOTERS_SENDING
+ ,
/**
* We have sent the entire reply.
* Shutdown connection or restart processing to get a new request.
*/
- MHD_CONNECTION_FULL_REPLY_SENT,
-
+ MHD_CONNECTION_FULL_REPLY_SENT
+ ,
+ /**
+ * Finished regular connection processing.
+ * Initial buffers cleanup and freeing.
+ */
+ MHD_CONNECTION_PRE_CLOSING
+ ,
/**
* This connection is to be closed.
*/
@@ -337,7 +343,7 @@ enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE
struct mhd_ConnDebugData
{
- bool pre_closed;
+ bool closing_started;
bool pre_cleaned;
};
diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h
@@ -831,6 +831,15 @@ struct mhd_DaemonRequestProcessingSettings
};
+/**
+ * Get whether bare LF in HTTP header and other protocol elements
+ * should be treated as the line termination depending on the configured
+ * strictness level.
+ * RFC 9112, section 2.2
+ */
+#define mhd_ALLOW_BARE_LF_AS_CRLF(discp_lvl) (0 >= discp_lvl)
+
+
#ifndef NDEBUG
/**
* Various debugging data
@@ -846,6 +855,7 @@ struct mhd_daemon_debug
bool threading_inited;
bool connections_inited;
bool avoid_accept4;
+ size_t initial_lbuf_size;
};
#endif /* NDEBUG */
@@ -934,7 +944,6 @@ struct MHD_Daemon
#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) \
@@ -967,3 +976,5 @@ struct MHD_Daemon
#define mhd_D_IS_USING_EDGE_TRIG(d) \
(mhd_D_IS_USING_EPOLL (d) || \
(mhd_WM_INT_EXTERNAL_EVENTS_EDGE ==((d)->wmode_int)))
+
+#endif /* ! MHD_DAEMON_H */
diff --git a/src/mhd2/mhd_dlinked_list.h b/src/mhd2/mhd_dlinked_list.h
@@ -142,11 +142,14 @@
#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_obj) != (p_list)->first); \
+ mhd_assert ((p_obj) != (p_list)->last); \
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; \
+ mhd_assert ((p_obj) != (p_list)->first->links_name.next); \
+ (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)
@@ -162,11 +165,14 @@
#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_obj) != (p_list)->first); \
+ mhd_assert ((p_obj) != (p_list)->last); \
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; \
+ mhd_assert ((p_obj) != (p_list)->last->links_name.prev); \
+ (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)
@@ -178,17 +184,21 @@
* 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) \
+#define mhd_DLINKEDL_DEL_D(p_list,p_obj,links_name) do { \
+ mhd_assert (NULL != (p_list)->first); \
+ mhd_assert (NULL != (p_list)->last); \
+ mhd_assert ((p_obj) != (p_obj)->links_name.prev); \
+ mhd_assert ((p_obj) != (p_obj)->links_name.next); \
+ 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 ((p_obj) != (p_list)->last); \
+ (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); \
+ mhd_assert ((p_obj) != (p_list)->first); \
(p_obj)->links_name.prev->links_name.next = \
(p_obj)->links_name.next; } else \
{ mhd_assert ((p_obj) == (p_list)->first); \
diff --git a/src/mhd2/mhd_post_parser.h b/src/mhd2/mhd_post_parser.h
@@ -0,0 +1,642 @@
+/*
+ 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_post_parser.h
+ * @brief The definition of the post parsers data structures
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_POST_PARSER_H
+#define MHD_POST_PARSER_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+
+#include "http_post_enc.h"
+#include "mhd_post_result.h"
+#include "mhd_buffer.h"
+
+#ifdef SIZE_MAX
+# define mhd_POST_INVALID_POS SIZE_MAX
+#else
+# define mhd_POST_INVALID_POS ((size_t) (~((size_t) (0))))
+#endif
+
+/**
+ * The states of the "application/x-www-form-urlencoded" field parsing
+ */
+enum MHD_FIXED_ENUM_ mhd_PostUrlEncState
+{
+ /**
+ * The field processing has not been started
+ */
+ mhd_POST_UENC_ST_NOT_STARTED = 0
+ ,
+ /**
+ * Processing name of the field
+ */
+ mhd_POST_UENC_ST_NAME
+ ,
+ /**
+ * At the '=' character after the name.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_UENC_ST_AT_EQ
+ ,
+ /**
+ * The '=' character after the name has been found.
+ * Looking for the first value character.
+ */
+ mhd_POST_UENC_ST_EQ_FOUND
+ ,
+ /**
+ * Processing the value of the field.
+ */
+ mhd_POST_UENC_ST_VALUE
+ ,
+ /**
+ * At the ampersand '&' character.
+ * Means that full field is found.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_UENC_ST_AT_AMPRSND
+ ,
+ /**
+ * Full field found.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_UENC_ST_FULL_FIELD_FOUND
+};
+
+/**
+ * The "application/x-www-form-urlencoded" parsing data
+ */
+struct mhd_PostParserUrlEncData
+{
+ /**
+ * The parsing state
+ */
+ enum mhd_PostUrlEncState st;
+
+ /**
+ * The index of the start of the name.
+ */
+ size_t name_idx;
+
+ /**
+ * The length of the name of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t name_len;
+
+ /**
+ * The index of the start of the value.
+ * Zero until the value is found.
+ * Cannot be zero if any (including zero-length) value available.
+ */
+ size_t value_idx;
+
+ /**
+ * The length of the value of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ * If @a st is #mhd_POST_UENC_ST_VALUE and @a value_len is not zero,
+ * then it is the length of the partial (decoded) value provided previously
+ * to the "stream" processing callback (which responded with a "suspend"
+ * action).
+ */
+ size_t value_len;
+
+ /**
+ * The index of the last percent ('%') character found.
+ * Set to #mhd_POST_INVALID_POS when no '%' char found.
+ * Used for two proposes:
+ * + indicates that "name" or "value" needs persent-deconding
+ * + helps to detect incomplete percent-encoded char for stream processing
+ */
+ size_t last_pct_idx;
+};
+
+
+/**
+ * The states of the "multipart/form-data" parsing
+ */
+enum MHD_FIXED_ENUM_ mhd_PostMPartState
+{
+ /**
+ * The parsing has not been started
+ * Should not be used outside processing loop except initial initialisation.
+ */
+ mhd_POST_MPART_ST_NOT_STARTED = 0
+ ,
+ /**
+ * Check for delimiter failed, continuing processing of the preabmle
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_MPART_ST_BACK_TO_PREAMBL
+ ,
+ /**
+ * Processing preabmle
+ */
+ mhd_POST_MPART_ST_PREAMBL
+ ,
+ /**
+ * Found CR char in the preamble
+ */
+ mhd_POST_MPART_ST_PREAMBL_CR_FOUND
+ ,
+ /**
+ * Found LF char in the preamble (after CR or just bare LF if allowed)
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_MPART_ST_PREAMBL_LINE_START
+ ,
+ /**
+ * Checking for potential delimiter marker at the start of the string
+ */
+ mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM
+ ,
+ /**
+ * Found the first delimiter.
+ * Need to find the end of the delimiter string and check for possible "final"
+ * delimiter.
+ */
+ mhd_POST_MPART_ST_FIRST_DELIM_FOUND
+ ,
+ /**
+ * Found start of the first "part"
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ */
+ mhd_POST_MPART_ST_FIRST_PART_START
+ ,
+ /**
+ * Found start of the "part" (after the delimiter)
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ */
+ mhd_POST_MPART_ST_PART_START
+ ,
+ /**
+ * Starting processing of embedded header line
+ */
+ mhd_POST_MPART_ST_HEADER_LINE_START
+ ,
+ /**
+ * Processing embedded header line
+ */
+ mhd_POST_MPART_ST_HEADER_LINE
+ ,
+ /**
+ * Found CR char in the embedded header line
+ */
+ mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND
+ ,
+ /**
+ * Found complete embedded header line, at the final character.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_MPART_ST_HEADER_LINE_END
+ ,
+ /**
+ * Starting processing of the "value"
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ */
+ mhd_POST_MPART_ST_VALUE_START
+ ,
+ /**
+ * Check for delimiter failed, continuing processing of the "value"
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Can be used outside processing loop if streaming partial value.
+ */
+ mhd_POST_MPART_ST_BACK_TO_VALUE
+ ,
+ /**
+ * Processing "value"
+ */
+ mhd_POST_MPART_ST_VALUE
+ ,
+ /**
+ * Found CR char in the "value"
+ */
+ mhd_POST_MPART_ST_VALUE_CR_FOUND
+ ,
+ /**
+ * Found LF char in the "value"
+ */
+ mhd_POST_MPART_ST_VALUE_LINE_START
+ ,
+ /**
+ * Checking for potential delimiter marker at the start of the string
+ */
+ mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM
+ ,
+ /**
+ * Found the delimiter.
+ * Need to find the end of the delimiter string and check for possible "final"
+ * delimiter.
+ */
+ mhd_POST_MPART_ST_DELIM_FOUND
+ ,
+ /**
+ * Found the end of the "value"
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_MPART_ST_VALUE_END_FOUND
+ ,
+ /**
+ * Found the end of the "value" closed by the "final" delimiter
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL
+ ,
+ /**
+ * Found the complete field
+ */
+ mhd_POST_MPART_ST_FULL_FIELD_FOUND
+ ,
+ /**
+ * Found the complete field closed by the "final" delimiter
+ */
+ mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL
+ ,
+ /**
+ * Processing "epilogue"
+ */
+ mhd_POST_MPART_ST_EPILOGUE
+ ,
+ /**
+ * The format of the input data is invalid
+ */
+ mhd_POST_MPART_ST_FORMAT_ERROR
+};
+
+
+/**
+ * The "multipart/form-data" field parsing data
+ */
+struct mhd_PostParserMPartFieldData
+{
+ /**
+ * The index of the start of the name.
+ */
+ size_t name_idx;
+ /**
+ * The length of the name of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t name_len;
+ /**
+ * The index of the start of the value.
+ * Zero until the value is found.
+ * Cannot be zero if any (including zero-length) value available.
+ */
+ size_t value_idx;
+ /**
+ * The length of the value of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t value_len;
+ /**
+ * The index of the start of the filename of the current field.
+ * Zero until the value is found.
+ * Cannot be zero if any (including zero-length) filename available.
+ */
+ size_t filename_idx;
+ /**
+ * The length of the filename of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t filename_len;
+ /**
+ * The index of the start of the value of the Content-Type of the current
+ * field.
+ * Zero until the value is found.
+ * Cannot be zero if any (including zero-length) filename available.
+ */
+ size_t cntn_type_idx;
+ /**
+ * The length of the filename of the value of the Content-Type of the current
+ * field, not including the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t cntn_type_len;
+ /**
+ * The index of the start of the value of the Content-Encoding of the current
+ * field.
+ * Zero until the value is found.
+ * Cannot be zero if any (including zero-length) filename available.
+ */
+ size_t enc_idx;
+ /**
+ * The length of the filename of the value of the Content-Encoding of
+ * the current field, not including the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t enc_len;
+};
+
+/**
+ * The "multipart/form-data" parsing data
+ */
+struct mhd_PostParserMPartFormData
+{
+ /**
+ * The parsing state
+ */
+ enum mhd_PostMPartState st;
+
+ /**
+ * The field parsing data
+ */
+ struct mhd_PostParserMPartFieldData f;
+
+ /**
+ * Position of the first character when checking for the delimiter or for
+ * the embedded header
+ */
+ size_t line_start;
+
+ /**
+ * The first position where the check for the delimiter has been started.
+ * Should be CR char (or bare LR if allowed).
+ * If delimiter is not found, re-interpreted as part of the filed "value".
+ * If delimiter is found, this position can be moved to the second character
+ * if the first position of the delimiter is used to put zero-termination
+ * of previous field "value".
+ */
+ size_t delim_check_start;
+
+ /**
+ * The boundary marker.
+ * Allocated in the stream's memory pool
+ */
+ struct mhd_BufferConst bound;
+};
+
+
+/**
+ * The states of the "text/plain" parsing
+ */
+enum MHD_FIXED_ENUM_ mhd_PostTextState
+{
+ /**
+ * The line processing has not been started yet
+ */
+ mhd_POST_TEXT_ST_NOT_STARTED = 0
+ ,
+ /**
+ * Processing name of the field
+ */
+ mhd_POST_TEXT_ST_NAME
+ ,
+ /**
+ * At the '=' character after the name.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_TEXT_ST_AT_EQ
+ ,
+ /**
+ * The '=' character after the name has been found.
+ * Looking for the first value character.
+ */
+ mhd_POST_TEXT_ST_EQ_FOUND
+ ,
+ /**
+ * Processing the value of the field.
+ */
+ mhd_POST_TEXT_ST_VALUE
+ ,
+ /**
+ * At the CR character.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_TEXT_ST_AT_CR
+ ,
+ /**
+ * Looking for LF character after CR character.
+ */
+ mhd_POST_TEXT_ST_CR_FOUND
+ ,
+ /**
+ * At the LF character without preceding CR character.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_TEXT_ST_AT_LF_BARE
+ ,
+ /**
+ * End of the line found.
+ * This is an intermediate state, should be processed and switched to the next
+ * state immediately.
+ * Should not be used outside processing loop.
+ */
+ mhd_POST_TEXT_ST_FULL_LINE_FOUND
+};
+
+/**
+ * The "text/plain" parsing data
+ */
+struct mhd_PostParserTextData
+{
+ /**
+ * The parsing state
+ */
+ enum mhd_PostTextState st;
+
+ /**
+ * The index of the start of the name.
+ */
+ size_t name_idx;
+
+ /**
+ * The length of the name of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t name_len;
+
+ /**
+ * The index of the start of the value.
+ * Zero until the value is found.
+ * Cannot be zero if any (including zero-length) value available.
+ */
+ size_t value_idx;
+
+ /**
+ * The length of the value of the current field, not including
+ * the terminating zero.
+ * Zero until the length is found.
+ */
+ size_t value_len;
+};
+
+
+/**
+ * The encoding-specific parsing data
+ */
+union mhd_PostParserDetailedData
+{
+ /**
+ * The "application/x-www-form-urlencoded" parsing data
+ */
+ struct mhd_PostParserUrlEncData u_enc;
+
+ /**
+ * The "multipart/form-data" parsing data
+ */
+ struct mhd_PostParserMPartFormData m_form;
+
+ /**
+ * The "text/plain" parsing data
+ */
+ struct mhd_PostParserTextData text;
+};
+
+// TODO: remove?
+/**
+ * The type of partially processed data in the buffer
+ */
+enum MHD_FIXED_ENUM_ mhd_PostParserPartProcType
+{
+ /**
+ * No data in the buffer
+ */
+ mhd_POST_PARSER_PART_PROC_TYPE_NONE = 0
+ ,
+ /**
+ * The data is partially processed name
+ */
+ mhd_POST_PARSER_PART_PROC_TYPE_NAME
+ ,
+ /**
+ * The data is partially processed value
+ */
+ mhd_POST_PARSER_PART_PROC_TYPE_VALUE
+};
+
+// TODO: remove?
+/**
+ * Buffered partially processed data
+ */
+struct mhd_PostParserPartProcessedData
+{
+ /**
+ * Partially processed data, left from previous upload data portion
+ */
+ struct mhd_Buffer data;
+
+ /**
+ * The type of partially processed data in the @a data buffer
+ */
+ enum mhd_PostParserPartProcType d_type;
+};
+
+/**
+ * The POST parsing data
+ */
+struct mhd_PostParserData
+{
+ /**
+ * The result of parsing POST data
+ */
+ enum MHD_PostParseResult parse_result;
+
+ /**
+ * The type of POSE encoding is used.
+ * Active member of @a e_d depends on this type.
+ */
+ enum MHD_HTTP_PostEncoding enc;
+
+ /**
+ * The encoding-specific parsing data
+ */
+ union mhd_PostParserDetailedData e_d;
+
+ /**
+ * The size of the data currently in the @a lbuf
+ */
+ size_t lbuf_used;
+
+ /**
+ * The maximum possible lbuf allocation size
+ */
+ size_t lbuf_limit;
+
+ /**
+ * True if any POST data was parsed successfully.
+ */
+ bool some_data_provided;
+
+ /**
+ * The start index of the current field.
+ * If the field is processed by incremental callback, buffer can be freed or
+ * reused up to this position (inclusive).
+ */
+ size_t field_start;
+
+ /**
+ * 'true' if current filed 'value' must be "streamed".
+ */
+ bool force_streamed;
+
+ /**
+ * The offset in the current value data.
+ * Used when value is processed incrementally otherwise it is zero.
+ */
+ size_t value_off;
+
+ /**
+ * The position of the next character to be parsed
+ */
+ size_t next_parse_pos;
+};
+
+#endif /* ! MHD_POST_PARSER_H */
diff --git a/src/mhd2/mhd_post_result.h b/src/mhd2/mhd_post_result.h
@@ -0,0 +1,118 @@
+/*
+ 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_post_result.h
+ * @brief The definition of enum for POST parsing result
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_POST_RESULT_H
+#define MHD_POST_RESULT_H 1
+
+#include "mhd_sys_options.h"
+
+// TODO: describe and copy to the main header
+
+#ifndef MHD_POST_PARSE_RESULT_DEFINED
+
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_PostParseResult
+{
+ /**
+ * The POST data parsed successfully and completely.
+ */
+ MHD_POST_PARSE_RES_OK = 0
+ ,
+ /**
+ * The POST request has no content or zero-length content.
+ */
+ MHD_POST_PARSE_RES_REQUEST_EMPTY = 1
+ ,
+ /**
+ * The POST data parsed successfully, but has missing or incorrect
+ * termination.
+ * The last parsed field may have incorrect data.
+ */
+ MHD_POST_PARSE_RES_OK_BAD_TERMINATION = 2
+ ,
+ /**
+ * Parsing of the POST data is incomplete because client used incorrect
+ * format of POST encoding.
+ * The last parsed field may have incorrect data.
+ * Some POST data is available or has been provided via callback.
+ */
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT = 3
+ ,
+ /**
+ * The POST data cannot be parsed completely because the stream has
+ * no free pool memory.
+ * Some POST data may be parsed.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM = 60
+ ,
+ /**
+ * The POST data cannot be parsed completely because no "large shared buffer"
+ * space is available.
+ * Some POST data may be parsed.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM = 61
+ ,
+ /**
+ * The POST data cannot be parsed because 'Content-Type:' is unknown.
+ */
+ MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE = 80
+ ,
+ /**
+ * The POST data cannot be parsed because 'Content-Type:' header is not set.
+ */
+ MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE = 81
+ ,
+ /**
+ * The POST data cannot be parsed because "Content-Type:" request header has
+ * no "boundary" parameter for "multipart/form-data"
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY = 82
+ ,
+ /**
+ * The POST data cannot be parsed because "Content-Type: multipart/form-data"
+ * request header is misformed
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED = 83
+ ,
+ /**
+ * The application set POST encoding to "multipart/form-data", but the request
+ * has no "Content-Type: multipart/form-data" header which is required
+ * to find "boundary" used in this encoding
+ */
+ MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART = 84
+ ,
+ /**
+ * The POST data cannot be parsed because client used incorrect format
+ * of POST encoding.
+ */
+ MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT = 90
+
+};
+
+#define MHD_POST_PARSE_RESULT_DEFINED 1
+#endif /* ! MHD_POST_PARSE_RESULT_DEFINED */
+
+
+#endif /* ! MHD_POST_RESULT_H */
diff --git a/src/mhd2/mhd_postfield_int.h b/src/mhd2/mhd_postfield_int.h
@@ -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_postfield_int.h
+ * @brief The definition of the internal struct mhd_PostFieldInt
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_POSTFIELD_INT_H
+#define MHD_POSTFIELD_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+/**
+ * Position and length of a string in some buffer
+ */
+struct mhd_PositionAndLength
+{
+ /**
+ * The position
+ */
+ size_t pos;
+ /**
+ * The length
+ */
+ size_t len;
+};
+
+struct mhd_PostFieldInt
+{
+ /**
+ * The name of the field.
+ * May start at zero position.
+ */
+ struct mhd_PositionAndLength name;
+ /**
+ * The field data
+ * If not set or defined then position is zero.
+ */
+ struct mhd_PositionAndLength value;
+ /**
+ * The filename if provided (only for "multipart/form-data")
+ * If not set or defined then position is zero.
+ */
+ struct mhd_PositionAndLength filename;
+ /**
+ * The Content-Type if provided (only for "multipart/form-data")
+ * If not set or defined then position is zero.
+ */
+ struct mhd_PositionAndLength content_type;
+ /**
+ * The Transfer-Encoding if provided (only for "multipart/form-data")
+ * If not set or defined then position is zero.
+ */
+ struct mhd_PositionAndLength transfer_encoding;
+#if 0 // TODO: support processing in connection buffer
+ /**
+ * If 'true' then all strings are in the "large shared buffer".
+ * If 'false' then all strings are in the stream buffer.
+ */
+ bool buf_is_lbuf;
+#endif
+};
+
+#endif /* ! MHD_POSTFIELD_INT_H */
diff --git a/src/mhd2/mhd_request.h b/src/mhd2/mhd_request.h
@@ -41,6 +41,11 @@
#include "mhd_action.h"
#include "mhd_buffer.h"
+#ifdef HAVE_POST_PARSER
+# include "mhd_postfield_int.h"
+# include "mhd_post_parser.h"
+#endif
+
/**
* The action set by the application
@@ -200,6 +205,41 @@ struct mhd_RequestField
mhd_DLINKEDL_LIST_DEF (mhd_RequestField);
+#ifdef HAVE_POST_PARSER
+
+struct mhd_RequestPostField; /* forward declarations */
+
+mhd_DLINKEDL_LINKS_DEF (mhd_RequestPostField);
+
+/**
+ * The data for POST request fields
+ */
+struct mhd_RequestPostField
+{
+ /**
+ * The field data
+ */
+ struct mhd_PostFieldInt field;
+
+ /**
+ * Temporal representation of the @a field for application.
+ *
+ * Filled/updated only when application required short form of POST
+ * data.
+ */
+ struct MHD_NameAndValue field_for_app;
+
+ /**
+ * Headers are kept in a double-linked list.
+ */
+ mhd_DLNKDL_LINKS (mhd_RequestPostField,post_fields);
+};
+
+mhd_DLINKEDL_LIST_DEF (mhd_RequestPostField);
+
+
+#endif /* HAVE_POST_PARSER */
+
/**
* The request content data
@@ -219,17 +259,30 @@ struct mhd_ReqContentData
uint_fast64_t cntn_size;
/**
- * The size of the received content
+ * The size of the received content.
+ * Excluding chunked encoding framing.
*/
uint_fast64_t recv_size;
/**
- * The size of the processed content
+ * The size of the processed content.
+ * Excluding chunked encoding framing.
*/
uint_fast64_t proc_size;
};
+union mhd_ReqContentParsingData
+{
+#ifdef HAVE_POST_PARSER
+ /**
+ * The POST parsing data
+ */
+ struct mhd_PostParserData post;
+#endif /* HAVE_POST_PARSER */
+ // TODO: move "raw" upload processing data here
+};
+
/**
* Request-specific values.
*
@@ -242,6 +295,13 @@ struct MHD_Request
*/
mhd_DLNKDL_LIST (mhd_RequestField,fields);
+#ifdef HAVE_POST_PARSER
+ /**
+ * Linked list of parsed POST fields.
+ */
+ mhd_DLNKDL_LIST (mhd_RequestPostField,post_fields);
+#endif /* HAVE_POST_PARSER */
+
/**
* The action set by the application
*/
@@ -258,6 +318,16 @@ struct MHD_Request
bool too_large;
/**
+ * Upload processing data
+ */
+ union mhd_ReqContentParsingData u_proc;
+
+ /**
+ * Have "Expect: 100-continue" request header
+ */
+ bool have_expect_100;
+
+ /**
* HTTP version string (i.e. http/1.1). Allocated
* in pool.
*/
diff --git a/src/mhd2/mhd_response.h b/src/mhd2/mhd_response.h
@@ -354,7 +354,12 @@ struct MHD_Response
*/
struct mhd_ResponseInternalErrData special_resp;
- #ifndef NDEBUG
+ /**
+ * Should be always 'false' for the response lifetime
+ */
+ bool was_destroyed;
+
+#ifndef NDEBUG
struct mhd_ResponseDebug dbg;
#endif
};
diff --git a/src/mhd2/mhd_str.c b/src/mhd2/mhd_str.c
@@ -520,9 +520,9 @@ toxdigitvalue (char c)
if (c >= 'A')
return (unsigned char) (c - 'A' + 10);
}
- else if (c <= 'F')
+ else if (c >= 'a')
{
- if (c >= 'a')
+ if (c <= 'f')
return (unsigned char) (c - 'a' + 10);
}
@@ -542,8 +542,8 @@ MHD_static_inline_ bool
charsequalcaseless (const char c1, const char c2)
{
return ( (c1 == c2) ||
- (isasciiupper (c1) ?
- ((c1 - 'A' + 'a') == c2) :
+ (((c1 - 'A' + 'a') == c2) ?
+ isasciiupper (c1) :
((c1 == (c2 - 'A' + 'a')) && isasciiupper (c2))) );
}
@@ -670,8 +670,8 @@ charsequalcaseless (const char c1, const char c2)
*/
#define charsequalcaseless(c1, c2) \
( ((c1) == (c2)) || \
- (isasciiupper (c1) ? \
- (((c1) - 'A' + 'a') == (c2)) : \
+ ((((c1) - 'A' + 'a') == (c2)) ? \
+ isasciiupper (c1) : \
(((c1) == ((c2) - 'A' + 'a')) && isasciiupper (c2))) )
#endif /* !HAVE_INLINE_FUNCS */
@@ -1596,15 +1596,18 @@ mhd_hex_to_bin (const char *restrict hex,
if (0 != len % 2)
{
/* Assume the first byte is encoded with single digit */
- const int l = toxdigitvalue (hex[r++]);
+ const char c2 = hex[r++];
+ const int l = toxdigitvalue (c2);
if (0 > l)
return 0;
((uint8_t *) bin)[w++] = (uint8_t) ((unsigned int) l);
}
while (r < len)
{
- const int h = toxdigitvalue (hex[r++]);
- const int l = toxdigitvalue (hex[r++]);
+ const char c1 = hex[r++];
+ const char c2 = hex[r++];
+ const int h = toxdigitvalue (c1);
+ const int l = toxdigitvalue (c2);
if ((0 > h) || (0 > l))
return 0;
((uint8_t *) bin)[w++] = (uint8_t) ( ((uint8_t) (((uint8_t)
@@ -1649,8 +1652,10 @@ mhd_str_pct_decode_strict_n (const char *pct_encoded,
return 0;
else
{
- const int h = toxdigitvalue (pct_encoded[++r]);
- const int l = toxdigitvalue (pct_encoded[++r]);
+ const char c1 = pct_encoded[++r];
+ const char c2 = pct_encoded[++r];
+ const int h = toxdigitvalue (c1);
+ const int l = toxdigitvalue (c2);
unsigned char out;
if ((0 > h) || (0 > l))
return 0;
@@ -1679,8 +1684,10 @@ mhd_str_pct_decode_strict_n (const char *pct_encoded,
return 0;
else
{
- const int h = toxdigitvalue (pct_encoded[++r]);
- const int l = toxdigitvalue (pct_encoded[++r]);
+ const char c1 = pct_encoded[++r];
+ const char c2 = pct_encoded[++r];
+ const int h = toxdigitvalue (c1);
+ const int l = toxdigitvalue (c2);
unsigned char out;
if ((0 > h) || (0 > l))
return 0;
@@ -1705,7 +1712,7 @@ mhd_str_pct_decode_lenient_n (const char *pct_encoded,
size_t pct_encoded_len,
char *decoded,
size_t buf_size,
- bool *broken_encoding)
+ bool *restrict broken_encoding)
{
size_t r;
size_t w;
@@ -1729,8 +1736,10 @@ mhd_str_pct_decode_lenient_n (const char *pct_encoded,
}
else
{
- const int h = toxdigitvalue (pct_encoded[++r]);
- const int l = toxdigitvalue (pct_encoded[++r]);
+ const char c1 = pct_encoded[++r];
+ const char c2 = pct_encoded[++r];
+ const int h = toxdigitvalue (c1);
+ const int l = toxdigitvalue (c2);
unsigned char out;
if ((0 > h) || (0 > l))
{
@@ -1771,8 +1780,10 @@ mhd_str_pct_decode_lenient_n (const char *pct_encoded,
}
else
{
- const int h = toxdigitvalue (pct_encoded[++r]);
- const int l = toxdigitvalue (pct_encoded[++r]);
+ const char c1 = pct_encoded[++r];
+ const char c2 = pct_encoded[++r];
+ const int h = toxdigitvalue (c1);
+ const int l = toxdigitvalue (c2);
if ((0 > h) || (0 > l))
{
r -= 2;
@@ -1856,8 +1867,8 @@ mhd_str_pct_decode_in_place_strict (char *str)
MHD_INTERNAL size_t
-mhd_str_pct_decode_in_place_lenient (char *str,
- bool *broken_encoding)
+mhd_str_pct_decode_in_place_lenient (char *restrict str,
+ bool *restrict broken_encoding)
{
#ifdef MHD_FAVOR_SMALL_CODE
size_t len;
@@ -1992,6 +2003,10 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
}
+#endif /* DAUTH_SUPPORT */
+
+#if defined(DAUTH_SUPPORT) || defined(HAVE_POST_PARSER)
+
MHD_INTERNAL size_t
mhd_str_unquote (const char *quoted,
size_t quoted_len,
@@ -2017,7 +2032,7 @@ mhd_str_unquote (const char *quoted,
}
-#endif /* DAUTH_SUPPORT */
+#endif /* DAUTH_SUPPORT HAVE_POST_PARSER */
#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
@@ -2315,3 +2330,225 @@ MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
#undef mhd_base64_map_type
#endif /* BAUTH_SUPPORT */
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_str_starts_with_token_opt_param (const struct MHD_String *restrict str,
+ const struct MHD_String *restrict token)
+{
+ size_t i;
+
+ mhd_assert (0 != token->len);
+ mhd_assert (NULL == memchr (token->cstr, '=', token->len));
+ mhd_assert (NULL == memchr (token->cstr, ' ', token->len));
+ mhd_assert (NULL == memchr (token->cstr, '\t', token->len));
+
+ if (str->len < token->len)
+ return false; /* The string is too short to match */
+
+ if (! mhd_str_equal_caseless_bin_n (str->cstr,
+ token->cstr,
+ token->len))
+ return false; /* The string does not start with the token */
+
+ for (i = token->len; i < str->len; ++i)
+ {
+ const char c = str->cstr[i];
+ if ((' ' == c) || ('\t' == c))
+ continue;
+ if (';' == c)
+ return true; /* Found the start of the token parameters */
+ return false; /* The initial part of the string does not fully match the token */
+ }
+ mhd_assert (0 && "The string should not have whitespace at the end");
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (4)
+MHD_FN_PAR_OUT_ (5) enum mhd_StingStartsWithTokenResult
+mhd_str_starts_with_token_req_param (
+ const struct MHD_String *restrict str,
+ const struct MHD_String *restrict token,
+ const struct MHD_String *restrict par,
+ struct mhd_BufferConst *restrict par_value,
+ bool *restrict par_value_needs_unquote)
+{
+ size_t i;
+ const char *const restrict cstr = str->cstr;
+ bool token_found;
+ bool param_found;
+
+ mhd_assert (0 != token->len);
+ mhd_assert (NULL == memchr (token->cstr, '=', token->len));
+ mhd_assert (NULL == memchr (token->cstr, ' ', token->len));
+ mhd_assert (NULL == memchr (token->cstr, '\t', token->len));
+ mhd_assert (NULL == memchr (par->cstr, '=', par->len));
+ mhd_assert (NULL == memchr (par->cstr, ' ', par->len));
+ mhd_assert (NULL == memchr (par->cstr, '\t', par->len));
+
+ par_value->data = NULL;
+ par_value->size = 0;
+
+ if (str->len < token->len)
+ return mhd_STR_STARTS_W_TOKEN_NO_TOKEN; /* The string is too short to match */
+
+ if (! mhd_str_equal_caseless_bin_n (cstr,
+ token->cstr,
+ token->len))
+ return mhd_STR_STARTS_W_TOKEN_NO_TOKEN; /* The string does not start with the token */
+ token_found = false;
+ param_found = false;
+
+ i = token->len;
+ do
+ {
+ /* Find start of the next parameter */
+ for ((void) 0; i < str->len; ++i)
+ {
+ const char c = cstr[i];
+ if ((' ' == c) || ('\t' == c))
+ continue;
+ if (';' == c)
+ {
+ /* Found the start of the next token parameter */
+ if (param_found)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
+ ++i; /* Move to the next char */
+ break;
+ }
+ if (',' == c)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN; /* Found the start of the next token */
+
+ if (! token_found)
+ {
+ if (i == token->len)
+ {
+ /* The initial part of the string does not fully match the token or
+ formatting is not correct */
+ return mhd_STR_STARTS_W_TOKEN_NO_TOKEN;
+ }
+ /* The string has garbage after the token and whitespace */
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT;
+ }
+ /* The garbage after the parameter */
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT;
+ }
+ token_found = true;
+
+ if (i == str->len)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
+
+ /* 'i' is at the start of the parameter */
+
+ while ((' ' == cstr[i]) || ('\t' == cstr[i]))
+ {
+ if (++i == str->len)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
+ }
+
+ /* 'i' is at the start of the parameter name */
+
+ if (par->len > str->len - i - 1)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN; /* the token is found, but the parameter is not */
+ else
+ { /* Check the parameter */
+ bool val_needs_unquote;
+ size_t j;
+ const char *const prm_str = cstr + i;
+
+ for (j = 0; j < par->len; ++j)
+ if (! charsequalcaseless (prm_str[j],
+ par->cstr[j]))
+ break;
+ i += j;
+ mhd_assert (str->len > i);
+ if ((j == par->len) &&
+ ('=' == cstr[i]))
+ {
+ /* The parameter name matches required parameter */
+ param_found = true;
+ par_value->data = cstr + i + 1;
+ }
+ else
+ {
+ /* i points to the char in the parameter name */
+ while ('=' != cstr[i])
+ {
+ if ((';' == cstr[i]) /* end of the parameter */
+ || (',' == cstr[i]) /* end of the token */
+ || (str->len == ++i)) /* end of the field string */
+ {
+ /* parameter without the value */
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT;
+ }
+ }
+ }
+ mhd_assert (str->len > i);
+ mhd_assert ('=' == cstr[i]);
+
+ /* 'i' points to '=' between parameter name and parameter value */
+
+ ++i; /* Advance to the first char in the parameter value */
+ if (str->len == i)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN; /* Zero-length parameter value */
+
+ val_needs_unquote = false;
+
+ /* 'i' points to the char after '=' */
+
+ if ('"' == cstr[i])
+ {
+ /* The value is quoted */
+ if (param_found)
+ ++(par_value->data); /* Point to the first quoted char */
+ do
+ {
+ ++i; /* Advance to the next char */
+ if (str->len == i)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT; /* No closing quote */
+ if ('\\' == cstr[i])
+ {
+ val_needs_unquote = true;
+ ++i; /* Skip quoted char */
+ if (str->len == i)
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT; /* No closing quote */
+ }
+ } while ('"' != cstr[i]);
+ if (param_found)
+ {
+ par_value->size = (size_t) ((cstr + i) - par_value->data);
+ *par_value_needs_unquote = val_needs_unquote;
+ }
+ /* Complete value found */
+ /* Check for the garbage data at the end */
+ ++i; /* Advance to the next char */
+ }
+ else
+ {
+ /* The value is not quoted */
+ while ((' ' != cstr[i]) &&
+ ('\t' != cstr[i]))
+ {
+ if ((';' == cstr[i]) /* end of the parameter */
+ || (',' == cstr[i]) /* end of the token */
+ || (str->len == ++i)) /* end of the field string */
+ break;
+ }
+ /* The end parameter value */
+ if (param_found)
+ {
+ par_value->size = (size_t) ((cstr + i) - par_value->data);
+ *par_value_needs_unquote = false;
+ }
+ /* Check for the garbage data at the end */
+ }
+
+ /* 'i' points to the next char after end of the parameter value */
+ }
+ } while (i < str->len);
+
+ mhd_assert (token_found);
+ return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
+}
diff --git a/src/mhd2/mhd_str.h b/src/mhd2/mhd_str.h
@@ -27,12 +27,19 @@
#define MHD_STR_H 1
#include "mhd_sys_options.h"
+
#include "sys_base_types.h"
#include "sys_bool_type.h"
+
+#include "mhd_str_types.h"
+#include "mhd_buffer.h"
+
#include "mhd_str_macros.h"
+
#ifdef MHD_FAVOR_SMALL_CODE
# include "mhd_limits.h"
#endif
+
/*
* Block of functions/macros that use US-ASCII charset as required by HTTP
* standards. Not affected by current locale settings.
@@ -98,14 +105,14 @@ mhd_str_equal_caseless_bin_n (const char *const str1,
*
* Compares not more first than @a len bytes, including binary zero characters.
* Comparison stops at first unmatched byte.
- * @param a the statically allocated string to compare
- * @param s the string to compare
+ * @param arr the statically allocated string to compare
+ * @param str the string to compare
* @param l the number of characters in the @a s string
* @return 'true' if two strings are equal, 'false' otherwise.
*/
-#define mhd_str_equal_caseless_n_st(a,s,l) \
- ((mhd_SSTR_LEN (a) == (l)) \
- && mhd_str_equal_caseless_bin_n (a,s,l))
+#define mhd_str_equal_caseless_n_st(arr,str,l) \
+ ((mhd_SSTR_LEN (arr) == (l)) \
+ && mhd_str_equal_caseless_bin_n (arr,str,l))
/**
* Check whether @a str has case-insensitive @a token.
@@ -549,7 +556,7 @@ mhd_str_pct_decode_lenient_n (const char *pct_encoded,
size_t pct_encoded_len,
char *decoded,
size_t buf_size,
- bool *broken_encoding);
+ bool *restrict broken_encoding);
/**
@@ -588,8 +595,8 @@ mhd_str_pct_decode_in_place_strict (char *str);
* @return the number of character in decoded string
*/
MHD_INTERNAL size_t
-mhd_str_pct_decode_in_place_lenient (char *str,
- bool *broken_encoding);
+mhd_str_pct_decode_in_place_lenient (char *restrict str,
+ bool *restrict broken_encoding);
#ifdef DAUTH_SUPPORT
/**
@@ -682,6 +689,10 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
#define mhd_str_equal_caseless_quoted_s_bin_n(q,l,u) \
mhd_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
+#endif /* DAUTH_SUPPORT */
+
+#if defined(DAUTH_SUPPORT) || defined(HAVE_POST_PARSER)
+
/**
* Convert string from quoted to unquoted form as specified by
* RFC7230#section-3.2.6 and RFC7694#quoted.strings.
@@ -702,7 +713,7 @@ mhd_str_unquote (const char *quoted,
size_t quoted_len,
char *result);
-#endif /* DAUTH_SUPPORT */
+#endif /* DAUTH_SUPPORT HAVE_POST_PARSER */
#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
@@ -766,4 +777,78 @@ mhd_base64_to_bin_n (const char *base64,
#endif /* BAUTH_SUPPORT */
+
+/**
+ * Check whether the given field value string starts with given token.
+ * Token matched case-insensitive.
+ *
+ * Matched considered successful if string has given token at the first
+ * position. The token may be followed by optional whitespaces and the semicolon
+ * symbol. The string is not checked after the semicolon (if any).
+ *
+ * @param str the string to check
+ * @param token the token to find, must not be empty
+ * @return 'true' if match is successful,
+ * 'false' otherwise
+ */
+MHD_INTERNAL bool
+mhd_str_starts_with_token_opt_param (const struct MHD_String *restrict str,
+ const struct MHD_String *restrict token)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * The result of check for token with parameter
+ */
+enum MHD_FIXED_ENUM_ mhd_StingStartsWithTokenResult
+{
+ /**
+ * The string does not start with the specified token
+ */
+ mhd_STR_STARTS_W_TOKEN_NO_TOKEN = 0
+ ,
+ /**
+ * The string has specified token at the initial position
+ * @note While formatting problems are not detected, it does not guarantee
+ * that string has a perfect format.
+ */
+ mhd_STR_STARTS_W_TOKEN_HAS_TOKEN = 1
+ ,
+ /**
+ * The string has specified token at the initial position, but broken
+ * formatting is detected.
+ */
+ mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT = -1
+};
+
+/**
+ * Check whether the given field value string starts with given token, find
+ * required parameter.
+ * Token and parameter matched case-insensitive.
+ *
+ * Matched considered successful if string has given token at the first
+ * position. The token should be followed by optional whitespaces and
+ * one or more parameters, delimited by the semicolon symbol.
+ *
+ * @param str the string to check
+ * @param token the token to find, must not be empty
+ * @param par the name of the parameter, must not be empty
+ * @param[out] par_value set to the found parameter value or @a data member
+ * set to NULL if parameter is not found
+ * @param par_value_needs_unquote set to 'true' if @a par_value
+ * needs to be "unquoted"
+ * @return result of token detection and string parsing; if token is found
+ * the presence of the required parameter is indicated only
+ * by @a par_value
+ *
+ */
+MHD_INTERNAL enum mhd_StingStartsWithTokenResult
+mhd_str_starts_with_token_req_param (
+ const struct MHD_String *restrict str,
+ const struct MHD_String *restrict token,
+ const struct MHD_String *restrict par,
+ struct mhd_BufferConst *restrict par_value,
+ bool *restrict par_value_needs_unquote)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_(4) MHD_FN_PAR_OUT_ (5);
+
#endif /* MHD_STR_H */
diff --git a/src/mhd2/post_parser_funcs.c b/src/mhd2/post_parser_funcs.c
@@ -0,0 +1,2942 @@
+/*
+ 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/post_parser_funcs.c
+ * @brief The implementation of internal POST parser functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+
+#include "mhd_sys_options.h"
+
+#include "post_parser_funcs.h"
+
+#include "mhd_post_parser.h"
+
+#include <string.h>
+
+#include "mhd_action.h"
+#include "mhd_request.h"
+#include "mhd_connection.h"
+#include "mhd_daemon.h"
+
+#include "mhd_str_macros.h"
+
+#include "mhd_str.h"
+#include "daemon_logger.h"
+#include "stream_funcs.h"
+#include "stream_process_request.h"
+
+#include "daemon_funcs.h"
+#include "request_get_value.h"
+
+/**
+ * The result of 'multipart/form-data' processing
+ */
+enum MHD_FIXED_ENUM_ mhd_MPartDetectResult
+{
+ /**
+ * Sting processed successfully, boundary detected
+ */
+ mhd_MPART_DET_OK = 0
+ ,
+ /**
+ * Error processing string, the error result is set
+ */
+ mhd_MPART_DET_ERROR_SET
+ ,
+ /**
+ * The string is not 'multipart/form-data' header
+ */
+ mhd_MPART_DET_NO_MPART
+};
+
+
+/**
+ * Process 'Content-Type:' header value as 'multipart/form-data' data to
+ * prepare POST parsing data, including setting correct 'boundary' value
+ * @param c the stream to use
+ * @param h_cnt_tp the 'Content-Type:' header value string
+ * @return 'mhd_MPART_DET_OK' if processed successfully and boundary has been
+ * detected and set,
+ * 'mhd_MPART_DET_ERROR_SET' is has some error in processing which
+ * resulted in specific error set in
+ * the stream,
+ * 'mhd_MPART_DET_NO_MPART' is string is not 'multipart/form-data' data
+ */
+static MHD_FN_PAR_NONNULL_ALL_ enum mhd_MPartDetectResult
+process_mpart_header (struct MHD_Connection *restrict c,
+ const struct MHD_String *restrict h_cnt_tp)
+{
+ static const struct MHD_String mpart_token =
+ mhd_MSTR_INIT ("multipart/form-data");
+ static const struct MHD_String mpart_bound_par =
+ mhd_MSTR_INIT ("boundary");
+ struct mhd_BufferConst mpart_bound;
+ bool mpart_bound_quoted;
+ enum mhd_StingStartsWithTokenResult res;
+
+ mhd_assert (NULL != h_cnt_tp->cstr);
+
+ res = mhd_str_starts_with_token_req_param (h_cnt_tp,
+ &mpart_token,
+ &mpart_bound_par,
+ &mpart_bound,
+ &mpart_bound_quoted);
+
+ if (mhd_STR_STARTS_W_TOKEN_NO_TOKEN == res)
+ return mhd_MPART_DET_NO_MPART;
+
+ if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
+ {
+ mhd_LOG_PRINT (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED, \
+ mhd_LOG_FMT ("The request POST data cannot be parsed " \
+ "because 'Content-Type: " \
+ "multipart/form-data' header is " \
+ "misformed: %.*s%s"), \
+ (int) ((h_cnt_tp->len <= 127) ? h_cnt_tp->len : 127),
+ h_cnt_tp->cstr,
+ (h_cnt_tp->len <= 127) ? "" : "...");
+ c->rq.u_proc.post.parse_result =
+ MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED;
+ return mhd_MPART_DET_ERROR_SET;
+ }
+
+ mhd_assert (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res);
+
+ if (0 == mpart_bound.size)
+ {
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY, \
+ "The request POST data cannot be parsed because " \
+ "'Content-Type: multipart/form-data' header has " \
+ "no 'boundary' parameter value.");
+ c->rq.u_proc.post.parse_result =
+ MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY;
+ return mhd_MPART_DET_ERROR_SET;
+ }
+
+ mhd_assert (NULL != mpart_bound.data);
+
+ if (! mpart_bound_quoted)
+ {
+ c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
+ c->rq.u_proc.post.e_d.m_form.bound = mpart_bound;
+ }
+ else
+ {
+ char *buf;
+
+ mhd_assert (2 <= mpart_bound.size); /* At least one char and at least one '\' */
+
+ buf = mhd_stream_alloc_memory (c, mpart_bound.size);
+ if (NULL == buf)
+ {
+ /* It is very low probability that pool would not have memory just
+ * to held the small boundary string. While it could be possible
+ * to allocate memory from "large buffer", it would over-complicate
+ * code here and at freeing part. */
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \
+ "The request POST data cannot be parsed because " \
+ "there is not enough pool memory.");
+ c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM;
+ return mhd_MPART_DET_ERROR_SET;
+ }
+ c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
+ c->rq.u_proc.post.e_d.m_form.bound.size =
+ mhd_str_unquote (mpart_bound.data,
+ mpart_bound.size,
+ buf);
+ mhd_assert (0 != c->rq.u_proc.post.e_d.m_form.bound.size);
+ }
+ return mhd_MPART_DET_OK;
+}
+
+
+/**
+ * Detect used POST encoding and 'boundary' for 'multipart/form-data'.
+ * @param c the stream to use
+ * @return 'true' if detected successfully,
+ * 'false' if POST encoding cannot be detected
+ */
+static MHD_FN_PAR_NONNULL_ (1) bool
+detect_post_enc (struct MHD_Connection *restrict c)
+{
+ const struct MHD_StringNullable *h_cnt_tp;
+
+ mhd_assert (MHD_CONNECTION_BODY_RECEIVING > c->state);
+
+ h_cnt_tp = mhd_request_get_value_st (&(c->rq),
+ MHD_VK_HEADER,
+ MHD_HTTP_HEADER_CONTENT_TYPE);
+ if (NULL == h_cnt_tp)
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \
+ "The request POST data cannot be parsed because " \
+ "the request has no 'Content-Type:' header and no " \
+ "explicit POST encoding is set.");
+ c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE;
+ return false; /* The "Content-Type:" is not defined by the client */
+ }
+
+ mhd_assert (NULL != h_cnt_tp->cstr);
+
+ if (mhd_str_equal_caseless_n_st ("application/x-www-form-urlencoded",
+ h_cnt_tp->cstr,
+ h_cnt_tp->len))
+ {
+ c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ return true;
+ }
+
+ if (1)
+ {
+ enum mhd_MPartDetectResult res;
+
+ res = process_mpart_header (c,
+ (const struct MHD_String *) (const void *)
+ h_cnt_tp);
+
+ if (mhd_MPART_DET_OK == res)
+ return true;
+
+ if (mhd_MPART_DET_ERROR_SET == res)
+ return false;
+
+ mhd_assert (mhd_MPART_DET_NO_MPART == res);
+ }
+
+ if (1)
+ {
+ static const struct MHD_String txt_tkn = mhd_MSTR_INIT ("text/plain");
+ struct MHD_String h_cnt_tp_copy = {h_cnt_tp->len, h_cnt_tp->cstr};
+ mhd_assert (NULL != h_cnt_tp->cstr);
+
+ if (mhd_str_starts_with_token_opt_param (&h_cnt_tp_copy,
+ &txt_tkn))
+ {
+ c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN;
+ return true;
+ }
+ }
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE, \
+ "The request POST data cannot be parsed because " \
+ "'Content-Type' header value is unknown or unsupported.");
+ c->rq.u_proc.post.parse_result =
+ MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE;
+ return false;
+}
+
+
+/**
+ * Detect 'boundary' for 'multipart/form-data' POST encoding.
+ * @param c the stream to use
+ * @return 'true' if succeed,
+ * 'false' if failed and error result is set
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+detect_mpart_boundary_from_the_header (struct MHD_Connection *restrict c)
+{
+ const struct MHD_StringNullable *h_cnt_tp;
+ enum mhd_MPartDetectResult res;
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == \
+ c->rq.app_act.head_act.data.post_parse.enc);
+
+ h_cnt_tp = mhd_request_get_value_st (&(c->rq),
+ MHD_VK_HEADER,
+ MHD_HTTP_HEADER_CONTENT_TYPE);
+ if (NULL == h_cnt_tp)
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \
+ "The request POST data cannot be parsed because " \
+ "'multipart/form-data' requires 'boundary' parameter, but " \
+ "the request has no 'Content-Type:' header.");
+ c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE;
+ return false;
+ }
+
+ mhd_assert (NULL != h_cnt_tp->cstr);
+
+ res = process_mpart_header (c,
+ (const struct MHD_String *) (const void *)
+ h_cnt_tp);
+
+ if (mhd_MPART_DET_OK == res)
+ return true;
+
+ if (mhd_MPART_DET_NO_MPART == res)
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART, \
+ "The request POST data cannot be parsed because " \
+ "'multipart/form-data' requires 'boundary' parameter, but " \
+ "the request has no 'Content-Type: multipart/form-data' " \
+ "header.");
+ c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART;
+ return false;
+ }
+
+ mhd_assert (mhd_MPART_DET_ERROR_SET == res);
+ return false;
+}
+
+
+/**
+ * Reset field parsing data for "application/x-www-form-urlencoded"
+ * @param pdata the parsing data
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+reset_parse_field_data_urlenc (struct mhd_PostParserData *pdata)
+{
+ mhd_assert (MHD_HTTP_POST_ENCODING_FORM_URLENCODED == pdata->enc);
+ memset (&(pdata->e_d.u_enc), 0, sizeof(pdata->e_d.u_enc));
+ pdata->field_start = 0;
+}
+
+
+/**
+ * Initial reset field parsing data for "multipart/form-data"
+ * @param pdata the parsing data
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+reset_parse_field_data_mpart_init (struct mhd_PostParserData *pdata)
+{
+ mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == pdata->enc);
+ memset (&(pdata->e_d.m_form.f), 0, sizeof(pdata->e_d.m_form.f));
+ pdata->e_d.m_form.st = mhd_POST_MPART_ST_NOT_STARTED;
+ pdata->e_d.m_form.line_start = mhd_POST_INVALID_POS;
+ pdata->e_d.m_form.delim_check_start = mhd_POST_INVALID_POS;
+ mhd_assert (NULL != pdata->e_d.m_form.bound.data);
+ mhd_assert (0 != pdata->e_d.m_form.bound.size);
+ mhd_assert (NULL == memchr (pdata->e_d.m_form.bound.data, '\r', \
+ pdata->e_d.m_form.bound.size));
+ mhd_assert (NULL == memchr (pdata->e_d.m_form.bound.data, '\n', \
+ pdata->e_d.m_form.bound.size));
+ pdata->field_start = 0;
+}
+
+
+/**
+ * Reset field parsing data for "multipart/form-data" after processing
+ * previous field
+ * @param pdata the parsing data
+ * @param final 'true' if last field was "closed" by the "final" delimiter
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+reset_parse_field_data_mpart_cont (struct mhd_PostParserData *pdata,
+ bool final)
+{
+ mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == pdata->enc);
+ memset (&(pdata->e_d.m_form.f), 0, sizeof(pdata->e_d.m_form.f));
+ pdata->e_d.m_form.st = final ?
+ mhd_POST_MPART_ST_EPILOGUE :
+ mhd_POST_MPART_ST_PART_START;
+ pdata->field_start = 0;
+}
+
+
+/**
+ * Reset field parsing data for "text/plain"
+ * @param pdata the parsing data
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+reset_parse_field_data_text (struct mhd_PostParserData *pdata)
+{
+ mhd_assert (MHD_HTTP_POST_ENCODING_TEXT_PLAIN == pdata->enc);
+ memset (&(pdata->e_d.text), 0, sizeof(pdata->e_d.text));
+ pdata->field_start = 0;
+}
+
+
+/**
+ * Finish initialisation of data for POST parsing
+ * @param c the stream to use
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+init_post_parse_data (struct MHD_Connection *restrict c)
+{
+ struct mhd_PostParserData *const pdata =
+ &(c->rq.u_proc.post);
+
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+ mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != \
+ c->rq.u_proc.post.enc);
+ mhd_assert (0 == pdata->lbuf_used);
+
+ pdata->lbuf_limit = c->rq.app_act.head_act.data.post_parse.buffer_size;
+
+ switch (pdata->enc)
+ {
+ case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
+ reset_parse_field_data_urlenc (pdata);
+ break;
+ case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
+ reset_parse_field_data_mpart_init (pdata);
+ break;
+ case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
+ reset_parse_field_data_text (pdata);
+ break;
+ case MHD_HTTP_POST_ENCODING_OTHER:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ }
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) bool
+mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c)
+{
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+ if (MHD_HTTP_POST_ENCODING_OTHER ==
+ c->rq.app_act.head_act.data.post_parse.enc)
+ {
+ if (! detect_post_enc (c))
+ {
+ mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result);
+ c->discard_request = true;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return false;
+ }
+ }
+ else if (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ==
+ c->rq.app_act.head_act.data.post_parse.enc)
+ {
+ if (! detect_mpart_boundary_from_the_header (c))
+ {
+ mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result);
+ c->discard_request = true;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return false;
+ }
+ }
+ else
+ c->rq.u_proc.post.enc = c->rq.app_act.head_act.data.post_parse.enc;
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != \
+ c->rq.u_proc.post.enc);
+ mhd_assert ((MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
+ c->rq.u_proc.post.enc) || \
+ (0 != c->rq.u_proc.post.e_d.m_form.bound.size));
+
+ init_post_parse_data (c);
+
+ return true;
+}
+
+
+#if 0 /* Disable unused functions */
+
+/**
+ * Allocate memory from "large shared buffer" for POST parsing
+ * @param c the stream to use
+ * @param alloc_size the size to allocate
+ * @param[out] buf the buffer to allocate
+ * @return 'true' if succeed,
+ * 'false' otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) bool
+get_lbuf_fixed_size (struct MHD_Connection *restrict c,
+ size_t alloc_size,
+ struct mhd_Buffer *restrict buf)
+{
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+ mhd_assert (0 == buf->size);
+ mhd_assert (NULL == buf->data);
+
+ if (alloc_size > c->rq.u_proc.post.lbuf_limit)
+ return false;
+
+ return mhd_daemon_get_lbuf (c->daemon,
+ alloc_size,
+ buf);
+}
+
+
+/**
+ * Grow the allocated memory from "large shared buffer" for POST parsing
+ * @param c the stream to use
+ * @param grow_size the size to grow
+ * @param[in,out] buf the buffer to grow
+ * @return 'true' if succeed,
+ * 'false' otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) bool
+grow_lbuf_fixed_size (struct MHD_Connection *restrict c,
+ size_t grow_size,
+ struct mhd_Buffer *restrict buf)
+{
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+ mhd_assert (0 != buf->size);
+ mhd_assert (NULL != buf->data);
+ mhd_assert (c->rq.u_proc.post.lbuf_limit >= buf->size);
+
+ if (buf->size + grow_size > c->rq.u_proc.post.lbuf_limit)
+ return false;
+
+ return mhd_daemon_grow_lbuf (c->daemon,
+ grow_size,
+ buf);
+}
+
+
+#endif /* Disable unused functions */
+
+/**
+ * Grow or allocate the new memory from "large shared buffer" for POST parsing
+ * If the requested grow size is not possible, grow up to max possible size.
+ * @param c the stream to use
+ * @param desired_grow_size the desired size to grow
+ * @param[in,out] buf the buffer to grow
+ * @return the resulting grow size
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) size_t
+extend_lbuf_up_to (struct MHD_Connection *restrict c,
+ size_t desired_grow_size,
+ struct mhd_Buffer *restrict buf)
+{
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+ mhd_assert (c->rq.u_proc.post.lbuf_limit >= buf->size);
+
+ if (buf->size + desired_grow_size > c->rq.u_proc.post.lbuf_limit)
+ desired_grow_size = c->rq.u_proc.post.lbuf_limit - buf->size;
+
+ if (0 == desired_grow_size)
+ return 0;
+
+ return mhd_daemon_extend_lbuf_up_to (c->daemon,
+ desired_grow_size,
+ buf);
+}
+
+
+/**
+ * Report "no memory in larger shared buffer".
+ * Set parsing status accordingly
+ * @param c the stream to use
+ * @return 'true' if the stream state has been changed,
+ * 'false' otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+report_low_lbuf_mem (struct MHD_Connection *restrict c)
+{
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM, \
+ "Not enough large shared buffer memory to " \
+ "parse POST request.");
+ c->rq.u_proc.post.parse_result =
+ MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM;
+ if (c->state < MHD_CONNECTION_FULL_REQ_RECEIVED)
+ {
+ c->discard_request = true;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Report broken POST encoding termination
+ * @param c the stream to use
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+report_invalid_termination (struct MHD_Connection *restrict c)
+{
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_OK_BAD_TERMINATION, \
+ "The POST request content has invalid termination / ending. " \
+ "The last parsed field may be incorrect.");
+ c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_OK_BAD_TERMINATION;
+}
+
+
+/**
+ * Test whether current incomplete value must be provided to the "stream"
+ * reader.
+ * @param c the connection to use
+ * @param field_cur_size the current size of the current field
+ * @return 'true' if the value must be provided via the "stream" reader,
+ * 'false' otherwise.
+ */
+MHD_static_inline_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ bool
+is_value_streaming_needed (struct MHD_Connection *restrict c,
+ size_t field_cur_size)
+{
+ struct mhd_PostParseActionData *const p_par =
+ &(c->rq.app_act.head_act.data.post_parse);
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+
+ if (NULL == p_par->stream_reader)
+ {
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert (! p_data->force_streamed);
+ return false; /* No value streaming possible */
+ }
+
+ /* If part of the value has been already provided to "stream"
+ reader, the rest of the value should be provided
+ in the same way */
+ mhd_assert ((0 == p_data->value_off) || p_data->force_streamed);
+ if (p_data->force_streamed)
+ return true;
+
+ return (p_par->max_nonstream_size < field_cur_size);
+}
+
+
+/**
+ * Add parsed POST field to the list of request's fields
+ * @param c the stream to use
+ * @param name the name of the field
+ * @param filename the filename of the field
+ * @param content_type the "Content-Type:" of the field
+ * @param transfer_encoding the "Transfer-Encoding:" of the field
+ * @param value the value of the field
+ * @return 'true' if succeed,
+ * 'false' if memory allocation failed (no pool memory in the stream)
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+add_parsed_post_field (struct MHD_Connection *restrict c,
+ struct mhd_PositionAndLength *restrict name,
+ struct mhd_PositionAndLength *restrict filename,
+ struct mhd_PositionAndLength *restrict content_type,
+ struct mhd_PositionAndLength *restrict transfer_encoding,
+ struct mhd_PositionAndLength *restrict value)
+{
+ struct mhd_RequestPostField *pfield;
+
+ mhd_assert ((0 != filename->pos) || (0 == filename->len));
+ mhd_assert ((0 != content_type->pos) || (0 == content_type->len));
+ mhd_assert ((0 != transfer_encoding->pos) || \
+ (0 == transfer_encoding->len));
+ mhd_assert ((0 != value->pos) || (0 == value->len));
+
+ pfield = (struct mhd_RequestPostField *)
+ mhd_stream_alloc_memory (c,
+ sizeof (struct mhd_RequestPostField));
+ if (NULL == pfield)
+ return false;
+
+ pfield->field.name = *name;
+ pfield->field.value = *value;
+ pfield->field.filename = *filename;
+ pfield->field.content_type = *content_type;
+ pfield->field.transfer_encoding = *transfer_encoding;
+
+ mhd_DLINKEDL_INIT_LINKS (pfield, post_fields);
+
+ mhd_DLINKEDL_INS_LAST (&(c->rq), pfield, post_fields);
+
+ return true;
+}
+
+
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_ (1)
+MHD_FN_PAR_OUT_ (10) MHD_FN_PAR_OUT_ (11)
+MHD_FN_PAR_OUT_ (12) MHD_FN_PAR_OUT_ (13) void
+make_post_strings_from_buf_and_indices (const char *restrict buf,
+ size_t name_start,
+ size_t name_len,
+ size_t filename_start,
+ size_t filename_len,
+ size_t cntn_type_start,
+ size_t cntn_type_len,
+ size_t enc_start,
+ size_t enc_len,
+ struct MHD_String *name,
+ struct MHD_StringNullable *filename,
+ struct MHD_StringNullable *content_type,
+ struct MHD_StringNullable *encoding)
+{
+ name->len = name_len;
+ name->cstr = buf + name_start;
+
+ if (0 != filename_start)
+ {
+ filename->len = filename_len;
+ filename->cstr = buf + filename_start;
+ }
+ else
+ {
+ filename->len = 0;
+ filename->cstr = NULL;
+ }
+ if (0 != cntn_type_start)
+ {
+ content_type->len = cntn_type_len;
+ content_type->cstr = buf + cntn_type_start;
+ }
+ else
+ {
+ content_type->len = 0;
+ content_type->cstr = NULL;
+ }
+ if (0 != enc_start)
+ {
+ encoding->len = enc_len;
+ encoding->cstr = buf + enc_start;
+ }
+ else
+ {
+ encoding->len = 0;
+ encoding->cstr = NULL;
+ }
+}
+
+
+/**
+ * Process new full parsed POST field
+ * @param c the stream to use
+ * @param buf the buffer, where the data is
+ * @param pfield_next_pos the pointer to variable holding index of
+ * the next field to be parsed
+ * @param pdata_size the pointer to variable holding the size of the data
+ * @param field_start the start of the current field in the @a buf
+ * @param name_start the start of the name of the field in the @a buf
+ * @param name_len the length of the name, not including mandatory terminating
+ * zero
+ * @param filename_start the start of the filename of the field in the @a buf,
+ * zero if no filename is provided
+ * @param filename_len the length of the filename, not including mandatory
+ * terminating zero
+ * @param cntn_type_start the start of the Content-Type value of the field in
+ * the @a buf, zero if no Content-Type is provided
+ * @param cntn_type_len the length of the Content-Type value, not including
+ * mandatory terminating zero
+ * @param enc_start the start of the Content-Encoding value of the field in
+ * the @a buf, zero if no Content-Encoding is provided
+ * @param enc_len the length of the Content-Encoding value, not including
+ * mandatory terminating zero
+ * @param value_start the start of the field value in the @a buf, zero if
+ * no value is provided
+ * @param value_len the length of the field value, not including mandatory
+ * terminating zero
+ * @return 'true' if stream state has been changed,
+ * 'false' to continue parsing
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_complete_field_all (struct MHD_Connection *restrict c,
+ char *restrict buf,
+ size_t *restrict pfield_next_pos,
+ size_t *restrict pdata_size,
+ size_t field_start,
+ size_t name_start,
+ size_t name_len,
+ size_t filename_start,
+ size_t filename_len,
+ size_t cntn_type_start,
+ size_t cntn_type_len,
+ size_t enc_start,
+ size_t enc_len,
+ size_t value_start,
+ size_t value_len)
+{
+ struct mhd_PostParseActionData *const p_par =
+ &(c->rq.app_act.head_act.data.post_parse);
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+
+ mhd_assert ((0 == filename_start) || \
+ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
+ mhd_assert ((0 == cntn_type_start) || \
+ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
+ mhd_assert ((0 == enc_start) || \
+ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
+
+ mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state);
+ mhd_assert (value_start + value_len <= *pfield_next_pos);
+ mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \
+ (value_start + value_len < *pfield_next_pos));
+ mhd_assert (*pfield_next_pos <= *pdata_size);
+ mhd_assert ((name_start + name_len < value_start) || \
+ (0 == value_start));
+ mhd_assert (value_start + value_len <= *pfield_next_pos);
+ mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \
+ (name_start + name_len < *pfield_next_pos));
+ mhd_assert ((filename_start + filename_len < value_start) || \
+ (0 == value_start));
+ mhd_assert (filename_start + filename_len <= *pfield_next_pos);
+ mhd_assert ((cntn_type_start + cntn_type_len < value_start) || \
+ (0 == value_start));
+ mhd_assert (cntn_type_start + cntn_type_len <= *pfield_next_pos);
+ mhd_assert ((enc_start + enc_len < value_start) || \
+ (0 == value_start));
+ mhd_assert (enc_start + enc_len <= *pfield_next_pos);
+ mhd_assert (field_start <= name_start);
+ mhd_assert ((field_start <= filename_start) || (0 == filename_start));
+ mhd_assert ((field_start <= cntn_type_start) || (0 == cntn_type_start));
+ mhd_assert ((field_start <= enc_start) || (0 == enc_start));
+ mhd_assert ((field_start <= value_start) || (0 == value_start));
+ mhd_assert ((0 != filename_start) || (0 == filename_len));
+ mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
+ mhd_assert ((0 != enc_start) || (0 == enc_len));
+ mhd_assert ((0 != value_start) || (0 == value_len));
+ mhd_assert (0 == buf [name_start + name_len]);
+
+ p_data->some_data_provided = true;
+
+ if (is_value_streaming_needed (c, (*pfield_next_pos - field_start)))
+ {
+ bool res;
+ const struct MHD_UploadAction *act;
+ struct MHD_String name;
+ struct MHD_StringNullable filename;
+ struct MHD_StringNullable content_type;
+ struct MHD_StringNullable encoding;
+
+ make_post_strings_from_buf_and_indices (buf,
+ name_start,
+ name_len,
+ filename_start,
+ filename_len,
+ cntn_type_start,
+ cntn_type_len,
+ enc_start,
+ enc_len,
+ &name,
+ &filename,
+ &content_type,
+ &encoding);
+
+ act = p_par->stream_reader (&(c->rq),
+ p_par->reader_cls,
+ &name,
+ &filename,
+ &content_type,
+ &encoding,
+ value_len,
+ buf + value_start,
+ p_data->value_off,
+ MHD_YES);
+ p_data->some_data_provided = true;
+ res = mhd_stream_process_upload_action (c, act, false);
+ if (c->suspended)
+ return true;
+ p_data->force_streamed = false;
+ p_data->value_off = 0;
+ if (*pdata_size > *pfield_next_pos)
+ {
+ size_t consumed_size;
+ memmove (buf + field_start,
+ buf + *pfield_next_pos,
+ *pdata_size - *pfield_next_pos);
+ consumed_size = *pfield_next_pos - field_start;
+ *pfield_next_pos = field_start;
+ *pdata_size -= consumed_size;
+ }
+ else
+ {
+ *pfield_next_pos = field_start;
+ *pdata_size = field_start;
+ }
+ return res;
+ }
+ else
+ {
+ struct mhd_PositionAndLength name_i;
+ struct mhd_PositionAndLength filename_i;
+ struct mhd_PositionAndLength content_type_i;
+ struct mhd_PositionAndLength encoding_i;
+ struct mhd_PositionAndLength value_i;
+
+ mhd_assert (! p_data->force_streamed);
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert ((0 == value_start) || (0 == buf [value_start + value_len]));
+
+ name_i.pos = name_start;
+ name_i.len = name_len;
+ filename_i.pos = filename_start;
+ filename_i.len = filename_len;
+ content_type_i.pos = cntn_type_start;
+ content_type_i.len = cntn_type_len;
+ encoding_i.pos = enc_start;
+ encoding_i.len = enc_len;
+ value_i.pos = value_start;
+ value_i.len = value_len;
+
+ if (! add_parsed_post_field (c,
+ &name_i,
+ &filename_i,
+ &content_type_i,
+ &encoding_i,
+ &value_i))
+ {
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \
+ "The request POST data cannot be parsed completely " \
+ "because there is not enough pool memory.");
+ p_data->parse_result = MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM;
+ return true;
+ }
+
+ p_data->some_data_provided = true;
+ }
+
+ return false; /* Continue parsing */
+}
+
+
+/**
+ * Process new full parsed POST field
+ * @param c the stream to use
+ * @param buf the buffer, where the data is
+ * @param pfield_next_pos the pointer to variable holding index of
+ * the next field to be parsed
+ * @param pdata_size the pointer to variable holding the size of the data
+ * @param field_start the start of the current field in the @a buf
+ * @param name_start the start of the name of the field in the @a buf
+ * @param name_len the length of the name, not including mandatory terminating
+ * zero
+ * @param value_start the start of the field value in the @a buf, zero if
+ * no value is provided
+ * @param value_len the length of the field value, not including mandatory
+ * terminating zero
+ * @return 'true' if stream state has been changed,
+ * 'false' to continue parsing
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_complete_field (struct MHD_Connection *restrict c,
+ char *restrict buf,
+ size_t *restrict pfield_next_pos,
+ size_t *restrict pdata_size,
+ size_t field_start,
+ size_t name_start,
+ size_t name_len,
+ size_t value_start,
+ size_t value_len)
+{
+ mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state);
+ mhd_assert (value_start + value_len <= *pfield_next_pos);
+ mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \
+ (value_start + value_len < *pfield_next_pos));
+ mhd_assert ((name_start + name_len < value_start) || \
+ (0 == value_start));
+ mhd_assert (name_start + name_len <= *pfield_next_pos);
+ mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \
+ (name_start + name_len < *pfield_next_pos));
+ mhd_assert (field_start <= name_start);
+ mhd_assert ((field_start <= value_start) || (0 == value_start));
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
+ c->rq.u_proc.post.enc);
+
+ return process_complete_field_all (c,
+ buf,
+ pfield_next_pos,
+ pdata_size,
+ field_start,
+ name_start,
+ name_len,
+ 0, 0, 0, 0, 0, 0,
+ value_start,
+ value_len);
+}
+
+
+/**
+ * Process the part of the POST value.
+ *
+ * The part of the value are be provided for "streaming" processing by
+ * the application callback and removed from the buffer (the remaining of
+ * the data in the buffer is shifted backward).
+ * The function must be called only when streaming is the partial value is
+ * needed.
+ *
+ * @param c the connection to use
+ * @param buf the pointer to the buffer
+ * @param pnext_pos the position of the next character to be processed
+ * in the buffer
+ * @param pdata_size the size of the data in the buffer
+ * @param name_start the position of the "name", must be zero-terminated
+ * @param name_len the length of the "name", not including zero-termination
+ * @param filename_start the position of the filename, zero if not
+ * provided / set
+ * @param filename_len the length of the filename
+ * @param cntn_type_start the position of field "Content-Type" value, zero
+ * if not provided / set
+ * @param cntn_type_len the length of the field "Content-Type" value
+ * @param enc_start the position of the field "Content-Encoding" value, zero
+ * if not provided / set
+ * @param enc_len the length of the field "Content-Encoding" value
+ * @param part_value_start the position of partial value data, does not
+ * need to be zero-terminated
+ * @param part_value_len the length of the partial value data
+ * @return 'true' if connection/stream state has been changed,
+ * 'false' indicates the need to continuation of POST data parsing
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_partial_value_all (struct MHD_Connection *restrict c,
+ char *restrict buf,
+ size_t *restrict pnext_pos,
+ size_t *restrict pdata_size,
+ size_t name_start,
+ size_t name_len,
+ size_t filename_start,
+ size_t filename_len,
+ size_t cntn_type_start,
+ size_t cntn_type_len,
+ size_t enc_start,
+ size_t enc_len,
+ size_t part_value_start,
+ size_t part_value_len)
+{
+ struct mhd_PostParseActionData *const p_par =
+ &(c->rq.app_act.head_act.data.post_parse);
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct MHD_String name;
+ struct MHD_StringNullable filename;
+ struct MHD_StringNullable content_type;
+ struct MHD_StringNullable encoding;
+ const struct MHD_UploadAction *act;
+ bool res;
+
+ mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state);
+ mhd_assert (*pnext_pos <= *pdata_size);
+ mhd_assert (part_value_start + part_value_len <= *pnext_pos);
+ mhd_assert (0 != part_value_start);
+ mhd_assert (0 != part_value_len);
+ mhd_assert (name_start + name_len < *pnext_pos);
+ mhd_assert (filename_start + filename_len < part_value_start);
+ mhd_assert (filename_start + filename_len < *pnext_pos);
+ mhd_assert (cntn_type_start + cntn_type_len < part_value_start);
+ mhd_assert (cntn_type_start + cntn_type_len < *pnext_pos);
+ mhd_assert (enc_start + enc_len < part_value_start);
+ mhd_assert (enc_start + enc_len < *pnext_pos);
+ mhd_assert ((0 != filename_start) || (0 == filename_len));
+ mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
+ mhd_assert ((0 != enc_start) || (0 == enc_len));
+ mhd_assert (NULL != p_par->stream_reader);
+
+ make_post_strings_from_buf_and_indices (buf,
+ name_start,
+ name_len,
+ filename_start,
+ filename_len,
+ cntn_type_start,
+ cntn_type_len,
+ enc_start,
+ enc_len,
+ &name,
+ &filename,
+ &content_type,
+ &encoding);
+
+ act = p_par->stream_reader (&(c->rq),
+ p_par->reader_cls,
+ &name,
+ &filename,
+ &content_type,
+ &encoding,
+ part_value_len,
+ buf + part_value_start,
+ p_data->value_off,
+ MHD_NO);
+
+ p_data->some_data_provided = true;
+ p_data->force_streamed = true;
+
+ res = mhd_stream_process_upload_action (c, act, false);
+ if (c->suspended)
+ return true;
+
+ p_data->value_off += part_value_len;
+ if (*pdata_size > *pnext_pos)
+ {
+ size_t consumed_size;
+
+ memmove (buf + part_value_start,
+ buf + *pnext_pos,
+ *pdata_size - *pnext_pos);
+ consumed_size = *pnext_pos - part_value_start;
+ *pnext_pos = part_value_start;
+ *pdata_size -= consumed_size;
+ }
+ else
+ {
+ mhd_assert (*pdata_size == *pnext_pos);
+ *pnext_pos = part_value_start;
+ *pdata_size = part_value_start;
+ }
+ return res;
+}
+
+
+/**
+ * Process the part of the POST value.
+ * The part of the value are be provided for "streaming" processing by
+ * the application callback and removed from the buffer (the remaining of
+ * the data in the buffer is shifted backward).
+ * The function must be called only when streaming is the partial value is
+ * needed.
+ * @param c the connection to use
+ * @param buf the pointer to the buffer
+ * @param pnext_pos the position of the next character to be processed
+ * in the buffer
+ * @param pdata_size the size of the data in the buffer
+ * @param name_start the position of the "name", must be zero-terminated
+ * @param name_len the length of the "name", not including zero-termination
+ * @param part_value_start the position of partial value data, does not
+ * need to be zero-terminated
+ * @param part_value_len the length of the partial value data
+ * @return 'true' if connection/stream state has been changed,
+ * 'false' indicates the need to continuation of POST data parsing
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+process_partial_value (struct MHD_Connection *restrict c,
+ char *restrict buf,
+ size_t *restrict pnext_pos,
+ size_t *restrict pdata_size,
+ size_t name_start,
+ size_t name_len,
+ size_t part_value_start,
+ size_t part_value_len)
+{
+ mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state);
+ mhd_assert (part_value_start + part_value_len <= *pnext_pos);
+ mhd_assert (name_start + name_len < part_value_start);
+ mhd_assert (0 != part_value_start);
+ mhd_assert (0 != part_value_len);
+ mhd_assert (name_start + name_len < *pnext_pos);
+
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
+ c->rq.u_proc.post.enc);
+
+ return process_partial_value_all (c,
+ buf,
+ pnext_pos,
+ pdata_size,
+ name_start,
+ name_len,
+ 0, 0, 0, 0, 0, 0,
+ part_value_start,
+ part_value_len);
+}
+
+
+/**
+ * Parse "application/x-www-form-urlencoded" data
+ * @param c the stream to use
+ * @param pdata_size the pointer to variable holding the size of the data in
+ * the @a buf
+ * @param buf the buffer with the data
+ * @return 'true' if stream state changed,
+ * 'false' to continue parsing
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
+parse_post_urlenc (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct mhd_PostParserUrlEncData *const uf = &(p_data->e_d.u_enc); /**< the current "url-enc" field */
+ size_t i;
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_FORM_URLENCODED == c->rq.u_proc.post.enc);
+ mhd_assert (MHD_POST_PARSE_RES_OK == p_data->parse_result);
+ mhd_assert (! c->discard_request);
+ mhd_assert (p_data->next_parse_pos < *pdata_size);
+
+ if ((mhd_POST_UENC_ST_VALUE == uf->st) &&
+ (0 != uf->value_len))
+ {
+ /* The 'value' was partially decoded, but not processed because application
+ * asked for 'suspend' action */
+ mhd_assert (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader);
+ if (process_partial_value (c,
+ buf,
+ &p_data->next_parse_pos,
+ pdata_size,
+ uf->name_idx,
+ uf->name_len,
+ uf->value_idx,
+ uf->value_len))
+ return true;
+ uf->value_len = 0;
+ }
+
+ i = p_data->next_parse_pos;
+ while (*pdata_size > i)
+ {
+ switch (uf->st)
+ {
+ case mhd_POST_UENC_ST_NOT_STARTED:
+ mhd_assert (0 == p_data->field_start);
+ mhd_assert (0 == p_data->value_off);
+ p_data->field_start = i;
+ uf->name_idx = i;
+ uf->last_pct_idx = mhd_POST_INVALID_POS;
+ uf->st = mhd_POST_UENC_ST_NAME;
+ /* Intentional fall-through */
+ case mhd_POST_UENC_ST_NAME:
+ do /* Fast local loop */
+ {
+ if ('+' == buf[i])
+ buf[i] = ' ';
+ else if ('%' == buf[i])
+ uf->last_pct_idx = i;
+ else if ('=' == buf[i])
+ {
+ uf->st = mhd_POST_UENC_ST_AT_EQ;
+ break;
+ }
+ else if ('&' == buf[i])
+ {
+ uf->st = mhd_POST_UENC_ST_AT_AMPRSND;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_UENC_ST_AT_EQ == uf->st) || \
+ (mhd_POST_UENC_ST_AT_AMPRSND == uf->st) );
+ continue;
+ case mhd_POST_UENC_ST_AT_EQ:
+ mhd_assert (i > uf->name_idx);
+ mhd_assert (0 == uf->name_len);
+ mhd_assert (uf->last_pct_idx >= p_data->field_start);
+ mhd_assert (uf->last_pct_idx >= uf->name_idx);
+ mhd_assert ((uf->last_pct_idx == mhd_POST_INVALID_POS) || \
+ (uf->last_pct_idx < i));
+ mhd_assert (0 == uf->value_len);
+ if (uf->last_pct_idx != mhd_POST_INVALID_POS)
+ uf->name_len = mhd_str_pct_decode_lenient_n (buf + uf->name_idx,
+ i - uf->name_idx,
+ buf + uf->name_idx,
+ i - uf->name_idx,
+ NULL);
+ else
+ uf->name_len = i - uf->name_idx;
+ buf[uf->name_idx + uf->name_len] = 0; /* Zero-terminate the name */
+
+ uf->st = mhd_POST_UENC_ST_EQ_FOUND;
+ ++i; /* Process the next char */
+ continue; /* Check whether the next char is available */
+ case mhd_POST_UENC_ST_EQ_FOUND:
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert (0 == uf->value_idx);
+ mhd_assert (0 == uf->value_len);
+ mhd_assert (0 != i && "the 'value' should follow the 'name'");
+ uf->last_pct_idx = mhd_POST_INVALID_POS;
+ uf->value_idx = i;
+ uf->st = mhd_POST_UENC_ST_VALUE;
+ /* Intentional fall-through */
+ case mhd_POST_UENC_ST_VALUE:
+ do /* Fast local loop */
+ {
+ if ('+' == buf[i])
+ buf[i] = ' ';
+ else if ('%' == buf[i])
+ uf->last_pct_idx = i;
+ else if ('&' == buf[i])
+ {
+ uf->st = mhd_POST_UENC_ST_AT_AMPRSND;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_UENC_ST_AT_AMPRSND == uf->st));
+ continue;
+ case mhd_POST_UENC_ST_AT_AMPRSND:
+ mhd_assert (0 == uf->value_len);
+ mhd_assert ((uf->last_pct_idx == mhd_POST_INVALID_POS) || \
+ (uf->last_pct_idx < i));
+ mhd_assert ((uf->last_pct_idx == mhd_POST_INVALID_POS) || \
+ ((uf->name_idx + uf->name_len) < i));
+ if (0 != uf->value_idx)
+ {
+ /* Have 'name' and 'value' */
+ if (uf->last_pct_idx != mhd_POST_INVALID_POS)
+ uf->value_len = mhd_str_pct_decode_lenient_n (buf + uf->value_idx,
+ i - uf->value_idx,
+ buf + uf->value_idx,
+ i - uf->value_idx,
+ NULL);
+ else
+ uf->value_len = i - uf->value_idx;
+ buf[uf->value_idx + uf->value_len] = 0; /* Zero-terminate the value */
+ }
+ else
+ {
+ /* Have 'name' only (without any 'value') */
+ if (uf->last_pct_idx != mhd_POST_INVALID_POS)
+ uf->name_len = mhd_str_pct_decode_lenient_n (buf + uf->name_idx,
+ i - uf->name_idx,
+ buf + uf->name_idx,
+ i - uf->name_idx,
+ NULL);
+ else
+ uf->name_len = i - uf->name_idx;
+ buf[uf->name_idx + uf->name_len] = 0; /* Zero-terminate the name */
+ }
+ uf->st = mhd_POST_UENC_ST_FULL_FIELD_FOUND;
+ /* Intentional fall-through */
+ case mhd_POST_UENC_ST_FULL_FIELD_FOUND:
+ ++i; /* Consume current character,
+ advance to the next char to be checked */
+ if (process_complete_field (c,
+ buf,
+ &i,
+ pdata_size,
+ p_data->field_start,
+ uf->name_idx,
+ uf->name_len,
+ uf->value_idx,
+ uf->value_len))
+ {
+ if (c->suspended)
+ --i; /* Go back to the same position */
+ else
+ reset_parse_field_data_urlenc (p_data);
+ p_data->next_parse_pos = i;
+ return true;
+ }
+ mhd_assert (*pdata_size >= i);
+ reset_parse_field_data_urlenc (p_data);
+ continue; /* Process the next char */
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ }
+ mhd_assert (0 && "Should be unreachable");
+ MHD_UNREACHABLE_;
+ break;
+ }
+
+ mhd_assert (*pdata_size == i);
+
+ mhd_assert (mhd_POST_UENC_ST_AT_EQ != uf->st);
+ mhd_assert (mhd_POST_UENC_ST_AT_AMPRSND != uf->st);
+ mhd_assert (mhd_POST_UENC_ST_FULL_FIELD_FOUND != uf->st);
+ mhd_assert ((mhd_POST_UENC_ST_VALUE != uf->st) || \
+ (0 == uf->value_len));
+
+ mhd_assert (*pdata_size == i);
+
+ if ((mhd_POST_UENC_ST_VALUE == uf->st) &&
+ (i != uf->value_idx) && /* Encoded value position must be larger then zero */
+ is_value_streaming_needed (c, i - p_data->field_start))
+ {
+ size_t len_of_value_part;
+ if (uf->last_pct_idx != mhd_POST_INVALID_POS)
+ {
+ mhd_assert (uf->last_pct_idx < i);
+ mhd_assert (uf->last_pct_idx >= uf->value_idx);
+
+ if (2 >= (i - uf->last_pct_idx))
+ i = uf->last_pct_idx; /* The last percent-encoded character is incomplete */
+
+ len_of_value_part =
+ mhd_str_pct_decode_lenient_n (buf + uf->value_idx,
+ i - uf->value_idx,
+ buf + uf->value_idx,
+ i - uf->value_idx,
+ NULL);
+ }
+ else
+ len_of_value_part = i - uf->value_idx;
+
+ if (0 != len_of_value_part)
+ {
+ bool proc_res;
+
+ proc_res =
+ process_partial_value (c,
+ buf,
+ &i,
+ pdata_size,
+ uf->name_idx,
+ uf->name_len,
+ uf->value_idx,
+ len_of_value_part);
+
+ /* Reset position of last '%' char: it was already decoded or
+ * 'i' points to it and it will be processed again next time */
+ uf->last_pct_idx = mhd_POST_INVALID_POS;
+
+ if (proc_res)
+ {
+ if (c->suspended)
+ uf->value_len = len_of_value_part; /* Indicate that value has been
+ partially decoded and needs
+ to be "streamed" again */
+ p_data->next_parse_pos = i;
+ return true;
+ }
+ }
+ }
+
+ p_data->next_parse_pos = i;
+ return false; /* Continue parsing */
+}
+
+
+/**
+ * Parse "multipart/form-data" data
+ * @param c the stream to use
+ * @param pdata_size the pointer to variable holding the size of the data in
+ * the @a buf
+ * @param buf the buffer with the data
+ * @return 'true' if stream state changed,
+ * 'false' to continue parsing
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
+parse_post_mpart (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ const int discp_lvl = c->daemon->req_cfg.strictnees;
+ const bool bare_lf_as_crlf = (-2 >= discp_lvl); /* Bare LF termination is dangerous when used in "multipart/form-data" */
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct mhd_PostParserMPartFormData *const mf = &(p_data->e_d.m_form); /**< the current "form-data" parsing details */
+ size_t i;
+
+ mhd_assert (NULL != mf->bound.data);
+ mhd_assert (NULL == memchr (mf->bound.data, '\r', mf->bound.size));
+ mhd_assert (NULL == memchr (mf->bound.data, '\n', mf->bound.size));
+ mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == \
+ c->rq.u_proc.post.enc);
+ mhd_assert (MHD_POST_PARSE_RES_OK == p_data->parse_result);
+ mhd_assert (mhd_POST_MPART_ST_FORMAT_ERROR != mf->st);
+ mhd_assert (! c->discard_request);
+ mhd_assert (p_data->next_parse_pos < *pdata_size);
+
+ i = p_data->next_parse_pos;
+ while (*pdata_size > i)
+ {
+ switch (mf->st)
+ {
+ case mhd_POST_MPART_ST_BACK_TO_PREAMBL:
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mf->line_start = mhd_POST_INVALID_POS;
+ mf->st = mhd_POST_MPART_ST_PREAMBL;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_PREAMBL:
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
+ do /* Fast local loop */
+ {
+ if ('\r' == buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_PREAMBL_CR_FOUND;
+ ++i; /* Go to the next char */
+ break;
+ }
+ else if ('\n' == buf[i] && bare_lf_as_crlf)
+ {
+ mf->st = mhd_POST_MPART_ST_PREAMBL_LINE_START;
+ ++i; /* Go to the next char */
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_PREAMBL_CR_FOUND == mf->st) || \
+ (mhd_POST_MPART_ST_PREAMBL_LINE_START == mf->st) );
+ continue;
+ case mhd_POST_MPART_ST_PREAMBL_CR_FOUND:
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
+ if ('\n' == buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_PREAMBL_LINE_START;
+ ++i;
+ }
+ else
+ mf->st = mhd_POST_MPART_ST_PREAMBL;
+ continue;
+ case mhd_POST_MPART_ST_NOT_STARTED:
+ mhd_assert (0 == p_data->field_start);
+ mhd_assert (0 == p_data->value_off);
+ mf->delim_check_start = mhd_POST_INVALID_POS; /* Ignored for first delimiter */
+ p_data->field_start = i;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_PREAMBL_LINE_START:
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
+ mf->line_start = i;
+ mf->st = mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM:
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start); /* Ignored for first delimiter */
+ mhd_assert (i >= mf->line_start);
+ do /* Fast local loop */
+ {
+ mhd_assert (i - mf->line_start < mf->bound.size + 2);
+ if (i < mf->line_start + 2)
+ {
+ if ('-' != buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_BACK_TO_PREAMBL;
+ break;
+ }
+ }
+ else if (i <= mf->line_start + mf->bound.size + 1)
+ {
+ if (mf->bound.data[i - (mf->line_start + 2)] != buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_BACK_TO_PREAMBL;
+ break;
+ }
+ else if (i == mf->line_start + mf->bound.size + 1)
+ {
+ mf->st = mhd_POST_MPART_ST_FIRST_DELIM_FOUND;
+ ++i;
+ break;
+ }
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_FIRST_DELIM_FOUND == mf->st) || \
+ (mhd_POST_MPART_ST_BACK_TO_PREAMBL == mf->st));
+ continue;
+ case mhd_POST_MPART_ST_FIRST_DELIM_FOUND:
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start); /* Ignored for first delimiter */
+ mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
+ mhd_assert (i >= mf->line_start + mf->bound.size + 2);
+ do /* Fast local loop */
+ {
+ if ('\n' == buf[i])
+ {
+ if (bare_lf_as_crlf ||
+ ('\r' == buf [i - 1]))
+ {
+ mf->st = mhd_POST_MPART_ST_FIRST_PART_START;
+ ++i;
+ }
+ else
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+
+ break;
+ }
+ else if ('\r' == buf [i - 1])
+ {
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ break;
+ }
+ else if ((i == mf->line_start + mf->bound.size + 3) &&
+ ('-' == buf [i - 1]) &&
+ ('-' == buf [i]))
+ {
+ mf->st = mhd_POST_MPART_ST_EPILOGUE;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_FIRST_PART_START == mf->st) || \
+ (mhd_POST_MPART_ST_FORMAT_ERROR == mf->st) || \
+ (mhd_POST_MPART_ST_EPILOGUE == mf->st));
+ continue;
+ case mhd_POST_MPART_ST_FIRST_PART_START:
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start); /* Ignored for first delimiter */
+ mhd_assert (i > p_data->field_start);
+ mhd_assert (*pdata_size > i);
+ if ((c->rq.app_act.head_act.data.post_parse.max_nonstream_size <
+ i - p_data->field_start) ||
+ (*pdata_size - i < i - p_data->field_start))
+ {
+ /* Discard unused data */
+ memmove (buf + p_data->field_start,
+ buf + i,
+ *pdata_size - i);
+ *pdata_size -= (i - p_data->field_start);
+ i = p_data->field_start;
+ }
+ mf->delim_check_start = p_data->field_start;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_PART_START:
+ mhd_assert (0 == mf->f.name_len);
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ p_data->field_start = mf->delim_check_start;
+ mf->delim_check_start = mhd_POST_INVALID_POS;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_HEADER_LINE_START:
+ mf->line_start = i;
+ mf->st = mhd_POST_MPART_ST_HEADER_LINE;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_HEADER_LINE:
+ mhd_assert (i >= mf->line_start);
+ mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ do /* Fast local loop */
+ {
+ if ('\r' == buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND;
+ ++i;
+ break;
+ }
+ else if ('\n' == buf[i])
+ {
+ if (bare_lf_as_crlf)
+ mf->st = mhd_POST_MPART_ST_HEADER_LINE_END;
+ else
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ break;
+ }
+ else if (mf->line_start + mf->bound.size + 1 == i)
+ {
+ if (('-' == buf[mf->line_start]) &&
+ ('-' == buf[mf->line_start + 1]) &&
+ (0 == memcmp (buf + mf->line_start + 2,
+ mf->bound.data,
+ mf->bound.size)))
+ {
+ /* The delimiter before the end of the header */
+ if (2 > mf->line_start)
+ mf->delim_check_start = mf->line_start;
+ else if (! bare_lf_as_crlf)
+ mf->delim_check_start = mf->line_start - 2;
+ else
+ mf->delim_check_start = mf->line_start - 1; /* Actually can be one char earlier */
+ mf->st = mhd_POST_MPART_ST_DELIM_FOUND;
+ break;
+ }
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND == mf->st) || \
+ (mhd_POST_MPART_ST_HEADER_LINE_END == mf->st) || \
+ (mhd_POST_MPART_ST_DELIM_FOUND == mf->st) || \
+ (mhd_POST_MPART_ST_FORMAT_ERROR == mf->st) );
+ continue;
+ case mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND:
+ if ('\n' != buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ mf->st = mhd_POST_MPART_ST_HEADER_LINE_END;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_HEADER_LINE_END:
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mhd_assert (i >= mf->line_start);
+ mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
+ if (1)
+ {
+ size_t line_len;
+ if (i == mf->line_start)
+ line_len = 0;
+ else if ('\r' == buf[i - 1])
+ line_len = i - mf->line_start - 1;
+ else
+ line_len = i - mf->line_start;
+
+ if (0 == line_len)
+ {
+ ++i;
+ mf->st = mhd_POST_MPART_ST_VALUE_START;
+ continue;
+ }
+ else
+ {
+ static const struct MHD_String hdr =
+ mhd_MSTR_INIT ("Content-Disposition:");
+ static const struct MHD_String tkn = mhd_MSTR_INIT ("form-data");
+ static const struct MHD_String n_par = mhd_MSTR_INIT ("name");
+
+ if ((hdr.len + tkn.len + n_par.len + 2 <= line_len) &&
+ mhd_str_equal_caseless_bin_n (hdr.cstr,
+ buf + mf->line_start,
+ hdr.len))
+ {
+ size_t hdr_val_start;
+ struct MHD_String hdr_val;
+ enum mhd_StingStartsWithTokenResult res;
+ struct mhd_BufferConst name_buf;
+ bool hdr_has_name;
+ bool name_needs_unq;
+
+ buf [mf->line_start + line_len] = 0; /* Zero-terminate the header line */
+ hdr_val_start = mf->line_start + hdr.len;
+ /* Skip all whitespace chars */
+ while ((' ' == buf[hdr_val_start]) || ('\t' == buf[hdr_val_start]))
+ ++hdr_val_start;
+
+ mhd_assert (hdr_val_start <= i);
+
+ hdr_val.cstr = buf + hdr_val_start;
+ hdr_val.len = mf->line_start + line_len - hdr_val_start;
+
+ res = mhd_str_starts_with_token_req_param (&hdr_val,
+ &tkn,
+ &n_par,
+ &name_buf,
+ &name_needs_unq);
+ if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
+ {
+ /* Found two names for one field */
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ else if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res)
+ {
+ static const struct MHD_String fn_par =
+ mhd_MSTR_INIT ("filename");
+ struct mhd_BufferConst fname_buf;
+ bool fname_needs_unq;
+
+ if (NULL != name_buf.data)
+ {
+ mhd_assert (buf < name_buf.data);
+ if (0 != mf->f.name_idx)
+ {
+ /* Found two names for one field */
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ mf->f.name_idx = (size_t) (name_buf.data - buf);
+ mf->f.name_len = name_buf.size;
+ hdr_has_name = true;
+
+ /* Do not process (unquote, url-decode, zero-terminate) here yet
+ * as it may break the header format */
+ }
+ else
+ hdr_has_name = false;
+
+ res = mhd_str_starts_with_token_req_param (&hdr_val,
+ &tkn,
+ &fn_par,
+ &fname_buf,
+ &fname_needs_unq);
+ if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
+ {
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ else if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res)
+ {
+ if (NULL != fname_buf.data)
+ {
+ mhd_assert (buf < fname_buf.data);
+ if (0 != mf->f.filename_idx)
+ {
+ /* Found two filenames for one field */
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ mf->f.filename_idx = (size_t) (fname_buf.data - buf);
+ if (! fname_needs_unq)
+ mf->f.filename_len = fname_buf.size;
+ else
+ {
+ mf->f.filename_len =
+ mhd_str_unquote (fname_buf.data,
+ fname_buf.size,
+ buf + mf->f.filename_idx);
+ if ((0 == mf->f.filename_len) && (0 != fname_buf.size))
+ {
+ mhd_assert (0 && "broken quoting must be detected " \
+ "earlier by " \
+ "mhd_str_starts_with_token_req_param()");
+ MHD_UNREACHABLE_;
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ }
+ mhd_assert (mf->f.filename_idx + mf->f.filename_len <= i);
+ mf->f.filename_len =
+ mhd_str_pct_decode_lenient_n (buf + mf->f.filename_idx,
+ mf->f.filename_len,
+ buf + mf->f.filename_idx,
+ mf->f.filename_len,
+ NULL);
+ mhd_assert (mf->f.filename_idx + mf->f.filename_len <= i);
+
+ buf[mf->f.filename_idx + mf->f.filename_len] = 0; /* Zero-terminate the filename */
+ }
+ }
+ else
+ {
+ mhd_assert (mhd_STR_STARTS_W_TOKEN_NO_TOKEN == res);
+ mhd_assert (0 && "The presence of the token was "
+ "checked earlier");
+ MHD_UNREACHABLE_;
+ }
+
+ if (hdr_has_name)
+ {
+ if (name_needs_unq)
+ {
+ mf->f.name_len = mhd_str_unquote (buf + mf->f.name_idx,
+ mf->f.name_len,
+ buf + mf->f.name_idx);
+ if ((0 == mf->f.name_len) && (0 != name_buf.size))
+ {
+ mhd_assert (0 && "broken quoting must be detected " \
+ "earlier by " \
+ "mhd_str_starts_with_token_req_param()");
+ MHD_UNREACHABLE_;
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ continue;
+ }
+ }
+ mhd_assert (mf->f.name_idx + mf->f.name_len <= i);
+ mf->f.name_len =
+ mhd_str_pct_decode_lenient_n (buf + mf->f.name_idx,
+ mf->f.name_len,
+ buf + mf->f.name_idx,
+ mf->f.name_len,
+ NULL);
+ mhd_assert (mf->f.name_idx + mf->f.name_len <= i);
+
+ buf[mf->f.name_idx + mf->f.name_len] = 0; /* Zero-terminate the name */
+ }
+ }
+ }
+ }
+ }
+ ++i;
+ mf->st = mhd_POST_MPART_ST_HEADER_LINE_START;
+ continue;
+ case mhd_POST_MPART_ST_VALUE_START:
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert (0 == mf->f.value_idx);
+ mhd_assert (0 == mf->f.value_len);
+ mhd_assert (0 != i && "the 'value' should follow the 'name'");
+ if (0 == mf->f.name_idx)
+ {
+ mhd_LOG_MSG (c->daemon, \
+ p_data->some_data_provided ? \
+ MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT : \
+ MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
+ "The request 'multipart/form-data' POST field has no " \
+ "name of the field.");
+ p_data->parse_result =
+ p_data->some_data_provided ?
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT :
+ MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return true; /* Stop parsing the upload */
+ }
+ mhd_assert (0 != mf->f.name_len);
+ mhd_assert (i > mf->f.name_idx);
+ mf->f.value_idx = i;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_BACK_TO_VALUE:
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mf->line_start = mhd_POST_INVALID_POS;
+ mf->delim_check_start = mhd_POST_INVALID_POS;
+ mf->st = mhd_POST_MPART_ST_VALUE;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_VALUE:
+ mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ do /* Fast local loop */
+ {
+ if ('\r' == buf[i])
+ {
+ mf->delim_check_start = i;
+ mf->st = mhd_POST_MPART_ST_VALUE_CR_FOUND;
+ ++i;
+ break;
+ }
+ else if (bare_lf_as_crlf && '\n' == buf[i])
+ {
+ mf->delim_check_start = i;
+ mf->st = mhd_POST_MPART_ST_VALUE_LINE_START;
+ ++i;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_VALUE_CR_FOUND == mf->st) || \
+ (mhd_POST_MPART_ST_VALUE_LINE_START == mf->st));
+ continue;
+ case mhd_POST_MPART_ST_VALUE_CR_FOUND:
+ if ('\n' != buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
+ continue;
+ }
+ mf->st = mhd_POST_MPART_ST_VALUE_LINE_START;
+ ++i;
+ continue;
+ case mhd_POST_MPART_ST_VALUE_LINE_START:
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mf->line_start = i;
+ mf->st = mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM:
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mhd_assert (i >= mf->line_start);
+ do /* Fast local loop */
+ {
+ mhd_assert (i - mf->line_start < mf->bound.size + 2);
+ if (i < mf->line_start + 2)
+ {
+ if ('-' != buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
+ break;
+ }
+ }
+ else if (i <= mf->line_start + mf->bound.size + 1)
+ {
+ if (mf->bound.data[i - (mf->line_start + 2)] != buf[i])
+ {
+ mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
+ break;
+ }
+ if (i == mf->line_start + mf->bound.size + 1)
+ {
+ mf->st = mhd_POST_MPART_ST_DELIM_FOUND;
+ ++i;
+ break;
+ }
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_BACK_TO_VALUE == mf->st) || \
+ (mhd_POST_MPART_ST_DELIM_FOUND == mf->st));
+ continue;
+ case mhd_POST_MPART_ST_DELIM_FOUND:
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mhd_assert (i >= mf->line_start + mf->bound.size + 2);
+ do /* Fast local loop */
+ {
+ if ('\n' == buf[i])
+ {
+ if (bare_lf_as_crlf ||
+ ('\r' == buf [i - 1]))
+ mf->st = mhd_POST_MPART_ST_VALUE_END_FOUND;
+ else
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+
+ break;
+ }
+ else if ('\r' == buf [i - 1])
+ {
+ mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
+ break;
+ }
+ else if ((i == mf->line_start + mf->bound.size + 3) &&
+ ('-' == buf [i - 1]) &&
+ ('-' == buf [i]))
+ {
+ mf->st = mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_MPART_ST_VALUE_END_FOUND == mf->st) || \
+ (mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL == mf->st) || \
+ (mhd_POST_MPART_ST_FORMAT_ERROR == mf->st));
+ continue;
+ case mhd_POST_MPART_ST_VALUE_END_FOUND:
+ case mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL:
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ mhd_assert (mf->f.value_idx <= mf->delim_check_start);
+ mhd_assert (0 == mf->f.value_len);
+ mhd_assert (0 != mf->f.name_len);
+ mhd_assert (i > mf->f.name_idx);
+ mhd_assert (i > mf->delim_check_start);
+ if (0 != mf->f.value_idx)
+ {
+ mf->f.value_len = mf->delim_check_start - mf->f.value_idx;
+ buf[mf->f.value_idx + mf->f.value_len] = 0; /* Zero-terminate the value */
+ ++mf->delim_check_start; /* Shift start of the delimiter to add space for zero-termination */
+ }
+ if (mhd_POST_MPART_ST_VALUE_END_FOUND == mf->st)
+ mf->st = mhd_POST_MPART_ST_FULL_FIELD_FOUND;
+ else
+ mf->st = mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_FULL_FIELD_FOUND:
+ case mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL:
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
+ if (1)
+ {
+ size_t new_delim_check_start;
+ bool state_changed;
+
+ ++i; /* Consume current character */
+ new_delim_check_start = mf->delim_check_start;
+ state_changed =
+ process_complete_field_all (c,
+ buf,
+ &new_delim_check_start,
+ pdata_size,
+ p_data->field_start,
+ mf->f.name_idx,
+ mf->f.name_len,
+ mf->f.filename_idx,
+ mf->f.filename_len,
+ mf->f.cntn_type_idx,
+ mf->f.cntn_type_len,
+ mf->f.enc_idx,
+ mf->f.enc_len,
+ mf->f.value_idx,
+ mf->f.value_len);
+ if (c->suspended)
+ {
+ mhd_assert (mf->delim_check_start == new_delim_check_start);
+ mhd_assert (state_changed);
+ p_data->next_parse_pos = --i; /* Restore position */
+ return true;
+ }
+
+ if (mf->delim_check_start != new_delim_check_start)
+ {
+ size_t shift_size;
+ mhd_assert (mf->delim_check_start > new_delim_check_start);
+
+ shift_size = mf->delim_check_start - new_delim_check_start;
+ mf->delim_check_start = new_delim_check_start;
+ i -= shift_size;
+ }
+
+ mhd_assert (*pdata_size >= i);
+
+ reset_parse_field_data_mpart_cont (
+ p_data,
+ mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL == mf->st);
+
+ if (state_changed)
+ {
+ p_data->next_parse_pos = i;
+ return true;
+ }
+ }
+ continue; /* Process the next char */
+ case mhd_POST_MPART_ST_EPILOGUE:
+ /* Discard the rest of the content data */
+ *pdata_size = i;
+ p_data->next_parse_pos = i;
+ return false;
+ case mhd_POST_MPART_ST_FORMAT_ERROR:
+ if (p_data->some_data_provided)
+ {
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT, \
+ "The request POST has broken encoding or format and " \
+ "was parsed only partially.");
+ p_data->parse_result =
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
+ }
+ else
+ {
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
+ "The request POST has broken encoding or format and " \
+ "cannot be parsed.");
+ p_data->parse_result =
+ MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
+ }
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return true;
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ }
+ mhd_assert (0 && "Should be unreachable");
+ MHD_UNREACHABLE_;
+ break;
+ }
+
+ mhd_assert (*pdata_size == i);
+
+ mhd_assert (mhd_POST_MPART_ST_NOT_STARTED != mf->st);
+ mhd_assert (mhd_POST_MPART_ST_BACK_TO_PREAMBL != mf->st);
+ mhd_assert (mhd_POST_MPART_ST_PREAMBL_LINE_START != mf->st);
+ mhd_assert (mhd_POST_MPART_ST_HEADER_LINE_END != mf->st);
+ mhd_assert (mhd_POST_MPART_ST_BACK_TO_VALUE != mf->st);
+ mhd_assert (mhd_POST_MPART_ST_VALUE_END_FOUND != mf->st);
+ mhd_assert (mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL != mf->st);
+ mhd_assert ((mhd_POST_MPART_ST_VALUE != mf->st) || \
+ (0 == mf->f.value_len));
+
+ mhd_assert (*pdata_size == i);
+
+ if ((0 != mf->f.value_idx) &&
+ (((mhd_POST_MPART_ST_VALUE == mf->st) &&
+ (i != mf->f.value_idx) &&
+ is_value_streaming_needed (c, i - p_data->field_start)) ||
+ (((mhd_POST_MPART_ST_VALUE_CR_FOUND == mf->st) ||
+ (mhd_POST_MPART_ST_VALUE_LINE_START == mf->st) ||
+ (mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM == mf->st)) &&
+ (i != mf->delim_check_start) &&
+ is_value_streaming_needed (c, i - mf->delim_check_start))))
+ {
+ bool proc_res;
+
+ mhd_assert ((mhd_POST_MPART_ST_VALUE == mf->st) || \
+ (i >= mf->delim_check_start));
+ mhd_assert ((mhd_POST_MPART_ST_VALUE == mf->st) || \
+ (mhd_POST_INVALID_POS != mf->delim_check_start));
+ if (mhd_POST_MPART_ST_VALUE != mf->st)
+ {
+ i = mf->delim_check_start; /* Reset position */
+ mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
+ }
+
+ proc_res =
+ process_partial_value_all (c,
+ buf,
+ &i,
+ pdata_size,
+ mf->f.name_idx,
+ mf->f.name_len,
+ mf->f.filename_idx,
+ mf->f.filename_len,
+ mf->f.cntn_type_idx,
+ mf->f.cntn_type_len,
+ mf->f.enc_idx,
+ mf->f.enc_len,
+ mf->f.value_idx,
+ i - mf->f.value_idx);
+
+ p_data->next_parse_pos = i;
+
+ return proc_res;
+ }
+
+ p_data->next_parse_pos = i;
+ return false; /* Continue parsing */
+}
+
+
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
+parse_post_text (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ const int discp_lvl = c->daemon->req_cfg.strictnees;
+ /* Treat bare LF as the end of the line.
+ The same logic used here as for parsing HTTP headers.
+ Bare LF is processed as the end of the line or rejected as broken
+ request. */
+ const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct mhd_PostParserTextData *const tf = &(p_data->e_d.text); /**< the current "text" field */
+ size_t i;
+ bool enc_broken;
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_TEXT_PLAIN == c->rq.u_proc.post.enc);
+ mhd_assert (MHD_POST_PARSE_RES_OK == p_data->parse_result);
+ mhd_assert (! c->discard_request);
+ mhd_assert (p_data->next_parse_pos < *pdata_size);
+
+ enc_broken = false;
+ i = p_data->next_parse_pos;
+ while (*pdata_size > i)
+ {
+ switch (tf->st)
+ {
+ case mhd_POST_TEXT_ST_NOT_STARTED:
+ mhd_assert (0 == p_data->field_start);
+ mhd_assert (0 == p_data->value_off);
+ p_data->field_start = i;
+ tf->name_idx = i;
+ tf->st = mhd_POST_TEXT_ST_NAME;
+ /* Intentional fall-through */
+ case mhd_POST_TEXT_ST_NAME:
+ do /* Fast local loop */
+ {
+ if ('=' == buf[i])
+ {
+ tf->st = mhd_POST_TEXT_ST_AT_EQ;
+ break;
+ }
+ else if ('\r' == buf[i])
+ {
+ tf->st = mhd_POST_TEXT_ST_AT_CR;
+ break;
+ }
+ else if ('\n' == buf[i])
+ {
+ tf->st = mhd_POST_TEXT_ST_AT_LF_BARE;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_TEXT_ST_NAME != tf->st));
+ continue;
+ case mhd_POST_TEXT_ST_AT_EQ:
+ mhd_assert (i > tf->name_idx);
+ mhd_assert (0 == tf->name_len);
+ mhd_assert (0 == tf->value_len);
+ buf[i] = 0; /* Zero-terminate the name */
+ tf->name_len = i - tf->name_idx;
+ tf->st = mhd_POST_TEXT_ST_EQ_FOUND;
+ ++i; /* Process the next char */
+ continue;
+ case mhd_POST_TEXT_ST_EQ_FOUND:
+ mhd_assert (0 == p_data->value_off);
+ mhd_assert (0 == tf->value_idx);
+ mhd_assert (0 == tf->value_len);
+ mhd_assert (0 != i && "the 'value' should follow the 'name'");
+ tf->value_idx = i;
+ tf->st = mhd_POST_TEXT_ST_VALUE;
+ /* Intentional fall-through */
+ case mhd_POST_TEXT_ST_VALUE:
+ do /* Fast local loop */
+ {
+ if ('\r' == buf[i])
+ {
+ tf->st = mhd_POST_TEXT_ST_AT_CR;
+ break;
+ }
+ else if ('\n' == buf[i])
+ {
+ tf->st = mhd_POST_TEXT_ST_AT_LF_BARE;
+ break;
+ }
+ } while (*pdata_size > ++i);
+ mhd_assert ((*pdata_size == i) || \
+ (mhd_POST_TEXT_ST_AT_CR == tf->st) || \
+ (mhd_POST_TEXT_ST_AT_LF_BARE == tf->st));
+ continue;
+ case mhd_POST_TEXT_ST_AT_LF_BARE:
+ if (! bare_lf_as_crlf)
+ {
+ enc_broken = true;
+ break;
+ }
+ /* Intentional fall-through */
+ case mhd_POST_TEXT_ST_AT_CR:
+ mhd_assert (0 == tf->value_len);
+ buf[i] = 0; /* Zero-terminate the value (or the name) */
+ if (0 != tf->value_idx)
+ tf->value_len = i - tf->value_idx;
+ else
+ tf->name_len = i - tf->name_idx;
+ if ((0 == tf->name_len) && (0 == tf->value_len))
+ { /* Empty line */
+ ++i; /* Advance to the next char to be checked */
+ reset_parse_field_data_text (p_data);
+ tf->st = mhd_POST_TEXT_ST_NOT_STARTED;
+ }
+ else if (mhd_POST_TEXT_ST_AT_LF_BARE == tf->st)
+ tf->st = mhd_POST_TEXT_ST_FULL_LINE_FOUND;
+ else
+ {
+ tf->st = mhd_POST_TEXT_ST_CR_FOUND;
+ ++i; /* Process the next char */
+ }
+ continue;
+ case mhd_POST_TEXT_ST_CR_FOUND:
+ if ('\n' != buf[i])
+ {
+ enc_broken = true;
+ break;
+ }
+ tf->st = mhd_POST_TEXT_ST_FULL_LINE_FOUND;
+ /* Intentional fall-through */
+ case mhd_POST_TEXT_ST_FULL_LINE_FOUND:
+ ++i; /* Advance to the next char to be checked */
+ if (process_complete_field (c,
+ buf,
+ &i,
+ pdata_size,
+ p_data->field_start,
+ tf->name_idx,
+ tf->name_len,
+ tf->value_idx,
+ tf->value_len))
+ {
+ if (c->suspended)
+ --i; /* Go back to the same position */
+ else
+ reset_parse_field_data_text (p_data);
+ p_data->next_parse_pos = i;
+ return true;
+ }
+ mhd_assert (*pdata_size >= i);
+ reset_parse_field_data_text (p_data);
+ continue; /* Process the next char */
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ enc_broken = true;
+ break;
+ }
+ mhd_assert (enc_broken);
+ break;
+ }
+
+ mhd_assert ((*pdata_size == i) || enc_broken);
+
+ if (enc_broken)
+ {
+ if (p_data->some_data_provided)
+ {
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT, \
+ "The request POST has broken encoding or format and " \
+ "was parsed only partially.");
+ p_data->parse_result =
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
+ }
+ else
+ {
+ mhd_LOG_MSG (c->daemon, \
+ MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
+ "The request POST has broken encoding or format and " \
+ "cannot be parsed.");
+ p_data->parse_result =
+ MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
+ }
+ tf->st = mhd_POST_TEXT_ST_NOT_STARTED;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return true;
+ }
+
+ mhd_assert (mhd_POST_TEXT_ST_AT_EQ != tf->st);
+ mhd_assert (mhd_POST_TEXT_ST_AT_CR != tf->st);
+ mhd_assert (mhd_POST_TEXT_ST_AT_LF_BARE != tf->st);
+ mhd_assert (mhd_POST_TEXT_ST_FULL_LINE_FOUND != tf->st);
+
+ mhd_assert (*pdata_size == i);
+
+ if ((mhd_POST_TEXT_ST_VALUE == tf->st) &&
+ (i != tf->value_idx) &&
+ is_value_streaming_needed (c, i - p_data->field_start))
+ {
+ if (process_partial_value (c,
+ buf,
+ &i,
+ pdata_size,
+ tf->name_idx,
+ tf->name_len,
+ tf->value_idx,
+ i - tf->value_idx))
+ {
+ p_data->next_parse_pos = i;
+ return true;
+ }
+ }
+
+ p_data->next_parse_pos = i;
+ return false; /* Continue parsing */
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) bool
+mhd_stream_post_parse (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ size_t lbuf_left;
+
+ mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != p_data->enc);
+ mhd_assert (c->rq.cntn.lbuf.size <= p_data->lbuf_limit);
+
+ if ((MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc) &&
+ (mhd_POST_MPART_ST_EPILOGUE == p_data->e_d.m_form.st))
+ {
+ /* No need to process the data */
+ *pdata_size = 0; /* All data has been "processed" */
+ return false; /* Continue normal processing */
+ }
+
+ // TODO: support process in the connection buffer
+
+ mhd_assert (c->rq.cntn.lbuf.size >= p_data->lbuf_used);
+ lbuf_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
+
+ if (*pdata_size + 1 > lbuf_left)
+ (void) extend_lbuf_up_to (c,
+ *pdata_size + 1 - lbuf_left,
+ &(c->rq.cntn.lbuf));
+
+ while ((0 != *pdata_size) &&
+ (c->rq.cntn.lbuf.size > p_data->lbuf_used))
+ {
+ size_t data_size_before_parse;
+ size_t copy_size = *pdata_size;
+ lbuf_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
+ if (lbuf_left < copy_size)
+ copy_size = lbuf_left;
+
+ memcpy (c->rq.cntn.lbuf.data + p_data->lbuf_used,
+ buf,
+ copy_size);
+ p_data->lbuf_used += copy_size;
+ *pdata_size -= copy_size;
+
+ data_size_before_parse = p_data->lbuf_used;
+ switch (p_data->enc)
+ {
+ case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
+ if (parse_post_urlenc (c,
+ &(p_data->lbuf_used),
+ c->rq.cntn.lbuf.data))
+ return true;
+ break;
+ case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
+ if (parse_post_mpart (c,
+ &(p_data->lbuf_used),
+ c->rq.cntn.lbuf.data))
+ return true;
+ break;
+ case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
+ if (parse_post_text (c,
+ &(p_data->lbuf_used),
+ c->rq.cntn.lbuf.data))
+ return true;
+ break;
+ case MHD_HTTP_POST_ENCODING_OTHER:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ p_data->parse_result =
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return true;
+ }
+ if (data_size_before_parse == p_data->lbuf_used)
+ break; /* Nothing consumed, not possible to add new data to the buffer now */
+ }
+
+ if (0 != *pdata_size)
+ return report_low_lbuf_mem (c);
+
+ return false; /* Continue normal processing */
+}
+
+
+/**
+ * Check whether some unprocessed or partially processed data left in buffers
+ * for urlencoding POST encoding.
+ * @param c the stream to use
+ * @param pdata_size the pointer to the size of the data in the buffer
+ * @param buf the buffer with the data
+ * @return 'true' if stream state was changed,
+ * 'false' to continue normal processing
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
+check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct mhd_PostParserUrlEncData *const uf = &(p_data->e_d.u_enc); /**< the current "text" field */
+ size_t pos;
+ size_t name_start;
+ size_t name_len;
+ size_t value_start;
+ size_t value_len;
+ bool need_more_space;
+ bool have_extra_space;
+ bool need_value_stream;
+
+ pos = p_data->next_parse_pos; /* Points to the char AFTER the data */
+ mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
+ mhd_assert (*pdata_size >= pos);
+ have_extra_space = (pos < c->rq.cntn.lbuf.size);
+ need_more_space = false; /**< No space for zero termination */
+ need_value_stream = false; /**< Value cannot be zero-terminated and must be streamed */
+ switch (uf->st)
+ {
+ case mhd_POST_UENC_ST_NOT_STARTED:
+ mhd_assert (pos == *pdata_size);
+ return false; /* Continue processing */
+ case mhd_POST_UENC_ST_NAME:
+ mhd_assert (pos == *pdata_size);
+ /* Unfinished name */
+ name_start = uf->name_idx;
+ if (uf->last_pct_idx != mhd_POST_INVALID_POS)
+ name_len = mhd_str_pct_decode_lenient_n (buf + uf->name_idx,
+ pos - uf->name_idx,
+ buf + uf->name_idx,
+ pos - uf->name_idx,
+ NULL);
+ else
+ name_len = pos - uf->name_idx;
+ mhd_assert (name_start + name_len <= pos);
+ if (! have_extra_space &&
+ (name_start + name_len == pos))
+ need_more_space = true;
+ else
+ {
+ buf[name_start + name_len] = 0; /* Zero-terminate the result, an extra byte is available */
+ value_start = 0;
+ value_len = 0;
+ }
+ break;
+ case mhd_POST_UENC_ST_EQ_FOUND:
+ mhd_assert (pos == *pdata_size);
+ name_start = uf->name_idx;
+ name_len = uf->name_len;
+ value_start = pos;
+ value_len = 0;
+ if (! have_extra_space)
+ need_value_stream = true;
+ else
+ buf[value_start] = 0; /* Zero-terminate the result, an extra byte is available */
+ break;
+ case mhd_POST_UENC_ST_VALUE:
+ mhd_assert (0 != uf->value_idx);
+ name_start = uf->name_idx;
+ name_len = uf->name_len;
+ mhd_assert (0 == buf[name_start + name_len]);
+ if (0 != uf->value_len)
+ {
+ /* The value was partially decoded and then application requested stream
+ * suspending. */
+ mhd_assert (pos < *pdata_size);
+ mhd_assert (2 >= *pdata_size - pos);
+ value_start = uf->value_idx;
+ if (uf->value_idx + uf->value_len != pos)
+ memmove (buf + uf->value_idx + uf->value_len,
+ buf + pos,
+ *pdata_size - pos);
+ value_len = uf->value_len + *pdata_size - pos;
+ }
+ else
+ {
+ /* The value has not been decoded yet */
+ mhd_assert (pos == *pdata_size);
+ value_start = uf->value_idx;
+ if (uf->last_pct_idx != mhd_POST_INVALID_POS)
+ value_len = mhd_str_pct_decode_lenient_n (buf + uf->value_idx,
+ pos - uf->value_idx,
+ buf + uf->value_idx,
+ pos - uf->value_idx,
+ NULL);
+ else
+ value_len = pos - uf->value_idx;
+ }
+ if (! have_extra_space &&
+ (value_start + value_len == pos))
+ need_value_stream = true;
+ else
+ buf[value_start + value_len] = 0; /* Zero-terminate the result, an extra byte is available in the buffer */
+ break;
+ case mhd_POST_UENC_ST_FULL_FIELD_FOUND:
+ /* Full value was found, but the stream has been suspended by
+ * the application */
+ mhd_assert (pos + 1 == *pdata_size);
+ mhd_assert (0 != uf->value_idx);
+ mhd_assert (pos != uf->value_idx);
+ name_start = uf->name_idx;
+ name_len = uf->name_len;
+ value_start = uf->value_idx;
+ value_len = uf->value_len;
+ mhd_assert (0 == buf[name_start + name_len]);
+ mhd_assert (0 == buf[value_start + value_len]);
+ ++pos;
+ mhd_assert (pos == *pdata_size);
+ break;
+ case mhd_POST_UENC_ST_AT_EQ:
+ case mhd_POST_UENC_ST_AT_AMPRSND:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
+ return false;
+ }
+
+ /* Have field data */
+
+ p_data->next_parse_pos = pos;
+
+ if (need_value_stream)
+ {
+ if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
+ p_data->force_streamed = true;
+ else
+ need_more_space = true;
+ }
+
+ if (need_more_space)
+ return report_low_lbuf_mem (c);
+
+ if (process_complete_field (c,
+ buf,
+ &(p_data->next_parse_pos),
+ pdata_size,
+ p_data->field_start,
+ name_start,
+ name_len,
+ value_start,
+ value_len))
+ return true;
+
+ reset_parse_field_data_urlenc (p_data);
+
+ return false; /* Continue normal processing */
+}
+
+
+/**
+ * Check whether some unprocessed or partially processed data left in buffers
+ * for "multipart/form-data" POST encoding.
+ * @param c the stream to use
+ * @param pdata_size the pointer to the size of the data in the buffer
+ * @param buf the buffer with the data
+ * @return 'true' if stream state was changed,
+ * 'false' to continue normal processing
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
+check_post_leftovers_mpart (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct mhd_PostParserMPartFormData *const mf = &(p_data->e_d.m_form); /**< the current "form-data" parsing details */
+ size_t pos;
+ bool not_terminated;
+ bool add_field;
+ size_t value_pos;
+ size_t value_len;
+
+ pos = p_data->next_parse_pos; /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
+ mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
+ mhd_assert (*pdata_size >= pos);
+
+ not_terminated = false;
+ add_field = false;
+ value_pos = 0;
+ value_len = 0;
+
+ switch (mf->st)
+ {
+ case mhd_POST_MPART_ST_NOT_STARTED:
+ case mhd_POST_MPART_ST_PREAMBL:
+ case mhd_POST_MPART_ST_PREAMBL_CR_FOUND:
+ case mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM:
+ mhd_assert (pos == *pdata_size);
+ return false; /* Continue processing */
+ case mhd_POST_MPART_ST_FIRST_DELIM_FOUND:
+ case mhd_POST_MPART_ST_FIRST_PART_START:
+ case mhd_POST_MPART_ST_PART_START:
+ mhd_assert (pos == *pdata_size);
+ not_terminated = true;
+ break;
+ case mhd_POST_MPART_ST_HEADER_LINE_START:
+ case mhd_POST_MPART_ST_HEADER_LINE:
+ case mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND:
+ case mhd_POST_MPART_ST_VALUE_START:
+ mhd_assert (pos == *pdata_size);
+ not_terminated = true;
+ add_field = (0 != mf->f.name_idx);
+ break;
+ case mhd_POST_MPART_ST_VALUE:
+ case mhd_POST_MPART_ST_VALUE_CR_FOUND:
+ case mhd_POST_MPART_ST_VALUE_LINE_START:
+ case mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM:
+ mhd_assert (0 != mf->f.name_idx);
+ mhd_assert (0 != mf->f.value_idx);
+ not_terminated = true;
+ add_field = true;
+ value_pos = mf->f.value_idx;
+ value_len = pos - value_len;
+ break;
+ case mhd_POST_MPART_ST_DELIM_FOUND:
+ mhd_assert (0 != mf->f.name_idx);
+ mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
+ mhd_assert (pos > mf->delim_check_start);
+ not_terminated = true;
+ add_field = true;
+ if (0 != mf->f.value_idx)
+ {
+ value_pos = mf->f.value_idx;
+ value_len = mf->delim_check_start - mf->f.value_idx;
+ }
+ break;
+ case mhd_POST_MPART_ST_FULL_FIELD_FOUND:
+ not_terminated = true;
+ /* Intentional fall-through */
+ case mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL:
+ mhd_assert (0 != mf->f.name_idx);
+ add_field = true;
+ if (0 != mf->f.value_idx)
+ {
+ value_pos = mf->f.value_idx;
+ value_len = mf->f.value_len;
+ }
+ break;
+ case mhd_POST_MPART_ST_EPILOGUE:
+ case mhd_POST_MPART_ST_FORMAT_ERROR:
+ return false; /* Continue processing */
+ case mhd_POST_MPART_ST_BACK_TO_PREAMBL:
+ case mhd_POST_MPART_ST_PREAMBL_LINE_START:
+ case mhd_POST_MPART_ST_HEADER_LINE_END:
+ case mhd_POST_MPART_ST_BACK_TO_VALUE:
+ case mhd_POST_MPART_ST_VALUE_END_FOUND:
+ case mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
+ return false;
+ }
+
+ if (not_terminated)
+ report_invalid_termination (c); /* The "closing" delimiter is missing */
+
+ if (add_field)
+ {
+ if (c->rq.cntn.lbuf.size > (value_pos + value_len))
+ buf [value_pos + value_len] = 0; /* Zero-terminate the value */
+ else if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
+ p_data->force_streamed = true;
+ else
+ return report_low_lbuf_mem (c);
+
+ p_data->next_parse_pos = pos;
+
+ if (process_complete_field_all (c,
+ buf,
+ &p_data->next_parse_pos,
+ pdata_size,
+ p_data->field_start,
+ mf->f.name_idx,
+ mf->f.name_len,
+ mf->f.filename_idx,
+ mf->f.filename_len,
+ mf->f.cntn_type_idx,
+ mf->f.cntn_type_len,
+ mf->f.enc_idx,
+ mf->f.enc_len,
+ value_pos,
+ value_len))
+ return true;
+ }
+
+ reset_parse_field_data_mpart_cont (p_data,
+ ! not_terminated);
+
+ return false; /* Continue normal processing */
+}
+
+
+/**
+ * Check whether some unprocessed or partially processed data left in buffers
+ * for "text" POST encoding.
+ * @param c the stream to use
+ * @param pdata_size the pointer to the size of the data in the buffer
+ * @param buf the buffer with the data
+ * @return 'true' if stream state was changed,
+ * 'false' to continue normal processing
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
+check_post_leftovers_text (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ struct mhd_PostParserTextData *const tf = &(p_data->e_d.text); /**< the current "text" field */
+ size_t pos;
+ size_t name_start;
+ size_t name_len;
+ size_t value_start;
+ size_t value_len;
+
+ pos = p_data->next_parse_pos; /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
+ mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
+ switch (tf->st)
+ {
+ case mhd_POST_TEXT_ST_NOT_STARTED:
+ mhd_assert (pos == *pdata_size);
+ return false; /* Continue processing */
+ case mhd_POST_TEXT_ST_NAME:
+ /* Unfinished name */
+ mhd_assert (pos == *pdata_size);
+ name_start = tf->name_idx;
+ name_len = pos - name_start;
+ if (pos == c->rq.cntn.lbuf.size)
+ return report_low_lbuf_mem (c);
+ buf[pos] = 0; /* Zero-terminate the result, an extra byte is available */
+ value_start = 0;
+ value_len = 0;
+ break;
+ case mhd_POST_TEXT_ST_EQ_FOUND:
+ mhd_assert (pos == *pdata_size);
+ name_start = tf->name_idx;
+ name_len = tf->name_len;
+ value_start = pos;
+ value_len = 0;
+ break;
+ case mhd_POST_TEXT_ST_VALUE:
+ mhd_assert (pos == *pdata_size);
+ mhd_assert (0 != tf->value_idx);
+ mhd_assert (pos != tf->value_idx);
+ name_start = tf->name_idx;
+ name_len = tf->name_len;
+ value_start = tf->value_idx;
+ value_len = pos - value_start;
+ break;
+ case mhd_POST_TEXT_ST_CR_FOUND:
+ mhd_assert (pos == *pdata_size);
+ mhd_assert (0 != tf->value_idx);
+ mhd_assert (pos != tf->value_idx);
+ name_start = tf->name_idx;
+ name_len = tf->name_len;
+ value_start = tf->value_idx;
+ value_len = tf->value_len;
+ mhd_assert (value_start + value_len + 1 == pos);
+ mhd_assert (0 == buf[value_start + value_len]);
+ break;
+ case mhd_POST_TEXT_ST_FULL_LINE_FOUND:
+ /* Full value was found, but the stream has been suspended by
+ * the application */
+ mhd_assert (pos + 1 == *pdata_size);
+ mhd_assert (0 != tf->value_idx);
+ name_start = tf->name_idx;
+ name_len = tf->name_len;
+ value_start = tf->value_idx;
+ value_len = tf->value_len;
+ mhd_assert ((value_start + value_len + 1 == pos) || \
+ (value_start + value_len + 2 == pos));
+ mhd_assert (0 == buf[value_start + value_len]);
+ ++pos;
+ mhd_assert (pos == *pdata_size);
+ break;
+ case mhd_POST_TEXT_ST_AT_EQ:
+ case mhd_POST_TEXT_ST_AT_LF_BARE:
+ case mhd_POST_TEXT_ST_AT_CR:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
+ return false;
+ }
+
+ if (tf->st != mhd_POST_TEXT_ST_FULL_LINE_FOUND)
+ report_invalid_termination (c); /* The line must be terminated by CRLF, but it is not */
+
+ if (0 != value_start)
+ {
+ if (c->rq.cntn.lbuf.size > (value_start + value_len))
+ buf [value_start + value_len] = 0; /* Zero-terminate the value */
+ else if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
+ p_data->force_streamed = true;
+ else
+ return report_low_lbuf_mem (c);
+ }
+
+ if (process_complete_field (c,
+ buf,
+ &pos,
+ pdata_size,
+ p_data->field_start,
+ name_start,
+ name_len,
+ value_start,
+ value_len))
+ return true;
+
+ reset_parse_field_data_text (p_data);
+
+ return false; /* Continue normal processing */
+}
+
+
+/**
+ * Check in leftover POST data in the buffers
+ * @param c the stream to use
+ * @return 'true' if stream state is changed,
+ * 'false' to continue
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+check_post_leftovers (struct MHD_Connection *restrict c)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ // TODO: implement processing in the connection buffer
+ if (p_data->lbuf_used == c->rq.cntn.lbuf.size)
+ (void) extend_lbuf_up_to (c,
+ 1,
+ &(c->rq.cntn.lbuf));
+
+ switch (p_data->enc)
+ {
+ case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
+ return check_post_leftovers_urlenc (c,
+ &(p_data->lbuf_used),
+ c->rq.cntn.lbuf.data);
+ case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
+ return check_post_leftovers_mpart (c,
+ &(p_data->lbuf_used),
+ c->rq.cntn.lbuf.data);
+ case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
+ return check_post_leftovers_text (c,
+ &(p_data->lbuf_used),
+ c->rq.cntn.lbuf.data);
+ case MHD_HTTP_POST_ENCODING_OTHER:
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ p_data->parse_result =
+ MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ break;
+ }
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_process_post_finish (struct MHD_Connection *restrict c)
+{
+ struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+ const struct MHD_UploadAction *act;
+ bool state_changed;
+
+ if ((MHD_POST_PARSE_RES_OK == p_data->parse_result) &&
+ ! c->discard_request)
+ {
+ // TODO: implement processing in the connection buffer
+ if (check_post_leftovers (c))
+ return true;
+ }
+
+ act = c->rq.app_act.head_act.data.post_parse.done_cb (
+ &(c->rq),
+ c->rq.app_act.head_act.data.post_parse.done_cb_cls,
+ p_data->parse_result);
+
+ state_changed = mhd_stream_process_upload_action (c, act, true);
+ if (! c->suspended)
+ mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
+ return state_changed;
+}
diff --git a/src/mhd2/post_parser_funcs.h b/src/mhd2/post_parser_funcs.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/post_parser_funcs.h
+ * @brief The declarations of internal POST parser functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_POST_PARSER_FUNCS_H
+#define MHD_POST_PARSER_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Make preparation necessary for parsing POST data.
+ * @param c the stream to use
+ * @return 'true' if succeed,
+ * 'false' if failed and error result is set in the stream
+ */
+MHD_INTERNAL bool
+mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ (1);
+
+/**
+ * Parse POST data.
+ * @param c the stream to use
+ * @return // FIXME
+ */
+MHD_INTERNAL bool
+mhd_stream_post_parse (struct MHD_Connection *restrict c,
+ size_t *restrict pdata_size,
+ char *restrict buf)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (3);
+
+// TODO: describe
+MHD_INTERNAL bool
+mhd_stream_process_post_finish (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* ! MHD_POST_PARSER_FUNCS_H */
diff --git a/src/mhd2/request_get_value.c b/src/mhd2/request_get_value.c
@@ -1,5 +1,6 @@
/*
This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Christian Grothoff
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
@@ -48,18 +49,48 @@ mhd_request_get_value_n (struct MHD_Request *restrict request,
size_t key_len,
const char *restrict key)
{
- struct mhd_RequestField *f;
mhd_assert (strlen (key) == key_len);
- for (f = mhd_DLINKEDL_GET_FIRST (request, fields); NULL != f;
- f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ if (MHD_VK_POSTDATA != kind)
{
- if ((key_len == f->field.nv.name.len) &&
- (kind == f->field.kind) &&
- (0 == memcmp (key, f->field.nv.name.cstr, key_len)))
- return &(f->field.nv.value);
+ struct mhd_RequestField *f;
+
+ for (f = mhd_DLINKEDL_GET_FIRST (request, fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ {
+ if ((key_len == f->field.nv.name.len) &&
+ (0 != (kind & f->field.kind)) &&
+ (0 == memcmp (key, f->field.nv.name.cstr, key_len)))
+ return &(f->field.nv.value);
+ }
}
+
+#if HAVE_POST_PARSER
+ if (0 != (MHD_VK_POSTDATA & kind))
+ {
+ struct mhd_RequestPostField *f;
+ char *const buf = request->cntn.lbuf.data; // TODO: support processing in connection buffer
+ for (f = mhd_DLINKEDL_GET_FIRST (request, post_fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, post_fields))
+ {
+ if ((key_len == f->field.name.len) &&
+ (0 == memcmp (key, buf + f->field.name.pos, key_len)))
+ {
+ f->field_for_app.value.cstr =
+ (0 == f->field.value.pos) ?
+ NULL : (buf + f->field.value.pos);
+ f->field_for_app.value.len = f->field.value.len;
+
+ mhd_assert ((NULL != f->field_for_app.value.cstr) || \
+ (0 == f->field_for_app.value.len));
+
+ return &(f->field_for_app.value);
+ }
+ }
+ }
+#endif /* HAVE_POST_PARSER */
+
return NULL;
}
@@ -107,3 +138,140 @@ mhd_stream_has_header_token (const struct MHD_Connection *restrict c,
return false;
}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) size_t
+MHD_request_get_values_cb (struct MHD_Request *request,
+ enum MHD_ValueKind kind,
+ MHD_NameValueIterator iterator,
+ void *iterator_cls)
+{
+ size_t count;
+
+ count = 0;
+ if (MHD_VK_POSTDATA != kind)
+ {
+ struct mhd_RequestField *f;
+
+ for (f = mhd_DLINKEDL_GET_FIRST (request, fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ {
+ ++count;
+ if (NULL != iterator)
+ {
+ if (MHD_NO ==
+ iterator (iterator_cls,
+ f->field.kind,
+ &(f->field.nv)))
+ return count;
+ }
+ }
+ }
+
+#if HAVE_POST_PARSER
+ if (0 != (MHD_VK_POSTDATA & kind))
+ {
+ struct mhd_RequestPostField *f;
+ char *const buf = request->cntn.lbuf.data; // TODO: support processing in connection buffer
+ for (f = mhd_DLINKEDL_GET_FIRST (request, post_fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, post_fields))
+ {
+ ++count;
+ if (NULL != iterator)
+ {
+ if (f->field_for_app.name.cstr != buf + f->field.name.pos)
+ {
+ f->field_for_app.name.cstr = buf + f->field.name.pos;
+ f->field_for_app.name.len = f->field.name.len;
+ f->field_for_app.value.cstr =
+ (0 == f->field.value.pos) ?
+ NULL : (buf + f->field.value.pos);
+ f->field_for_app.value.len = f->field.value.len;
+ }
+
+ if (MHD_NO ==
+ iterator (iterator_cls,
+ MHD_VK_POSTDATA,
+ &(f->field_for_app)))
+ return count;
+ }
+ }
+ }
+#endif /* HAVE_POST_PARSER */
+
+ return count;
+}
+
+
+#if HAVE_POST_PARSER
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1) size_t
+MHD_request_get_post_data_cb (struct MHD_Request *request,
+ MHD_PostDataIterator iterator,
+ void *iterator_cls)
+{
+ struct mhd_RequestPostField *f;
+ char *const buf = request->cntn.lbuf.data; // TODO: support processing in connection buffer
+ size_t count;
+
+ count = 0;
+ for (f = mhd_DLINKEDL_GET_FIRST (request, post_fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, post_fields))
+ {
+ ++count;
+ if (NULL != iterator)
+ {
+ struct MHD_PostField field;
+
+ if (f->field_for_app.name.cstr != buf + f->field.name.pos)
+ {
+ f->field_for_app.name.cstr = buf + f->field.name.pos;
+ f->field_for_app.name.len = f->field.name.len;
+ if (0 == f->field.value.pos)
+ f->field_for_app.value.cstr = NULL;
+ else
+ f->field_for_app.value.cstr = buf + f->field.value.pos;
+ f->field_for_app.value.len = f->field.value.len;
+ }
+
+ field.name = f->field_for_app.name;
+ field.value = f->field_for_app.value;
+
+ if (0 == f->field.filename.pos)
+ field.filename.cstr = NULL;
+ else
+ field.filename.cstr = buf + f->field.filename.pos;
+ field.filename.len = f->field.filename.len;
+
+ if (0 == f->field.content_type.pos)
+ field.content_type.cstr = NULL;
+ else
+ field.content_type.cstr = buf + f->field.content_type.pos;
+ field.content_type.len = f->field.content_type.len;
+
+ if (0 == f->field.transfer_encoding.pos)
+ field.transfer_encoding.cstr = NULL;
+ else
+ field.transfer_encoding.cstr = buf + f->field.transfer_encoding.pos;
+ field.transfer_encoding.len = f->field.transfer_encoding.len;
+
+ mhd_assert ((NULL != field.value.cstr) || (0 == field.value.len));
+ mhd_assert ((NULL != field.filename.cstr) || (0 == field.filename.len));
+ mhd_assert ((NULL != field.content_type.cstr) || \
+ (0 == field.content_type.len));
+ mhd_assert ((NULL != field.transfer_encoding.cstr) || \
+ (0 == field.transfer_encoding.len));
+
+ if (MHD_NO ==
+ iterator (iterator_cls,
+ &field))
+ return count;
+ }
+ }
+ return count;
+}
+
+
+#endif /* HAVE_POST_PARSER */
diff --git a/src/mhd2/request_get_value.h b/src/mhd2/request_get_value.h
@@ -36,7 +36,7 @@
/**
* Get specified field value from request
- * If multiple values match the kind, return any one of them.
+ * If multiple values match the kind, return first found.
*
* The returned pointer is valid until the response is queued.
* If the data is needed beyond this point, it should be copied.
diff --git a/src/mhd2/respond_with_error.c b/src/mhd2/respond_with_error.c
@@ -62,9 +62,9 @@ respond_with_error_len (struct MHD_Connection *c,
/* Discard most of the request data */
- if (NULL != c->rq.cntn.lbuf.buf)
+ if (NULL != c->rq.cntn.lbuf.data)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
- c->rq.cntn.lbuf.buf = NULL;
+ c->rq.cntn.lbuf.data = NULL;
c->write_buffer = NULL;
c->write_buffer_size = 0;
diff --git a/src/mhd2/response_destroy.c b/src/mhd2/response_destroy.c
@@ -29,6 +29,7 @@
#include "mhd_response.h"
#include "mhd_assert.h"
+#include "mhd_panic.h"
#include "mhd_atomic_counter.h"
#include "sys_malloc.h"
@@ -39,6 +40,9 @@
#include "response_funcs.h"
#include "response_from.h"
+#define mhd_RESPONSE_DESTOYED "Attempt to use destroyed response, " \
+ "re-use non-reusable response or wrong MHD_Response pointer"
+
/**
* Perform full response de-initialisation, with cleaning-up / freeing
* all content data and headers.
@@ -46,7 +50,7 @@
* @param r the response to free
*/
static MHD_FN_PAR_NONNULL_ (1) void
-response_full_detinit (struct MHD_Response *restrict r)
+response_full_deinit (struct MHD_Response *restrict r)
{
mhd_response_remove_all_headers (r);
if (NULL != r->special_resp.spec_hdr)
@@ -54,14 +58,18 @@ response_full_detinit (struct MHD_Response *restrict r)
if (r->reuse.reusable)
mhd_response_deinit_reusable (r);
mhd_response_deinit_content_data (r);
+
+ r->was_destroyed = true;
free (r);
}
-MHD_INTERNAL void
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_response_dec_use_count (struct MHD_Response *restrict r)
{
mhd_assert (r->frozen);
+ if (r->was_destroyed)
+ MHD_PANIC (mhd_RESPONSE_DESTOYED);
if (r->reuse.reusable)
{
@@ -69,7 +77,21 @@ mhd_response_dec_use_count (struct MHD_Response *restrict r)
return; /* The response is still used somewhere */
}
- response_full_detinit (r);
+ response_full_deinit (r);
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_response_inc_use_count (struct MHD_Response *restrict r)
+{
+ mhd_assert (r->frozen);
+ if (r->was_destroyed)
+ MHD_PANIC (mhd_RESPONSE_DESTOYED);
+
+ if (! r->reuse.reusable)
+ return;
+
+ (void) mhd_atomic_counter_inc_get (&(r->reuse.counter));
}
@@ -77,6 +99,9 @@ MHD_EXTERN_
MHD_FN_PAR_NONNULL_ (1) void
MHD_response_destroy (struct MHD_Response *response)
{
+ if (response->was_destroyed)
+ MHD_PANIC (mhd_RESPONSE_DESTOYED);
+
if (! response->frozen)
{
/* This response has been never used for actions */
@@ -86,7 +111,7 @@ MHD_response_destroy (struct MHD_Response *response)
/* Decrement counter to avoid triggering assert in deinit function */
mhd_assert (0 == mhd_atomic_counter_dec_get (&(response->reuse.counter)));
#endif
- response_full_detinit (response);
+ response_full_deinit (response);
return;
}
diff --git a/src/mhd2/response_destroy.h b/src/mhd2/response_destroy.h
@@ -34,10 +34,20 @@ struct MHD_Response; /* forward declaration */
/**
* Free/destroy non-reusable response, decrement use count for reusable
* response and free/destroy if it is not used any more.
- * @param response the response to manipulate
+ * @param r the response to manipulate
*/
MHD_INTERNAL void
-mhd_response_dec_use_count (struct MHD_Response *restrict response);
+mhd_response_dec_use_count (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Mark reusable response as being used in (one more) request.
+ * No-op for non-reusable responses
+ * @param r the response to manipulate
+ */
+MHD_INTERNAL void
+mhd_response_inc_use_count (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_RESPONSE_DESTROY_H */
diff --git a/src/mhd2/response_from.c b/src/mhd2/response_from.c
@@ -132,7 +132,7 @@ 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)],
+ const char *buffer,
MHD_FreeCallback free_cb,
void *free_cb_cls)
{
@@ -168,13 +168,13 @@ MHD_response_from_buffer_copy (
{
struct MHD_Response *restrict res;
const unsigned char *buf_copy;
+ unsigned char *new_buf;
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;
@@ -185,6 +185,7 @@ MHD_response_from_buffer_copy (
}
else
{
+ new_buf = NULL;
buf_copy = empty_buf;
res = response_create_basic (sc, 0, NULL, NULL);
}
@@ -193,8 +194,13 @@ MHD_response_from_buffer_copy (
{
res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
res->cntn.buf = buf_copy;
+ return res; /* Success exit point */
}
- return res;
+
+ /* Cleanup path */
+ if (NULL != new_buf)
+ free (new_buf);
+ return NULL;
}
diff --git a/src/mhd2/response_from.h b/src/mhd2/response_from.h
@@ -41,6 +41,16 @@ MHD_INTERNAL void
mhd_response_deinit_content_data (struct MHD_Response *restrict r)
MHD_FN_PAR_NONNULL_ (1);
+/**
+ * Create special internal-only response for sending automatic error messages
+ * @param sc the HTTP status code (enum MHD_HTTP_StatusCode)
+ * @param cntn_len the length of the @a cntn
+ * @param cntn the response content
+ * @param spec_hdr_len the length of the @a spec_hdr
+ * @param spec_hdr the special string to be used as a header string
+ * @return the response object if succeed,
+ * NULL if failed (out of memory)
+ */
MHD_INTERNAL struct MHD_Response *
mhd_response_special_for_error (unsigned int sc,
size_t cntn_len,
diff --git a/src/mhd2/response_funcs.c b/src/mhd2/response_funcs.c
@@ -39,7 +39,7 @@
MHD_INTERNAL
-MHD_FN_PAR_NONNULL_ (1) bool
+MHD_FN_PAR_NONNULL_ALL_ bool
response_make_reusable (struct MHD_Response *restrict r)
{
mhd_assert (! r->reuse.reusable);
@@ -60,7 +60,7 @@ response_make_reusable (struct MHD_Response *restrict r)
MHD_INTERNAL
-MHD_FN_PAR_NONNULL_ (1) void
+MHD_FN_PAR_NONNULL_ALL_ void
mhd_response_deinit_reusable (struct MHD_Response *restrict r)
{
mhd_assert (r->reuse.reusable);
@@ -71,7 +71,7 @@ mhd_response_deinit_reusable (struct MHD_Response *restrict r)
}
-static void
+static MHD_FN_PAR_NONNULL_ALL_ void
response_set_properties (struct MHD_Response *restrict r)
{
struct ResponseOptions *restrict const s = r->settings;
@@ -108,12 +108,7 @@ response_set_properties (struct MHD_Response *restrict r)
}
-/**
- * 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_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_response_check_frozen_freeze (struct MHD_Response *restrict response)
{
bool need_unlock;
diff --git a/src/mhd2/response_funcs.h b/src/mhd2/response_funcs.h
@@ -40,7 +40,7 @@ struct MHD_Response; /* forward declaration */
*/
MHD_INTERNAL bool
response_make_reusable (struct MHD_Response *restrict r)
-MHD_FN_PAR_NONNULL_ (1);
+MHD_FN_PAR_NONNULL_ALL_;
/**
@@ -49,7 +49,7 @@ MHD_FN_PAR_NONNULL_ (1);
*/
MHD_INTERNAL void
mhd_response_deinit_reusable (struct MHD_Response *restrict r)
-MHD_FN_PAR_NONNULL_ (1);
+MHD_FN_PAR_NONNULL_ALL_;
/**
@@ -58,7 +58,8 @@ MHD_FN_PAR_NONNULL_ (1);
* @param response the response to manipulate
*/
MHD_INTERNAL void
-mhd_response_check_frozen_freeze (struct MHD_Response *restrict r);
+mhd_response_check_frozen_freeze (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_RESPONSE_FUNCS_H */
diff --git a/src/mhd2/response_options.h b/src/mhd2/response_options.h
@@ -61,7 +61,7 @@ struct ResponseOptions
* the function to call,
* NULL to not use the callback
*/
- struct MHD_ResponeOptionValueTermCB termination_callback;
+ struct MHD_ResponeOptionValueEndedCB termination_callback;
};
diff --git a/src/mhd2/response_set_options.c b/src/mhd2/response_set_options.c
@@ -3,21 +3,21 @@
/* *INDENT-OFF* */
/**
* @file response_set_options.c
- * @author response-options-generator.c
+ * @author options-generator.c
*/
#include "mhd_sys_options.h"
-#include "sys_bool_type.h"
+#include "response_set_options.h"
#include "sys_base_types.h"
-#include "sys_malloc.h"
-#include <string.h>
-#include "mhd_response.h"
+#include "sys_bool_type.h"
#include "response_options.h"
+#include "mhd_response.h"
#include "mhd_public_api.h"
#include "mhd_locks.h"
#include "mhd_assert.h"
#include "response_funcs.h"
+
MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_
enum MHD_StatusCode
MHD_response_set_options (
@@ -38,7 +38,7 @@ MHD_response_set_options (
if (! mhd_mutex_lock(&response->reuse.settings_lock))
return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
mhd_assert (1 == mhd_atomic_counter_get(&response->reuse.counter));
- if (! response->frozen) /* Firm re-check under the lock */
+ if (response->frozen) /* Firm re-check under the lock */
{
mhd_mutex_unlock_chk(&response->reuse.settings_lock);
return MHD_SC_TOO_LATE;
@@ -55,7 +55,23 @@ MHD_response_set_options (
i = options_max_num - 1;
break;
case MHD_R_O_REUSABLE:
- settings->reusable = option->val.reusable;
+ /* custom setter */
+ if (response->reuse.reusable)
+ {
+ if (MHD_NO == option->val.reusable)
+ {
+ res = MHD_SC_RESPONSE_CANNOT_CLEAR_REUSE;
+ i = options_max_num - 1;
+ break;
+ }
+ }
+ else if ((MHD_NO != option->val.reusable) &&
+ (! response_make_reusable(response)))
+ {
+ res = MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
+ i = options_max_num - 1;
+ break;
+ }
continue;
case MHD_R_O_HEAD_ONLY_RESPONSE:
settings->head_only_response = option->val.head_only_response;
@@ -76,8 +92,8 @@ MHD_response_set_options (
settings->insanity_header_content_length = option->val.insanity_header_content_length;
continue;
case MHD_R_O_TERMINATION_CALLBACK:
- settings->termination_callback.v_term_cb = option->val.termination_callback.v_term_cb;
- settings->termination_callback.v_term_cb_cls = option->val.termination_callback.v_term_cb_cls;
+ settings->termination_callback.v_ended_cb = option->val.termination_callback.v_ended_cb;
+ settings->termination_callback.v_ended_cb_cls = option->val.termination_callback.v_ended_cb_cls;
continue;
case MHD_R_O_SENTINEL:
default: /* for -Wswitch-default -Wswitch-enum */
diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c
@@ -496,13 +496,15 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
{
mhd_assert (! c->stop_with_error || (NULL == c->rp.response) || \
(c->rp.response->cfg.int_err_resp));
- /* Next function will destroy response, notify client,
- * destroy memory pool and set connection state to "CLOSED" */
- mhd_conn_pre_close (c,
- c->stop_with_error ?
- mhd_CONN_CLOSE_ERR_REPLY_SENT :
- mhd_CONN_CLOSE_HTTP_COMPLETED,
- NULL);
+
+ /* Next function will notify client and set connection
+ * state to "PRE-CLOSING" */
+ /* Later response and memory pool will be destroyed */
+ mhd_conn_start_closing (c,
+ c->stop_with_error ?
+ mhd_CONN_CLOSE_ERR_REPLY_SENT :
+ mhd_CONN_CLOSE_HTTP_COMPLETED,
+ NULL);
}
else
{
@@ -510,7 +512,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
size_t new_read_buf_size;
mhd_assert (! c->stop_with_error);
mhd_assert (! c->discard_request);
- mhd_assert (NULL == c->rq.cntn.lbuf.buf);
+ mhd_assert (NULL == c->rq.cntn.lbuf.data);
#if 0 // TODO: notification callback
if ( (NULL != d->notify_completed) &&
@@ -518,7 +520,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
d->notify_completed (d->notify_completed_cls,
c,
&c->rq.app_context,
- MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ MHD_REQUEST_ENDED_COMPLETED_OK);
c->rq.app_aware = false;
#endif
@@ -537,7 +539,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
c->state = MHD_CONNECTION_INIT;
c->event_loop_info =
(0 == c->read_buffer_offset) ?
- MHD_EVENT_LOOP_INFO_READ : MHD_EVENT_LOOP_INFO_PROCESS;
+ MHD_EVENT_LOOP_INFO_RECV : MHD_EVENT_LOOP_INFO_PROCESS;
memset (&c->rq, 0, sizeof(c->rq));
@@ -570,7 +572,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
-mhd_stream_check_timedout (struct MHD_Connection *restrict c)
+mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c)
{
const uint_fast64_t timeout = c->connection_timeout_ms;
uint_fast64_t now;
@@ -647,13 +649,12 @@ mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3) void
-mhd_conn_pre_close (struct MHD_Connection *restrict c,
- enum mhd_ConnCloseReason reason,
- const char *log_msg)
+mhd_conn_start_closing (struct MHD_Connection *restrict c,
+ enum mhd_ConnCloseReason reason,
+ const char *log_msg)
{
bool close_hard;
- bool use_local_lingering;
- enum MHD_RequestTerminationCode term_code;
+ enum MHD_RequestEndedCode end_code;
enum MHD_StatusCode sc;
sc = MHD_SC_INTERNAL_ERROR;
@@ -661,61 +662,61 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
{
case mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ end_code = MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
sc = MHD_SC_REQ_MALFORMED;
break;
case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
+ end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
break;
case mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ end_code = MHD_REQUEST_ENDED_CLIENT_ABORT;
sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE;
break;
case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY:
close_hard = true;
- term_code = (! c->stop_with_error || c->rq.too_large) ?
- MHD_REQUEST_TERMINATED_NO_RESOURCES :
- MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ end_code = (! c->stop_with_error || c->rq.too_large) ?
+ MHD_REQUEST_ENDED_NO_RESOURCES :
+ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE;
break;
case mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE:
close_hard = true;
- term_code = c->rq.too_large ?
- MHD_REQUEST_TERMINATED_NO_RESOURCES :
- MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ end_code = c->rq.too_large ?
+ MHD_REQUEST_ENDED_NO_RESOURCES :
+ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
sc = MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE;
break;
case mhd_CONN_CLOSE_APP_ERROR:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_BY_APP_ERROR;
+ end_code = MHD_REQUEST_ENDED_BY_APP_ERROR;
sc = MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED;
break;
case mhd_CONN_CLOSE_APP_ABORTED:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_BY_APP_ABORT;
+ end_code = MHD_REQUEST_ENDED_BY_APP_ABORT;
sc = MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION;
break;
case mhd_CONN_CLOSE_INT_ERROR:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
+ end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
break;
case mhd_CONN_CLOSE_SOCKET_ERR:
close_hard = true;
switch (c->sk_discnt_err)
{
case mhd_SOCKET_ERR_NOMEM:
- term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
+ end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
break;
case mhd_SOCKET_ERR_REMT_DISCONN:
close_hard = false;
- term_code = (MHD_CONNECTION_INIT == c->state) ?
- MHD_REQUEST_TERMINATED_COMPLETED_OK /* Not used */ :
- MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ end_code = (MHD_CONNECTION_INIT == c->state) ?
+ MHD_REQUEST_ENDED_COMPLETED_OK /* Not used */ :
+ MHD_REQUEST_ENDED_CLIENT_ABORT;
break;
case mhd_SOCKET_ERR_CONNRESET:
- term_code = MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ end_code = MHD_REQUEST_ENDED_CLIENT_ABORT;
break;
case mhd_SOCKET_ERR_CONN_BROKEN:
case mhd_SOCKET_ERR_NOTCONN:
@@ -729,7 +730,7 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
case mhd_SOCKET_ERR_OTHER:
case mhd_SOCKET_ERR_INTERNAL:
case mhd_SOCKET_ERR_NO_ERROR:
- term_code = MHD_REQUEST_TERMINATED_CONNECTION_ERROR;
+ end_code = MHD_REQUEST_ENDED_CONNECTION_ERROR;
break;
case mhd_SOCKET_ERR_AGAIN:
case mhd_SOCKET_ERR_INTR:
@@ -740,48 +741,47 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
break;
case mhd_CONN_CLOSE_DAEMON_SHUTDOWN:
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN;
+ end_code = MHD_REQUEST_ENDED_DAEMON_SHUTDOWN;
break;
case mhd_CONN_CLOSE_TIMEDOUT:
if (MHD_CONNECTION_INIT == c->state)
{
close_hard = false;
- term_code = MHD_REQUEST_TERMINATED_COMPLETED_OK; /* Not used */
+ end_code = MHD_REQUEST_ENDED_COMPLETED_OK; /* Not used */
break;
}
close_hard = true;
- term_code = MHD_REQUEST_TERMINATED_TIMEOUT_REACHED;
+ end_code = MHD_REQUEST_ENDED_TIMEOUT_REACHED;
break;
case mhd_CONN_CLOSE_ERR_REPLY_SENT:
close_hard = false;
- term_code = c->rq.too_large ?
- MHD_REQUEST_TERMINATED_NO_RESOURCES :
- MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
+ end_code = c->rq.too_large ?
+ MHD_REQUEST_ENDED_NO_RESOURCES :
+ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
break;
case mhd_CONN_CLOSE_HTTP_COMPLETED:
close_hard = false;
- term_code = MHD_REQUEST_TERMINATED_COMPLETED_OK;
+ end_code = MHD_REQUEST_ENDED_COMPLETED_OK;
break;
default:
mhd_assert (0 && "Unreachable code");
MHD_UNREACHABLE_;
- term_code = MHD_REQUEST_TERMINATED_COMPLETED_OK;
+ end_code = MHD_REQUEST_ENDED_COMPLETED_OK;
close_hard = false;
}
mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc));
- use_local_lingering = false;
/* Make changes on the socket early to let the kernel and the remote
* to process the changes in parallel. */
if (close_hard)
{
/* Use abortive closing, send RST to remote to indicate a problem */
(void) mhd_socket_set_hard_close (c->socket_fd);
- c->state = MHD_CONNECTION_CLOSED;
+ c->state = MHD_CONNECTION_PRE_CLOSING;
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
}
else
@@ -789,13 +789,12 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
if (mhd_socket_shut_wr (c->socket_fd) && (! c->sk_rmt_shut_wr))
{
(void) 0; // TODO: start local lingering phase
- c->state = MHD_CONNECTION_CLOSED; // TODO: start local lingering phase
+ c->state = MHD_CONNECTION_PRE_CLOSING; // TODO: start local lingering phase
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase
- // use_local_lingering = true;
}
else
{ /* No need / not possible to linger */
- c->state = MHD_CONNECTION_CLOSED;
+ c->state = MHD_CONNECTION_PRE_CLOSING;
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
}
}
@@ -809,13 +808,6 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
(void) log_msg;
#endif /* ! HAVE_LOG_FUNCTIONALITY */
- mhd_stream_call_dcc_cleanup_if_needed (c);
- if (NULL != c->rp.resp_iov.iov)
- {
- free (c->rp.resp_iov.iov);
- c->rp.resp_iov.iov = NULL;
- }
-
#if 0 // TODO: notification callback
mhd_assert ((MHD_CONNECTION_INIT != c->state) || (! c->rq.app_aware));
if ( (NULL != d->notify_completed) &&
@@ -823,9 +815,9 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
d->notify_completed (d->notify_completed_cls,
c,
&c->rq.app_context,
- MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ MHD_REQUEST_ENDED_COMPLETED_OK);
#else
- (void) term_code;
+ (void) end_code;
#endif
c->rq.app_aware = false;
@@ -840,11 +832,8 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
}
#ifndef NDEBUG
- c->dbg.pre_closed = true;
+ c->dbg.closing_started = true;
#endif
-
- if (! use_local_lingering)
- mhd_conn_pre_clean (c);
}
@@ -854,11 +843,20 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c)
{
// TODO: support suspended connections
+ mhd_assert (c->dbg.closing_started);
+ mhd_assert (! c->dbg.pre_cleaned);
+
mhd_conn_mark_unready (c, c->daemon);
- if (NULL != c->rq.cntn.lbuf.buf)
+ mhd_stream_call_dcc_cleanup_if_needed (c);
+ if (NULL != c->rp.resp_iov.iov)
+ {
+ free (c->rp.resp_iov.iov);
+ c->rp.resp_iov.iov = NULL;
+ }
+
+ if (NULL != c->rq.cntn.lbuf.data)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
- c->rq.cntn.lbuf.buf = NULL;
if (NULL != c->rp.response)
mhd_response_dec_use_count (c->rp.response);
c->rp.response = NULL;
@@ -893,6 +891,7 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c)
}
#endif /* MHD_USE_EPOLL */
+ c->state = MHD_CONNECTION_CLOSED;
#ifndef NDEBUG
c->dbg.pre_cleaned = true;
#endif
diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h
@@ -127,7 +127,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
MHD_FN_PAR_NONNULL_ALL_;
/**
- * Update last activity mark to the current time..
+ * Update last activity mark to the current time.
* @param c the connection to update
*/
MHD_INTERNAL void
@@ -135,13 +135,14 @@ mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
- * Update last activity mark to the current time..
+ * Check whether connection's timeout is expired.
* @param c the connection to update
- * @return 'true' if connection has not been timed out,
+ * @return 'true' if connection timeout expired and connection needs to be
+ * closed,
* 'false' otherwise
*/
MHD_INTERNAL bool
-mhd_stream_check_timedout (struct MHD_Connection *restrict c)
+mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
@@ -234,24 +235,35 @@ enum mhd_ConnCloseReason
/**
- * Prepare connection for closing.
+ * Start closing of the connection.
+ *
+ * Application is notified about connection closing (if callback is set),
+ * the socket is shut downed for sending and the connection is marked for
+ * closing. The real resource deallocation and socket closing are performed
+ * later.
+ *
+ * As no resources are deallocated by this function, it is safe to call it
+ * "deep" in the code. Upon return all connection resources still could be used,
+ * pointers can be dereferenced etc. The real cleanup is performed when
+ * connection state is processed by #mhd_conn_process_data().
+ *
* @param c the connection for pre-closing
* @param reason the reason for closing
* @param log_msg the message for the log
*/
MHD_INTERNAL void
-mhd_conn_pre_close (struct MHD_Connection *restrict c,
- enum mhd_ConnCloseReason reason,
- const char *log_msg)
+mhd_conn_start_closing (struct MHD_Connection *restrict c,
+ enum mhd_ConnCloseReason reason,
+ const char *log_msg)
MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
/**
* Abort the stream and log message
*/
#ifdef HAVE_LOG_FUNCTIONALITY
-# define mhd_STREAM_ABORT(c,r,m) (mhd_conn_pre_close ((c),(r),(m)))
+# define mhd_STREAM_ABORT(c,r,m) (mhd_conn_start_closing ((c),(r),(m)))
#else /* ! HAVE_LOG_FUNCTIONALITY */
-# define mhd_STREAM_ABORT(c,r,m) (mhd_conn_pre_close ((c),(r),NULL))
+# define mhd_STREAM_ABORT(c,r,m) (mhd_conn_start_closing ((c),(r),NULL))
#endif /* ! HAVE_LOG_FUNCTIONALITY */
/**
@@ -260,7 +272,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_app_abort(c) \
- mhd_conn_pre_close ((c), mhd_CONN_CLOSE_APP_ABORTED, NULL)
+ mhd_conn_start_closing ((c), mhd_CONN_CLOSE_APP_ABORTED, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -268,7 +280,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_skt_err(c) \
- mhd_conn_pre_close ((c), mhd_CONN_CLOSE_SOCKET_ERR, NULL)
+ mhd_conn_start_closing ((c), mhd_CONN_CLOSE_SOCKET_ERR, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -276,7 +288,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_req_finished(c) \
- mhd_conn_pre_close ((c), mhd_CONN_CLOSE_HTTP_COMPLETED, NULL)
+ mhd_conn_start_closing ((c), mhd_CONN_CLOSE_HTTP_COMPLETED, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -284,7 +296,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_timedout(c) \
- mhd_conn_pre_close ((c), mhd_CONN_CLOSE_TIMEDOUT, NULL)
+ mhd_conn_start_closing ((c), mhd_CONN_CLOSE_TIMEDOUT, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -292,11 +304,13 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_d_shutdown(c) \
- mhd_conn_pre_close ((c), mhd_CONN_CLOSE_DAEMON_SHUTDOWN, NULL)
+ mhd_conn_start_closing ((c), mhd_CONN_CLOSE_DAEMON_SHUTDOWN, NULL)
/**
- * Perform initial connection cleanup.
- * The connection must be prepared for closing.
+ * Perform initial connection cleanup after start of the connection closing
+ * procedure.
+ * This cleanup should be performed in the same thread that processes
+ * the connection recv/send/data.
* @param c the connection for pre-closing
*/
MHD_INTERNAL void
diff --git a/src/mhd2/stream_process_reply.c b/src/mhd2/stream_process_reply.c
@@ -811,6 +811,8 @@ build_header_response_inn (struct MHD_Connection *restrict c)
MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
return false;
}
+ use_conn_close = false;
+ use_conn_k_alive = false;
}
/* User-defined headers */
@@ -1009,7 +1011,7 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
size_to_fill);
c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
if (! preprocess_dcc_action (c, act))
- return false;
+ return true;
if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
{
mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
@@ -1029,7 +1031,7 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
mhd_CONN_CLOSE_APP_ERROR,
"Closing connection (application returned more data "
"than requested).");
- return false;
+ return true;
}
c->rp.rsp_cntn_read_pos += filled;
c->write_buffer_append_offset += filled;
@@ -1122,6 +1124,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
mhd_STREAM_ABORT (c,
mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
"No memory in the pool for the reply chunked content.");
+ return true;
}
mhd_assert (max_chunk_overhead < \
(c->write_buffer_size));
@@ -1178,7 +1181,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
size_to_fill);
c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
if (! preprocess_dcc_action (c, act))
- return false;
+ return true;
if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
{
mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
@@ -1196,7 +1199,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
mhd_CONN_CLOSE_APP_ERROR,
"Closing connection (application returned more data "
"than requested).");
- return false;
+ return true;
}
c->rp.rsp_cntn_read_pos += filled;
c->write_buffer_append_offset += filled;
@@ -1300,7 +1303,7 @@ prep_chunked_footer_inn (struct MHD_Connection *restrict c)
}
-MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
{
if (! prep_chunked_footer_inn (c))
@@ -1308,7 +1311,8 @@ mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
mhd_STREAM_ABORT (c,
mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
"No memory in the pool for the reply chunked footer.");
- return;
+ return true;
}
c->state = MHD_CONNECTION_FOOTERS_SENDING;
+ return false;
}
diff --git a/src/mhd2/stream_process_reply.h b/src/mhd2/stream_process_reply.h
@@ -75,6 +75,8 @@ MHD_FN_PAR_NONNULL_ALL_;
/**
* Prepare the chunked response content of this connection for sending.
*
+ * @param c the connection
+ *
* @return 'true' if connection new state could be processed now,
* 'false' if no new state processing is needed.
*/
@@ -88,8 +90,11 @@ MHD_FN_PAR_NONNULL_ALL_;
* with response footers.
*
* @param c the connection
+ *
+ * @return 'true' if connection new state could be processed now,
+ * 'false' if no new state processing is needed.
*/
-MHD_INTERNAL void
+MHD_INTERNAL bool
mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
diff --git a/src/mhd2/stream_process_request.c b/src/mhd2/stream_process_request.c
@@ -58,6 +58,10 @@
#include "stream_funcs.h"
#include "daemon_funcs.h"
+#ifdef HAVE_POST_PARSER
+# include "post_parser_funcs.h"
+#endif /* HAVE_POST_PARSER */
+
#include "mhd_public_api.h"
@@ -359,6 +363,16 @@
"</html>"
/**
+ * Response text used when the request has unsupported "Expect:" value.
+ */
+#define ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE \
+ "<html>" \
+ "<head><title>Unsupported 'Expect:'</title></head>" \
+ "<body>The value of 'Expect:' header used in the request is " \
+ "not supported.</body>" \
+ "</html>"
+
+/**
* Response text used when the request has unsupported both headers:
* "Transfer-Encoding:" and "Content-Length:"
*/
@@ -423,14 +437,6 @@
#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
/**
- * Get whether bare LF in HTTP header and other protocol elements
- * should be treated as the line termination depending on the configured
- * strictness level.
- * RFC 9112, section 2.2
- */
-#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
-
-/**
* The valid length of any HTTP version string
*/
#define HTTP_VER_LEN (mhd_SSTR_LEN (MHD_HTTP_VERSION_1_1_STR))
@@ -586,7 +592,7 @@ get_request_line_inner (struct MHD_Connection *restrict c)
(skip_empty_lines && (-3 >= discp_lvl));
/* Treat bare LF as the end of the line.
RFC 9112, section 2.2 */
- const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+ const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
/* Treat tab as whitespace delimiter.
RFC 9112, section 3 */
const bool tab_as_wsp = (0 >= discp_lvl);
@@ -1240,17 +1246,14 @@ process_request_target (struct MHD_Connection *c)
/* Log callback before the request-target is modified/decoded */
if (NULL != c->daemon->req_cfg.uri_cb.cb)
{
- struct MHD_String full_uri;
struct MHD_EarlyUriCbData req_data;
- full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt;
- full_uri.len = c->rq.req_target_len;
req_data.request = &(c->rq);
- req_data.request_app_context = NULL;
+ req_data.full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt;
+ req_data.full_uri.len = c->rq.req_target_len;
c->rq.app_aware = true;
c->daemon->req_cfg.uri_cb.cb (c->daemon->req_cfg.uri_cb.cls,
- &full_uri,
- &req_data);
- c->rq.app_context = req_data.request_app_context;
+ &req_data,
+ &(c->rq.app_context));
}
if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
@@ -1564,7 +1567,7 @@ get_req_header (struct MHD_Connection *restrict c,
RFC 9112, section 2.2-3
Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
Bare LF is processed as end of the line or rejected as broken request. */
- const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+ const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
/* Keep bare CR character as is.
Violates RFC 9112, section 2.2-4 */
const bool bare_cr_keep = (-3 >= discp_lvl);
@@ -2616,23 +2619,6 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
continue;
}
-#ifdef COOKIE_SUPPORT
- /* "Cookie:" */
- if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE,
- f->field.nv.name.cstr,
- f->field.nv.name.len))
- {
- if (MHD_PARSE_COOKIE_NO_MEMORY ==
- parse_cookie_header (c,
- &(f->field.nv.value)))
- {
- handle_req_cookie_no_space (c);
- return;
- }
- continue;
- }
-#endif /* COOKIE_SUPPORT */
-
/* "Content-Length:" */
if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH,
f->field.nv.name.cstr,
@@ -2746,7 +2732,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
}
else
{
- mhd_LOG_MSG (c->daemon, MHD_SC_CHUNKED_ENCODING_UNSUPPORTED, \
+ mhd_LOG_MSG (c->daemon, MHD_SC_TRANSFER_ENCODING_UNSUPPORTED, \
"The 'Transfer-Encoding' used in request is " \
"unsupported or invalid.");
mhd_RESPOND_WITH_ERROR_STATIC (c,
@@ -2757,6 +2743,48 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
has_trenc = true;
continue;
}
+
+#ifdef COOKIE_SUPPORT
+ /* "Cookie:" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ if (MHD_PARSE_COOKIE_NO_MEMORY ==
+ parse_cookie_header (c,
+ &(f->field.nv.value)))
+ {
+ handle_req_cookie_no_space (c);
+ return;
+ }
+ continue;
+ }
+#endif /* COOKIE_SUPPORT */
+
+ /* "Expect: 100-continue" */
+ if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_EXPECT,
+ f->field.nv.name.cstr,
+ f->field.nv.name.len))
+ {
+ if (mhd_str_equal_caseless_n_st ("100-continue",
+ f->field.nv.value.cstr,
+ f->field.nv.value.len))
+ c->rq.have_expect_100 = true;
+ else
+ {
+ if (0 < c->daemon->req_cfg.strictnees)
+ {
+ mhd_LOG_MSG (c->daemon, MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED, \
+ "The 'Expect' header value used in request is " \
+ "unsupported or invalid.");
+ mhd_RESPOND_WITH_ERROR_STATIC (c,
+ MHD_HTTP_STATUS_EXPECTATION_FAILED,
+ ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE);
+ return;
+ }
+ }
+ continue;
+ }
}
if (has_trenc && has_cntnlen)
@@ -2806,7 +2834,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
/**
- * Is "100 CONTINUE" needed to be sent for current request?
+ * Is "100 Continue" needed to be sent for current request?
*
* @param c the connection to check
* @return false 100 CONTINUE is not needed,
@@ -2815,28 +2843,23 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
static MHD_FN_PAR_NONNULL_ALL_ bool
need_100_continue (struct MHD_Connection *restrict c)
{
- const struct MHD_StringNullable *hvalue;
-
mhd_assert (MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver));
+ mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state);
mhd_assert (MHD_CONNECTION_BODY_RECEIVING > c->state);
- if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
- return false;
+ if (! c->rq.have_expect_100)
+ return false; /* "100 Continue" has not been requested by the client */
if (0 != c->read_buffer_offset)
return false; /* Part of the content has been received already */
- hvalue = mhd_request_get_value_st (&(c->rq),
- MHD_VK_HEADER,
- MHD_HTTP_HEADER_EXPECT);
- if (NULL == hvalue)
- return false;
+ if (0 == c->rq.cntn.cntn_size)
+ return false; /* There is no content or zero-sized content for this request */
- if (mhd_str_equal_caseless_n_st ("100-continue", \
- hvalue->cstr, hvalue->len))
- return true;
+ if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
+ return false; /* '100 Continue' is not allowed for HTTP/1.0 */
- return false;
+ return true;
}
@@ -2852,7 +2875,7 @@ static MHD_FN_PAR_NONNULL_ALL_ bool
check_and_alloc_buf_for_upload_processing (struct MHD_Connection *restrict c)
{
mhd_assert ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) || \
- (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act));
+ (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act));
if (c->rq.have_chunked_upload)
return true; /* The size is unknown, buffers will be dynamically allocated
@@ -2952,15 +2975,33 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
}
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
- case mhd_ACTION_POST_PROCESS:
- mhd_assert (0 && "Not implemented yet");
+#ifdef HAVE_POST_PARSER
+ case mhd_ACTION_POST_PARSE:
+ if (0 == c->rq.cntn.cntn_size)
+ {
+ c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY;
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ return true;
+ }
+ if (! mhd_stream_prepare_for_post_parse (c))
+ {
+ mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVED < c->state);
+ return true;
+ }
+ if (need_100_continue (c))
+ {
+ c->state = MHD_CONNECTION_CONTINUE_SENDING;
+ return true;
+ }
+ c->state = MHD_CONNECTION_BODY_RECEIVING;
return true;
+#endif /* HAVE_POST_PARSER */
case mhd_ACTION_SUSPEND:
c->suspended = true;
return false;
case mhd_ACTION_ABORT:
mhd_conn_pre_close_app_abort (c);
- return false;
+ return true;
case mhd_ACTION_NO_ACTION:
default:
mhd_assert (0 && "Impossible value");
@@ -2979,10 +3020,11 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
* @return true if connection state has been changed,
* false otherwise
*/
-static MHD_FN_PAR_NONNULL_ (1) bool
-process_upload_action (struct MHD_Connection *restrict c,
- const struct MHD_UploadAction *act,
- bool final)
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) bool
+mhd_stream_process_upload_action (struct MHD_Connection *restrict c,
+ const struct MHD_UploadAction *act,
+ bool final)
{
if (NULL != act)
{
@@ -3014,7 +3056,7 @@ process_upload_action (struct MHD_Connection *restrict c,
return false;
case mhd_UPLOAD_ACTION_ABORT:
mhd_conn_pre_close_app_abort (c);
- return false;
+ return true;
case mhd_UPLOAD_ACTION_NO_ACTION:
default:
mhd_assert (0 && "Impossible value");
@@ -3037,7 +3079,7 @@ process_request_chunked_body (struct MHD_Connection *restrict c)
RFC 9112, section 2.2-3
Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
Bare LF is processed as end of the line or rejected as broken request. */
- const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+ const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
/* Allow "Bad WhiteSpace" in chunk extension.
RFC 9112, Section 7.1.1, Paragraph 2 */
const bool allow_bws = (2 < discp_lvl);
@@ -3232,79 +3274,98 @@ process_request_chunked_body (struct MHD_Connection *restrict c)
}
mhd_assert (c->rq.app_aware);
- if (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act)
+#ifdef HAVE_POST_PARSER
+ if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
{
- mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
- return false;
- }
+ size_t size_provided;
- if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
+ c->rq.cntn.recv_size += cntn_data_ready;
+ size_provided = cntn_data_ready;
+
+ state_updated = mhd_stream_post_parse (c,
+ &cntn_data_ready,
+ buffer_head);
+ // TODO: support one chunk in-place processing?
+ mhd_assert ((0 == cntn_data_ready) || \
+ (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \
+ (MHD_CONNECTION_BODY_RECEIVING != c->state));
+ if (MHD_CONNECTION_BODY_RECEIVING != c->state)
+ c->discard_request = true;
+ c->rq.cntn.recv_size += size_provided;
+ }
+ else
+#endif /* HAVE_POST_PARSER */
+ if (1)
{
- need_inc_proc = false;
-
- mhd_assert (0 == c->rq.cntn.proc_size);
- if ((uint_fast64_t) c->rq.cntn.lbuf.size <
- c->rq.cntn.recv_size + cntn_data_ready)
+ mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
+ if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
{
- size_t grow_size;
-
- grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready
- - c->rq.cntn.lbuf.size);
- if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) <
- cntn_data_ready) || (! mhd_daemon_grow_lbuf (d,
- grow_size,
- &(c->rq.cntn.lbuf))))
+ need_inc_proc = false;
+
+ mhd_assert (0 == c->rq.cntn.proc_size);
+ if ((uint_fast64_t) c->rq.cntn.lbuf.size <
+ c->rq.cntn.recv_size + cntn_data_ready)
{
- /* Failed to grow the buffer, no space to put the new data */
- const struct MHD_UploadAction *act;
- if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
+ size_t grow_size;
+
+ grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready
+ - c->rq.cntn.lbuf.size);
+ if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) <
+ cntn_data_ready) ||
+ (! mhd_daemon_grow_lbuf (d,
+ grow_size,
+ &(c->rq.cntn.lbuf))))
{
- mhd_RESPOND_WITH_ERROR_STATIC (
- c,
- MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
- ERR_RSP_MSG_REQUEST_TOO_BIG);
- return true;
+ /* Failed to grow the buffer, no space to put the new data */
+ const struct MHD_UploadAction *act;
+ if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
+ {
+ mhd_RESPOND_WITH_ERROR_STATIC (
+ c,
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+ ERR_RSP_MSG_REQUEST_TOO_BIG);
+ return true;
+ }
+ c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */
+ /* Process previously buffered data */
+ mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ c->rq.cntn.recv_size,
+ c->rq.cntn.lbuf.data);
+ c->rq.cntn.proc_size = c->rq.cntn.recv_size;
+ mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf));
+ if (mhd_stream_process_upload_action (c, act, false))
+ return true;
+ need_inc_proc = true;
}
- c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */
- /* Process previously buffered data */
- mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
- act = c->rq.app_act.head_act.data.upload.inc.cb (
- c->rq.app_act.head_act.data.upload.inc.cls,
- &(c->rq),
- c->rq.cntn.recv_size,
- c->rq.cntn.lbuf.buf);
- c->rq.cntn.proc_size = c->rq.cntn.recv_size;
- mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf));
- if (process_upload_action (c, act, false))
- return true;
- need_inc_proc = true;
+ }
+ if (! need_inc_proc)
+ {
+ memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size,
+ buffer_head, cntn_data_ready);
+ c->rq.cntn.recv_size += cntn_data_ready;
}
}
- if (! need_inc_proc)
+ else
+ need_inc_proc = true;
+
+ if (need_inc_proc)
{
- memcpy (c->rq.cntn.lbuf.buf + c->rq.cntn.recv_size,
- buffer_head, cntn_data_ready);
+ const struct MHD_UploadAction *act;
+ mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
+
c->rq.cntn.recv_size += cntn_data_ready;
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ cntn_data_ready,
+ buffer_head);
+ c->rq.cntn.proc_size += cntn_data_ready;
+ state_updated = mhd_stream_process_upload_action (c, act, false);
}
}
- else
- need_inc_proc = true;
-
- if (need_inc_proc)
- {
- const struct MHD_UploadAction *act;
- mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
-
- c->rq.cntn.recv_size += cntn_data_ready;
- act = c->rq.app_act.head_act.data.upload.inc.cb (
- c->rq.app_act.head_act.data.upload.inc.cls,
- &(c->rq),
- cntn_data_ready,
- buffer_head);
- c->rq.cntn.proc_size += cntn_data_ready;
- state_updated = process_upload_action (c, act, false);
- }
-
/* dh left "processed" bytes in buffer for next time... */
buffer_head += cntn_data_ready;
available -= cntn_data_ready;
@@ -3344,24 +3405,29 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c)
else
cntn_data_ready = c->read_buffer_offset;
- if (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act)
- {
- mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
- return false;
- }
-
- mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
read_buf_reuse = false;
state_updated = false;
- if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
+
+#ifdef HAVE_POST_PARSER
+ if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
{
- // TODO: implement processing in pool memory if buffer is large enough
- mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <=
- (uint_fast64_t) c->rq.cntn.lbuf.size);
- memcpy (c->rq.cntn.lbuf.buf + c->rq.cntn.recv_size,
- c->read_buffer, cntn_data_ready);
+ size_t size_provided;
+ // TODO: rework to correctly support partial processing
+ // TODO: rework to support receiving directly into "large buffer"
c->rq.cntn.recv_size += cntn_data_ready;
+ size_provided = cntn_data_ready;
+
+ state_updated = mhd_stream_post_parse (c,
+ &size_provided,
+ c->read_buffer);
+ mhd_assert ((0 == size_provided) || \
+ (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \
+ (MHD_CONNECTION_BODY_RECEIVING != c->state));
+ if (MHD_CONNECTION_BODY_RECEIVING != c->state)
+ c->discard_request = true;
+
read_buf_reuse = true;
+ c->rq.cntn.proc_size += cntn_data_ready;
if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
{
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
@@ -3369,19 +3435,40 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c)
}
}
else
+#endif /* HAVE_POST_PARSER */
+ if (1)
{
- const struct MHD_UploadAction *act;
- mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
+ mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
+ if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
+ {
+ // TODO: implement processing in pool memory if buffer is large enough
+ mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <=
+ (uint_fast64_t) c->rq.cntn.lbuf.size);
+ memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size,
+ c->read_buffer, cntn_data_ready);
+ c->rq.cntn.recv_size += cntn_data_ready;
+ read_buf_reuse = true;
+ if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
+ {
+ c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ state_updated = true;
+ }
+ }
+ else
+ {
+ const struct MHD_UploadAction *act;
+ mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
- c->rq.cntn.recv_size += cntn_data_ready;
- act = c->rq.app_act.head_act.data.upload.inc.cb (
- c->rq.app_act.head_act.data.upload.inc.cls,
- &(c->rq),
- cntn_data_ready,
- c->read_buffer);
- c->rq.cntn.proc_size += cntn_data_ready;
- read_buf_reuse = true;
- state_updated = process_upload_action (c, act, false);
+ c->rq.cntn.recv_size += cntn_data_ready;
+ act = c->rq.app_act.head_act.data.upload.inc.cb (
+ c->rq.app_act.head_act.data.upload.inc.cls,
+ &(c->rq),
+ cntn_data_ready,
+ c->read_buffer);
+ c->rq.cntn.proc_size += cntn_data_ready;
+ read_buf_reuse = true;
+ state_updated = mhd_stream_process_upload_action (c, act, false);
+ }
}
if (read_buf_reuse)
@@ -3390,8 +3477,9 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c)
mhd_assert (c->read_buffer_offset >= cntn_data_ready);
data_left_size = c->read_buffer_offset - cntn_data_ready;
if (0 != data_left_size)
- memmove (c->read_buffer, c->read_buffer + cntn_data_ready, data_left_size)
- ;
+ memmove (c->read_buffer,
+ c->read_buffer + cntn_data_ready,
+ data_left_size);
c->read_buffer_offset = data_left_size;
}
@@ -3413,27 +3501,29 @@ MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
{
const struct MHD_UploadAction *act;
- mhd_assert (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act || \
+ bool state_changed;
+ mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act || \
mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
- if (mhd_ACTION_POST_PROCESS == c->rq.app_act.head_act.act)
- {
- mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
- return false;
- }
+#ifdef HAVE_POST_PARSER
+ if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
+ return mhd_stream_process_post_finish (c);
+#endif /* HAVE_POST_PARSER */
+
+ mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
{
mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size);
mhd_assert (0 == c->rq.cntn.proc_size);
- mhd_assert (NULL != c->rq.cntn.lbuf.buf);
+ mhd_assert (NULL != c->rq.cntn.lbuf.data);
mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
// TODO: implement processing in pool memory if it is large enough
act = c->rq.app_act.head_act.data.upload.full.cb (
c->rq.app_act.head_act.data.upload.full.cls,
&(c->rq),
c->rq.cntn.recv_size,
- c->rq.cntn.lbuf.buf);
+ c->rq.cntn.lbuf.data);
c->rq.cntn.proc_size = c->rq.cntn.recv_size;
}
else
@@ -3446,16 +3536,21 @@ mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
0,
NULL);
}
- return process_upload_action (c, act, true);
+
+ state_changed = mhd_stream_process_upload_action (c, act, true);
+ if (! c->suspended)
+ mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
+
+ return state_changed;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
{
- if (NULL != c->rq.cntn.lbuf.buf)
+ if (NULL != c->rq.cntn.lbuf.data)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
- c->rq.cntn.lbuf.buf = NULL;
+ c->rq.cntn.lbuf.data = NULL;
if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size)
c->discard_request = true;
mhd_assert (NULL != c->rp.response);
@@ -3723,7 +3818,7 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
*/
bool rbuff_grow_required;
- mhd_assert (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info));
+ mhd_assert (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info));
mhd_assert (! c->discard_request);
rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
@@ -3824,6 +3919,7 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
case MHD_CONNECTION_CHUNKED_BODY_SENT:
case MHD_CONNECTION_FOOTERS_SENDING:
case MHD_CONNECTION_FULL_REPLY_SENT:
+ case MHD_CONNECTION_PRE_CLOSING:
case MHD_CONNECTION_CLOSED:
#if 0 // def UPGRADE_SUPPORT // TODO: Upgrade support
case MHD_CONNECTION_UPGRADE:
diff --git a/src/mhd2/stream_process_request.h b/src/mhd2/stream_process_request.h
@@ -40,6 +40,7 @@
#include "mhd_str_types.h"
struct MHD_Connection; /* forward declaration */
+struct MHD_UploadAction; /* forward declaration */
/**
* Callback for iterating over GET parameters
@@ -145,6 +146,21 @@ MHD_INTERNAL bool
mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * React on provided action for upload
+ * @param c the stream to use
+ * @param act the action provided by application
+ * @param final set to 'true' if this is final upload callback
+ * @return true if connection state has been changed,
+ * false otherwise
+ */
+MHD_INTERNAL bool
+mhd_stream_process_upload_action (struct MHD_Connection *restrict c,
+ const struct MHD_UploadAction *act,
+ bool final)
+MHD_FN_PAR_NONNULL_ (1);
+
/**
* Process non-chunked request body or upload chunking encoding.
* Call the upload handler of the application.
@@ -152,7 +168,7 @@ MHD_FN_PAR_NONNULL_ALL_;
*
* @param c the connection to process
* @return true if advanced to the next state,
- * false if more data needed or connection is suspended or aborted
+ * false if more data needed or connection is suspended
*/
MHD_INTERNAL bool
mhd_stream_process_request_body (struct MHD_Connection *restrict c)
@@ -163,7 +179,7 @@ MHD_FN_PAR_NONNULL_ALL_;
* Advance to the next state, handle errors.
* @param c the connection to process
* @return true if advanced to the next state,
- * false if connection is suspended or aborted
+ * false if connection is suspended
*/
MHD_INTERNAL bool
mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
@@ -174,7 +190,7 @@ MHD_FN_PAR_NONNULL_ALL_;
* Advance to the next state, handle errors.
* @param c the connection to process
* @return true if advanced to the next state,
- * false if connection is suspended or aborted
+ * false if connection is suspended
*/
MHD_INTERNAL bool
mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c
@@ -76,127 +76,128 @@ update_active_state (struct MHD_Connection *restrict c)
}
}
#endif /* HTTPS_SUPPORT */
- while (1)
+ switch (c->state)
{
- switch (c->state)
- {
- case MHD_CONNECTION_INIT:
- case MHD_CONNECTION_REQ_LINE_RECEIVING:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
- break;
- case MHD_CONNECTION_REQ_LINE_RECEIVED:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
- break;
- case MHD_CONNECTION_HEADERS_RECEIVED:
- case MHD_CONNECTION_HEADERS_PROCESSED:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_CONTINUE_SENDING:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
- break;
- case MHD_CONNECTION_BODY_RECEIVING:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
- break;
- case MHD_CONNECTION_BODY_RECEIVED:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_FOOTERS_RECEIVING:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
- break;
- case MHD_CONNECTION_FOOTERS_RECEIVED:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_FULL_REQ_RECEIVED:
- mhd_assert (0 && "Should not be possible");
- c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
- break;
- case MHD_CONNECTION_REQ_RECV_FINISHED:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_START_REPLY:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_HEADERS_SENDING:
- /* headers in buffer, keep writing */
- c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
- break;
- case MHD_CONNECTION_HEADERS_SENT:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
- mhd_assert (0 && "Should not be possible");
- c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
- break;
- case MHD_CONNECTION_UNCHUNKED_BODY_READY:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
- break;
- case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
- mhd_assert (0 && "Should not be possible");
- c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
- break;
- case MHD_CONNECTION_CHUNKED_BODY_READY:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
- break;
- case MHD_CONNECTION_CHUNKED_BODY_SENT:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_FOOTERS_SENDING:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
- break;
- case MHD_CONNECTION_FULL_REPLY_SENT:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- break;
- case MHD_CONNECTION_CLOSED:
- c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
- return false; /* do nothing, not even reading */
+ case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
+ break;
+ case MHD_CONNECTION_REQ_LINE_RECEIVED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
+ break;
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
+ break;
+ case MHD_CONNECTION_BODY_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
+ break;
+ case MHD_CONNECTION_BODY_RECEIVED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ mhd_assert (0 && "Should not be possible");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ break;
+ case MHD_CONNECTION_REQ_RECV_FINISHED:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_START_REPLY:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_HEADERS_SENDING:
+ /* headers in buffer, keep writing */
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
+ mhd_assert (0 && "Should not be possible");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ break;
+ case MHD_CONNECTION_UNCHUNKED_BODY_READY:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ mhd_assert (0 && "Should not be possible");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_SENT:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
+ break;
+ case MHD_CONNECTION_FULL_REPLY_SENT:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ break;
+ case MHD_CONNECTION_PRE_CLOSING:
+ mhd_assert (0 && "Should be unreachable");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ break;
+ case MHD_CONNECTION_CLOSED:
+ mhd_assert (0 && "Should be unreachable");
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+ return false; /* do nothing, not even reading */
#if 0 // def UPGRADE_SUPPORT // TODO: Upgrade support
- case MHD_CONNECTION_UPGRADE:
- mhd_assert (0);
- break;
+ case MHD_CONNECTION_UPGRADE:
+ mhd_assert (0);
+ break;
#endif /* UPGRADE_SUPPORT */
- default:
- mhd_assert (0 && "Impossible value");
- MHD_UNREACHABLE_;
- }
+ default:
+ mhd_assert (0 && "Impossible value");
+ MHD_UNREACHABLE_;
+ }
- if (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
+ if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
+ {
+ /* Check whether the space is available to receive data */
+ if (! mhd_stream_check_and_grow_read_buffer_space (c))
{
- /* Check whether the space is available to receive data */
- if (! mhd_stream_check_and_grow_read_buffer_space (c))
- {
- mhd_assert (c->discard_request);
- continue;
- }
+ mhd_assert (c->discard_request);
+ return false;
}
+ }
- /* Current MHD design assumes that data must be always processes when
- * available. If it is not possible, connection must be suspended. */
- mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
+ /* Current MHD design assumes that data must be always processes when
+ * available. If it is not possible, connection must be suspended. */
+ mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
- /* Sockets errors must be already handled */
- mhd_assert (0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY));
+ /* Sockets errors must be already handled */
+ mhd_assert (0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY));
- if (0 !=
- (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info)
- & (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_WRITE)))
- mhd_conn_mark_ready (c, c->daemon);
- else
- mhd_conn_mark_unready (c, c->daemon);
+ if (0 !=
+ (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info)
+ & (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_SEND)))
+ mhd_conn_mark_ready (c, c->daemon);
+ else
+ mhd_conn_mark_unready (c, c->daemon);
- break; /* Everything was processed. */
- }
return true;
}
@@ -214,11 +215,11 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
{
if (0 == c->read_buffer_offset)
{ /* Read buffer is empty, connection state is actual */
- mhd_conn_pre_close (c,
- (MHD_CONNECTION_INIT == c->state) ?
- mhd_CONN_CLOSE_HTTP_COMPLETED :
- mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
- NULL);
+ mhd_conn_start_closing (c,
+ (MHD_CONNECTION_INIT == c->state) ?
+ mhd_CONN_CLOSE_HTTP_COMPLETED :
+ mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
+ NULL);
return false;
}
}
@@ -252,7 +253,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
return false;
}
- while (true) // TODO: support suspend
+ while (! c->suspended)
{
#ifdef HTTPS_SUPPORT
// TODO: support TLS, handshake
@@ -357,7 +358,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_assert (NULL != c->rp.response);
mhd_stream_switch_from_recv_to_send (c);
if (! mhd_stream_build_header_response (c))
- break;
+ continue;
mhd_assert (MHD_CONNECTION_START_REPLY != c->state);
break;
case MHD_CONNECTION_HEADERS_SENDING:
@@ -441,7 +442,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_assert (c->write_buffer_send_offset <= \
c->write_buffer_append_offset);
mhd_stream_call_dcc_cleanup_if_needed (c);
- mhd_stream_prep_chunked_footer (c);
+ if (mhd_stream_prep_chunked_footer (c))
+ continue;
break;
case MHD_CONNECTION_FOOTERS_SENDING:
mhd_assert (c->rp.props.send_reply_body);
@@ -457,12 +459,16 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
&& ! c->discard_request
&& ! c->sk_rmt_shut_wr);
continue;
- case MHD_CONNECTION_CLOSED:
- break;
+ case MHD_CONNECTION_PRE_CLOSING:
+ return false;
#if 0 // def UPGRADE_SUPPORT
case MHD_CONNECTION_UPGRADE:
return MHD_YES; /* keep open */
#endif /* UPGRADE_SUPPORT */
+ case MHD_CONNECTION_CLOSED:
+ mhd_assert (0 && "Should be unreachable");
+ MHD_UNREACHABLE_;
+ break;
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
@@ -471,8 +477,13 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
break;
}
- if (MHD_CONNECTION_CLOSED == c->state)
+ mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+
+ if (MHD_CONNECTION_PRE_CLOSING == c->state)
+ {
+ mhd_assert (0 && "Pre-closing should be already caught in the loop");
return false;
+ }
if (c->suspended)
{
@@ -483,21 +494,22 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
if ((c->sk_rmt_shut_wr) && (MHD_CONNECTION_START_REPLY > c->state))
{
- mhd_conn_pre_close (c,
- (MHD_CONNECTION_INIT == c->state) ?
- mhd_CONN_CLOSE_HTTP_COMPLETED :
- mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
- NULL);
+ mhd_conn_start_closing (c,
+ (MHD_CONNECTION_INIT == c->state) ?
+ mhd_CONN_CLOSE_HTTP_COMPLETED :
+ mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
+ NULL);
return false;
}
- if (mhd_stream_check_timedout (c)) // TODO: centralise timeout checks
+ if (mhd_stream_is_timeout_expired (c)) // TODO: centralise timeout checks
{
mhd_conn_pre_close_timedout (c);
return false;
}
- update_active_state (c);
- /* MHD_connection_update_event_loop_info (c);*/
+
+ if (! update_active_state (c))
+ return false;
return true;
}