You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2026/03/09 15:27:35 UTC

svn commit: r1932236 - in httpd/httpd/branches/2.4.x: . changes-entries modules/http2 test/clients test/modules/http2

Author: icing
Date: Mon Mar  9 15:27:35 2026
New Revision: 1932236

Log:
Merge GH PR #602

mod_http2 source sync to v2.0.38

Added:
   httpd/httpd/branches/2.4.x/changes-entries/h2_v2.0.38.txt
Modified:
   httpd/httpd/branches/2.4.x/STATUS
   httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_c2.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_c2_filter.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_mplx.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_util.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_session.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_util.h
   httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
   httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_ws.c
   httpd/httpd/branches/2.4.x/modules/http2/h2_ws.h
   httpd/httpd/branches/2.4.x/test/clients/h2ws.c
   httpd/httpd/branches/2.4.x/test/modules/http2/test_800_websockets.py

Modified: httpd/httpd/branches/2.4.x/STATUS
==============================================================================
--- httpd/httpd/branches/2.4.x/STATUS	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/STATUS	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -181,12 +181,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
                   or https://github.com/apache/httpd/pull/587
      +1: jorton, rpluem, covener
 
-  *) mod_http2: do full source sync to trunk. A small set of deviations have
-     crept in over time. Let's do a full sync, making life easier for
-     maintainers and distros.
-     2.4.x patch: https://github.com/apache/httpd/pull/602
-     +1: icing, steffenal, covener
-
   *) mod_http2: update to v2.0.39
      https://github.com/apache/httpd/pull/602 should have been applied!
      trunk patch: https://svn.apache.org/r1931949

Added: httpd/httpd/branches/2.4.x/changes-entries/h2_v2.0.38.txt
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/branches/2.4.x/changes-entries/h2_v2.0.38.txt	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -0,0 +1,3 @@
+  *) mod_http2: update to version 2.0.38
+     Source sync with mod_h2 github repository. No functional change.
+     [Stefan Eissing]

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -47,7 +47,10 @@
 
 static struct h2_workers *workers;
 
