[master] ac8609f Add H/2 stream timeouts

Dag Haavi Finstad daghf at varnish-software.com
Mon Feb 26 13:19:09 UTC 2018


commit ac8609f6ca53338fafd66216d603837d61c096a5
Author: Dag Haavi Finstad <daghf at varnish-software.com>
Date:   Mon Feb 26 14:13:40 2018 +0100

    Add H/2 stream timeouts
    
    Avoid keeping H/2 streams alive indefinitely.
    
    This adds timeouts for:
      - How long we allow a stream to wait for a WINDOW_UPDATE, subject
        to idle_send_timeout
      - Total duration after we started transmitting a response,
        subject to send_timeout
    
    We currently do not make any attempt at sending a GOAWAY or a PING for
    client inactivity. This may be something to reconsider at a later point.

diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
index 2883789..51b175b 100644
--- a/bin/varnishd/http2/cache_http2.h
+++ b/bin/varnishd/http2/cache_http2.h
@@ -121,6 +121,8 @@ struct h2_req {
 	enum h2_stream_e		state;
 	struct h2_sess			*h2sess;
 	struct req			*req;
+	double				t_send;
+	double				t_winupd;
 	pthread_cond_t			*cond;
 	VTAILQ_ENTRY(h2_req)		list;
 	int64_t				t_window;
diff --git a/bin/varnishd/http2/cache_http2_deliver.c b/bin/varnishd/http2/cache_http2_deliver.c
index 46b0f38..a9f43e9 100644
--- a/bin/varnishd/http2/cache_http2_deliver.c
+++ b/bin/varnishd/http2/cache_http2_deliver.c
@@ -254,6 +254,8 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody)
 	if (sendbody && req->resp_len == 0)
 		sendbody = 0;
 
+	r2->t_send = req->t_prev;
+
 	H2_Send_Get(req->wrk, r2->h2sess, r2);
 	H2_Send(req->wrk, r2, H2_F_HEADERS,
 	    (sendbody ? 0 : H2FF_HEADERS_END_STREAM) | H2FF_HEADERS_END_HEADERS,
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
index 72d0342..f09bed9 100644
--- a/bin/varnishd/http2/cache_http2_proto.c
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -889,6 +889,37 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2,
 	return (h2_tx_rst(wrk, h2, h2e));
 }
 
+static int
+h2_stream_tmo(struct h2_sess *h2, const struct h2_req *r2)
+{
+	int r = 0;
+
+	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+
+	if (r2->t_winupd != 0 || r2->t_send != 0) {
+		Lck_Lock(&h2->sess->mtx);
+		if (r2->t_winupd != 0 &&
+		    h2->sess->t_idle - r2->t_winupd >
+		    cache_param->idle_send_timeout) {
+			VSLb(h2->vsl, SLT_Debug,
+			     "H2: stream %u: Hit idle_send_timeout waiting "
+			     "for WINDOW_UPDATE", r2->stream);
+			r = 1;
+		}
+
+		if (r == 0 && r2->t_send != 0 &&
+		    h2->sess->t_idle - r2->t_send > cache_param->send_timeout) {
+			VSLb(h2->vsl, SLT_Debug,
+			     "H2: stream %u: Hit send_timeout", r2->stream);
+			r = 1;
+		}
+		Lck_Unlock(&h2->sess->mtx);
+	}
+
+	return (r);
+}
+
 /***********************************************************************
  * Called in loop from h2_new_session()
  */
@@ -912,6 +943,7 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
 	h2_error h2e;
 	struct h2_req *r2, *r22;
 	char b[8];
+	int abort = 0;
 
 	ASSERT_RXTHR(h2);
 	(void)VTCP_blocking(*h2->htc->rfd);
@@ -925,6 +957,8 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
 		break;
 	case HTC_S_TIMEOUT:
 		VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
+			if (abort)
+				break;
 			switch (r2->state) {
 			case H2_S_CLOSED:
 				if (!r2->scheduled)
@@ -933,6 +967,10 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
 			case H2_S_OPEN:
 			case H2_S_CLOS_REM:
 			case H2_S_CLOS_LOC:
+				if (h2_stream_tmo(h2, r2)) {
+					abort = 1;
+					continue;
+				}
 				return (1);
 			default:
 				break;
diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c
index a369df7..839193f 100644
--- a/bin/varnishd/http2/cache_http2_send.c
+++ b/bin/varnishd/http2/cache_http2_send.c
@@ -35,6 +35,7 @@
 #include "http2/cache_http2.h"
 
 #include "vend.h"
+#include "vtim.h"
 
 static void
 h2_send_get(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
@@ -204,15 +205,14 @@ h2_do_window(struct worker *wrk, struct h2_req *r2,
 
 	Lck_Lock(&h2->sess->mtx);
 	if (r2->t_window <= 0 || h2->req0->t_window <= 0) {
+		r2->t_winupd = VTIM_real();
 		h2_send_rel(h2, r2);
 		while (r2->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
 			r2->cond = &wrk->cond;
-			// XXX: timeout handling (subject to send_timeout?)
 			AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0));
 			r2->cond = NULL;
 		}
 		while (h2->req0->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
-			// XXX: timeout handling
 			AZ(Lck_CondWait(h2->cond, &h2->sess->mtx, 0));
 		}
 
@@ -235,6 +235,7 @@ h2_do_window(struct worker *wrk, struct h2_req *r2,
 		h2_win_charge(r2, h2, w);
 		assert (w > 0);
 	}
+	r2->t_winupd = 0;
 	Lck_Unlock(&h2->sess->mtx);
 	return (w);
 }


More information about the varnish-commit mailing list