diff --git a/AUTHORS b/AUTHORS index 7addfb8..db3bf4d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,5 +18,6 @@ Individuals * Jean-Frédéric Clere * Tarek Ibrahim + * Alexander Gerasimov diff --git a/CHANGES b/CHANGES index fff1d37..a93e7c8 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,21 @@ mod_http3 changes Changes are listed most recent first. Security-related entries always appear at the top of their release block. +v0.0.71 (2026-10-11) +-------------------- + *) Fixed an event-thread spin while a client holds its flow-control window + shut: a blocked stream is now blocked again each time nghttp3 offers it. + [Alexander Gerasimov ] + + *) Fixed a graceful restart that never ended: a handshake still pending + when the child started to drain did not finish or time out, so the old + child kept its connection count and the port. + [Alexander Gerasimov ] + + *) Fixed a heap race that crashed the child: worker threads and the event + thread allocated from one session allocator that had no mutex. + [Alexander Gerasimov ] + v0.0.70 (2026-09-06) -------------------- *) SECURITY: Updated the httpd submodule so mpm_event tolerates a connection diff --git a/mod_http3/include/h3_version.h b/mod_http3/include/h3_version.h index 4aa3ee9..41161f8 100644 --- a/mod_http3/include/h3_version.h +++ b/mod_http3/include/h3_version.h @@ -22,13 +22,13 @@ #define MOD_HTTP3_VERSION_MAJOR 0 #define MOD_HTTP3_VERSION_MINOR 0 -#define MOD_HTTP3_VERSION_PATCH 70 +#define MOD_HTTP3_VERSION_PATCH 71 // Construct a 24-bit packed version number from major, minor and patch. Version 1.2.3 becomes 0x010203. #define MOD_HTTP3_MAKE_VERSION(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch)) #define MOD_HTTP3_VERSION MOD_HTTP3_MAKE_VERSION(MOD_HTTP3_VERSION_MAJOR, MOD_HTTP3_VERSION_MINOR, MOD_HTTP3_VERSION_PATCH) -#define MOD_HTTP3_VERSION_STRING "0.0.70" +#define MOD_HTTP3_VERSION_STRING "0.0.71" #endif /* H3_VERSION_H */ diff --git a/mod_http3/src/h3_io.c b/mod_http3/src/h3_io.c index 7e74937..7f764b7 100644 --- a/mod_http3/src/h3_io.c +++ b/mod_http3/src/h3_io.c @@ -271,6 +271,15 @@ static apr_status_t spawn_serviced_session(h3_io_t* io, h3q_conn* conn) } apr_allocator_owner_set(allocator, session_pool); apr_pool_tag(session_pool, "h3_session"); + /* Workers allocate from stream subpools while this thread makes its own: share the allocator safely. */ + apr_thread_mutex_t* alloc_mutex = NULL; + if (apr_thread_mutex_create(&alloc_mutex, APR_THREAD_MUTEX_DEFAULT, session_pool) != APR_SUCCESS) + { + apr_pool_destroy(session_pool); + h3q_conn_free(conn); + return APR_EGENERAL; + } + apr_allocator_mutex_set(allocator, alloc_mutex); h3_session* session = NULL; if (h3_session_create(&session, io->server, conn, session_pool) != APR_SUCCESS) diff --git a/mod_http3/src/h3_stream.c b/mod_http3/src/h3_stream.c index 894b5bd..c999028 100644 --- a/mod_http3/src/h3_stream.c +++ b/mod_http3/src/h3_stream.c @@ -128,13 +128,14 @@ void flush_nghttp3(h3_session* session) nghttp3_conn_add_ack_offset(session->ngh3, sid, res.accepted); if (res.blocked) { - /* Send buffer full: skip this stream instead of busy-looping on the same vec. */ + /* Send buffer full: skip this stream instead of busy-looping on the same vec. + * Block it again every time: resume_stream reschedules a blocked stream. */ if (!h3s->write_blocked) { h3s->write_blocked = 1; session->blocked_streams++; - nghttp3_conn_block_stream(session->ngh3, sid); } + nghttp3_conn_block_stream(session->ngh3, sid); continue; } } diff --git a/mod_http3/src/h3_threads.c b/mod_http3/src/h3_threads.c index bfe1d66..68dbc50 100644 --- a/mod_http3/src/h3_threads.c +++ b/mod_http3/src/h3_threads.c @@ -89,6 +89,10 @@ void* APR_THREAD_FUNC h3_event_thread(apr_thread_t* thread, void* data) h3q_conn_free(conn); } } + } + if (io->thread_running) + { + /* Also while draining: a pending handshake counts as an MPM connection, so it must finish or time out. */ progress_pending_handshakes(io); }