-static int async_mpm, mpm_can_waitio;
+static int async_mpm;
+#ifdef AP_MPMQ_CAN_WAITIO
+static int mpm_can_waitio;
+#endif
 
 APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_c_logio_add_bytes_in;
 APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_c_logio_add_bytes_out;

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_c2.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c2.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c2.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -370,6 +370,13 @@ static apr_status_t h2_c2_filter_out(ap_
     h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
     apr_status_t rv;
 
+    if (bb == NULL) {
+#if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
+        f->c->data_in_output_filters = 0;
+#endif
+        return APR_SUCCESS;
+    }
+
    if (bb == NULL) {
 #if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
         f->c->data_in_output_filters = 0;

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_c2_filter.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c2_filter.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c2_filter.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -204,7 +204,7 @@ static int uniq_field_values(void *d, co
          */
         for (i = 0, strpp = (char **) values->elts; i < values->nelts;
              ++i, ++strpp) {
-            if (*strpp && apr_strnatcasecmp(*strpp, start) == 0) {
+            if (*strpp && ap_cstr_casecmp(*strpp, start) == 0) {
                 break;
             }
         }
@@ -312,7 +312,7 @@ static h2_headers *create_response(reque
 
         while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) {
             for (i = 0; i < r->content_languages->nelts; ++i) {
-                if (!apr_strnatcasecmp(token, languages[i]))
+                if (!ap_cstr_casecmp(token, languages[i]))
                     break;
             }
             if (i == r->content_languages->nelts) {

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_mplx.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_mplx.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_mplx.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -398,10 +398,11 @@ apr_status_t h2_mplx_c1_streams_do(h2_mp
 {
     stream_iter_ctx_t x;
     
-    H2_MPLX_ENTER(m);
-
     x.cb = cb;
     x.ctx = ctx;
+
+    H2_MPLX_ENTER(m);
+
     h2_ihash_iter(m->streams, m_stream_iter_wrap, &x);
         
     H2_MPLX_LEAVE(m);
@@ -1033,7 +1034,8 @@ static void s_c2_done(h2_mplx *m, conn_r
         int i;
 
         for (i = 0; i < m->spurge->nelts; ++i) {
-            if (stream == APR_ARRAY_IDX(m->spurge, i, h2_stream*)) {
+            stream = APR_ARRAY_IDX(m->spurge, i, h2_stream*);
+            if (stream && (stream->id == conn_ctx->stream_id)) {
                 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c2,
                               H2_STRM_LOG(APLOGNO(03517), stream, "already in spurge"));
                 ap_assert("stream should not be in spurge" == NULL);

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -866,8 +866,8 @@ static apr_status_t open_stream(h2_proxy
              * Host: header */
             authority = r->server->server_hostname;
             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10511)
-                          "HTTP/0.9 request (with no host line) "
-                          "on incoming request and preserve host set "
+                          "incoming HTTP/0.9 request (with no Host header) "
+                          "and preserve host set, "
                           "forcing hostname to be %s for uri %s",
                           authority, r->uri);
             apr_table_setn(r->headers_in, "Host", authority);

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_util.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_util.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_util.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -381,7 +381,7 @@ static int iq_bubble_down(h2_proxy_iqueu
  * h2_proxy_ngheader
  ******************************************************************************/
 #define H2_HD_MATCH_LIT_CS(l, name)  \
-    ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+    ((strlen(name) == sizeof(l) - 1) && !ap_cstr_casecmp(l, name))
 
 static int h2_util_ignore_header(const char *name) 
 {
@@ -500,7 +500,7 @@ static int ignore_header(const literal *
     
     for (i = 0; i < llen; ++i) {
         lit = &lits[i];
-        if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
+        if (lit->len == nlen && !ap_cstr_casecmp(lit->name, name)) {
             return 1;
         }
     }
@@ -542,7 +542,7 @@ void h2_proxy_util_camel_case_header(cha
 
 /** Match a header value against a string constance, case insensitive */
 #define H2_HD_MATCH_LIT(l, name, nlen)  \
-    ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+    ((nlen == sizeof(l) - 1) && !ap_cstr_casecmp(l, name))
 
 static apr_status_t h2_headers_add_h1(apr_table_t *headers, apr_pool_t *pool, 
                                       const char *name, size_t nlen,

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_session.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_session.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_session.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -125,7 +125,7 @@ static h2_stream *h2_session_open_stream
     apr_allocator_t *allocator;
     apr_pool_t *stream_pool;
     apr_status_t rv;
-    
+
     rv = apr_allocator_create(&allocator);
     if (rv != APR_SUCCESS)
       return NULL;
@@ -135,7 +135,7 @@ static h2_stream *h2_session_open_stream
     apr_allocator_owner_set(allocator, stream_pool);
     apr_pool_abort_set(abort_on_oom, stream_pool);
     apr_pool_tag(stream_pool, "h2_stream");
-    
+
     stream = h2_stream_create(stream_id, stream_pool, session, 
                               session->monitor, initiated_on);
     if (stream) {

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.h
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.h	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.h	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -337,7 +337,7 @@ apr_size_t h2_util_table_bytes(apr_table
 
 /** Match a header value against a string constance, case insensitive */
 #define H2_HD_MATCH_LIT(l, name, nlen)  \
-    ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+    ((nlen == sizeof(l) - 1) && !ap_cstr_casecmp(l, name))
 
 /*******************************************************************************
  * HTTP/2 header helpers

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_version.h	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_version.h	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "2.0.37"
+#define MOD_HTTP2_VERSION "2.0.38"
 
 /**
  * @macro
@@ -35,7 +35,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_HTTP2_VERSION_NUM 0x020025
+#define MOD_HTTP2_VERSION_NUM 0x020026
 
 
 #endif /* mod_h2_h2_version_h */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -131,8 +131,13 @@ static apr_status_t activate_slot(h2_wor
     apr_pool_tag(pool, "h2_worker_slot");
     slot->pool = pool;
 
+#if defined(AP_HAS_THREAD_LOCAL)
     rv = ap_thread_create(&slot->thread, workers->thread_attr,
                           slot_run, slot, slot->pool);
+#else
+    rv = apr_thread_create(&slot->thread, workers->thread_attr,
+                           slot_run, slot, slot->pool);
+#endif
 
 cleanup:
     if (rv != APR_SUCCESS) {

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_ws.c
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_ws.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_ws.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -239,7 +239,7 @@ static void ws_handle_resp(conn_rec *c2,
                  * or in the request processings implementation of WebSockets */
                 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c2, APLOGNO(10463)
                               "h2_c2(%s-%d): websocket CONNECT, 101 response "
-                              "with 'Sec-WebSocket-Accept: %s' but expected %s",
+                              "without 'Sec-WebSocket-Accept: %s' but expected %s",
                               conn_ctx->id, conn_ctx->stream_id, hd,
                               ws_ctx->ws_accept_base64);
             }

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_ws.h
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_ws.h	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_ws.h	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -23,7 +23,7 @@
  * Rewrite a websocket request.
  *
  * @param req the h2 request to rewrite
- * @param c2 the connection to process the request on
+ * @param conn the connection to process the request on
  * @param no_body != 0 iff the request is known to have no body
  * @return the websocket request for internal submit
  */

Modified: httpd/httpd/branches/2.4.x/test/clients/h2ws.c
==============================================================================
--- httpd/httpd/branches/2.4.x/test/clients/h2ws.c	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/test/clients/h2ws.c	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -141,13 +141,14 @@ static int parse_host_port(const char **
                            int *pipv6, size_t *pconsumed,
                            const char *s, size_t len, uint16_t def_port)
 {
-    size_t i, offset=0;
+    size_t i, offset;
     char *host = NULL;
     int port = 0;
     int rv = 1, ipv6 = 0;
 
     if (!len)
         goto leave;
+    offset = 0;
     if (s[offset] == '[') {
         ipv6 = 1;
         for (i = offset++; i < len; ++i) {
@@ -489,7 +490,7 @@ static int h2_session_on_frame_recv(nght
                                     const nghttp2_frame *frame,
                                     void *user_data)
 {
-    (void)user_data;
+    struct h2_session *session = user_data;
 
     switch (frame->hd.type) {
     case NGHTTP2_HEADERS:
@@ -510,6 +511,8 @@ static int h2_session_on_frame_recv(nght
         break;
     case NGHTTP2_GOAWAY:
         log_infof("frame recv", "FRAME[GOAWAY]");
+        fprintf(stdout, "[%d] GOAWAY\n", frame->hd.stream_id);
+        session->aborted = 1;
         break;
     }
     return 0;
@@ -743,6 +746,9 @@ static nfds_t h2_session_set_poll(struct
     int want_read, want_write;
     struct h2_stream *stream;
 
+    if(session->aborted)
+      return 0;
+
     want_read = (nghttp2_session_want_read(session->ngh2) ||
                  session->want_io == IO_WANT_READ);
     want_write = (nghttp2_session_want_write(session->ngh2) ||

Modified: httpd/httpd/branches/2.4.x/test/modules/http2/test_800_websockets.py
==============================================================================
--- httpd/httpd/branches/2.4.x/test/modules/http2/test_800_websockets.py	Mon Mar  9 15:10:52 2026	(r1932235)
+++ httpd/httpd/branches/2.4.x/test/modules/http2/test_800_websockets.py	Mon Mar  9 15:27:35 2026	(r1932236)
@@ -1,6 +1,7 @@
 import inspect
 import logging
 import os
+import re
 import shutil
 import subprocess
 import time
@@ -73,8 +74,8 @@ def ws_run(env: H2TestEnv, path, authori
                 proc.communicate(timeout=timeout)
     end = datetime.now()
     lines = open(f'{env.gen_dir}/h2ws.stdout').read().splitlines()
-    infos = [line for line in lines if line.startswith('[1] ')]
-    hex_content = ' '.join([line for line in lines if not line.startswith('[1] ')])
+    infos = [line for line in lines if re.match(r'^\[\d+] ', line)]
+    hex_content = ' '.join([line for line in lines if not re.match(r'^\[\d+] ', line)])
     if len(infos) > 0 and infos[0] == '[1] :status: 200':
         frames = WsFrameReader.parse(bytearray.fromhex(hex_content))
     else:
@@ -128,6 +129,9 @@ class TestWebSockets:
         if os.path.exists(path):
             return shutil.rmtree(path)
 
+    def infos_without_rst(self, infos):
+        return [info for info in infos if info != '[1] RST']
+
     @pytest.fixture(autouse=True, scope='class')
     def ws_server(self, env):
         # Run our python websockets server that has some special behaviour
@@ -167,14 +171,16 @@ class TestWebSockets:
     def test_h2_800_03_not_found(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/does-not-exist')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] :status: 404', '[1] EOF'] or infos == ['[1] :status: 404', '[1] EOF', '[1] RST'], f'{r}'
+        infos = self.infos_without_rst(infos)
+        assert infos == ['[1] :status: 404', '[1] EOF'], f'{r}'
 
     # CONNECT to a URL path that is a normal HTTP file resource
     # we do not want to receive the body of that
     def test_h2_800_04_non_ws_resource(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/alive.json')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] :status: 502', '[1] EOF'] or infos == ['[1] :status: 502', '[1] EOF', '[1] RST'], f'{r}'
+        infos = self.infos_without_rst(infos)
+        assert infos == ['[1] :status: 502', '[1] EOF'], f'{r}'
         assert frames == b''
 
     # CONNECT to a URL path that sends a delayed HTTP response body
@@ -182,7 +188,8 @@ class TestWebSockets:
     def test_h2_800_05_non_ws_delay_resource(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/h2test/error?body_delay=100ms')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] :status: 502', '[1] EOF'] or infos == ['[1] :status: 502', '[1] EOF', '[1] RST'], f'{r}'
+        infos = self.infos_without_rst(infos)
+        assert infos == ['[1] :status: 502', '[1] EOF'], f'{r}'
         assert frames == b''
 
     # CONNECT missing the sec-webSocket-version header
@@ -195,26 +202,27 @@ class TestWebSockets:
     def test_h2_800_07_miss_path(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/ws/echo/', scenario='miss-path')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] RST'], f'{r}'
+        assert infos == ['[1] RST'] or infos == ['[0] GOAWAY'], f'{r}'
 
     # CONNECT missing the :scheme header
     def test_h2_800_08_miss_scheme(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/ws/echo/', scenario='miss-scheme')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] RST'], f'{r}'
+        assert infos == ['[1] RST'] or infos == ['[0] GOAWAY'], f'{r}'
 
     # CONNECT missing the :authority header
     def test_h2_800_09a_miss_authority(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/ws/echo/', scenario='miss-authority')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] RST'], f'{r}'
+        assert infos == ['[1] RST'] or infos == ['[0] GOAWAY'], f'{r}'
 
     # CONNECT to authority with disabled websockets
     def test_h2_800_09b_unsupported(self, env: H2TestEnv, ws_server):
         r, infos, frames = ws_run(env, path='/ws/echo/',
                                   authority=f'test1.{env.http_tld}:{env.http_port}')
         assert r.exit_code == 0, f'{r}'
-        assert infos == ['[1] :status: 501', '[1] EOF'] or infos == ['[1] :status: 501', '[1] EOF', '[1] RST'], f'{r}'
+        infos = self.infos_without_rst(infos)
+        assert infos == ['[1] :status: 501', '[1] EOF'], f'{r}'
 
     # CONNECT and exchange a PING
     def test_h2_800_10_ws_ping(self, env: H2TestEnv, ws_server):