Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libmicrohttpd for openSUSE:Factory checked in at 2026-08-18 16:35:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libmicrohttpd (Old) and /work/SRC/openSUSE:Factory/.libmicrohttpd.new.1258 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libmicrohttpd" Tue Aug 18 16:35:22 2026 rev:54 rq:1371434 version:1.0.10 Changes: -------- --- /work/SRC/openSUSE:Factory/libmicrohttpd/libmicrohttpd.changes 2026-08-06 16:18:47.577309321 +0200 +++ /work/SRC/openSUSE:Factory/.libmicrohttpd.new.1258/libmicrohttpd.changes 2026-08-18 16:35:28.498763888 +0200 @@ -1,0 +2,10 @@ +Fri Aug 7 20:46:21 UTC 2026 - Andreas Stieger <[email protected]> + +- update to 1.0.10: + * Fix race in thread-per-connection mode on wake-up signal + between daemon and thread that could result in lost wake-ups + and thus connections that did not finish processing + * Fix rare assertion failure in relation to suspend-and-resume + processing (harmless if assertions were turned off) + +------------------------------------------------------------------- Old: ---- libmicrohttpd-1.0.9.tar.gz libmicrohttpd-1.0.9.tar.gz.sig New: ---- libmicrohttpd-1.0.10.tar.gz libmicrohttpd-1.0.10.tar.gz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libmicrohttpd.spec ++++++ --- /var/tmp/diff_new_pack.o6pf5a/_old 2026-08-18 16:35:29.989817280 +0200 +++ /var/tmp/diff_new_pack.o6pf5a/_new 2026-08-18 16:35:29.990817316 +0200 @@ -21,7 +21,7 @@ %global sover 12 %global libname %{name}%{sover} Name: libmicrohttpd -Version: 1.0.9 +Version: 1.0.10 Release: 0 Summary: Small Embeddable HTTP Server Library # Some internal tests are licenced as GPL-3.0+ - they are only used in ++++++ libmicrohttpd-1.0.9.tar.gz -> libmicrohttpd-1.0.10.tar.gz ++++++ ++++ 2315 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/ChangeLog new/libmicrohttpd-1.0.10/ChangeLog --- old/libmicrohttpd-1.0.9/ChangeLog 2026-07-30 17:34:10.000000000 +0200 +++ new/libmicrohttpd-1.0.10/ChangeLog 2026-08-07 22:16:28.000000000 +0200 @@ -1,3 +1,11 @@ +Fri Aug 7 10:15:01 PM CEST 2026 + Fix race in thread-per-connection mode on wake-up signal + between daemon and thread that could result in lost wake-ups + and thus connections that did not finish processing. + Fix rare assertion failure in relation to suspend-and-resume + processing (harmless if assertions were turned off). + Releasing GNU libmicrohttpd 1.0.10. -CG + Thu Jul 30 05:32:00 PM CEST 2026 Enfocing a minimal size on the PSK. Fix support for red zones in memorypool. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/NEWS new/libmicrohttpd-1.0.10/NEWS --- old/libmicrohttpd-1.0.9/NEWS 2026-07-30 17:34:43.000000000 +0200 +++ new/libmicrohttpd-1.0.10/NEWS 2026-08-07 22:16:58.000000000 +0200 @@ -1,3 +1,11 @@ +Fri Aug 7 10:15:01 PM CEST 2026 +Released GNU libmicrohttpd 1.0.10. + + This is a bugfix release, fixing minor bugs + and expanding the test suite. + + -- Christian Grothoff + Thu Jul 30 05:34:18 PM CEST 2026 Released GNU libmicrohttpd 1.0.9. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/configure.ac new/libmicrohttpd-1.0.10/configure.ac --- old/libmicrohttpd-1.0.9/configure.ac 2026-07-30 17:31:55.000000000 +0200 +++ new/libmicrohttpd-1.0.10/configure.ac 2026-08-07 22:14:41.000000000 +0200 @@ -23,7 +23,7 @@ # AC_PREREQ([2.64]) LT_PREREQ([2.4.0]) -AC_INIT([GNU libmicrohttpd],[1.0.9],[[email protected]]) +AC_INIT([GNU libmicrohttpd],[1.0.10],[[email protected]]) AC_CONFIG_AUX_DIR([build-aux]) MHD_AUX_DIR='build-aux' # Must be set to the same value as in the previous line AC_CONFIG_HEADERS([MHD_config.h]) @@ -31,7 +31,7 @@ m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl LIB_VERSION_CURRENT=74 -LIB_VERSION_REVISION=7 +LIB_VERSION_REVISION=8 LIB_VERSION_AGE=62 AC_SUBST([LIB_VERSION_CURRENT]) AC_SUBST([LIB_VERSION_REVISION]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/doc/doxygen/libmicrohttpd.doxy new/libmicrohttpd-1.0.10/doc/doxygen/libmicrohttpd.doxy --- old/libmicrohttpd-1.0.9/doc/doxygen/libmicrohttpd.doxy 2026-07-30 17:35:10.000000000 +0200 +++ new/libmicrohttpd-1.0.10/doc/doxygen/libmicrohttpd.doxy 2026-08-07 22:15:00.000000000 +0200 @@ -38,7 +38,7 @@ # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.0.9 +PROJECT_NUMBER = 1.0.10 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/doc/libmicrohttpd.info new/libmicrohttpd-1.0.10/doc/libmicrohttpd.info --- old/libmicrohttpd-1.0.9/doc/libmicrohttpd.info 2026-07-30 17:36:09.000000000 +0200 +++ new/libmicrohttpd-1.0.10/doc/libmicrohttpd.info 2026-08-07 22:20:22.000000000 +0200 @@ -1,7 +1,7 @@ This is libmicrohttpd.info, produced by makeinfo version 7.1.1 from libmicrohttpd.texi. -This manual is for GNU libmicrohttpd (version 1.0.9, 29 July 2026), a +This manual is for GNU libmicrohttpd (version 1.0.10, 29 July 2026), a library for embedding an HTTP(S) server into C applications. Copyright © 2007-2019 Christian Grothoff @@ -23,7 +23,7 @@ The GNU libmicrohttpd Library ***************************** -This manual is for GNU libmicrohttpd (version 1.0.9, 29 July 2026), a +This manual is for GNU libmicrohttpd (version 1.0.10, 29 July 2026), a library for embedding an HTTP(S) server into C applications. Copyright © 2007-2019 Christian Grothoff @@ -4865,44 +4865,44 @@ Tag Table: -Node: Top813 -Node: microhttpd-intro3099 -Ref: fig:performance8010 -Ref: tbl:supported8982 -Node: microhttpd-const17407 -Node: microhttpd-struct53243 -Node: microhttpd-cb54099 -Node: microhttpd-init64357 -Node: microhttpd-inspect70235 -Node: microhttpd-requests73012 -Node: microhttpd-responses77062 -Node: microhttpd-response enqueue78261 -Ref: microhttpd-response enqueue-Footnote-180586 -Node: microhttpd-response create80821 -Node: microhttpd-response headers88831 -Node: microhttpd-response options91305 -Node: microhttpd-response inspect92188 -Node: microhttpd-response upgrade93409 -Node: microhttpd-flow98751 -Node: microhttpd-dauth103264 -Node: microhttpd-dauth basic104824 -Node: microhttpd-dauth digest106415 -Node: microhttpd-post120026 -Node: microhttpd-post api123010 -Node: microhttpd-info125678 -Node: microhttpd-info daemon126094 -Node: microhttpd-info conn129542 -Node: microhttpd-option conn134801 -Node: microhttpd-util135881 -Node: microhttpd-util feature136148 -Node: microhttpd-util unescape139734 -Node: GNU-LGPL140378 -Node: eCos License168471 -Node: GNU-GPL169862 -Node: GNU-FDL189124 -Node: Concept Index214193 -Node: Function and Data Index223639 -Node: Type Index230797 +Node: Top814 +Node: microhttpd-intro3101 +Ref: fig:performance8012 +Ref: tbl:supported8984 +Node: microhttpd-const17409 +Node: microhttpd-struct53245 +Node: microhttpd-cb54101 +Node: microhttpd-init64359 +Node: microhttpd-inspect70237 +Node: microhttpd-requests73014 +Node: microhttpd-responses77064 +Node: microhttpd-response enqueue78263 +Ref: microhttpd-response enqueue-Footnote-180588 +Node: microhttpd-response create80823 +Node: microhttpd-response headers88833 +Node: microhttpd-response options91307 +Node: microhttpd-response inspect92190 +Node: microhttpd-response upgrade93411 +Node: microhttpd-flow98753 +Node: microhttpd-dauth103266 +Node: microhttpd-dauth basic104826 +Node: microhttpd-dauth digest106417 +Node: microhttpd-post120028 +Node: microhttpd-post api123012 +Node: microhttpd-info125680 +Node: microhttpd-info daemon126096 +Node: microhttpd-info conn129544 +Node: microhttpd-option conn134803 +Node: microhttpd-util135883 +Node: microhttpd-util feature136150 +Node: microhttpd-util unescape139736 +Node: GNU-LGPL140380 +Node: eCos License168473 +Node: GNU-GPL169864 +Node: GNU-FDL189126 +Node: Concept Index214195 +Node: Function and Data Index223641 +Node: Type Index230799 End Tag Table diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/doc/stamp-vti new/libmicrohttpd-1.0.10/doc/stamp-vti --- old/libmicrohttpd-1.0.9/doc/stamp-vti 2026-07-30 17:36:08.000000000 +0200 +++ new/libmicrohttpd-1.0.10/doc/stamp-vti 2026-08-07 22:20:20.000000000 +0200 @@ -1,4 +1,4 @@ @set UPDATED 29 July 2026 @set UPDATED-MONTH July 2026 -@set EDITION 1.0.9 -@set VERSION 1.0.9 +@set EDITION 1.0.10 +@set VERSION 1.0.10 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/doc/version.texi new/libmicrohttpd-1.0.10/doc/version.texi --- old/libmicrohttpd-1.0.9/doc/version.texi 2026-07-30 17:36:08.000000000 +0200 +++ new/libmicrohttpd-1.0.10/doc/version.texi 2026-08-07 22:20:20.000000000 +0200 @@ -1,4 +1,4 @@ @set UPDATED 29 July 2026 @set UPDATED-MONTH July 2026 -@set EDITION 1.0.9 -@set VERSION 1.0.9 +@set EDITION 1.0.10 +@set VERSION 1.0.10 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/po/configure.ac new/libmicrohttpd-1.0.10/po/configure.ac --- old/libmicrohttpd-1.0.9/po/configure.ac 2026-07-30 17:35:19.000000000 +0200 +++ new/libmicrohttpd-1.0.10/po/configure.ac 2026-08-07 22:20:20.000000000 +0200 @@ -1,4 +1,4 @@ -AC_INIT([GNU libmicrohttpd],[1.0.9],[[email protected]]) +AC_INIT([GNU libmicrohttpd],[1.0.10],[[email protected]]) CONFIG_STATUS='./po-config.status' AC_CONFIG_AUX_DIR([build-aux]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/po-configure new/libmicrohttpd-1.0.10/po-configure --- old/libmicrohttpd-1.0.9/po-configure 2026-07-30 17:35:19.000000000 +0200 +++ new/libmicrohttpd-1.0.10/po-configure 2026-08-07 22:20:21.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for GNU libmicrohttpd 1.0.9. +# Generated by GNU Autoconf 2.72 for GNU libmicrohttpd 1.0.10. # # Report bugs to <[email protected]>. # @@ -604,8 +604,8 @@ # Identity of this package. PACKAGE_NAME='GNU libmicrohttpd' PACKAGE_TARNAME='libmicrohttpd' -PACKAGE_VERSION='1.0.9' -PACKAGE_STRING='GNU libmicrohttpd 1.0.9' +PACKAGE_VERSION='1.0.10' +PACKAGE_STRING='GNU libmicrohttpd 1.0.10' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='https://www.gnu.org/software/libmicrohttpd/' @@ -1264,7 +1264,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures GNU libmicrohttpd 1.0.9 to adapt to many kinds of systems. +'configure' configures GNU libmicrohttpd 1.0.10 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1330,7 +1330,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of GNU libmicrohttpd 1.0.9:";; + short | recursive ) echo "Configuration of GNU libmicrohttpd 1.0.10:";; esac cat <<\_ACEOF @@ -1438,7 +1438,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -GNU libmicrohttpd configure 1.0.9 +GNU libmicrohttpd configure 1.0.10 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -1646,7 +1646,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by GNU libmicrohttpd $as_me 1.0.9, which was +It was created by GNU libmicrohttpd $as_me 1.0.10, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -7308,7 +7308,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by GNU libmicrohttpd $as_me 1.0.9, which was +This file was extended by GNU libmicrohttpd $as_me 1.0.10, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -7369,7 +7369,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -GNU libmicrohttpd config.status 1.0.9 +GNU libmicrohttpd config.status 1.0.10 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/src/examples/json_echo.c new/libmicrohttpd-1.0.10/src/examples/json_echo.c --- old/libmicrohttpd-1.0.9/src/examples/json_echo.c 2025-07-14 16:48:36.000000000 +0200 +++ new/libmicrohttpd-1.0.10/src/examples/json_echo.c 2026-08-07 22:20:09.000000000 +0200 @@ -33,13 +33,13 @@ * Bad request page. */ #define BAD_REQUEST_ERROR \ - "<html><head><title>Illegal request</title></head><body>Go away.</body></html>" + "<html><head><title>Illegal request</title></head><body>Go away.</body></html>" /** * Invalid JSON page. */ #define NOT_FOUND_ERROR \ - "<html><head><title>Not found</title></head><body>Go away.</body></html>" + "<html><head><title>Not found</title></head><body>Go away.</body></html>" /** @@ -177,7 +177,6 @@ struct Request *request = *req_cls; struct MHD_Response *response; enum MHD_Result ret; - unsigned int i; (void) cls; /* Unused. Silence compiler warning. */ (void) version; /* Unused. Silence compiler warning. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/src/include/microhttpd.h new/libmicrohttpd-1.0.10/src/include/microhttpd.h --- old/libmicrohttpd-1.0.9/src/include/microhttpd.h 2026-07-30 17:34:58.000000000 +0200 +++ new/libmicrohttpd-1.0.10/src/include/microhttpd.h 2026-08-07 22:19:29.000000000 +0200 @@ -101,7 +101,7 @@ * they are parsed as decimal numbers. * Example: 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x01000900 +#define MHD_VERSION 0x01001000 /* If generic headers don't work on your platform, include headers which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/src/microhttpd/daemon.c new/libmicrohttpd-1.0.10/src/microhttpd/daemon.c --- old/libmicrohttpd-1.0.9/src/microhttpd/daemon.c 2026-07-29 17:13:37.000000000 +0200 +++ new/libmicrohttpd-1.0.10/src/microhttpd/daemon.c 2026-08-07 22:14:15.000000000 +0200 @@ -1351,6 +1351,21 @@ if (con->tls_read_ready) read_ready = true; #endif /* HTTPS_SUPPORT */ + if (con->resumed) + { + /* The connection was resumed since its states were last updated, + so 'con->event_loop_info' still describes the state the + connection was in before it was suspended -- see the field's + description. Dispatching on it would, for instance, call + MHD_connection_handle_write() on a connection whose content + reader has just reported that it has no data yet, which is not a + state the write handler has an action for. */ + con->resumed = false; + ret = MHD_connection_handle_idle (con); + if (MHD_NO == ret) + return ret; /* Connection died and was cleaned up. */ + states_info_processed = true; + } if ( (0 != (MHD_EVENT_LOOP_INFO_READ & con->event_loop_info)) && (read_ready || (force_close && con->sk_nonblck)) ) { @@ -2043,6 +2058,53 @@ /** + * Complete the resume of a connection that has its own thread. + * + * This is the thread-per-connection counterpart of + * resume_suspended_connections(): the connection's own thread does the + * bookkeeping for itself instead of waiting for the daemon's thread to + * do it. That is what makes the wake-up reliable -- the notification + * is sent to the connection's own ITC and is acted upon by the only + * thread that reads it, so it can neither be consumed by the daemon's + * thread nor be missed between the check of @e resuming and the wait. + * + * @param connection the suspended connection that is to be resumed, + * must have @e resuming set + */ +static void +resume_connection_own_thread_ (struct MHD_Connection *connection) +{ + struct MHD_Daemon *const daemon = connection->daemon; + + mhd_assert (MHD_D_IS_USING_THREAD_PER_CONN_ (daemon)); + mhd_assert (! MHD_D_IS_USING_EPOLL_ (daemon)); +#ifdef UPGRADE_SUPPORT + mhd_assert (NULL == connection->urh); +#endif /* UPGRADE_SUPPORT */ +#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) + MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); +#endif + mhd_assert (connection->suspended); + mhd_assert (connection->resuming); + DLL_remove (daemon->suspended_connections_head, + daemon->suspended_connections_tail, + connection); + connection->suspended = false; + DLL_insert (daemon->connections_head, + daemon->connections_tail, + connection); + connection->resuming = false; +#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) + MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); +#endif + /* Drop the notification that led here, as well as any notification + left over from a resume that was undone by a suspend before this + thread got to see it. */ + MHD_itc_clear_ (connection->resume_itc); +} + + +/** * Main function of the thread that handles an individual * connection when #MHD_USE_THREAD_PER_CONNECTION is set. * @@ -2094,10 +2156,20 @@ { /* Connection was suspended, wait for resume. */ was_suspended = true; + if (con->resuming) + { + /* The application resumed this connection. Complete the resume + right here: this thread is the only one that handles this + connection, and doing it here is what keeps the wake-up below + free of lost-notification races. */ + resume_connection_own_thread_ (con); + continue; + } + mhd_assert (MHD_ITC_IS_VALID_ (con->resume_itc)); if (! use_poll) { FD_ZERO (&rs); - if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), + if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (con->resume_itc), &rs, NULL, FD_SETSIZE)) @@ -2108,7 +2180,7 @@ #endif goto exit; } - if (0 > MHD_SYS_select_ (MHD_itc_r_fd_ (daemon->itc) + 1, + if (0 > MHD_SYS_select_ (MHD_itc_r_fd_ (con->resume_itc) + 1, &rs, NULL, NULL, @@ -2131,7 +2203,7 @@ else /* use_poll */ { p[0].events = POLLIN; - p[0].fd = MHD_itc_r_fd_ (daemon->itc); + p[0].fd = MHD_itc_r_fd_ (con->resume_itc); p[0].revents = 0; if (0 > MHD_sys_poll_ (p, 1, @@ -2148,7 +2220,7 @@ } } #endif /* HAVE_POLL */ - MHD_itc_clear_ (daemon->itc); + MHD_itc_clear_ (con->resume_itc); continue; /* Check again for resume. */ } /* End of "suspended" branch. */ @@ -2736,6 +2808,29 @@ if (0 != connection->connection_timeout_ms) connection->last_activity = MHD_monotonic_msec_counter (); + MHD_itc_set_invalid_ (connection->resume_itc); + if ( (MHD_D_IS_USING_THREAD_PER_CONN_ (daemon)) && + (0 != (daemon->options & MHD_TEST_ALLOW_SUSPEND_RESUME)) && + (! MHD_itc_init_ (connection->resume_itc)) ) + { + eno = errno; +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + _ ("Failed to create inter-thread communication channel " \ + "for the connection: %s\n"), + MHD_itc_last_strerror_ ()); +#endif + MHD_socket_close_chk_ (client_socket); + MHD_ip_limit_del (daemon, + addr, + addrlen); + if (NULL != connection->addr) + free (connection->addr); + free (connection); + errno = eno; + return NULL; + } + if (0 == (daemon->options & MHD_USE_TLS)) { /* set default connection handlers */ @@ -2916,6 +3011,8 @@ } #endif /* HTTPS_SUPPORT */ MHD_socket_close_chk_ (connection->socket_fd); + if (MHD_ITC_IS_VALID_ (connection->resume_itc)) + MHD_itc_destroy_chk_ (connection->resume_itc); MHD_ip_limit_del (daemon, connection->addr, connection->addr_len); @@ -3445,6 +3542,7 @@ MHD_resume_connection (struct MHD_Connection *connection) { struct MHD_Daemon *daemon = connection->daemon; + bool own_thread; /**< The connection's own thread completes the resume */ #if defined(MHD_USE_THREADS) mhd_assert (NULL == daemon->worker_pool); #endif /* MHD_USE_THREADS */ @@ -3456,12 +3554,35 @@ MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); #endif connection->resuming = true; - daemon->resuming = true; + /* An "upgraded" connection is resumed only to be moved to the cleanup + list, which is the daemon thread's job even with a thread per + connection. */ + own_thread = MHD_D_IS_USING_THREAD_PER_CONN_ (daemon) +#ifdef UPGRADE_SUPPORT + && (NULL == connection->urh) +#endif /* UPGRADE_SUPPORT */ + ; + if (! own_thread) + daemon->resuming = true; #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); #endif - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_itc_activate_ (daemon->itc, "r")) ) + if (own_thread) + { + /* Wake the connection's own thread. See the description of + @e resume_itc for why the daemon-wide ITC will not do. */ + mhd_assert (MHD_ITC_IS_VALID_ (connection->resume_itc)); + if (! MHD_itc_activate_ (connection->resume_itc, "r")) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + _ ("Failed to signal resume via the connection's " \ + "inter-thread communication channel.\n")); +#endif + } + } + else if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && + (! MHD_itc_activate_ (daemon->itc, "r")) ) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -3577,6 +3698,16 @@ pos); if (! used_thr_p_c) { + /* The states of a suspended connection are not updated, so they + are stale now that it is running again. Updating them here + would mean running the state machine under the cleanup mutex, + which is what 0ecf4f26e4c1a4c03d66e1d04bf4cae62bd236a0 backed + out of; flag the connection instead and let call_handlers() + update it before it acts on @e event_loop_info. Thread-per- + connection does the same for itself, in + thread_main_handle_connection(). */ + pos->resumed = true; + /* Reset timeout timer on resume. */ if (0 != pos->connection_timeout_ms) pos->last_activity = MHD_monotonic_msec_counter (); @@ -4198,6 +4329,8 @@ } if (MHD_INVALID_SOCKET != pos->socket_fd) MHD_socket_close_chk_ (pos->socket_fd); + if (MHD_ITC_IS_VALID_ (pos->resume_itc)) + MHD_itc_destroy_chk_ (pos->resume_itc); if (NULL != pos->addr) free (pos->addr); free (pos); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/src/microhttpd/internal.h new/libmicrohttpd-1.0.10/src/microhttpd/internal.h --- old/libmicrohttpd-1.0.9/src/microhttpd/internal.h 2026-07-29 00:00:21.000000000 +0200 +++ new/libmicrohttpd-1.0.10/src/microhttpd/internal.h 2026-08-07 22:14:15.000000000 +0200 @@ -1627,6 +1627,36 @@ volatile bool resuming; /** + * Has the connection been resumed without its states having been + * updated since? + * + * #MHD_connection_update_event_loop_info() deliberately does not + * touch a suspended connection, so whatever the application did + * while the connection was suspended -- queueing a response, or the + * content reader reporting that it has no data yet and moving the + * connection to #MHD_CONNECTION_NORMAL_BODY_UNREADY -- leaves + * @e event_loop_info describing the state from before the + * suspension. The event loop must not act on that stale value; see + * the use in call_handlers(). + */ + bool resumed; + + /** + * Inter-thread communication channel used to wake up the thread that + * handles this connection when the connection is resumed. + * + * Only initialised in thread-per-connection mode with + * #MHD_ALLOW_SUSPEND_RESUME enabled, invalid otherwise. + * + * The daemon-wide ITC cannot serve this purpose: the daemon's own + * thread waits on it as well, and #MHD_itc_clear_() drains it, so + * whichever of the two threads runs first consumes the notification + * and the other one sleeps through it. A channel that only this + * connection's thread ever reads cannot lose the wake-up that way. + */ + struct MHD_itc_ resume_itc; + + /** * Special member to be returned by #MHD_get_connection_info() */ union MHD_ConnectionInfo connection_info_dummy; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/src/testcurl/Makefile.am new/libmicrohttpd-1.0.10/src/testcurl/Makefile.am --- old/libmicrohttpd-1.0.9/src/testcurl/Makefile.am 2025-07-14 16:48:33.000000000 +0200 +++ new/libmicrohttpd-1.0.10/src/testcurl/Makefile.am 2026-08-07 22:14:15.000000000 +0200 @@ -59,6 +59,7 @@ THREAD_ONLY_TESTS += \ test_get_wait \ test_get_wait11 \ + test_suspend_resume_thread \ $(EMPTY_ITEM) if HEAVY_TESTS @@ -272,6 +273,13 @@ test_quiesce_stream_LDADD = \ $(PTHREAD_LIBS) $(LDADD) +test_suspend_resume_thread_SOURCES = \ + test_suspend_resume_thread.c +test_suspend_resume_thread_CFLAGS = \ + $(AM_CFLAGS) $(PTHREAD_CFLAGS) +test_suspend_resume_thread_LDADD = \ + $(PTHREAD_LIBS) $(LDADD) + test_callback_SOURCES = \ test_callback.c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/src/testcurl/test_suspend_resume_thread.c new/libmicrohttpd-1.0.10/src/testcurl/test_suspend_resume_thread.c --- old/libmicrohttpd-1.0.9/src/testcurl/test_suspend_resume_thread.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libmicrohttpd-1.0.10/src/testcurl/test_suspend_resume_thread.c 2026-08-07 22:14:15.000000000 +0200 @@ -0,0 +1,393 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2026 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/** + * @file test_suspend_resume_thread.c + * @brief Testcase for suspend/resume with a thread per connection + * + * The content reader suspends the connection and returns zero, which is + * the pattern microhttpd.h prescribes for "no body data yet". The + * resume comes from a separate thread, as it would from an application + * that is waiting on some other I/O. Several clients run at once and + * every response stalls repeatedly, so a single lost resume anywhere + * hangs the connection it belongs to and the body arrives short. + * + * @author Christian Grothoff + */ +#include "mhd_options.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <curl/curl.h> +#include <microhttpd.h> + +/* Turn any MHD_PANIC() or failing mhd_assert() reached from this + test into a marked, classifiable test error (TESTING.md, P5). */ +#include "mhd_panic_tripwire.h" + +#ifndef WINDOWS +#include <unistd.h> +#endif + +/** + * Number of clients to run against the daemon at the same time. + */ +#define NUM_CLIENTS 4 + +/** + * Total size of the response body. + */ +#define BODY_SIZE 64 + +/** + * Number of bytes the content reader hands out at a time. + */ +#define CHUNK_SIZE 8 + +/** + * Number of times the content reader stalls before each chunk. + */ +#define NUM_STALLS 3 + +/** + * The byte the response body is made of. + */ +#define BODY_FILL 'x' + + +struct ReaderData +{ + /** + * Connection to suspend and resume. + */ + struct MHD_Connection *connection; + + /** + * Number of stalls left before the next chunk is handed out. + */ + unsigned int stalls_left; +}; + + +static uint16_t port; + +static volatile unsigned int panicked; + + +_MHD_NORETURN static void +test_panic_cb (void *cls, + const char *file, + unsigned int line, + const char *reason) +{ + (void) cls; + fprintf (stderr, + "PANIC: %s at %s:%u\n", + (NULL != reason) ? reason : "", + file, + line); + panicked = 1; + exit (99); +} + + +static void * +resume_thread (void *cls) +{ + /* Give the connection's thread a chance to actually park itself, + so that the resume has to travel between threads. */ + (void) usleep (1000); + MHD_resume_connection (cls); + return NULL; +} + + +static ssize_t +content_reader (void *cls, + uint64_t pos, + char *buf, + size_t max) +{ + struct ReaderData *data = cls; + pthread_t tid; + + if (pos >= BODY_SIZE) + return MHD_CONTENT_READER_END_OF_STREAM; + if (0 != data->stalls_left) + { + /* No data yet. Park the connection and report that, as + documented for #MHD_ContentReaderCallback. */ + data->stalls_left--; + MHD_suspend_connection (data->connection); + if (0 != pthread_create (&tid, + NULL, + &resume_thread, + data->connection)) + return MHD_CONTENT_READER_END_WITH_ERROR; + (void) pthread_detach (tid); + return 0; + } + data->stalls_left = NUM_STALLS; + if (max > CHUNK_SIZE) + max = CHUNK_SIZE; + if (max > (size_t) (BODY_SIZE - pos)) + max = (size_t) (BODY_SIZE - pos); + memset (buf, + BODY_FILL, + max); + return (ssize_t) max; +} + + +static void +free_reader_data (void *cls) +{ + free (cls); +} + + +static enum MHD_Result +ahc_echo (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **req_cls) +{ + static int marker; + struct MHD_Response *response; + struct ReaderData *data; + enum MHD_Result ret; + (void) cls; (void) url; (void) method; (void) version; + (void) upload_data; (void) upload_data_size; + + if (&marker != *req_cls) + { + *req_cls = ▮ + return MHD_YES; + } + data = malloc (sizeof (struct ReaderData)); + if (NULL == data) + return MHD_NO; + data->connection = connection; + data->stalls_left = NUM_STALLS; + response = MHD_create_response_from_callback (BODY_SIZE, + 4096, + &content_reader, + data, + &free_reader_data); + if (NULL == response) + { + free (data); + return MHD_NO; + } + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); + MHD_destroy_response (response); + return ret; +} + + +struct Buffer +{ + size_t used; + char data[2 * BODY_SIZE]; +}; + + +static size_t +copy_buffer (void *ptr, + size_t size, + size_t nmemb, + void *cls) +{ + struct Buffer *buf = cls; + + if (0 == size * nmemb) + return 0; + if (buf->used + size * nmemb > sizeof (buf->data)) + return 0; /* overflow */ + memcpy (&buf->data[buf->used], + ptr, + size * nmemb); + buf->used += size * nmemb; + return size * nmemb; +} + + +/** + * Fetch the response once. + * + * @param cls unused + * @return NULL on success, non-NULL on failure + */ +static void * +client_thread (void *cls) +{ + static int failure = 1; + char url[128]; + struct Buffer buf; + CURL *c; + CURLcode errornum; + (void) cls; + + memset (&buf, 0, sizeof (buf)); + c = curl_easy_init (); + if (NULL == c) + return &failure; + snprintf (url, + sizeof (url), + "http://127.0.0.1:%u/", + (unsigned int) port); + curl_easy_setopt (c, CURLOPT_URL, url); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©_buffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &buf); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 30L); + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + errornum = curl_easy_perform (c); + curl_easy_cleanup (c); + if (CURLE_OK != errornum) + { + fprintf (stderr, + "curl_easy_perform() failed: `%s'\n", + curl_easy_strerror (errornum)); + return &failure; + } + if (BODY_SIZE != buf.used) + { + fprintf (stderr, + "Got %u bytes of body, expected %u.\n", + (unsigned int) buf.used, + (unsigned int) BODY_SIZE); + return &failure; + } + if (BODY_SIZE != strspn (buf.data, "x")) + { + fprintf (stderr, + "Body has unexpected content.\n"); + return &failure; + } + return NULL; +} + + +/** + * Run all clients against a daemon started with the given flags. + * + * @param flags the flags to start the daemon with + * @return 0 on success + */ +static unsigned int +test_daemon (unsigned int flags) +{ + pthread_t clients[NUM_CLIENTS]; + struct MHD_Daemon *d; + const union MHD_DaemonInfo *dinfo; + void *res; + unsigned int i; + unsigned int started; + unsigned int failures; + + d = MHD_start_daemon (flags + | MHD_USE_THREAD_PER_CONNECTION + | MHD_USE_INTERNAL_POLLING_THREAD + | MHD_ALLOW_SUSPEND_RESUME + | MHD_USE_ERROR_LOG, + 0, + NULL, NULL, + &ahc_echo, NULL, + MHD_OPTION_END); + if (NULL == d) + { + fprintf (stderr, + "Failed to start daemon with flags %x.\n", + flags); + return 1; + } + dinfo = MHD_get_daemon_info (d, + MHD_DAEMON_INFO_BIND_PORT); + if ( (NULL == dinfo) || + (0 == dinfo->port) ) + { + MHD_stop_daemon (d); + fprintf (stderr, + "Failed to get the port number.\n"); + return 1; + } + port = dinfo->port; + + failures = 0; + for (started = 0; started < NUM_CLIENTS; started++) + { + if (0 != pthread_create (&clients[started], + NULL, + &client_thread, + NULL)) + { + fprintf (stderr, + "Failed to create a client thread.\n"); + failures++; + break; + } + } + for (i = 0; i < started; i++) + { + res = NULL; + if (0 != pthread_join (clients[i], + &res)) + { + fprintf (stderr, + "Failed to join a client thread.\n"); + failures++; + } + else if (NULL != res) + failures++; + } + MHD_stop_daemon (d); + return failures; +} + + +int +main (int argc, + char *const *argv) +{ + unsigned int failures = 0; + (void) argc; (void) argv; + + MHD_set_panic_func (&test_panic_cb, + NULL); + if (0 != curl_global_init (CURL_GLOBAL_WIN32)) + return 2; + /* Without an ITC, and with one; both use select() internally. */ + failures += test_daemon (0); + failures += test_daemon (MHD_USE_ITC); + if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_POLL)) + failures += test_daemon (MHD_USE_POLL | MHD_USE_ITC); + curl_global_cleanup (); + if (0 != panicked) + return 99; + return (0 == failures) ? 0 : 1; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libmicrohttpd-1.0.9/w32/common/microhttpd_dll_res_vc.rc new/libmicrohttpd-1.0.10/w32/common/microhttpd_dll_res_vc.rc --- old/libmicrohttpd-1.0.9/w32/common/microhttpd_dll_res_vc.rc 2026-07-30 17:35:10.000000000 +0200 +++ new/libmicrohttpd-1.0.10/w32/common/microhttpd_dll_res_vc.rc 2026-08-07 22:15:00.000000000 +0200 @@ -4,8 +4,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,9,0 - PRODUCTVERSION 1,0,9,0 + FILEVERSION 1,0,10,0 + PRODUCTVERSION 1,0,10,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #if defined(_DEBUG) FILEFLAGS VS_FF_DEBUG @@ -21,8 +21,8 @@ BLOCK "04090000" /* Lang = US English, Charset = ASCII */ BEGIN VALUE "ProductName", "GNU libmicrohttpd\0" - VALUE "ProductVersion", "1.0.9\0" - VALUE "FileVersion", "1.0.9\0" + VALUE "ProductVersion", "1.0.10\0" + VALUE "FileVersion", "1.0.10\0" VALUE "FileDescription", "GNU libmicrohttpd DLL for Windows (VC build)\0" VALUE "InternalName", "libmicrohttpd\0" #if defined(_DEBUG)
