MINOR: cache: Add Expires header value parsing

When no Cache-Control max-age or s-maxage information is present in a
cached response, we need to parse the Expires header value (RFC 7234#5.3).
An invalid Expires date value or a date earlier than the reception date
will make the cache_entry stale upon creation.
For now, the Cache-Control and Expires headers are parsed after the
insertion of the response in the cache so even if the parsing of the
Expires results in an already stale entry, the entry will exist in the
cache.
This commit is contained in:
Remi Tricot-Le Breton 2020-10-28 17:52:53 +01:00 committed by William Lallemand
parent bc0af6a199
commit a6476114ec
2 changed files with 150 additions and 1 deletions

122
reg-tests/cache/expires.vtc vendored Normal file
View File

@ -0,0 +1,122 @@
varnishtest "Expires support"
#REQUIRE_VERSION=2.3
feature ignore_unknown_macro
server s1 {
rxreq
txresp -nolen -hdr "Transfer-Encoding: chunked" \
-hdr "Cache-Control: max-age=5"
chunkedlen 15
chunkedlen 15
chunkedlen 15
chunkedlen 0
} -start
server s2 {
rxreq
txresp -nolen -hdr "Transfer-Encoding: chunked"
chunkedlen 16
chunkedlen 16
chunkedlen 16
chunkedlen 0
} -start
server s3 {
rxreq
txresp -nolen -hdr "Transfer-Encoding: chunked"
chunkedlen 16
chunkedlen 16
chunkedlen 16
chunkedlen 0
rxreq
txresp -nolen -hdr "Transfer-Encoding: chunked"
chunkedlen 17
chunkedlen 17
chunkedlen 17
chunkedlen 0
} -start
haproxy h1 -conf {
defaults
mode http
${no-htx} option http-use-htx
timeout connect 1s
timeout client 1s
timeout server 1s
frontend fe
bind "fd@${fe}"
use_backend cache_control_be if { path_beg /cache_control }
use_backend future_expires_be if { path_beg /future }
default_backend past_expires_be
backend cache_control_be
# Expires header should be ignored since a Cache-Control one is present
http-request cache-use my_cache
server www ${s1_addr}:${s1_port}
http-response set-header X-Cache-Hit %[res.cache_hit]
http-response set-header Expires %[date(-1),http_date]
http-response cache-store my_cache
backend future_expires_be
# Expires value set in the future (current_time+5s)
http-request cache-use my_cache
server www ${s2_addr}:${s2_port}
http-response set-header X-Cache-Hit %[res.cache_hit]
http-response set-header Expires %[date(5),http_date]
http-response cache-store my_cache
backend past_expires_be
# Expires value set in the past
http-request cache-use my_cache
server www ${s3_addr}:${s3_port}
http-response set-header X-Cache-Hit %[res.cache_hit]
http-response set-header Expires %[date(-1),http_date]
http-response cache-store my_cache
cache my_cache
total-max-size 3
max-age 20
max-object-size 3072
} -start
client c1 -connect ${h1_fe_sock} {
txreq -url "/cache_control"
rxresp
expect resp.status == 200
expect resp.bodylen == 45
txreq -url "/cache_control"
rxresp
expect resp.status == 200
expect resp.bodylen == 45
expect resp.http.X-Cache-Hit == 1
txreq -url "/future"
rxresp
expect resp.status == 200
expect resp.bodylen == 48
txreq -url "/future"
rxresp
expect resp.status == 200
expect resp.bodylen == 48
expect resp.http.X-Cache-Hit == 1
txreq -url "/past"
rxresp
expect resp.status == 200
expect resp.bodylen == 48
txreq -url "/past"
rxresp
expect resp.status == 200
expect resp.bodylen == 51
expect resp.http.X-Cache-Hit == 0
} -run

View File

@ -482,6 +482,9 @@ int http_calc_maxage(struct stream *s, struct cache *cache)
struct http_hdr_ctx ctx = { .blk = NULL };
int smaxage = -1;
int maxage = -1;
int expires = -1;
struct tm tm = {};
time_t expires_val = 0;
while (http_find_header(htx, ist("cache-control"), &ctx, 0)) {
char *value;
@ -505,7 +508,28 @@ int http_calc_maxage(struct stream *s, struct cache *cache)
}
}
/* TODO: Expires - Data */
/* Look for Expires header if no s-maxage or max-age Cache-Control data
* was found. */
if (maxage == -1 && smaxage == -1) {
ctx.blk = NULL;
if (http_find_header(htx, ist("expires"), &ctx, 1)) {
if (parse_http_date(istptr(ctx.value), istlen(ctx.value), &tm)) {
expires_val = my_timegm(&tm);
/* A request having an expiring date earlier
* than the current date should be considered as
* stale. */
expires = (expires_val >= now.tv_sec) ?
(expires_val - now.tv_sec) : 0;
}
else {
/* Following RFC 7234#5.3, an invalid date
* format must be treated as a date in the past
* so the cache entry must be seen as already
* expired. */
expires = 0;
}
}
}
if (smaxage > 0)
@ -514,6 +538,9 @@ int http_calc_maxage(struct stream *s, struct cache *cache)
if (maxage > 0)
return MIN(maxage, cache->maxage);
if (expires >= 0)
return MIN(expires, cache->maxage);
return cache->maxage;
}