]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cache: Add Expires header value parsing
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Wed, 28 Oct 2020 16:52:53 +0000 (17:52 +0100)
committerWilliam Lallemand <wlallemand@haproxy.org>
Fri, 30 Oct 2020 10:08:38 +0000 (11:08 +0100)
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.

reg-tests/cache/expires.vtc [new file with mode: 0644]
src/cache.c

diff --git a/reg-tests/cache/expires.vtc b/reg-tests/cache/expires.vtc
new file mode 100644 (file)
index 0000000..51c7487
--- /dev/null
@@ -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
+
index 2c37ca13b53d8f4aa459930c1f2c8fd4988fa3b1..a73f2668f1ee3f6c9abed0c5eb9c054cf7fbcf96 100644 (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;
 
 }