From: Graham Leggett <minfrin@apache.org>
Date: Mon, 13 Dec 2021 10:33:48 +0000
Subject: mod_http2: Don't send GOAWAY too early when MaxRequestsPerChild is
 reached

Backport:

  *) mod_http2: fixes PR65731 and https://github.com/icing/mod_h2/issues/212
     trunk patch: na, fixed on 2.4.x source base
     backport PR: https://github.com/apache/httpd/pull/281
     +1: icing, minfrin, ylavic

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1895869 13f79535-47bb-0310-9956-ffa450edef68

Origin: backport, https://github.com/apache/httpd/commit/c1e16a66718d724feee75322cfef1a96794f00ce
Bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=65731
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/focal/+source/apache2/+bug/1969629
---
 modules/http2/h2_session.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index 2b773c1..d078bf7 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -275,7 +275,7 @@ static int on_begin_headers_cb(nghttp2_session *ngh2,
                                const nghttp2_frame *frame, void *userp)
 {
     h2_session *session = (h2_session *)userp;
-    h2_stream *s;
+    h2_stream *s = NULL;
     
     /* We may see HEADERs at the start of a stream or after all DATA
      * streams to carry trailers. */
@@ -284,7 +284,7 @@ static int on_begin_headers_cb(nghttp2_session *ngh2,
     if (s) {
         /* nop */
     }
-    else {
+    else if (session->local.accepting) {
         s = h2_session_open_stream(userp, frame->hd.stream_id, 0);
     }
     return s? 0 : NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
@@ -2108,7 +2108,16 @@ apr_status_t h2_session_process(h2_session *session, int async)
         now = apr_time_now();
         session->have_read = session->have_written = 0;
 
-        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) {
                 dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
