MEDIUM: cache: Add "Origin" header to secondary cache key
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Tue, 3 Oct 2023 12:33:41 +0000 (14:33 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Thu, 5 Oct 2023 08:53:54 +0000 (10:53 +0200)
This patch add a hash of the Origin header to the cache's secondary key.
This enables to manage store responses that have a "Vary: Origin" header
in the cache when vary is enabled.
This cannot be considered as a means to manage CORS requests though, it
only processes the Origin header and hashes the presented value without
any form of URI normalization.

This need was expressed by Philipp Hossner in GitHub issue #251.

Co-Authored-by: Philipp Hossner <philipp.hossner@posteo.de>

doc/configuration.txt
include/haproxy/http_ana-t.h
reg-tests/cache/vary.vtc
src/cache.c

index cd15e93..dd78f45 100644 (file)
@@ -17178,7 +17178,7 @@ The cache won't store and won't deliver objects in these cases:
 - If the response is not a 200
 - If the response contains a Vary header and either the process-vary option is
   disabled, or a currently unmanaged header is specified in the Vary value (only
-  accept-encoding and referer are managed for now)
+  accept-encoding, referer and origin are managed for now)
 - If the Content-Length + the headers size is greater than "max-object-size"
 - If the response is not cacheable
 - If the response does not have an explicit expiration time (s-maxage or max-age
@@ -17229,8 +17229,10 @@ process-vary <on/off>
   Enable or disable 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 off (disabled).
+  (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 secondary key is built out of
+  the contents of the 'accept-encoding', 'referer' and 'origin' headers for
+  now. The default value is off (disabled).
 
 max-secondary-entries <number>
   Define the maximum number of simultaneous secondary entries with the same primary
index 39ec989..6d8022a 100644 (file)
@@ -157,7 +157,7 @@ static forceinline char *hmsg_show_flags(char *buf, size_t len, const char *deli
  * request/response pairs, because they depend on the responses' optional Vary
  * header. The different sizes can be found in the vary_information object (see
  * cache.c).*/
-#define HTTP_CACHE_SEC_KEY_LEN (sizeof(uint32_t)+sizeof(uint64_t))
+#define HTTP_CACHE_SEC_KEY_LEN (sizeof(uint32_t)+sizeof(uint64_t)+sizeof(uint64_t))
 
 
 /* Redirect flags */
index b1c1bda..6c8cedf 100644 (file)
@@ -77,6 +77,20 @@ server s1 {
                -hdr "Content-Encoding: gzip" \
                -bodylen 57
 
+       rxreq
+       expect req.url == "/origin-referer-accept-encoding"
+       txresp -hdr "Vary: origin,referer,accept-encoding" \
+              -hdr "Cache-Control: max-age=5" \
+              -hdr "Content-Encoding: gzip" \
+              -bodylen 58
+
+       rxreq
+       expect req.url == "/origin-referer-accept-encoding"
+       txresp -hdr "Vary: origin,referer,accept-encoding" \
+              -hdr "Cache-Control: max-age=5" \
+              -hdr "Content-Encoding: gzip" \
+              -bodylen 59
+
        # Multiple Accept-Encoding headers
        rxreq
        expect req.url == "/multiple_headers"
@@ -315,6 +329,43 @@ client c1 -connect ${h1_fe_sock} {
        expect resp.http.X-Cache-Hit == 1
 
 
+       # Mixed Vary (Accept-Encoding + Referer + Origin)
+       txreq -url "/origin-referer-accept-encoding" \
+               -hdr "Accept-Encoding: br, gzip" \
+               -hdr "Referer: referer" \
+               -hdr "Origin: origin"
+       rxresp
+       expect resp.status == 200
+       expect resp.bodylen == 58
+       expect resp.http.X-Cache-Hit == 0
+
+       txreq -url "/origin-referer-accept-encoding" \
+               -hdr "Accept-Encoding: br, gzip" \
+               -hdr "Referer: referer" \
+               -hdr "Origin: origin"
+       rxresp
+       expect resp.status == 200
+       expect resp.bodylen == 58
+       expect resp.http.X-Cache-Hit == 1
+
+       txreq -url "/origin-referer-accept-encoding" \
+               -hdr "Accept-Encoding: br, gzip" \
+               -hdr "Referer: referer" \
+               -hdr "Origin: other-origin"
+       rxresp
+       expect resp.status == 200
+       expect resp.bodylen == 59
+       expect resp.http.X-Cache-Hit == 0
+
+       txreq -url "/origin-referer-accept-encoding" \
+               -hdr "Accept-Encoding: br, gzip" \
+               -hdr "Referer: referer" \
+               -hdr "Origin: other-origin"
+       rxresp
+       expect resp.status == 200
+       expect resp.bodylen == 59
+       expect resp.http.X-Cache-Hit == 1
+
        # Multiple Accept-encoding headers
        txreq -url "/multiple_headers" \
                -hdr "Accept-Encoding: gzip" \
index b9fd47e..eab3f97 100644 (file)
@@ -93,6 +93,7 @@ struct show_cache_ctx {
 enum vary_header_bit {
        VARY_ACCEPT_ENCODING = (1 << 0),
        VARY_REFERER =         (1 << 1),
+       VARY_ORIGIN =          (1 << 2),
        VARY_LAST  /* should always be last */
 };
 
@@ -143,6 +144,7 @@ static int accept_encoding_bitmap_cmp(const void *ref, const void *new, unsigned
 const struct vary_hashing_information vary_information[] = {
        { IST("accept-encoding"), VARY_ACCEPT_ENCODING, sizeof(uint32_t), &accept_encoding_normalizer, &accept_encoding_bitmap_cmp },
        { IST("referer"), VARY_REFERER, sizeof(uint64_t), &default_normalizer, NULL },
+       { IST("origin"), VARY_ORIGIN, sizeof(uint64_t), &default_normalizer, NULL },
 };
 
 
@@ -2370,7 +2372,7 @@ static int accept_encoding_normalizer(struct htx *htx, struct ist hdr_name,
 #undef ACCEPT_ENCODING_MAX_ENTRIES
 
 /*
- * Normalizer used by default for the Referer header. It only
+ * Normalizer used by default for the Referer and Origin header. It only
  * calculates a hash of the whole value using xxhash algorithm.
  * Only the first occurrence of the header will be taken into account in the
  * hash.