From 388aca3844b7eb63f674ba045dbaf42120f5adbd Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 16 May 2025 13:29:16 +0200 Subject: [PATCH 1/2] cache_fetch: Send Content-Length: 0 for anything not GET or HEAD otherwise keep the existing logic of sending no Content-Length header if there is no request body. This approach was determined to be better than the inverse of sending C-L: 0 for specific methods to avoid backends sending 411 responses as noted in https://github.com/varnishcache/varnish-cache/issues/4331#issuecomment-2911514551 This code runs before vcl_backend_fetch, so should VCL gain back control over Content-Length as proposed in #4331, it will be possible again to override this default. Fixes #4331 for the special case --- bin/varnishd/cache/cache_fetch.c | 11 +++++++++-- bin/varnishtest/tests/e00036.vtc | 4 ++-- bin/varnishtest/tests/r04340.vtc | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 bin/varnishtest/tests/r04340.vtc diff --git a/bin/varnishd/cache/cache_fetch.c b/bin/varnishd/cache/cache_fetch.c index 55374cf0912..0cdfe0239fe 100644 --- a/bin/varnishd/cache/cache_fetch.c +++ b/bin/varnishd/cache/cache_fetch.c @@ -382,6 +382,7 @@ vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) vtim_real now; unsigned handling, skip_vbr = 0; struct objcore *oc; + const char *met; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); @@ -396,8 +397,14 @@ vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) http_PrintfHeader(bo->bereq, "X-Varnish: %ju", VXID(bo->vsl->wid)); - if (bo->bereq_body == NULL && bo->req == NULL) - http_Unset(bo->bereq, H_Content_Length); + if (bo->bereq_body == NULL && bo->req == NULL) { + met = http_GetMethod(bo->bereq); + if (http_method_eq(met, GET) || + http_method_eq(met, HEAD)) + http_Unset(bo->bereq, H_Content_Length); + else + http_ForceHeader(bo->bereq, H_Content_Length, "0"); + } VCL_backend_fetch_method(bo->vcl, wrk, NULL, bo, NULL); diff --git a/bin/varnishtest/tests/e00036.vtc b/bin/varnishtest/tests/e00036.vtc index 9e6e7d86f57..3cd0254e125 100644 --- a/bin/varnishtest/tests/e00036.vtc +++ b/bin/varnishtest/tests/e00036.vtc @@ -17,7 +17,7 @@ server s1 { expect req.method == XGET expect req.url == "/foo/body1" expect req.bodylen == 0 - expect req.http.Content-Length == "" + expect req.http.Content-Length == "0" expect req.http.Transfer-Encoding == "" txresp -body { Included file @@ -40,7 +40,7 @@ server s1 { expect req.method == XGET expect req.url == "/foo/body2" expect req.bodylen == 0 - expect req.http.Content-Length == "" + expect req.http.Content-Length == "0" expect req.http.Transfer-Encoding == "" txresp -body { Included file diff --git a/bin/varnishtest/tests/r04340.vtc b/bin/varnishtest/tests/r04340.vtc new file mode 100644 index 00000000000..4a5c6b934a4 --- /dev/null +++ b/bin/varnishtest/tests/r04340.vtc @@ -0,0 +1,18 @@ +varnishtest "C-L with empty POST" + +server s1 { + rxreq + txresp + expect req.http.content-length == 0 +} -start + +varnish v1 -vcl+backend { + +} -start + +client c1 { + txreq -method POST -hdr "content-length: 0" + rxresp +} -run + +server s1 -wait From 133aaa50d477ef09f4836b854841abc521742a91 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Mon, 2 Jun 2025 13:30:47 +0200 Subject: [PATCH 2/2] r4340: demo how we can send a GET with C-L: 0 today --- bin/varnishtest/tests/r04340.vtc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/bin/varnishtest/tests/r04340.vtc b/bin/varnishtest/tests/r04340.vtc index 4a5c6b934a4..64a842d6bfa 100644 --- a/bin/varnishtest/tests/r04340.vtc +++ b/bin/varnishtest/tests/r04340.vtc @@ -2,17 +2,41 @@ varnishtest "C-L with empty POST" server s1 { rxreq + expect req.method == "POST" + txresp + expect req.http.content-length == 0 + + rxreq + expect req.method == "GET" txresp expect req.http.content-length == 0 } -start varnish v1 -vcl+backend { - + sub vcl_backend_error { + if (bereq.method == "GETCL0") { + return (retry); + } + } + sub vcl_backend_fetch { + # trick to get a GET with a C-L: 0 + if (bereq.retries == 0 && bereq.method == "GET") { + set bereq.method = "GETCL0"; + return (error); + } + else if (bereq.method == "GETCL0") { + set bereq.method = "GET"; + } + return (fetch); + } } -start client c1 { txreq -method POST -hdr "content-length: 0" rxresp + + txreq -method GET -hdr "content-length: 0" + rxresp } -run server s1 -wait