diff --git a/src/proxy/hdrs/MIME.cc b/src/proxy/hdrs/MIME.cc index 9430385d42c..d479c6fe72e 100644 --- a/src/proxy/hdrs/MIME.cc +++ b/src/proxy/hdrs/MIME.cc @@ -3812,9 +3812,13 @@ MIMEHdrImpl::recompute_cooked_stuff(MIMEField *changing_field_or_null, const std csv_value_mask = 0; m_cooked_stuff.m_cache_control.m_mask &= ~mask; } + } else if (token_wks == MIME_VALUE_MAX_STALE.c_str()) { + // RFC 9111, Section 5.2.1.2: a valueless max-stale request + // directive permits a stale response of any age. + m_cooked_stuff.m_cache_control.m_secs_max_stale = INT_MAX; } else { - // No '=' found, or whitespace before '='. This is malformed. - // For directives that require values, this is an error. + // No '=' found, or whitespace before '='. This is malformed + // for directives that require values. // Clear the mask for this directive. csv_value_mask = 0; m_cooked_stuff.m_cache_control.m_mask &= ~mask; diff --git a/src/proxy/hdrs/unit_tests/test_HdrUtils.cc b/src/proxy/hdrs/unit_tests/test_HdrUtils.cc index befd37b5502..c6b60a87bcb 100644 --- a/src/proxy/hdrs/unit_tests/test_HdrUtils.cc +++ b/src/proxy/hdrs/unit_tests/test_HdrUtils.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -426,6 +427,11 @@ TEST_CASE("Cache-Control Valid Cooking", "[proxy][hdrutils]") MIME_COOKED_MASK_CC_MAX_STALE, 0, 0, 100, 0}, + {"max-stale without a value", + "Cache-Control: max-stale\r\n\r\n", + MIME_COOKED_MASK_CC_MAX_STALE, + 0, 0, INT_MAX, 0}, + {"min-fresh=60", "Cache-Control: min-fresh=60\r\n\r\n", MIME_COOKED_MASK_CC_MIN_FRESH, diff --git a/tests/gold_tests/cache/replay/cache-control-basic.replay.yaml b/tests/gold_tests/cache/replay/cache-control-basic.replay.yaml index 02a6264256f..cb6d670e124 100644 --- a/tests/gold_tests/cache/replay/cache-control-basic.replay.yaml +++ b/tests/gold_tests/cache/replay/cache-control-basic.replay.yaml @@ -19,7 +19,7 @@ meta: # Configuration section for autest integration autest: - description: 'Test basic cache operations: miss, hit, no-cache-control, stale, and only-if-cached' + description: 'Test basic cache operations: miss, hit, no-cache-control, stale, only-if-cached, and max-stale' dns: name: 'dns-cache-basic' @@ -221,3 +221,74 @@ sessions: fields: - [X-Cache, { value: "miss", as: equal }] - [Cache-Control, { value: "no-store", as: equal }] + + ############################################################################# + # Test 6: Populate an entry for a valueless max-stale request + ############################################################################# + - client-request: + method: GET + url: /max-stale + version: '1.1' + headers: + fields: + - [Host, www.example.com] + - [x-debug, "x-cache,x-cache-key,via"] + - [uuid, max-stale-populate] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Type, text/plain] + - [Content-Length, "5"] + # max-age=0 makes the cached response stale immediately. + - [Cache-Control, "max-age=0"] + content: + encoding: plain + data: stale + + proxy-response: + status: 200 + headers: + fields: + - [X-Cache, { value: "miss", as: equal }] + content: + encoding: plain + data: stale + verify: { as: equal } + + ############################################################################# + # Test 7: Valueless max-stale permits serving a stale response of any age + ############################################################################# + - client-request: + # Allow the cache write from the preceding transaction to finish. + delay: 100ms + method: GET + url: /max-stale + version: '1.1' + headers: + fields: + - [Host, www.example.com] + - [Cache-Control, "max-stale"] + - [x-debug, "x-cache,x-cache-key,via"] + - [uuid, max-stale-serve] + + proxy-request: + expect: absent + + # The server response is a sentinel and must not be used. + server-response: + status: 502 + reason: Bad Gateway + + proxy-response: + status: 200 + headers: + fields: + # ATS reports an acceptable-stale lookup as a fresh cache hit. + - [X-Cache, { value: "hit-fresh", as: equal }] + content: + encoding: plain + data: stale + verify: { as: equal }