From 62ffbeb3594e3742a53b23bd403f5d953bc20002 Mon Sep 17 00:00:00 2001 From: Daniel Fox Franke Date: Fri, 9 Oct 2026 15:37:00 -0400 Subject: [PATCH 1/2] Add tests for when a 304 changes a vary header --- tests/conditional-lm.mjs | 26 +++++++++++++ tests/vary.mjs | 84 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/tests/conditional-lm.mjs b/tests/conditional-lm.mjs index eae5c2be..6d58568f 100644 --- a/tests/conditional-lm.mjs +++ b/tests/conditional-lm.mjs @@ -114,6 +114,32 @@ export default { setup_tests: ['expected_type'] } ] + }, + { + name: 'An optimal HTTP cache generates an `If-Modified-Since` request when holding a stale response with a matching weak `Last-Modified`', + id: 'conditional-last-modified-weak-generate', + kind: 'optimal', + depends_on: ['freshness-max-age-stale'], + requests: [ + { + response_headers: [ + ['Cache-Control', 'max-age=2'], + ['Date', 0], + ['Last-Modified', 0], + ], + setup: true, + pause_after: true + }, + { + response_headers: [ + ['Cache-Control', 'max-age=2'], + ['Date', 0], + ['Last-Modified', -3], + ], + expected_type: 'lm_validated', + } + ] + } ] } diff --git a/tests/vary.mjs b/tests/vary.mjs index d3daa893..93e0a4cf 100644 --- a/tests/vary.mjs +++ b/tests/vary.mjs @@ -473,6 +473,90 @@ export default { expected_type: 'cached' } ] + }, + { + name: 'HTTP cache must not serve a mismatched stored representation after a 304 response matching on a weak `Last-Modified` timestamp alters the `Vary` header', + id: 'vary-altered-by-304-lm', + kind: 'required', + depends_on: ['conditional-last-modified-weak-generate'], + requests: [ + { + request_headers: [ + //['Accept-Encoding', 'gzip;q=0.5'] + ], + response_headers: [ + ['Cache-Control', 'max-age=2, no-transform'], + ['Date', 0], + ['Last-Modified', 0], + ['Vary', 'Foo'], + ], + response_body: 'abc', + setup: true, + pause_after: true + }, + { + request_headers: [ + ['Accept-Encoding', 'gzip'], + ], + response_headers: [ + ['Cache-Control', 'max-age=2, no-transform'], + ['Date', 0], + ['Last-Modified', -3], + ['Vary', 'Accept-Encoding'], + ['Content-Encoding', 'gzip'] + ], + setup_tests: ['expected_type'], + expected_type: 'lm_validated', + expected_response_headers: [ + ['Vary', 'Accept-Encoding'], + ['Content-Encoding', 'gzip'] + ], + // If `Content-Encoding: gzip` is supplied as expected, Fetch will decode it prior to the + // text comparison. + expected_response_text: 'abc' + } + ] + }, + { + name: 'HTTP cache must not serve a mismatched stored representation after a 304 response matching on a weak `ETag` alters the `Vary` header', + id: 'vary-altered-by-304-etag', + kind: 'required', + depends_on: ['conditional-etag-weak-generate-weak'], + requests: [ + { + request_headers: [ + //['Accept-Encoding', 'gzip;q=0.5'] + ], + response_headers: [ + ['Cache-Control', 'max-age=2, no-transform'], + ['ETag', 'W/"v1"'], + ['Vary', 'Foo'] + ], + response_body: 'abc', + setup: true, + pause_after: true + }, + { + request_headers: [ + ['Accept-Encoding', 'gzip'], + ], + response_headers: [ + ['Cache-Control', 'max-age=2, no-transform'], + ['ETag', 'W/"v1"'], + ['Vary', 'Accept-Encoding'], + ['Content-Encoding', 'gzip'] + ], + setup_tests: ['expected_type'], + expected_type: 'etag_validated', + expected_response_headers: [ + ['Vary', 'Accept-Encoding'], + ['Content-Encoding', 'gzip'] + ], + // If `Content-Encoding: gzip` is supplied as expected, Fetch will decode it prior to the + // text comparison. + expected_response_text: 'abc' + } + ] } ] } From 67c4d827c7f80d65fa000e7a2fa3df7cc344a7a7 Mon Sep 17 00:00:00 2001 From: Daniel Fox Franke Date: Sat, 10 Oct 2026 10:19:33 -0400 Subject: [PATCH 2/2] Report reasons for body-decoding failure This helps with the vary-altered-by-304 tests. If a cache serves a plaintext body with Content-Encoding: gzip, we'll report a comprehensible failure message rather than just "terminated". --- test-engine/client/test.mjs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index da1be01f..f529e382 100644 --- a/test-engine/client/test.mjs +++ b/test-engine/client/test.mjs @@ -228,7 +228,25 @@ function checkResponse (test, requests, idx, response, interimResponses) { `Received ${interimResponses.length} interim response(s), expected ${reqConfig.expected_interim_responses.length}`) } - return response.text().then(makeCheckResponseBody(test, reqConfig, response.status)) + return response.text().then( + makeCheckResponseBody(test, reqConfig, response.status), + err => { + const details = [] + const seen = new Set() + for (let cause = err; cause != null && !seen.has(cause); cause = cause.cause) { + seen.add(cause) + const code = cause.code ? `${cause.code}: ` : '' + details.push(`${code}${cause.message || String(cause)}`) + } + const encoding = response.headers.get('Content-Encoding') + const context = encoding ? ` (Content-Encoding: ${encoding})` : '' + const message = details.length ? details.join('; caused by ') : String(err) + const readError = new Error(`Response ${reqNum} body could not be read${context}: ${message}`) + readError.name = err?.name || 'unknown' + readError.cause = err + throw readError + } + ) } function makeCheckResponseBody (test, reqConfig, statusCode) {