[4.1] d327423 So one of those strange cornercases in HTTP/1

Lasse Karstensen lkarsten at varnish-software.com
Thu Jan 14 15:15:03 CET 2016


commit d327423897e1f49323496640224227935046ebf1
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Fri Oct 30 14:22:11 2015 +0000

    So one of those strange cornercases in HTTP/1
    
    If we send the backend a HTTP/1.0 request, and it doesn't have a
    Content-Length, it cannot use Chunked and must fall back to EOF.
    
    However, the protocol field in the response tells us what version
    backend *could* have used, not what it *does* use.
    
    So we can get a response with HTTP/1.1 and EOF, following HTTP/1.0
    semantics - because we asked for it.
    
    Most sensible backends avoid this, either by buffering and creation
    of a C-L or, smartly, returning "HTTP/1.0", even though that
    is strictly speaking against the apocrphal texts.
    
    Anyway, now we cope...
    
    Fixes: 	#1810

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 93e5de4..e7700d0 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -805,7 +805,8 @@ enum sess_close http_DoConnection(struct http *hp);
 
 htc_complete_f HTTP1_Complete;
 uint16_t HTTP1_DissectRequest(struct http_conn *, struct http *);
-uint16_t HTTP1_DissectResponse(struct http_conn *, struct http *);
+uint16_t HTTP1_DissectResponse(struct http_conn *, struct http *resp,
+    const struct http *req);
 unsigned HTTP1_Write(const struct worker *w, const struct http *hp, const int*);
 
 #define HTTPH(a, b, c) extern char b[];
diff --git a/bin/varnishd/http1/cache_http1_fetch.c b/bin/varnishd/http1/cache_http1_fetch.c
index 1b6ceb4..a69fb92 100644
--- a/bin/varnishd/http1/cache_http1_fetch.c
+++ b/bin/varnishd/http1/cache_http1_fetch.c
@@ -193,7 +193,7 @@ V1F_FetchRespHdr(struct busyobj *bo)
 
 	hp = bo->beresp;
 
-	i = HTTP1_DissectResponse(htc, hp);
+	i = HTTP1_DissectResponse(htc, hp, bo->bereq);
 	bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b;
 	if (i) {
 		VSLb(bo->vsl, SLT_FetchError, "http format error");
diff --git a/bin/varnishd/http1/cache_http1_proto.c b/bin/varnishd/http1/cache_http1_proto.c
index 8d08278..deb191d 100644
--- a/bin/varnishd/http1/cache_http1_proto.c
+++ b/bin/varnishd/http1/cache_http1_proto.c
@@ -321,15 +321,15 @@ http1_body_status(const struct http *hp, struct http_conn *htc)
 
 /*--------------------------------------------------------------------*/
 
-static void
-http1_proto_ver(struct http *hp)
+static int8_t
+http1_proto_ver(const struct http *hp)
 {
 	if (!strcasecmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.0"))
-		hp->protover = 10;
+		return (10);
 	else if (!strcasecmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.1"))
-		hp->protover = 11;
+		return (11);
 	else
-		hp->protover = 0;
+		return (0);
 }
 
 /*--------------------------------------------------------------------*/
@@ -347,7 +347,7 @@ HTTP1_DissectRequest(struct http_conn *htc, struct http *hp)
 	retval = http1_splitline(hp, htc, HTTP1_Req);
 	if (retval != 0)
 		return (retval);
-	http1_proto_ver(hp);
+	hp->protover = http1_proto_ver(hp);
 	if (hp->protover == 0)
 		return (400);
 
@@ -391,22 +391,28 @@ HTTP1_DissectRequest(struct http_conn *htc, struct http *hp)
 /*--------------------------------------------------------------------*/
 
 uint16_t
-HTTP1_DissectResponse(struct http_conn *htc, struct http *hp)
+HTTP1_DissectResponse(struct http_conn *htc, struct http *hp,
+    const struct http *req)
 {
 	uint16_t retval = 0;
 	const char *p;
+	int8_t rv;
 
 
 	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
 	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	CHECK_OBJ_NOTNULL(req, HTTP_MAGIC);
 
 	if (http1_splitline(hp, htc, HTTP1_Resp))
 		retval = 503;
 
 	if (retval == 0) {
-		http1_proto_ver(hp);
+		hp->protover = http1_proto_ver(hp);
 		if (hp->protover == 0)
 			retval = 503;
+		rv = http1_proto_ver(req);
+		if (hp->protover > rv)
+			hp->protover = rv;
 	}
 
 	if (retval == 0 && Tlen(hp->hd[HTTP_HDR_STATUS]) != 3)
diff --git a/bin/varnishtest/tests/r01810.vtc b/bin/varnishtest/tests/r01810.vtc
new file mode 100644
index 0000000..77e84db
--- /dev/null
+++ b/bin/varnishtest/tests/r01810.vtc
@@ -0,0 +1,21 @@
+varnishtest "POST HTTP/1.0 response"
+
+server s1 {
+	non-fatal
+	rxreq
+	txresp -proto HTTP/1.1 -nolen -hdr "Connection: close"
+	send "Hello World\n"
+	delay .4
+} -start
+
+varnish v1 -vcl+backend { 
+	sub vcl_backend_fetch {
+		set bereq.proto = "HTTP/1.0";
+	}
+} -start
+
+client c1 {
+	txreq -req POST -hdr "Content-Length: 0"
+	rxresp
+	expect resp.bodylen == 12
+} -run



More information about the varnish-commit mailing list