]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
* When reaching server limits, such as MaxRequestsPerChild, the HTTP/2 connection
authorStefan Eissing <icing@apache.org>
Thu, 9 Dec 2021 14:15:19 +0000 (14:15 +0000)
committerStefan Eissing <icing@apache.org>
Thu, 9 Dec 2021 14:15:19 +0000 (14:15 +0000)
   send a GOAWAY frame much too early on new connections, leading to invalid
   protocol state and a client failing the request. See PR65731 at
   <https://bz.apache.org/bugzilla/show_bug.cgi?id=65731>.
   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

modules/http2/h2_session.c
modules/http2/h2_version.h
test/modules/http2/env.py
test/modules/http2/test_106_shutdown.py

index dc00eee0ea87b201a0bf070f22ecda1cea1471a1..12090c0e7e9d4f8b3630eceb8c2c2f8f679663f6 100644 (file)
@@ -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)) {
index 847c9d9ff7584c1d3d2510db2bab1f2e9653aa19..cbc6d5e38a057ab3d22b81ccd69ff660b56a2bbf 100644 (file)
@@ -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 */
index 158646809c944333e5026871d319456f92cc9eaa..f70852dd2aefe681a6e4b9449b993aa22610a832 100644 (file)
@@ -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.*'),
index 862656dce8ef933ecc3e97214b4ce2f2bb9335f6..21036ed28e940c9bea7dbb436203ca0471169064 100644 (file)
@@ -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