diff --git a/doc/configuration.txt b/doc/configuration.txt index b24c61b849..ee93b39693 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -14336,6 +14336,13 @@ max-age seconds, which means that you can't cache an object more than 60 seconds by default. +process-vary <0 or 1> + Disable or enable the processing of the Vary header. When disabled, a response + containing such a header will never be cached. When enabled, we need to calculate + a preliminary hash for a subset of request headers on all the incoming requests + (which might come with a cpu cost) which will be used to build a secondary key + for a given request (see RFC 7234#4.1). The default value is 0 (disabled). + 6.2.2. Proxy section --------------------- diff --git a/reg-tests/cache/vary.vtc b/reg-tests/cache/vary.vtc index aeffbf68b0..9690490427 100644 --- a/reg-tests/cache/vary.vtc +++ b/reg-tests/cache/vary.vtc @@ -93,6 +93,30 @@ server s1 { chunkedlen 19 chunkedlen 0 + +} -start + +server s2 { + # Responses that should not be cached + rxreq + expect req.url == "/no_vary_support" + txresp -nolen -hdr "Transfer-Encoding: chunked" \ + -hdr "Vary: accept-encoding" \ + -hdr "Cache-Control: max-age=5" + chunkedlen 19 + chunkedlen 19 + chunkedlen 19 + chunkedlen 0 + + rxreq + expect req.url == "/no_vary_support" + txresp -nolen -hdr "Transfer-Encoding: chunked" \ + -hdr "Vary: accept-encoding" \ + -hdr "Cache-Control: max-age=5" + chunkedlen 19 + chunkedlen 19 + chunkedlen 19 + chunkedlen 0 } -start haproxy h1 -conf { @@ -105,6 +129,7 @@ haproxy h1 -conf { frontend fe bind "fd@${fe}" + use_backend no_vary_be if { path_beg /no_vary_support } default_backend test backend test @@ -113,10 +138,23 @@ haproxy h1 -conf { http-response cache-store my_cache http-response set-header X-Cache-Hit %[res.cache_hit] + backend no_vary_be + http-request cache-use no_vary_cache + server www ${s2_addr}:${s2_port} + http-response cache-store no_vary_cache + http-response set-header X-Cache-Hit %[res.cache_hit] + cache my_cache total-max-size 3 max-age 20 max-object-size 3072 + process-vary 1 + + cache no_vary_cache + total-max-size 3 + max-age 20 + max-object-size 3072 + process-vary 0 } -start @@ -231,4 +269,20 @@ client c1 -connect ${h1_fe_sock} { expect resp.status == 200 expect resp.bodylen == 57 expect resp.http.X-Cache-Hit == 1 + + # The following requests are trated by a backend that does not cache + # responses containing a Vary header + txreq -url "/no_vary_support" + rxresp + expect resp.status == 200 + expect resp.bodylen == 57 + expect resp.http.X-Cache-Hit == 0 + + txreq -url "/no_vary_support" + rxresp + expect resp.status == 200 + expect resp.bodylen == 57 + expect resp.http.X-Cache-Hit == 0 + + } -run diff --git a/src/cache.c b/src/cache.c index 07ecc03d46..bdad8fdb7c 100644 --- a/src/cache.c +++ b/src/cache.c @@ -49,6 +49,7 @@ struct cache { unsigned int maxage; /* max-age */ unsigned int maxblocks; unsigned int maxobjsz; /* max-object-size (in bytes) */ + uint8_t vary_processing_enabled; /* boolean : manage Vary header (disabled by default) */ char id[33]; /* cache name */ }; @@ -706,7 +707,8 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px, struct filter *filter; struct shared_block *first = NULL; struct cache_flt_conf *cconf = rule->arg.act.p[0]; - struct shared_context *shctx = shctx_ptr(cconf->c.cache); + struct cache *cache = cconf->c.cache; + struct shared_context *shctx = shctx_ptr(cache); struct cache_st *cache_ctx = NULL; struct cache_entry *object, *old; unsigned int key = read_u32(txn->cache_hash); @@ -763,8 +765,14 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px, /* Only a subset of headers are supported in our Vary implementation. If * any other header is present in the Vary header value, we won't be - * able to use the cache. */ - if (!http_check_vary_header(htx, &vary_signature)) { + * able to use the cache. Likewise, if Vary header support is disabled, + * avoid caching responses that contain such a header. */ + ctx.blk = NULL; + if (cache->vary_processing_enabled) { + if (!http_check_vary_header(htx, &vary_signature)) + goto out; + } + else if (http_find_header(htx, ist("Vary"), &ctx, 0)) { goto out; } @@ -1507,7 +1515,7 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p /* Shared context does not need to be locked while we calculate the * secondary hash. */ - if (!res) { + if (!res && cache->vary_processing_enabled) { /* Build a complete secondary hash until the server response * tells us which fields should be kept (if any). */ http_request_prebuild_full_secondary_key(s); @@ -1640,6 +1648,19 @@ int cfg_parse_cache(const char *file, int linenum, char **args, int kwm) goto out; } tmp_cache_config->maxobjsz = maxobjsz; + } else if (strcmp(args[0], "process-vary") == 0) { + if (alertif_too_many_args(1, file, linenum, args, &err_code)) { + err_code |= ERR_ABORT; + goto out; + } + + if (!*args[1]) { + ha_warning("parsing [%s:%d]: '%s' expects 0 or 1 (disable or enable vary processing).\n", + file, linenum, args[0]); + err_code |= ERR_WARN; + } + + tmp_cache_config->vary_processing_enabled = atoi(args[1]); } else if (*args[0] != 0) { ha_alert("parsing [%s:%d] : unknown keyword '%s' in 'cache' section\n", file, linenum, args[0]);