Something went wrong. Try again.
An adversarial testing framework for OCaml HTTP/1.1 clients and servers
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672open Conformance_typesopen Conformance_rules
let cache_cases = [ hit ~id:"fresh-max-age" ~title:"Fresh stored response is reused" ~tags:[ "freshness" ] ~references:[ rfc 9111 "4"; rfc 9111 "5.2.2.1" ] ~stored:[ ("Cache-Control", "max-age=3600") ] (); hit ~id:"expires-fresh" ~title:"Future Expires makes a response fresh" ~tags:[ "freshness" ] ~references:[ rfc 9111 "4.2.1"; rfc 9111 "5.3" ] ~stored:[ ("Expires", "Thu, 31 Dec 2099 23:59:59 GMT") ] (); miss ~id:"expires-invalid" ~title:"Invalid Expires is already stale" ~tags:[ "freshness"; "expires"; "invalid" ] ~references:[ rfc 9111 "5.3" ] ~stored:[ ("Expires", "0") ] (); cache_resource_twice ~id:"max-age-overrides-expires" ~title:"Max-age overrides an earlier Expires date" ~tags:[ "freshness"; "precedence" ] ~references:[ rfc 9111 "4.2.1" ] [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); ] (Fixed "stored"); unexpected_network_response; ] [ "stored"; "stored" ]; (let id = "max-age-zero-revalidate" in let target = cache_path id "resource" and probe = cache_path id "probe" in let names = [ "if-none-match" ] in cache_workflow ~id ~title:"Zero max-age response is revalidated" ~tags:[ "freshness"; "revalidation"; "etag" ] ~references:[ rfc 9111 "4.3" ] [ request target; request target; request probe ] [ varying_endpoint target [ response ~headers:[ ("Cache-Control", "max-age=0"); ("ETag", "\"v1\"") ] (Fixed "stored"); response ~status:304 ~headers:[ ("ETag", "\"v1\"") ] (Fixed ""); ]; echo_stored_request probe target names; ] [ "stored"; "stored"; expected_trace ~attempt:2 ~headers:[ ("If-None-Match", "\"v1\"") ] ~names target; ]); cache_resource_twice ~id:"age-reduces-freshness" ~title:"Age reduces the remaining freshness lifetime" ~tags:[ "freshness"; "age" ] ~references:[ rfc 9111 "4.2.3"; rfc 9111 "5.1" ] [ response ~headers:[ ("Cache-Control", "max-age=60"); ("Age", "3600") ] (Fixed "first"); response (Fixed "second"); ] [ "first"; "second" ]; cache_resource_twice ~id:"no-store" ~title:"No-store response is not reused" ~tags:[ "storage" ] ~references:[ rfc 9111 "5.2.2.5" ] [ response ~headers:[ ("Cache-Control", "no-store") ] (Fixed "first"); response (Fixed "second"); ] [ "first"; "second" ]; cache_resource_twice ~id:"must-revalidate-stale" ~title:"Must-revalidate does not permit stale reuse" ~tags:[ "revalidation"; "stale"; "error" ] ~references:[ rfc 9111 "5.2.2.2" ] [ response ~headers:[ ("Cache-Control", "max-age=0, must-revalidate") ] (Fixed "stored"); response ~status:500 (Fixed "origin-error"); ] [ "stored"; "origin-error" ]; cache_resource_twice ~requirement:Limit ~id:"stale-while-revalidate" ~title:"Stale-while-revalidate permits immediate stale reuse" ~tags:[ "stale"; "extension" ] ~references:[ rfc 5861 "3" ] ~rationale: "This capability case records whether the cache serves the stale \ response while it revalidates in the background." [ response ~headers:[ ("Cache-Control", "max-age=0, stale-while-revalidate=60") ] (Fixed "stored"); response (Fixed "network"); ] [ "stored"; "stored" ]; cache_resource_twice ~requirement:Limit ~id:"stale-if-error" ~title:"Stale-if-error permits stale reuse after an origin error" ~tags:[ "stale"; "extension"; "error" ] ~references:[ rfc 5861 "4" ] ~rationale: "This capability case records whether the cache substitutes a stale \ response for the origin's 503 response." [ response ~headers:[ ("Cache-Control", "max-age=0, stale-if-error=60") ] (Fixed "stored"); response ~status:503 (Fixed "origin-error"); ] [ "stored"; "stored" ]; cache_resource_twice ~requirement:Should ~id:"immutable-no-revalidate" ~title:"Immutable fresh response is reused on a refresh request" ~tags:[ "freshness"; "immutable" ] ~references:[ rfc 8246 "2" ] ~rationale: "A request with max-age=0 models a user-agent refresh. The immutable \ response remains fresh and ought not be conditionally revalidated." ~second_request:(fun target -> request ~headers:[ ("Cache-Control", "max-age=0") ] target) [ response ~headers:[ ("Cache-Control", "max-age=3600, immutable") ] (Fixed "stored"); unexpected_network_response; ] [ "stored"; "stored" ]; cache_resource_twice ~requirement:Limit ~id:"heuristic-freshness" ~title:"Last-Modified permits heuristic freshness" ~tags:[ "freshness"; "heuristic" ] ~references:[ rfc 9111 "4.2.2" ] ~rationale: "This capability case records use of a heuristic lifetime for a \ response without explicit freshness information." [ response ~headers:[ ("Last-Modified", "Wed, 01 Jan 2020 00:00:00 GMT") ] (Fixed "stored"); response (Fixed "network"); ] [ "stored"; "stored" ]; cache_resource_twice ~requirement:May ~id:"pragma-ignored" ~title:"Pragma has no response cache semantics" ~tags:[ "freshness"; "pragma"; "deprecated" ] ~references:[ rfc 9111 "5.4" ] [ response ~headers:[ ("Cache-Control", "max-age=3600"); ("Pragma", "no-cache") ] (Fixed "stored"); unexpected_network_response; ] [ "stored"; "stored" ]; cache_resource_twice ~id:"vary-star" ~title:"Vary star never matches" ~tags:[ "vary" ] ~references:[ rfc 9111 "4.1" ] [ response ~headers:[ ("Cache-Control", "max-age=3600"); ("Vary", "*") ] (Fixed "first"); response (Fixed "second"); ] [ "first"; "second" ]; cache_resource_twice ~id:"vary-absent-present" ~title:"Vary distinguishes absence from presence" ~tags:[ "vary" ] ~references:[ rfc 9111 "4.1" ] ~rationale: "A request without Accept-Language does not match a later request that \ carries it." ~second_request:(fun target -> request ~headers:[ ("Accept-Language", "en") ] target) [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Vary", "Accept-Language") ] (Fixed "absent"); response (Fixed "present"); ] [ "absent"; "present" ]; (let id = "vary-same-value" in let headers = [ ("Accept-Encoding", "gzip") ] in cache_resource_twice ~id ~title:"Equal Vary values reuse a stored response" ~tags:[ "vary" ] ~references:[ rfc 9111 "4.1" ] ~first_request:(fun target -> request ~headers target) ~second_request:(fun target -> request ~headers target) [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Vary", "Accept-Encoding") ] (Fixed "gzip-variant"); unexpected_network_response; ] [ "gzip-variant"; "gzip-variant" ]); cache_resource_twice ~id:"vary-different-value" ~title:"Different Vary values select another response" ~tags:[ "vary" ] ~references:[ rfc 9111 "4.1" ] ~first_request:(fun target -> request ~headers:[ ("Accept-Encoding", "gzip") ] target) ~second_request:(fun target -> request ~headers:[ ("Accept-Encoding", "br") ] target) [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Vary", "Accept-Encoding") ] (Fixed "gzip-variant"); response ~headers: [ ("Cache-Control", "max-age=3600"); ("Vary", "Accept-Encoding") ] (Fixed "br-variant"); ] [ "gzip-variant"; "br-variant" ]; cache_resource_twice ~id:"vary-multiple-fields" ~title:"Every field named by Vary participates" ~tags:[ "vary"; "multiple-fields" ] ~references:[ rfc 9111 "4.1" ] ~first_request:(fun target -> request ~headers:[ ("Accept-Encoding", "gzip"); ("X-A", "one") ] target) ~second_request:(fun target -> request ~headers:[ ("Accept-Encoding", "gzip"); ("X-A", "two") ] target) [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Vary", "Accept-Encoding, X-A"); ] (Fixed "one"); response ~headers: [ ("Cache-Control", "max-age=3600"); ("Vary", "Accept-Encoding, X-A"); ] (Fixed "two"); ] [ "one"; "two" ]; (let id = "etag-revalidation" in let target = cache_path id "resource" and probe = cache_path id "probe" in let names = [ "if-none-match" ] in cache_workflow ~id ~title:"Stale response is revalidated by entity tag" ~tags:[ "revalidation"; "etag" ] ~references:[ rfc 9111 "4.3.1"; rfc 9111 "4.3.4" ] [ request target; request target; request probe ] [ varying_endpoint target [ response ~headers:[ ("Cache-Control", "no-cache"); ("ETag", "\"v1\"") ] (Fixed "stored"); response ~status:304 ~headers:[ ("ETag", "\"v1\"") ] (Fixed ""); ]; echo_stored_request probe target names; ] [ "stored"; "stored"; expected_trace ~attempt:2 ~headers:[ ("If-None-Match", "\"v1\"") ] ~names target; ]); (let id = "last-modified-revalidation" in let target = cache_path id "resource" and probe = cache_path id "probe" in let modified = "Sun, 06 Nov 1994 08:49:37 GMT" in let names = [ "if-modified-since" ] in cache_workflow ~id ~title:"Stale response is revalidated by date" ~tags:[ "revalidation"; "date" ] ~references:[ rfc 9111 "4.3.1"; rfc 9111 "4.3.4" ] [ request target; request target; request probe ] [ varying_endpoint target [ response ~headers: [ ("Cache-Control", "no-cache"); ("Last-Modified", modified) ] (Fixed "stored"); response ~status:304 ~headers:[ ("Last-Modified", modified) ] (Fixed ""); ]; echo_stored_request probe target names; ] [ "stored"; "stored"; expected_trace ~attempt:2 ~headers:[ ("If-Modified-Since", modified) ] ~names target; ]); (let id = "304-updates-headers" in let target = cache_path id "resource" in cache_responses ~id ~title:"A 304 response updates stored response fields" ~tags:[ "revalidation"; "304"; "metadata" ] ~requires:[ "response-metadata" ] ~references:[ rfc 9111 "3.2"; rfc 9110 "15.4.5" ] ~rationale: "After successful validation, the cache reuses the stored content but \ replaces its X-Ver field with the value received in the 304 \ response." [ request target; request target ] [ varying_endpoint target [ response ~headers: [ ("Cache-Control", "no-cache"); ("ETag", "\"v1\""); ("X-Ver", "1"); ] (Fixed "stored"); response ~status:304 ~headers:[ ("ETag", "\"v1\""); ("X-Ver", "2") ] (Fixed ""); ]; ] [ [ expect_response ~headers:[ ("X-Ver", "1") ] "stored"; expect_response ~headers:[ ("X-Ver", "2") ] "stored"; ]; ]); (let id = "unsafe-invalidation" in let target = cache_path id "resource" in cache_workflow ~id ~title:"Successful unsafe request invalidates a response" ~tags:[ "invalidation" ] ~references:[ rfc 9111 "4.4" ] [ request target; request ~method_:Post ~body:"update" target; request target; ] [ varying_endpoint target [ response ~headers:[ ("Cache-Control", "max-age=3600") ] (Fixed "before"); response ~status:204 (Fixed ""); response ~headers:[ ("Cache-Control", "max-age=3600") ] (Fixed "after"); ]; ] [ "before"; ""; "after" ]); (let id = "post-invalidates-location" in let target = cache_path id "resource" and update = cache_path id "update" in cache_workflow ~requirement:Should ~id ~title:"POST invalidates a same-origin Location target" ~tags:[ "invalidation"; "location" ] ~references:[ rfc 9111 "4.4" ] [ request target; request ~method_:Post ~body:"update" update; request target; ] [ varying_endpoint target [ response ~headers:[ ("Cache-Control", "max-age=3600") ] (Fixed "before"); response ~headers:[ ("Cache-Control", "max-age=3600") ] (Fixed "after"); ]; endpoint ~status:204 ~headers:[ ("Location", target) ] update (Fixed ""); ] [ "before"; ""; "after" ]); (let id = "cache-group-safe-ignored" in let target = cache_path id "resource" and signal = cache_path id "signal" in cache_workflow ~id ~title:"A safe response cannot invalidate a cache group" ~tags:[ "cache-groups"; "invalidation"; "safe-method" ] ~references:[ rfc 9875 "2.1"; rfc 9875 "3" ] ~rationale: "Cache-Group-Invalidation on a response to GET is ignored, so the \ fresh grouped response remains reusable without another origin \ request." [ request target; request signal; request target ] [ varying_endpoint target [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Cache-Groups", "\"scripts\""); ] (Fixed "before"); unexpected_network_response; ]; endpoint ~headers: [ ("Cache-Control", "no-store"); ("Cache-Group-Invalidation", "\"scripts\""); ] signal (Fixed "signal"); ] [ "before"; "signal"; "before" ]); (let id = "cache-group-case-sensitive" in let target = cache_path id "resource" and signal = cache_path id "signal" in cache_workflow ~id ~title:"Cache group identifiers are case sensitive" ~tags:[ "cache-groups"; "invalidation"; "case-sensitive" ] ~references:[ rfc 9875 "2.1"; rfc 9875 "3" ] ~rationale: "The stored response belongs to Scripts, not scripts. An unsafe \ invalidation for the distinct lowercase identifier cannot evict it." [ request target; request ~method_:Post ~body:"update" signal; request target; ] [ varying_endpoint target [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Cache-Groups", "\"Scripts\""); ] (Fixed "before"); unexpected_network_response; ]; endpoint ~status:204 ~headers:[ ("Cache-Group-Invalidation", "\"scripts\"") ] signal (Fixed ""); ] [ "before"; ""; "before" ]); (let id = "cache-group-origin-scoped" in let target = cache_path id "resource" and signal = cache_path id "signal" in cache_workflow ~id ~requires:[ "logical-origins" ] ~title:"Cache groups do not cross origin boundaries" ~tags:[ "cache-groups"; "invalidation"; "origin" ] ~references:[ rfc 9875 "2.1"; rfc 9875 "3" ] ~rationale: "An invalidation from b.example cannot evict a same-named group \ stored for a.example." [ request ~origin:"a.example" target; request ~origin:"b.example" ~method_:Post ~body:"update" signal; request ~origin:"a.example" target; ] [ varying_endpoint target [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("Cache-Groups", "\"scripts\""); ] (Fixed "before"); unexpected_network_response; ]; endpoint ~status:204 ~headers:[ ("Cache-Group-Invalidation", "\"scripts\"") ] signal (Fixed ""); ] [ "before"; ""; "before" ]); (let id = "head-preserves-stored" in let target = cache_path id "resource" in cache_workflow ~requirement:Should ~id ~title:"A validating HEAD preserves the stored GET response" ~tags:[ "revalidation"; "head" ] ~references:[ rfc 9111 "4.3.5" ] [ request target; request ~method_:Head ~headers:[ ("Cache-Control", "no-cache") ] target; request target; ] [ varying_endpoint target [ response ~headers: [ ("Cache-Control", "max-age=3600"); ("ETag", "\"v1\"") ] (Fixed "stored"); response ~headers: [ ("Cache-Control", "max-age=3600"); ("ETag", "\"v1\"") ] (Fixed "stored"); unexpected_network_response; ]; ] [ "stored"; ""; "stored" ]); (let id = "only-if-cached-miss" in let target = cache_path id "resource" in cache_workflow ~id ~title:"Only-if-cached miss does not contact origin" ~tags:[ "request-directive"; "offline" ] ~references:[ rfc 9111 "5.2.1.7" ] ~rationale: "A cache answers an unsatisfied only-if-cached request with 504. The \ empty body distinguishes that response from the origin sentinel." [ request ~headers:[ ("Cache-Control", "only-if-cached") ] target ] [ endpoint target (Fixed "unexpected-network-request") ] [ "" ]); cache_resource_twice ~id:"request-no-store" ~title:"Request no-store prevents response storage" ~tags:[ "request-directive"; "storage" ] ~references:[ rfc 9111 "5.2.1.5" ] ~first_request:(fun target -> request ~headers:[ ("Cache-Control", "no-store") ] target) [ response ~headers:[ ("Cache-Control", "max-age=3600") ] (Fixed "first"); response (Fixed "second"); ] [ "first"; "second" ]; cache_resource_twice ~requirement:Limit ~id:"authorization-not-shared" ~title:"Authenticated response records private-cache behavior" ~tags:[ "authorization"; "shared-cache"; "storage" ] ~references:[ rfc 9111 "3.5" ] ~rationale: "A private user-agent cache may reuse this response; a shared cache \ must not store it without an enabling response directive. This limit \ case records which cache model the adapter exposes." ~first_request:(fun target -> request ~headers:[ ("Authorization", "Bearer secret") ] target) ~second_request:(fun target -> request ~headers:[ ("Authorization", "Bearer secret") ] target) [ response ~headers:[ ("Cache-Control", "max-age=3600") ] (Fixed "stored"); response (Fixed "network"); ] [ "stored"; "stored" ]; cache_resource_twice ~requirement:Should ~id:"s-maxage-private-ignored" ~title:"Private cache ignores s-maxage" ~tags:[ "freshness"; "shared-cache" ] ~references:[ rfc 9111 "5.2.2.10" ] [ response ~headers:[ ("Cache-Control", "s-maxage=0, max-age=3600") ] (Fixed "stored"); unexpected_network_response; ] [ "stored"; "stored" ]; timed_cache ~id:"max-age-expiry-crossing" ~title:"Max-age expires after virtual time advances" ~tags:[ "freshness"; "max-age" ] ~references:[ rfc 9111 "4.2.1"; rfc 9111 "5.2.2.1" ] ~advance_ms:61_000 ~stored:[ ("Cache-Control", "max-age=60") ] ~expected:"network" (); timed_cache ~id:"age-corrected-expiry" ~title:"Age reduces lifetime before a clock advance" ~tags:[ "freshness"; "age" ] ~references:[ rfc 9111 "4.2.3"; rfc 9111 "5.1" ] ~advance_ms:61_000 ~stored:[ ("Cache-Control", "max-age=120"); ("Age", "60") ] ~expected:"network" (); timed_cache ~id:"fresh-before-expiry" ~title:"Entry remains fresh before its expiry boundary" ~tags:[ "freshness"; "boundary" ] ~references:[ rfc 9111 "4.2" ] ~advance_ms:59_000 ~stored:[ ("Cache-Control", "max-age=60") ] ~network:unexpected_network_response ~expected:"stored" (); timed_cache ~requirement:May ~id:"stale-request-max-stale" ~title:"Max-stale permits reuse after virtual time advances" ~tags:[ "freshness"; "request-directive"; "stale" ] ~references:[ rfc 9111 "5.2.1.2" ] ~advance_ms:61_000 ~stored:[ ("Cache-Control", "max-age=0") ] ~request_after:(fun target -> request ~headers:[ ("Cache-Control", "max-stale=120") ] target) ~network:unexpected_network_response ~expected:"stored" (); timed_cache ~id:"max-age-exact-boundary" ~title:"An entry is stale at its exact max-age boundary" ~tags:[ "freshness"; "max-age"; "boundary" ] ~references:[ rfc 9111 "4.2" ] ~advance_ms:60_000 ~stored:[ ("Cache-Control", "max-age=60") ] ~expected:"network" (); timed_cache ~id:"request-min-fresh-crossing" ~title:"Min-fresh requires enough remaining freshness" ~tags:[ "freshness"; "request-directive"; "min-fresh" ] ~references:[ rfc 9111 "5.2.1.3" ] ~advance_ms:30_000 ~stored:[ ("Cache-Control", "max-age=60") ] ~request_after:(fun target -> request ~headers:[ ("Cache-Control", "min-fresh=31") ] target) ~expected:"network" (); timed_cache ~id:"request-max-age-crossing" ~title:"Request max-age limits acceptable response age" ~tags:[ "freshness"; "request-directive"; "max-age" ] ~references:[ rfc 9111 "5.2.1.1" ] ~advance_ms:61_000 ~stored:[ ("Cache-Control", "max-age=120") ] ~request_after:(fun target -> request ~headers:[ ("Cache-Control", "max-age=60") ] target) ~expected:"network" (); timed_cache ~id:"request-no-cache-revalidation" ~title:"Request no-cache forces validation while fresh" ~tags:[ "freshness"; "request-directive"; "revalidation"; "etag" ] ~references:[ rfc 9111 "5.2.1.4" ] ~advance_ms:10_000 ~stored:[ ("Cache-Control", "max-age=60"); ("ETag", "\"v1\"") ] ~request_after:(fun target -> request ~headers:[ ("Cache-Control", "no-cache") ] target) ~network:(response ~status:304 (Fixed "")) ~expected:"stored" (); timed_cache ~id:"must-revalidate-after-expiry" ~title:"Must-revalidate forbids stale reuse after expiry" ~tags:[ "freshness"; "must-revalidate"; "error" ] ~references:[ rfc 9111 "5.2.2.2" ] ~advance_ms:61_000 ~stored:[ ("Cache-Control", "max-age=60, must-revalidate") ] ~network:(response ~status:503 (Fixed "origin-error")) ~expected:"origin-error" (); timed_cache ~requirement:May ~id:"stale-if-error-window" ~title:"Stale-if-error permits stale reuse within its window" ~tags:[ "freshness"; "stale"; "stale-if-error" ] ~references:[ rfc 5861 "4" ] ~advance_ms:30_000 ~stored:[ ("Cache-Control", "max-age=0, stale-if-error=60") ] ~network:(response ~status:503 (Fixed "origin-error")) ~expected:"stored" (); timed_cache ~id:"stale-if-error-expired" ~title:"Stale-if-error stops applying after its window" ~tags:[ "freshness"; "stale"; "stale-if-error"; "boundary" ] ~references:[ rfc 5861 "4" ] ~advance_ms:61_000 ~stored:[ ("Cache-Control", "max-age=0, stale-if-error=60") ] ~network:(response ~status:503 (Fixed "origin-error")) ~expected:"origin-error" (); timed_cache ~requirement:Should ~id:"immutable-before-expiry" ~title:"Immutable response avoids reload validation while fresh" ~tags:[ "freshness"; "immutable"; "request-directive" ] ~references:[ rfc 8246 "2" ] ~advance_ms:30_000 ~stored:[ ("Cache-Control", "max-age=60, immutable") ] ~request_after:(fun target -> request ~headers:[ ("Cache-Control", "max-age=0") ] target) ~network:unexpected_network_response ~expected:"stored" (); timed_cache ~requirement:May ~id:"stale-while-revalidate-window" ~title:"Stale-while-revalidate permits stale reuse within its window" ~tags:[ "freshness"; "stale"; "stale-while-revalidate" ] ~references:[ rfc 5861 "3" ] ~advance_ms:30_000 ~stored:[ ("Cache-Control", "max-age=0, stale-while-revalidate=60") ] ~expected:"stored" (); timed_cache ~id:"date-skew-expiry-crossing" ~title:"A future Date does not extend an explicit Expires lifetime" ~tags:[ "freshness"; "date"; "expires"; "skew" ] ~references:[ rfc 9111 "4.2.1"; rfc 9111 "4.2.3" ] ~advance_ms:61_000 ~stored: [ ("Date", "Thu, 31 Dec 2099 23:58:00 GMT"); ("Expires", "Thu, 31 Dec 2099 23:59:00 GMT"); ] ~expected:"network" (); timed_cache ~id:"age-corrected-still-fresh" ~title:"Corrected Age remains fresh before the boundary" ~tags:[ "freshness"; "age"; "boundary" ] ~references:[ rfc 9111 "4.2.3"; rfc 9111 "5.1" ] ~advance_ms:59_000 ~stored:[ ("Cache-Control", "max-age=120"); ("Age", "60") ] ~network:unexpected_network_response ~expected:"stored" (); ]