[6.0] 6f5cf2073 H2: Make rapid reset handling be calleable from any context

Martin Blix Grydeland martin at varnish-software.com
Wed Aug 13 12:03:06 UTC 2025


commit 6f5cf20733883ad2d66587146909371a066b8a89
Author: Martin Blix Grydeland <martin at varnish-software.com>
Date:   Tue Jul 1 15:32:25 2025 +0200

    H2: Make rapid reset handling be calleable from any context
    
    This patch splits the rapid reset handling into a check and a charge
    step. The check determines if this was a benign reset, that is whether it
    should be charged against the budgest or not.
    
    The charge step subtracts from the budget, and handles raises an error
    when exceeded. On error it will send a GOAWAY frame on the session
    immediately. To allow an error to be sent from this function, and to give
    protection to the rapid reset state variables, it is required that the
    caller holds the send mutex when calling.

diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
index 30dd89171..fed24a7e5 100644
--- a/bin/varnishd/http2/cache_http2.h
+++ b/bin/varnishd/http2/cache_http2.h
@@ -248,6 +248,7 @@ h2_error h2h_decode_bytes(struct h2_sess *h2, const uint8_t *ptr,
     size_t len);
 
 /* cache_http2_send.c */
+#define H2_SEND_HELD(h2, r2) (VTAILQ_FIRST(&(h2)->txqueue) == (r2))
 void H2_Send_Get(struct worker *, struct h2_sess *, struct h2_req *);
 void H2_Send_Rel(struct h2_sess *, const struct h2_req *);
 
@@ -272,6 +273,10 @@ void h2_kill_req(struct worker *, struct h2_sess *, struct h2_req *, h2_error);
 int h2_rxframe(struct worker *, struct h2_sess *);
 h2_error h2_set_setting(struct h2_sess *, const uint8_t *);
 void h2_req_body(struct req*);
+int h2_rapid_reset_check(struct worker *wrk, struct h2_sess *h2,
+    const struct h2_req *r2);
+h2_error h2_rapid_reset_charge(struct worker *wrk, struct h2_sess *h2,
+    const struct h2_req *r2);
 task_func_t h2_do_req;
 #ifdef TRANSPORT_MAGIC
 vtr_req_fail_f h2_req_fail;
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
index dac8b3129..78a32a1cb 100644
--- a/bin/varnishd/http2/cache_http2_proto.c
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -321,14 +321,14 @@ h2_rx_push_promise(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 /**********************************************************************
  */
 
-static h2_error
-h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
+int
+h2_rapid_reset_check(struct worker *wrk, struct h2_sess *h2,
+    const struct h2_req *r2)
 {
 	vtim_real now;
-	vtim_dur d;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	ASSERT_RXTHR(h2);
+	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
 	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
 
 	if (h2->rapid_reset_limit == 0)
@@ -340,6 +340,23 @@ h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 	if (now - r2->req->t_first > h2->rapid_reset)
 		return (0);
 
+	return (1);
+}
+
+h2_error
+h2_rapid_reset_charge(struct worker *wrk, struct h2_sess *h2,
+    const struct h2_req *r2)
+{
+	vtim_real now;
+	vtim_dur d;
+	h2_error h2e = NULL;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	AN(H2_SEND_HELD(h2, r2));
+	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+
+	now = VTIM_real();
+
 	d = now - h2->last_rst;
 	h2->rst_budget += h2->rapid_reset_limit * d /
 	    h2->rapid_reset_period;
@@ -347,20 +364,23 @@ h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 	    h2->rapid_reset_limit);
 	h2->last_rst = now;
 
-	if (h2->rst_budget < 1.0) {
+	h2->rst_budget -= 1.0;
+
+	if (h2->rst_budget < 0) {
 		Lck_Lock(&h2->sess->mtx);
-		VSLb(h2->vsl, SLT_Error, "H2: Hit RST limit. Closing session.");
+		VSLb(h2->vsl, SLT_SessError, "H2: Hit RST limit. Closing session.");
 		Lck_Unlock(&h2->sess->mtx);
-		return (H2CE_RAPID_RESET);
+		h2e = H2CE_RAPID_RESET;
+		H2_Send_GOAWAY(wrk, h2, r2, h2e);
 	}
-	h2->rst_budget -= 1.0;
-	return (0);
+
+	return (h2e);
 }
 
 static h2_error v_matchproto_(h2_rxframe_f)
 h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
-	h2_error h2e;
+	h2_error h2e = NULL;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	ASSERT_RXTHR(h2);
@@ -370,7 +390,11 @@ h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 		return (H2CE_FRAME_SIZE_ERROR);
 	if (r2 == NULL)
 		return (0);
-	h2e = h2_rapid_reset(wrk, h2, r2);
+	if (h2_rapid_reset_check(wrk, h2, r2)) {
+		H2_Send_Get(wrk, h2, r2);
+		h2e = h2_rapid_reset_charge(wrk, h2, r2);
+		H2_Send_Rel(h2, r2);
+	}
 	h2_kill_req(wrk, h2, r2, h2_streamerror(vbe32dec(h2->rxf_data)));
 	return (h2e);
 }
diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c
index eb8b17e3e..520f3e5eb 100644
--- a/bin/varnishd/http2/cache_http2_send.c
+++ b/bin/varnishd/http2/cache_http2_send.c
@@ -40,8 +40,6 @@
 #include "vend.h"
 #include "vtim.h"
 
-#define H2_SEND_HELD(h2, r2) (VTAILQ_FIRST(&(h2)->txqueue) == (r2))
-
 static h2_error
 h2_errcheck(const struct h2_req *r2, const struct h2_sess *h2)
 {


More information about the varnish-commit mailing list