Skip to content

Commit e3f84b3

Browse files
committed
cache_req_fsm: keep the cache object's Content-Length for HEAD always
Previously, we would only keep the Content-Length header for HEAD requests on hit-for-miss objects, now we simply keep it always to enable "fallback" caching of HEAD requests. The added vtc implements the basics of the logic to enable the (reasonable) use case documented in #2107 (comment) but using Vary instead of cache key modification plus restart. Fixes #4245
1 parent e3c2808 commit e3f84b3

File tree

2 files changed

+87
-8
lines changed

2 files changed

+87
-8
lines changed

bin/varnishd/cache/cache_req_fsm.c

+5-8
Original file line numberDiff line numberDiff line change
@@ -483,15 +483,12 @@ cnt_transmit(struct worker *wrk, struct req *req)
483483
http_Unset(req->resp, H_Content_Length);
484484
} else if (clval >= 0 && clval == req->resp_len) {
485485
/* Reuse C-L header */
486-
} else if (head && req->objcore->flags & OC_F_HFM) {
487-
/*
488-
* Don't touch C-L header (debatable)
489-
*
490-
* The only way to do it correctly would be to GET
491-
* to the backend, and discard the body once the
492-
* filters have had a chance to chew on it, but that
493-
* would negate the "pass for huge objects" use case.
486+
} else if (head) {
487+
/* rfc9110,l,3226,3227
488+
* "MAY send Content-Length ... [for] HEAD"
489+
* do not touch to support cached HEAD #4245
494490
*/
491+
req->resp_len = 0;
495492
} else {
496493
http_Unset(req->resp, H_Content_Length);
497494
if (req->resp_len >= 0)

bin/varnishtest/tests/r04245.vtc

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
varnishtest "cache a HEAD as a fallback for a GET - Content-Length preserved for cached HEAD"
2+
3+
server s1 {
4+
rxreq
5+
expect req.method == "HEAD"
6+
expect req.http.t == "headmiss"
7+
txresp -nolen -hdr "Content-Length: 42"
8+
9+
rxreq
10+
expect req.method == "GET"
11+
expect req.http.t == "getmiss"
12+
txresp -bodylen 42
13+
} -start
14+
15+
varnish v1 -vcl+backend {
16+
sub vcl_recv {
17+
if (req.method == "HEAD") {
18+
set req.http.X-Fetch-Method = "HEAD";
19+
} else {
20+
unset req.http.X-Fetch-Method;
21+
}
22+
}
23+
24+
sub vcl_backend_fetch {
25+
if (bereq.http.X-Fetch-Method) {
26+
set bereq.method = bereq.http.X-Fetch-Method;
27+
}
28+
}
29+
30+
sub vcl_backend_response {
31+
# NOTE: this use of Vary is specific to this case, it is
32+
# usually WRONG to only set Vary for a specific condition
33+
if (bereq.http.X-Fetch-Method) {
34+
if (beresp.http.Vary) {
35+
set beresp.http.Vary += ", X-Fetch-Method";
36+
} else {
37+
set beresp.http.Vary = "X-Fetch-Method";
38+
}
39+
}
40+
set beresp.http.t = bereq.http.t;
41+
}
42+
43+
sub vcl_deliver {
44+
# Vary cleanup
45+
if (resp.http.Vary == "X-Fetch-Method") {
46+
unset resp.http.Vary;
47+
} else if (resp.http.Vary ~ ", X-Fetch-Method$") {
48+
set resp.http.Vary =
49+
regsub(resp.http.Vary, ", X-Fetch-Method$", "");
50+
}
51+
}
52+
} -start
53+
54+
client c1 {
55+
# miss
56+
txreq -method "HEAD" -hdr "t: headmiss"
57+
rxresphdrs
58+
expect resp.http.t == "headmiss"
59+
expect resp.http.Content-Length == 42
60+
# hit
61+
txreq -method "HEAD" -hdr "t: headhit"
62+
rxresphdrs
63+
expect resp.http.t == "headmiss"
64+
expect resp.http.Content-Length == 42
65+
66+
# miss
67+
txreq -hdr "t: getmiss"
68+
rxresp
69+
expect resp.http.t == "getmiss"
70+
expect resp.http.Content-Length == 42
71+
# hits on full object
72+
txreq -hdr "t: gethit"
73+
rxresp
74+
expect resp.http.t == "getmiss"
75+
expect resp.http.Content-Length == 42
76+
txreq -method "HEAD" -hdr "t: getheadhit"
77+
rxresphdrs
78+
expect resp.http.t == "getmiss"
79+
expect resp.http.Content-Length == 42
80+
} -run
81+
82+
server s1 -wait

0 commit comments

Comments
 (0)