From decce82a706abd78dfc32821a03ad93841d7758a Mon Sep 17 00:00:00 2001
From: Stefan Eissing <icing@apache.org>
Date: Mon, 16 Oct 2023 09:05:00 +0000
Subject: [PATCH] mod_http2: improved early cleanup of streams

Conflict:Some features of mod_http2 are added and most code of mod_http2
is reconstructed in the pre-patch(9767274b884). Therefore, the pre-patch
is not integrated. As a result, We need context adaptation.
Reference:https://github.com/apache/httpd/commit/decce82a706abd78dfc32821a03ad93841d7758a

---
 changes-entries/h2_cleanup.txt. |  2 ++
 modules/http2/h2_mplx.c         | 26 ++++++++++++++++++++++----
 modules/http2/h2_mplx.h         |  3 ++-
 modules/http2/h2_session.c      | 18 +++++++++++++++++-
 modules/http2/h2_stream.c       |  2 +-
 5 files changed, 44 insertions(+), 7 deletions(-)
 create mode 100644 changes-entries/h2_cleanup.txt.

diff --git a/changes-entries/h2_cleanup.txt. b/changes-entries/h2_cleanup.txt.
new file mode 100644
index 0000000..d330b6a
--- /dev/null
+++ b/changes-entries/h2_cleanup.txt.
@@ -0,0 +1,2 @@
+* mod_http2: import early cleanup of streams
+  [Stefan Eissing]
diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c
index e02ad4e..db8db8a 100644
--- a/modules/http2/h2_mplx.c
+++ b/modules/http2/h2_mplx.c
@@ -1158,14 +1158,32 @@ static int reset_is_acceptable(h2_stream *stream)
     return 1; /* otherwise, be forgiving */
 }
 
-apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id)
+apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id, h2_stream *stream)
 {
-    h2_stream *stream;
     apr_status_t status = APR_SUCCESS;
+    int registered;
 
     H2_MPLX_ENTER_ALWAYS(m);
-    stream = h2_ihash_get(m->streams, stream_id);
-    if (stream && !reset_is_acceptable(stream)) {
+    registered = (h2_ihash_get(m->streams, stream_id) != Null);
+    if (!stream) {
+        /* a RST might arrive so late, we have already forgotten
+         * about it. Seems ok. */
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c1,
+                      H2_MPLX_MSG(m, "RST on unknown stream %d"), stream_id);
+        AP_DEBUG_ASSERT(!registered);
+    }
+    else if (!registered) {
+        /* a RST on a stream that mplx has not been told about, but
+         * which the session knows. Very early and annoying. */
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c1,
+                      H2_STRM_MSG(stream, "very earyly RST, drop"));
+        h2_stream_set_monior(stream, NULL);
+        h2_stream_rst(stream, H2_ERR_STREAM_CLOSED);
+        h2_stream_dispatch(stream, H2_SEV_EOS_SENT);
+        m_stream_cleanup(m, stream);
+        m_be_annoyed(m);
+    }
+    else if (!reset_is_acceptable(stream)) {
         status = m_be_annoyed(m);
     }
     H2_MPLX_LEAVE(m);
diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h
index c61629d..4a05de2 100644
--- a/modules/http2/h2_mplx.h
+++ b/modules/http2/h2_mplx.h
@@ -187,7 +187,8 @@ typedef int h2_mplx_stream_cb(struct h2_stream *s, void *ctx);
 
 apr_status_t h2_mplx_m_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx);
 
-apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id);
+apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id, 
+                                  struct h2_stream *stream);
 
 /**
  * Master connection has entered idle mode.
diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index dc883b5..afd9edb 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -391,6 +391,10 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
                           session->id, (int)frame->hd.stream_id,
                           (int)frame->rst_stream.error_code);
             stream = get_stream(session, frame->hd.stream_id);
+            if (stream) {
+                rv = h2_stream_recv_frame(stream, NGHTTP2_RST_STREAM, frame->hd.flags,
+                    frame->hd.length + H2_FRAME_HDR_LEN);
+            }
             if (stream && stream->initiated_on) {
                 /* A stream reset on a request we sent it. Normal, when the
                  * client does not want it. */
@@ -399,7 +403,8 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
             else {
                 /* A stream reset on a request it sent us. Could happen in a browser
                  * when the user navigates away or cancels loading - maybe. */
-                h2_mplx_m_client_rst(session->mplx, frame->hd.stream_id);
+                h2_mplx_m_client_rst(session->mplx, frame->hd.stream_id,
+                                     stream);
                 ++session->streams_reset;
             }
             break;
@@ -780,6 +785,17 @@ static apr_status_t session_cleanup(h2_session *session, const char *trigger)
                       "goodbye, clients will be confused, should not happen"));
     }
 
+    if (!h2_iq_empty(seesion->ready_to_process)) {
+        int sid;
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                      H2_SSSN_LOG(APLOG(), session,
+                      "cleanup, resetting %d streams in ready-to-process"),
+                      h2_iq_count(session->ready_to_process));
+        while ((sid = h2_iq_shift(session->ready_to_process)) > 0) {
+            h2_mplx_m_client_rst(session->mplx, sid, get_stream(session, sid));
+        }
+    }
+
     transit(session, trigger, H2_SESSION_ST_CLEANUP);
     h2_mplx_m_release_and_join(session->mplx, session->iowait);
     session->mplx = NULL;
diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c
index 4fec537..49d89cb 100644
--- a/modules/http2/h2_stream.c
+++ b/modules/http2/h2_stream.c
@@ -120,7 +120,7 @@ static int trans_on_event[][H2_SS_MAX] = {
 { S_XXX, S_ERR,  S_ERR,  S_CL_L, S_CLS,  S_XXX,  S_XXX,  S_XXX, },/* EV_CLOSED_L*/
 { S_ERR, S_ERR,  S_ERR,  S_CL_R, S_ERR,  S_CLS,  S_NOP,  S_NOP, },/* EV_CLOSED_R*/
 { S_CLS, S_CLS,  S_CLS,  S_CLS,  S_CLS,  S_CLS,  S_NOP,  S_NOP, },/* EV_CANCELLED*/
-{ S_NOP, S_XXX,  S_XXX,  S_XXX,  S_XXX,  S_CLS,  S_CLN,  S_XXX, },/* EV_EOS_SENT*/
+{ S_NOP, S_XXX,  S_XXX,  S_XXX,  S_XXX,  S_CLS,  S_CLN,  S_NOP, },/* EV_EOS_SENT*/
 };
 
 static int on_map(h2_stream_state_t state, int map[H2_SS_MAX])
-- 
2.23.0

