From: Stefan Eissing Date: Thu, 9 Dec 2021 14:15:19 +0000 (+0000) Subject: * When reaching server limits, such as MaxRequestsPerChild, the HTTP/2 connection X-Git-Tag: 2.5.0-alpha2-ci-test-only~656 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f8e37926912a0f6846b7c1fba0cee83f14c58a81;p=thirdparty%2Fapache%2Fhttpd.git * When reaching server limits, such as MaxRequestsPerChild, the HTTP/2 connection send a GOAWAY frame much too early on new connections, leading to invalid protocol state and a client failing the request. See PR65731 at . The module now initializes the HTTP/2 protocol correctly and allows the client to submit one request before the shutdown via a GOAWAY frame is being announced. No changes-entries, since this fix is proposed for backport on the 1.15.x module codebase in 2.4.x in a separate PR. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1895724 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index dc00eee0ea8..12090c0e7e9 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -1755,12 +1755,22 @@ apr_status_t h2_session_process(h2_session *session, int async) while (session->state != H2_SESSION_ST_DONE) { - if (session->local.accepting + /* PR65731: we may get a new connection to process while the + * MPM already is stopping. For example due to having reached + * MaxRequestsPerChild limit. + * Since this is supposed to handle things gracefully, we need to: + * a) fully initialize the session before GOAWAYing + * b) give the client the chance to submit at least one request + */ + if (session->state != H2_SESSION_ST_INIT /* no longer intializing */ + && session->local.accepted_max > 0 /* have gotten at least one stream */ + && session->local.accepting /* have not already locally shut down */ && !ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) { if (mpm_state == AP_MPMQ_STOPPING) { h2_session_dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL); } } + session->status[0] = '\0'; if (h2_session_want_send(session)) { diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index 847c9d9ff75..cbc6d5e38a0 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -27,7 +27,7 @@ * @macro * Version number of the http2 module as c string */ -#define MOD_HTTP2_VERSION "2.0.1" +#define MOD_HTTP2_VERSION "2.0.2" /** * @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 0x020001 +#define MOD_HTTP2_VERSION_NUM 0x020002 #endif /* mod_h2_h2_version_h */ diff --git a/test/modules/http2/env.py b/test/modules/http2/env.py index 158646809c9..f70852dd2ae 100644 --- a/test/modules/http2/env.py +++ b/test/modules/http2/env.py @@ -85,6 +85,7 @@ class H2TestEnv(HttpdTestEnv): 'AH01630', 'AH00135', 'AH02261', # Re-negotiation handshake failed (our test_101) + 'AH03490', # scoreboard full, happens on limit tests ]) self.httpd_error_log.add_ignored_patterns([ re.compile(r'.*malformed header from script \'hecho.py\': Bad header: x.*'), diff --git a/test/modules/http2/test_106_shutdown.py b/test/modules/http2/test_106_shutdown.py index 862656dce8e..21036ed28e9 100644 --- a/test/modules/http2/test_106_shutdown.py +++ b/test/modules/http2/test_106_shutdown.py @@ -46,3 +46,23 @@ class TestShutdown: assert r.response, f"no response via {r.args} in {r.stderr}\nstdout: {len(r.stdout)} bytes" assert r.response["status"] == 200, f"{r}" assert len(r.response["body"]) == (lines * (len(text)+1)), f"{r}" + + def test_h2_106_02(self, env): + # PR65731: invalid GOAWAY frame at session start when + # MaxRequestsPerChild is reached + # Create a low limit and only 2 children, so we'll encounter this easily + conf = H2Conf(env, extras={ + 'base': [ + "ServerLimit 2", + "MaxRequestsPerChild 3" + ] + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + url = env.mkurl("https", "test1", "/index.html") + for i in range(7): + r = env.curl_get(url, options=['-vvv']) + assert r.exit_code == 0, f"failed on {i}. request: {r.stdout} {r.stderr}" + assert r.response["status"] == 200 + assert "HTTP/2" == r.response["protocol"] \ No newline at end of file