Skip to content

Commit b59f59a

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 varnishcache#2107 (comment) but using Vary instead of cache key modification plus restart. Fixes varnishcache#4245
1 parent df7d3ac commit b59f59a

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

bin/varnishd/cache/cache_req_fsm.c

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

bin/varnishtest/tests/r04245.vtc

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
varnishtest "cache a HEAD as a fallback for a GET"
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+
# hit
60+
txreq -method "HEAD" -hdr "t: headhit"
61+
rxresphdrs
62+
expect resp.http.t == "headmiss"
63+
64+
# miss
65+
txreq -hdr "t: getmiss"
66+
rxresp
67+
expect resp.http.t == "getmiss"
68+
# hits on full object
69+
txreq -hdr "t: gethit"
70+
rxresp
71+
expect resp.http.t == "getmiss"
72+
txreq -method "HEAD" -hdr "t: getheadhit"
73+
rxresphdrs
74+
expect resp.http.t == "getmiss"
75+
} -run
76+
77+
server s1 -wait

0 commit comments

Comments
 (0)