[master] ace8e8fe1 Don't transition to CLOS_REM state until we've seen END_STREAM

Dag Haavi Finstad daghf at varnish-software.com
Fri Jun 22 09:44:14 UTC 2018


commit ace8e8fe1a3601f0a6b760a2b50cd3cbb4fc75fe
Author: Dag Haavi Finstad <daghf at varnish-software.com>
Date:   Fri Jun 22 11:20:12 2018 +0200

    Don't transition to CLOS_REM state until we've seen END_STREAM
    
    Previously we've been incorrectly transtitioning to CLOS_REM on
    END_HEADERS, which prevents us from seeing if a client incorrectly
    transmitted a DATA frame on a closed stream.
    
    This slightly complicates things in that we can now be in state OPEN
    with an inactive hpack decoding state, and we need to make sure on
    cleanup if that has already been finalized.
    
    This would be simpler if the h/2 spec had split the OPEN state in two
    parts, with an extra state transition on END_HEADERS.
    
    Again big thanks to @xcir for his help in diagnosing this.
    
    Fixes: #2623

diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
index 7001b60fc..2a0d73b81 100644
--- a/bin/varnishd/http2/cache_http2_proto.c
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -225,7 +225,7 @@ h2_kill_req(struct worker *wrk, const struct h2_sess *h2,
 			AZ(pthread_cond_signal(r2->cond));
 		r2 = NULL;
 	} else {
-		if (r2->state == H2_S_OPEN) {
+		if (r2->state == H2_S_OPEN && r2->decode != NULL) {
 			(void)h2h_decode_fini(h2, r2->decode);
 			FREE_OBJ(r2->decode);
 		}
@@ -559,7 +559,8 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2,
 	assert(r2->state == H2_S_OPEN);
 	h2e = h2h_decode_fini(h2, r2->decode);
 	FREE_OBJ(r2->decode);
-	r2->state = H2_S_CLOS_REM;
+	if (h2->rxf_flags & H2FF_HEADERS_END_STREAM)
+		r2->state = H2_S_CLOS_REM;
 	h2->new_req = NULL;
 	if (h2e != NULL) {
 		Lck_Lock(&h2->sess->mtx);
@@ -693,7 +694,7 @@ h2_rx_continuation(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 	h2_error h2e;
 
 	ASSERT_RXTHR(h2);
-	if (r2 == NULL || r2->state != H2_S_OPEN)
+	if (r2 == NULL || r2->state != H2_S_OPEN || r2->req != h2->new_req)
 		return (H2CE_PROTOCOL_ERROR);	// XXX spec ?
 	req = r2->req;
 	h2e = h2h_decode_bytes(h2, r2->decode, h2->rxf_data, h2->rxf_len);
@@ -726,6 +727,10 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 	ASSERT_RXTHR(h2);
 	if (r2 == NULL || !r2->scheduled)
 		return (0);
+	if (r2->state >= H2_S_CLOS_REM) {
+		r2->error = H2SE_STREAM_CLOSED;
+		return (H2SE_STREAM_CLOSED); // rfc7540,l,1766,1769
+	}
 	Lck_Lock(&h2->sess->mtx);
 	AZ(h2->mailcall);
 	h2->mailcall = r2;
@@ -780,6 +785,7 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp)
 	*lp = 0;
 
 	Lck_Lock(&h2->sess->mtx);
+	assert (r2->state == H2_S_OPEN);
 	r2->cond = &vc->wrk->cond;
 	while (h2->mailcall != r2 && h2->error == 0 && r2->error == 0)
 		AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0));
@@ -803,8 +809,10 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp)
 			return (VFP_OK);
 		}
 		if (h2->rxf_len == 0) {
-			if (h2->rxf_flags & H2FF_DATA_END_STREAM)
+			if (h2->rxf_flags & H2FF_DATA_END_STREAM) {
 				retval = VFP_END;
+				r2->state = H2_S_CLOS_REM;
+			}
 		}
 		h2->mailcall = NULL;
 		AZ(pthread_cond_signal(h2->cond));
diff --git a/bin/varnishtest/tests/t02014.vtc b/bin/varnishtest/tests/t02014.vtc
index bc49f4e84..17489d362 100644
--- a/bin/varnishtest/tests/t02014.vtc
+++ b/bin/varnishtest/tests/t02014.vtc
@@ -1,8 +1,8 @@
 varnishtest "Exercise h/2 sender flow control code"
 
-barrier b1 sock 3
+barrier b1 sock 3 -cyclic
 
-server s1 {
+server s1 -repeat 2 {
 	rxreq
 	txresp -bodylen 66300
 } -start
@@ -45,3 +45,59 @@ client c1 {
 
 	stream 0 -wait
 } -run
+
+client c1 {
+	stream 0 {
+		barrier b1 sync
+	} -start
+
+	stream 1 {
+		txreq
+		txdata -data "fail"
+		rxrst
+		expect rst.err == STREAM_CLOSED
+		barrier b1 sync
+	} -run
+
+	stream 0 -wait
+} -run
+
+client c1 {
+	stream 0 {
+		barrier b1 sync
+		barrier b1 sync
+		delay .5
+		txwinup -size 256
+		delay .5
+		txwinup -size 256
+		delay .5
+		txwinup -size 256
+
+	} -start
+
+	stream 1 {
+		txreq -req "POST" -nostrend
+		txdata -data "ok"
+		rxwinup
+		txdata -data "fail"
+		rxrst
+		expect rst.err == STREAM_CLOSED
+		barrier b1 sync
+	} -run
+
+	stream 3 {
+		txreq
+		barrier b1 sync
+		delay .5
+		txwinup -size 256
+		delay .5
+		txwinup -size 256
+		delay .5
+		txwinup -size 256
+		rxresp
+		expect resp.status == 200
+		expect resp.bodylen == 66300
+	} -run
+
+	stream 0 -wait
+} -run


More information about the varnish-commit mailing list