From fefdd299fb17e26033a876b572adc6efa271d5b7 Mon Sep 17 00:00:00 2001 From: Mark Nottingham Date: Wed, 28 Feb 2024 10:35:19 +1100 Subject: [PATCH] Rough in collapsing --- test-engine/client/test.mjs | 14 ++++++++++++-- test-engine/server/handle-test.mjs | 4 ++-- tests/collapse.mjs | 27 +++++++++++++++++++++++++++ tests/index.mjs | 3 ++- 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 tests/collapse.mjs diff --git a/test-engine/client/test.mjs b/test-engine/client/test.mjs index 4180160..ded6504 100644 --- a/test-engine/client/test.mjs +++ b/test-engine/client/test.mjs @@ -24,7 +24,7 @@ export function makeTest (test) { const reqNum = idx + 1 const url = clientUtils.makeTestUrl(uuid, reqConfig) let prevRes - if (i > 0) { + if (i > 0 && !('async' in requests[i - 1])) { prevRes = Object.fromEntries(responses[i - 1].headers) } const init = fetching.init(idx, reqConfig, prevRes) @@ -43,7 +43,8 @@ export function makeTest (test) { clearTimeout(timeout) }) }, - pauseAfter: 'pause_after' in requests[i] + pauseAfter: 'pause_after' in requests[i], + async: 'async' in requests[i] }) } @@ -51,6 +52,10 @@ export function makeTest (test) { function runNextStep () { if (fetchFunctions.length) { const nextFetchFunction = fetchFunctions.shift() + if (nextFetchFunction.async === true) { + nextFetchFunction.code(idx++) + return runNextStep() + } if (nextFetchFunction.pauseAfter === true) { return nextFetchFunction.code(idx++) .then(clientUtils.pause) @@ -77,6 +82,7 @@ export function makeTest (test) { resolve() }) .catch(err => { // fail + throw (err) if (test.id in testResults) throw new Error(`Duplicate test ${test.id}`) testResults[test.id] = [(err.name || 'unknown'), err.message] resolve() @@ -111,6 +117,10 @@ function checkResponse (test, requests, idx, response) { if (reqConfig.expected_type === 'not_cached') { assert(typeSetup, resNum === reqNum, `Response ${reqNum} comes from cache`) } + if (reqConfig.expected_type === 'collapsed') { + assert(typeSetup, resNum === 1, `Request ${reqNum} was not collapsed`) + } + } // check response status diff --git a/test-engine/server/handle-test.mjs b/test-engine/server/handle-test.mjs index 13aae63..09a6daa 100644 --- a/test-engine/server/handle-test.mjs +++ b/test-engine/server/handle-test.mjs @@ -25,7 +25,7 @@ export default function handleTest (pathSegs, request, response) { sendResponse(response, 409, `${requests[0].id} config not found for request ${srvReqNum} (anticipating ${requests.length})`) return } - if (reqConfig.dump) logRequest(request, srvReqNum) + if (reqConfig.dump) logRequest(request, reqNum) // response_pause if ('response_pause' in reqConfig) { @@ -114,5 +114,5 @@ function continueHandleTest (uuid, request, response, requests, serverState) { } // logging - if (reqConfig.dump) logResponse(response, srvReqNum) + if (reqConfig.dump) logResponse(response, reqNum) } diff --git a/tests/collapse.mjs b/tests/collapse.mjs new file mode 100644 index 0000000..315a29f --- /dev/null +++ b/tests/collapse.mjs @@ -0,0 +1,27 @@ +import * as templates from './lib/templates.mjs' +import * as utils from './lib/utils.mjs' + +export default + +{ + name: 'Collapsed Requests', + id: 'collapse', + description: 'These tests check how caches [collapse requests](https://httpwg.org/specs/rfc9111.html#constructing.responses.from.caches).', + tests: [ + { + name: 'Does HTTP cache collapse two requests?', + id: 'collapse-basic', + depends_on: ['freshness-max-age'], + requests: [ + templates.fresh({ + response_pause: true, + async: true, + expected_type: 'collapsed' + }), + { + expected_type: 'collapsed' + } + ] + } + ] +} diff --git a/tests/index.mjs b/tests/index.mjs index 3b7378c..18eeff7 100644 --- a/tests/index.mjs +++ b/tests/index.mjs @@ -20,7 +20,8 @@ import updateHead from './updateHead.mjs' import invalidation from './invalidation.mjs' import partial from './partial.mjs' import auth from './authorization.mjs' +import collapse from './collapse.mjs' import other from './other.mjs' import cdncc from './cdn-cache-control.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, collapse, other, cdncc]