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):