From aca2b85b34c4aa9bdb9cc02fdfe5429caece59a4 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 5 Mar 2025 23:53:11 +0100 Subject: [PATCH 01/11] Add tests for interim response handling --- docker/nginx/nginx.conf | 1 + package.json | 3 +- test-engine/client/test.mjs | 44 +++++++++++++++++++++- test-engine/client/utils.mjs | 43 ++++++++++++++++++++++ test-engine/server/handle-test.mjs | 11 ++++++ tests/index.mjs | 3 +- tests/interim.mjs | 59 ++++++++++++++++++++++++++++++ 7 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 tests/interim.mjs diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index bb4a16c9..74fb6c73 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -9,5 +9,6 @@ server { proxy_pass http://localhost:8000; proxy_cache my-cache; proxy_cache_revalidate on; + proxy_http_version 1.1; } } diff --git a/package.json b/package.json index aa3b8aa5..92bd8aee 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "dependencies": { "liquidjs": "^10.9.2", "marked": "^15.0.0", - "npm": "^11.0.0" + "npm": "^11.0.0", + "undici": "^7.4.0" }, "scripts": { "server": "node test-engine/server/server.mjs", diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index 56487a11..5a129b30 100644 --- a/test-engine/client/test.mjs +++ b/test-engine/client/test.mjs @@ -4,6 +4,7 @@ import * as utils from '../lib/utils.mjs' import * as config from './config.mjs' import * as clientUtils from './utils.mjs' import * as fetching from './fetching.mjs' +import { getGlobalDispatcher } from 'undici' const assert = utils.assert const setupCheck = clientUtils.setupCheck @@ -32,11 +33,19 @@ export async function makeTest (test) { controller.abort() }, config.requestTimeout * 1000) init.signal = controller.signal + + const interimResponses = [] + if ('expected_interim_responses' in reqConfig) { + const globalDispatcher = getGlobalDispatcher() + const dispatcher = globalDispatcher.compose(clientUtils.interimResponsesCollectingInterceptor(interimResponses)) + init.dispatcher = dispatcher + } + if (test.dump === true) clientUtils.logRequest(url, init, reqNum) return fetch(url, init) .then(response => { responses.push(response) - return checkResponse(test, requests, idx, response) + return checkResponse(test, requests, idx, response, interimResponses) }) .finally(() => { clearTimeout(timeout) @@ -84,7 +93,7 @@ export async function makeTest (test) { }) } -function checkResponse (test, requests, idx, response) { +function checkResponse (test, requests, idx, response, interimResponses) { const reqNum = idx + 1 const reqConfig = requests[idx] const resNum = parseInt(response.headers.get('Server-Request-Count')) @@ -185,6 +194,37 @@ function checkResponse (test, requests, idx, response) { } }) } + + // check interim responses + if ('expected_interim_responses' in reqConfig) { + const isSetup = setupCheck(reqConfig, 'expected_interim_responses') + + reqConfig.expected_interim_responses.forEach(([statusCode, headers = []], idx) => { + if (interimResponses[idx] == null) { + assert(isSetup, false, `Interim response ${idx + 1} not received`) + } else { + assert(isSetup, interimResponses[idx][0] === statusCode, `Interim response ${idx + 1} status is ${interimResponses[idx][0]}, not ${statusCode}`) + + const receivedHeaders = interimResponses[idx][1] + headers.forEach(([header, value]) => { + if (typeof header === 'string') { + assert(isSetup, header in receivedHeaders, + `Interim response ${idx + 1} ${header} header not present.`) + } else if (header.length > 2) { + assert(isSetup, header in receivedHeaders, + `Interim response ${idx + 1} ${header} header not present.`) + + const receivedValue = receivedHeaders[header] + assert(isSetup, value === receivedValue, + `Interim response ${idx + 1} header ${header} is ${receivedValue}, should ${value}`) + } else { + console.log('ERROR: Unknown header item in expected_interim_responses', header) + } + }) + } + }) + } + return response.text().then(makeCheckResponseBody(test, reqConfig, response.status)) } diff --git a/test-engine/client/utils.mjs b/test-engine/client/utils.mjs index 21d918d1..772a7dc9 100644 --- a/test-engine/client/utils.mjs +++ b/test-engine/client/utils.mjs @@ -80,3 +80,46 @@ export function logResponse (response, reqNum) { }) console.log('') } + +class InterimResponsesCollectingHandler { + #handler + #interimResponses + + constructor (handler, interimResponses) { + this.#handler = handler + this.#interimResponses = interimResponses + } + + onRequestStart (controller, context) { + this.#handler.onRequestStart?.(controller, context) + } + + onRequestUpgrade (controller, statusCode, headers, socket) { + this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket) + } + + onResponseStart (controller, statusCode, headers, statusMessage) { + if (statusCode < 200) this.#interimResponses.push([statusCode, headers]) + this.#handler.onResponseStart?.(controller, statusCode, headers, statusMessage) + } + + onResponseData (controller, data) { + this.#handler.onResponseData?.(controller, data) + } + + onResponseEnd (controller, trailers) { + this.#handler.onResponseEnd?.(controller, trailers) + } + + onResponseError (controller, err) { + this.#handler.onResponseError?.(controller, err) + } +} + +export function interimResponsesCollectingInterceptor (collectInto) { + return (dispatch) => { + return (opts, handler) => { + return dispatch(opts, new InterimResponsesCollectingHandler(handler, collectInto)) + } + } +} diff --git a/test-engine/server/handle-test.mjs b/test-engine/server/handle-test.mjs index 13aae63b..b2f6541e 100644 --- a/test-engine/server/handle-test.mjs +++ b/test-engine/server/handle-test.mjs @@ -43,6 +43,17 @@ function continueHandleTest (uuid, request, response, requests, serverState) { const previousConfig = requests[reqNum - 2] const now = Date.now() + const interimResponses = reqConfig.interim_responses || [] + for (const [status, headers = []] of interimResponses) { + if (status === 102) { + response.writeProcessing() + } else if (status === 103) { + response.writeEarlyHints(Object.fromEntries(headers)) + } else { + console.log(`ERROR: Sending ${status} is not yet supported`) + } + } + // Determine what the response status should be let httpStatus = reqConfig.response_status || [200, 'OK'] if ('expected_type' in reqConfig && reqConfig.expected_type.endsWith('validated')) { diff --git a/tests/index.mjs b/tests/index.mjs index 3b7378c1..1a79eff4 100644 --- a/tests/index.mjs +++ b/tests/index.mjs @@ -22,5 +22,6 @@ import partial from './partial.mjs' import auth from './authorization.mjs' import other from './other.mjs' import cdncc from './cdn-cache-control.mjs' +import interim from './interim.mjs' -export default [ccFreshness, ccParse, ageParse, expires, expiresParse, ccResponse, stale, heuristic, methods, statuses, ccRequest, pragma, vary, varyParse, conditionalLm, conditionalEtag, headers, update304, updateHead, invalidation, partial, auth, other, cdncc] +export default [ccFreshness, ccParse, ageParse, expires, expiresParse, ccResponse, stale, heuristic, methods, statuses, ccRequest, pragma, vary, varyParse, conditionalLm, conditionalEtag, headers, update304, updateHead, invalidation, partial, auth, other, cdncc, interim] diff --git a/tests/interim.mjs b/tests/interim.mjs new file mode 100644 index 00000000..174b71ca --- /dev/null +++ b/tests/interim.mjs @@ -0,0 +1,59 @@ +export default + +{ + name: 'Interim Response Handling', + id: 'interim', + description: 'These tests check how caches handle interim responses.', + tests: [ + { + name: 'An optimal HTTP cache passes a 102 response through and caches the final response', + id: 'interim-102', + browser_skip: true, // Fetch API in browsers don't expose interim responses + kind: 'optimal', + requests: [ + { + interim_responses: [[102]], + expected_interim_responses: [[102]], + response_headers: [ + ['Cache-Control', 'max-age=100000'], + ['Date', 0] + ], + pause_after: true + }, + { + expected_type: 'cached' + } + ] + }, + { + name: 'An optimal HTTP cache passes a 103 response through and caches the final response', + id: 'interim-103', + browser_skip: true, + kind: 'optimal', + requests: [ + { + interim_responses: [ + [103, [ + ['link', '; rel=preload; as=style'], + ['x-my-header', 'test'] + ]] + ], + expected_interim_responses: [ + [103, [ + ['link', '; rel=preload; as=style'], + ['x-my-header', 'test'] + ]] + ], + response_headers: [ + ['Cache-Control', 'max-age=100000'], + ['Date', 0] + ], + pause_after: true + }, + { + expected_type: 'cached' + } + ] + } + ] +} From b0eeee9d720e68f9bcedddfa4352d3f3d7154b97 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Thu, 6 Mar 2025 00:02:50 +0100 Subject: [PATCH 02/11] Add test results --- results/apache.json | 2 ++ results/caddy.json | 8 ++++++++ results/nginx.json | 8 ++++++++ results/squid.json | 2 ++ results/trafficserver.json | 8 ++++++++ results/varnish.json | 8 ++++++++ 6 files changed, 36 insertions(+) diff --git a/results/apache.json b/results/apache.json index 86c732f6..c323acc7 100644 --- a/results/apache.json +++ b/results/apache.json @@ -423,6 +423,8 @@ "heuristic-delta-60": true, "heuristic-delta-600": true, "heuristic-delta-86400": true, + "interim-102": true, + "interim-103": true, "invalidate-DELETE": true, "invalidate-DELETE-cl": [ "Assertion", diff --git a/results/caddy.json b/results/caddy.json index d45ef15d..68282425 100644 --- a/results/caddy.json +++ b/results/caddy.json @@ -537,6 +537,14 @@ "Assertion", "Response 2 does not come from cache" ], + "interim-102": [ + "Assertion", + "Interim response 1 not received" + ], + "interim-103": [ + "Assertion", + "Interim response 1 not received" + ], "invalidate-DELETE": true, "invalidate-DELETE-cl": [ "Assertion", diff --git a/results/nginx.json b/results/nginx.json index 1f12ac99..9adacac1 100644 --- a/results/nginx.json +++ b/results/nginx.json @@ -564,6 +564,14 @@ "Assertion", "Response 2 does not come from cache" ], + "interim-102": [ + "Assertion", + "Response 2 does not come from cache" + ], + "interim-103": [ + "Assertion", + "Response 2 does not come from cache" + ], "invalidate-DELETE": [ "Assertion", "Response 3 comes from cache" diff --git a/results/squid.json b/results/squid.json index 906f12a9..29f16d1e 100644 --- a/results/squid.json +++ b/results/squid.json @@ -438,6 +438,8 @@ "heuristic-delta-60": true, "heuristic-delta-600": true, "heuristic-delta-86400": true, + "interim-102": true, + "interim-103": true, "invalidate-DELETE": true, "invalidate-DELETE-cl": true, "invalidate-DELETE-failed": true, diff --git a/results/trafficserver.json b/results/trafficserver.json index c2112d1a..3924f6f8 100644 --- a/results/trafficserver.json +++ b/results/trafficserver.json @@ -456,6 +456,14 @@ "Assertion", "Response 2 does not come from cache" ], + "interim-102": [ + "AbortError", + "This operation was aborted" + ], + "interim-103": [ + "Assertion", + "Interim response 1 not received" + ], "invalidate-DELETE": [ "Setup", "Response 2 status is 403, not 200" diff --git a/results/varnish.json b/results/varnish.json index 067c395f..42cdc85d 100644 --- a/results/varnish.json +++ b/results/varnish.json @@ -498,6 +498,14 @@ "Assertion", "Response 2 does not come from cache" ], + "interim-102": [ + "Setup", + "Response 1 status is 503, not 200" + ], + "interim-103": [ + "Setup", + "Response 1 status is 503, not 200" + ], "invalidate-DELETE": [ "Assertion", "Response 3 comes from cache" From 6a95ba9266d47a15dd63949644ed1af5abef7b3a Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 12:32:26 +0100 Subject: [PATCH 03/11] Add tests for https://github.com/http-tests/cache-tests/issues/78 --- tests/interim.mjs | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/interim.mjs b/tests/interim.mjs index 174b71ca..4ffb3139 100644 --- a/tests/interim.mjs +++ b/tests/interim.mjs @@ -54,6 +54,71 @@ export default expected_type: 'cached' } ] + }, + { + name: 'An HTTP cache should not cache non-final responses', + id: 'interim-not-cached', + browser_skip: true, + kind: 'required', + requests: [ + { + interim_responses: [ + [103, [ + ['link', '; rel=preload; as=style'] + ]] + ], + expected_interim_responses: [ + [103, [ + ['link', '; rel=preload; as=style'] + ]] + ], + response_headers: [ + ['Cache-Control', 'max-age=100000'], + ['Date', 0] + ], + pause_after: true + }, + { + expected_type: 'cached', + expected_interim_responses: [] + } + ] + }, + { + name: 'An optimal HTTP cache should not store headers from non-final responses', + id: 'interim-no-header-reuse', + browser_skip: true, + kind: 'optimal', + requests: [ + { + interim_responses: [ + [103, [ + ['link', '; rel=preload; as=style'], + ['x-my-header', 'test'] + ]] + ], + expected_interim_responses: [ + [103, [ + ['link', '; rel=preload; as=style'], + ['x-my-header', 'test'] + ]] + ], + response_headers: [ + ['Cache-Control', 'max-age=100000'], + ['Date', 0] + ], + expected_response_headers_missing: [ + 'x-my-header' + ], + pause_after: true + }, + { + expected_type: 'cached', + expected_response_headers_missing: [ + 'x-my-header' + ] + } + ] } ] } From 605acef0decef12036ce62ecc910157f90f6a0e0 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 12:42:48 +0100 Subject: [PATCH 04/11] Log interim responses --- test-engine/client/test.mjs | 2 +- test-engine/client/utils.mjs | 9 ++++++++- test-engine/server/handle-test.mjs | 2 +- test-engine/server/utils.mjs | 9 ++++++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index 5a129b30..2cbe4be1 100644 --- a/test-engine/client/test.mjs +++ b/test-engine/client/test.mjs @@ -97,7 +97,7 @@ function checkResponse (test, requests, idx, response, interimResponses) { const reqNum = idx + 1 const reqConfig = requests[idx] const resNum = parseInt(response.headers.get('Server-Request-Count')) - if (test.dump === true) clientUtils.logResponse(response, reqNum) + if (test.dump === true) clientUtils.logResponse(response, interimResponses, reqNum) // catch retries if (response.headers.has('Request-Numbers')) { diff --git a/test-engine/client/utils.mjs b/test-engine/client/utils.mjs index 772a7dc9..a23f3e9f 100644 --- a/test-engine/client/utils.mjs +++ b/test-engine/client/utils.mjs @@ -72,8 +72,15 @@ export function logRequest (url, init, reqNum) { console.log('') } -export function logResponse (response, reqNum) { +export function logResponse (response, interimResponses, reqNum) { console.log(`${defines.GREEN}=== Client response ${reqNum}${defines.NC}`) + for (const [statusCode, headers] of interimResponses) { + console.log(` HTTP ${statusCode}`) + for (const [key, value] of Object.entries(headers)) { + console.log(` ${key}: ${value}`) + } + console.log('') + } console.log(` HTTP ${response.status} ${response.statusText}`) response.headers.forEach((hvalue, hname) => { // for some reason, node-fetch reverses these console.log(` ${hname}: ${hvalue}`) diff --git a/test-engine/server/handle-test.mjs b/test-engine/server/handle-test.mjs index b2f6541e..80802046 100644 --- a/test-engine/server/handle-test.mjs +++ b/test-engine/server/handle-test.mjs @@ -125,5 +125,5 @@ function continueHandleTest (uuid, request, response, requests, serverState) { } // logging - if (reqConfig.dump) logResponse(response, srvReqNum) + if (reqConfig.dump) logResponse(response, interimResponses, srvReqNum) } diff --git a/test-engine/server/utils.mjs b/test-engine/server/utils.mjs index 7f37a665..ce496a86 100644 --- a/test-engine/server/utils.mjs +++ b/test-engine/server/utils.mjs @@ -40,11 +40,18 @@ export function logRequest (request, reqNum) { console.log('') } -export function logResponse (response, resNum) { +export function logResponse (response, interimResponses, resNum) { console.log(`${BLUE}=== Server response ${resNum}${NC}`) if (response === 'disconnect') { console.log(' [ server disconnect ]') } else { + for (const [statusCode, headers] of interimResponses) { + console.log(` HTTP ${statusCode}`) + for (const [key, value] of headers) { + console.log(` ${key}: ${value}`) + } + console.log('') + } console.log(` HTTP ${response.statusCode} ${response.statusPhrase}`) for (const [key, value] of Object.entries(response.getHeaders())) { console.log(` ${key}: ${value}`) From 1ad5f70e14f8c96af0880e0ee43e63aa96ecebdf Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 12:53:30 +0100 Subject: [PATCH 05/11] Fix browser tests Undici is only available in Node.js, so don't require it by default in browsers --- test-engine/client/test.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index 2cbe4be1..226614dc 100644 --- a/test-engine/client/test.mjs +++ b/test-engine/client/test.mjs @@ -4,7 +4,6 @@ import * as utils from '../lib/utils.mjs' import * as config from './config.mjs' import * as clientUtils from './utils.mjs' import * as fetching from './fetching.mjs' -import { getGlobalDispatcher } from 'undici' const assert = utils.assert const setupCheck = clientUtils.setupCheck @@ -19,7 +18,7 @@ export async function makeTest (test) { const fetchFunctions = [] for (let i = 0; i < requests.length; ++i) { fetchFunctions.push({ - code: idx => { + code: async idx => { const reqConfig = requests[idx] const reqNum = idx + 1 const url = clientUtils.makeTestUrl(uuid, reqConfig) @@ -36,7 +35,9 @@ export async function makeTest (test) { const interimResponses = [] if ('expected_interim_responses' in reqConfig) { - const globalDispatcher = getGlobalDispatcher() + // Dynamic import since undici is only available in Node.js + const undici = await import('undici') + const globalDispatcher = undici.getGlobalDispatcher() const dispatcher = globalDispatcher.compose(clientUtils.interimResponsesCollectingInterceptor(interimResponses)) init.dispatcher = dispatcher } From 162752771ab43c3a7e5612e553157417e199d643 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 13:09:44 +0100 Subject: [PATCH 06/11] Update schema --- test-engine/lib/testsuite-schema.json | 96 +++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/test-engine/lib/testsuite-schema.json b/test-engine/lib/testsuite-schema.json index 95294ff8..1890c8da 100644 --- a/test-engine/lib/testsuite-schema.json +++ b/test-engine/lib/testsuite-schema.json @@ -133,6 +133,102 @@ "description": "Whether to rewrite Location and Content-Location to full URLs", "type": "boolean" }, + "interim_responses": { + "description": "Interim responses to send before the final response", + "type": "array", + "items": { + "oneOf": [ + { + "description": "Status code only", + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": [ + { + "$ref": "#/definitions/status-code" + } + ] + }, + { + "description": "Status code and headers", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": [ + { + "$ref": "#/definitions/status-code" + }, + { + "description": "Interim response headers", + "type": "array", + "items": { + "type": "array", + "additionalItems": false, + "minItems": 2, + "maxItems": 2, + "items": [ + { + "$ref": "#/definitions/field-name" + }, + { + "$ref": "#/definitions/magic-field-value" + } + ] + } + } + ] + } + ] + } + }, + "expected_interim_responses": { + "description": "Interim responses expected to be received by the client", + "type": "array", + "items": { + "oneOf": [ + { + "description": "Status code only", + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": [ + { + "$ref": "#/definitions/status-code" + } + ] + }, + { + "description": "Status code and headers", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": [ + { + "$ref": "#/definitions/status-code" + }, + { + "description": "Expected interim response headers", + "type": "array", + "items": { + "type": "array", + "additionalItems": false, + "minItems": 2, + "maxItems": 2, + "items": [ + { + "$ref": "#/definitions/field-name" + }, + { + "$ref": "#/definitions/magic-field-value" + } + ] + } + } + ] + } + ] + } + }, "magic_ims": { "description": "Whether to rewrite If-Modified-Since to a delta from the previous Last-Modified", "type": "boolean" From 5903028256fe1c806defb371fb2ee2d63044c70d Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 13:14:05 +0100 Subject: [PATCH 07/11] Document new directives --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4a65c40..7b1a828b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,6 +72,9 @@ Possible members of a request object: `false`. - `response_body` - String to send as the response body from the origin. Defaults to the test identifier. +- `interim_responses` - An array of interim responses to send before the final response. Each item can be either: + - `[status_code]` - Just a status code (e.g., `[102]`) + - `[status_code, headers_array]` - Status code and headers, where headers_array is an array of `[header_name, header_value]` pairs - `response_pause` - Integer number of seconds for the server to pause before generating a response. - `check_body` - Whether to check the response body. Default `true`. - `expected_type` - One of: @@ -98,6 +101,7 @@ Possible members of a request object: - `header_name_string` representing headers to check that the response on the client does not include. - `[header_name_string, header_value_string]`: headers to check that the response is either missing, or if they're present, that they do _not_ contain the given value string (evaluated against the whole header value). - `expected_response_text` - A string to check the response body against on the client. +- `expected_interim_responses` - An array of interim responses expected to be received by the client. Format is the same as `interim_responses`. - `setup` - Boolean to indicate whether this is a setup request; failures don't mean the actual test failed. - `setup_tests` - Array of values that indicate whether the specified check is part of setup; failures don't mean the actual test failed. One of: `["expected_type", "expected_method", "expected_status", "expected_response_headers", "expected_response_text", "expected_request_headers"]` From 564591cf41796c43e0361852cde33f0a623d65ef Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 13:40:35 +0100 Subject: [PATCH 08/11] fixup! Fix browser tests Avoid interoperability issues with undici from userland and Node.js as in https://github.com/nodejs/undici/issues/3973#issuecomment-2564674529. --- test-engine/client/test.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index 226614dc..fb08d807 100644 --- a/test-engine/client/test.mjs +++ b/test-engine/client/test.mjs @@ -37,8 +37,7 @@ export async function makeTest (test) { if ('expected_interim_responses' in reqConfig) { // Dynamic import since undici is only available in Node.js const undici = await import('undici') - const globalDispatcher = undici.getGlobalDispatcher() - const dispatcher = globalDispatcher.compose(clientUtils.interimResponsesCollectingInterceptor(interimResponses)) + const dispatcher = new undici.Agent().compose(clientUtils.interimResponsesCollectingInterceptor(interimResponses)) init.dispatcher = dispatcher } From 42892d38761dc8c678f9820bc2f516dfe2fb5645 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 12 Mar 2025 13:46:47 +0100 Subject: [PATCH 09/11] Update test results --- results/apache.json | 2 ++ results/caddy.json | 8 ++++++++ results/nginx.json | 8 ++++++++ results/squid.json | 2 ++ results/trafficserver.json | 8 ++++++++ results/varnish.json | 8 ++++++++ 6 files changed, 36 insertions(+) diff --git a/results/apache.json b/results/apache.json index c323acc7..5da2d1df 100644 --- a/results/apache.json +++ b/results/apache.json @@ -425,6 +425,8 @@ "heuristic-delta-86400": true, "interim-102": true, "interim-103": true, + "interim-no-header-reuse": true, + "interim-not-cached": true, "invalidate-DELETE": true, "invalidate-DELETE-cl": [ "Assertion", diff --git a/results/caddy.json b/results/caddy.json index 68282425..b9bcb867 100644 --- a/results/caddy.json +++ b/results/caddy.json @@ -545,6 +545,14 @@ "Assertion", "Interim response 1 not received" ], + "interim-no-header-reuse": [ + "Assertion", + "Interim response 1 not received" + ], + "interim-not-cached": [ + "Assertion", + "Interim response 1 not received" + ], "invalidate-DELETE": true, "invalidate-DELETE-cl": [ "Assertion", diff --git a/results/nginx.json b/results/nginx.json index 9adacac1..c3dd75bf 100644 --- a/results/nginx.json +++ b/results/nginx.json @@ -569,6 +569,14 @@ "Response 2 does not come from cache" ], "interim-103": [ + "AbortError", + "This operation was aborted" + ], + "interim-no-header-reuse": [ + "Assertion", + "Response 2 does not come from cache" + ], + "interim-not-cached": [ "Assertion", "Response 2 does not come from cache" ], diff --git a/results/squid.json b/results/squid.json index 29f16d1e..1872efc9 100644 --- a/results/squid.json +++ b/results/squid.json @@ -440,6 +440,8 @@ "heuristic-delta-86400": true, "interim-102": true, "interim-103": true, + "interim-no-header-reuse": true, + "interim-not-cached": true, "invalidate-DELETE": true, "invalidate-DELETE-cl": true, "invalidate-DELETE-failed": true, diff --git a/results/trafficserver.json b/results/trafficserver.json index 3924f6f8..93e49f9f 100644 --- a/results/trafficserver.json +++ b/results/trafficserver.json @@ -464,6 +464,14 @@ "Assertion", "Interim response 1 not received" ], + "interim-no-header-reuse": [ + "Assertion", + "Interim response 1 not received" + ], + "interim-not-cached": [ + "Assertion", + "Interim response 1 not received" + ], "invalidate-DELETE": [ "Setup", "Response 2 status is 403, not 200" diff --git a/results/varnish.json b/results/varnish.json index 42cdc85d..75faf830 100644 --- a/results/varnish.json +++ b/results/varnish.json @@ -506,6 +506,14 @@ "Setup", "Response 1 status is 503, not 200" ], + "interim-no-header-reuse": [ + "Setup", + "Response 1 status is 503, not 200" + ], + "interim-not-cached": [ + "Setup", + "Response 1 status is 503, not 200" + ], "invalidate-DELETE": [ "Assertion", "Response 3 comes from cache" From 10f2e1235aa2d57b04849a1258778577e3d497b5 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Thu, 13 Mar 2025 08:35:41 +0100 Subject: [PATCH 10/11] Add results for HAProxy --- results/haproxy.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/results/haproxy.json b/results/haproxy.json index 51fdd058..9b30c981 100644 --- a/results/haproxy.json +++ b/results/haproxy.json @@ -522,6 +522,10 @@ "heuristic-delta-60": true, "heuristic-delta-600": true, "heuristic-delta-86400": true, + "interim-102": true, + "interim-103": true, + "interim-no-header-reuse": true, + "interim-not-cached": true, "invalidate-DELETE": true, "invalidate-DELETE-cl": [ "Assertion", From 1b31a0dcf1df0f68690bdd7c0457caa8b4b81bf3 Mon Sep 17 00:00:00 2001 From: Marius Kleidl Date: Wed, 2 Apr 2025 15:03:21 +0200 Subject: [PATCH 11/11] Show interim responses in tooltip --- test-engine/lib/tpl/checks.liquid | 7 +++++++ test-engine/lib/tpl/explain-test.liquid | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/test-engine/lib/tpl/checks.liquid b/test-engine/lib/tpl/checks.liquid index 730290f5..0193a063 100644 --- a/test-engine/lib/tpl/checks.liquid +++ b/test-engine/lib/tpl/checks.liquid @@ -7,6 +7,13 @@ ### The following checks will be performed: +{%- if request.interim_responses -%} +{%- for interim_response in request.interim_responses %} +- The client will check that an interim response with the `{{ interim_response[0] }}` status code{% if interim_response[1] %} and the following headers{% endif %} is received. +{%- render 'header-list' with interim_response[1] as headers %} +{%- endfor -%} +{%- endif -%} + {%- if request.expected_type %} - The client will check that this response {% case request.expected_type %}{% when "cached" %}is cached{% when "not_cached" %}is not cached{% when "lm_validated" %}is validated using `Last-Modified`{% when "etag_validated" %}is validated using `ETag`{% endcase %} {% if test.setup_tests contains "expected_type" %}{{ setup_prop }}{% endif %}{% endif -%} diff --git a/test-engine/lib/tpl/explain-test.liquid b/test-engine/lib/tpl/explain-test.liquid index 975a6cee..c791ab02 100644 --- a/test-engine/lib/tpl/explain-test.liquid +++ b/test-engine/lib/tpl/explain-test.liquid @@ -62,8 +62,16 @@ The server will pause for {{ request.response_pause }} seconds before responding {%- if request.response_status or request.response_headers or request.response_body %} -### The server sends a response containing: +### The server sends {% if request.interim_responses %}responses{% else %}a response{% endif %} containing: + ~~~ +{% if request.interim_responses -%} +{% for interim_response in request.interim_responses -%} +HTTP/1.1 {{ interim_response[0] }} +{% for header in interim_response[1] %}{% render 'header-magic' with header as header %} +{% endfor %} +{% endfor -%} +{%- endif -%} {% if request.expected_type == "lm_validated" or request.expected_type = "etag_validated" -%} HTTP/1.1 304 Not Modified {%- else -%}