diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index da1be01..f529e38 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) { diff --git a/tests/conditional-lm.mjs b/tests/conditional-lm.mjs index eae5c2b..6d58568 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 d3daa89..93e0a4c 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' + } + ] } ] }