fix: invalidate Location and Content-Location URIs on unsafe methods#5514
Open
jeswr wants to merge 2 commits into
Open
fix: invalidate Location and Content-Location URIs on unsafe methods#5514jeswr wants to merge 2 commits into
jeswr wants to merge 2 commits into
Conversation
CacheHandler.onResponseStart only deleted the request URI's cache entry
on a successful (2xx/3xx) response to an unsafe method. RFC 9111 section
4.4 also says caches SHOULD invalidate the URIs in the response's
Location and Content-Location header fields when they share the request
URI's origin, so the classic "POST /collection -> 201 Location:
/collection/123" flow left a previously cached "GET /collection/123"
stale.
Repro (before): cache GET /target; POST /src returning "Location:
<origin>/target"; GET /target again is served from cache (1 origin hit).
After: the second GET /target is refetched (2 origin hits). Only
same-origin URIs are invalidated, per the RFC's security note.
This also turns the eight invalidate-{POST,PUT,DELETE,M-SEARCH}-{location,cl}
tests of the mnot cache-tests conformance suite from failed-optional
into passing in every test environment (220 -> 228 passed, 58 -> 50
failed-optional, 0 required failures).
Fixes nodejs#5509
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5514 +/- ##
==========================================
- Coverage 93.44% 93.44% -0.01%
==========================================
Files 110 110
Lines 37328 37381 +53
==========================================
+ Hits 34881 34930 +49
- Misses 2447 2451 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
7 tasks
…4/ubuntu) 'MockAgent - handle delays to simulate work' asserts elapsed >= 50ms on a 50ms setTimeout and measured 49ms (classic timer early-fire/rounding). Passes 10/10 locally on this branch and 15/15 on main; this PR only touches lib/handler/cache-handler.js, which MockAgent never loads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5509
What
CacheHandler.onResponseStartdeleted only the request URI's entry on a 2xx/3xx response to an unsafe method. RFC 9111 §4.4 also says caches SHOULD invalidate the URIs in the response'sLocationandContent-Locationheader fields when they share the request URI's origin — so the classicPOST /collection→201 Location: /collection/123flow left a previously cachedGET /collection/123stale.This adds a
#deleteLocationHeaderEntriesstep to the existing invalidation branch: it resolves each header value against the request URI (relative references included), applies the same-origin check from the RFC's security note (invalidating cross-origin URIs could be abused for cache poisoning), and deletes the matching entry from the store.Repro / evidence
Origin:
GET /targetserved withCache-Control: public, s-maxage=100;POST /srcanswers201withLocation: <origin>/target.Before (main @ cb4c2f1, Node 22):
GET /target→ origin hit, cachedPOST /src(response carriesLocation: <origin>/target)GET /target→ served from cache (1 origin hit total on/target)After: step 3 goes back to the origin (2 hits), for both
LocationandContent-Location, absolute and relative forms. ALocationpointing at another origin (even with a path that collides with a cached entry) does not invalidate — covered by the added test intest/interceptors/cache.js.Tests
unsafe methods invalidate the URIs in Location and Content-Location response headersintest/interceptors/cache.js(fails on main withexpected: 2, actual: 1origin hits, passes with this change).test/interceptors/cache*.js,test/cache-interceptor/*): 122 pass / 0 fail (121 baseline + 1 new).node test/cache-interceptor/cache-tests.mjs): the eightinvalidate-{POST,PUT,DELETE,M-SEARCH}-{location,cl}tests move from failed-optional to passing in every environment — totals go 220 → 228 passed, 58 → 50 failed-optional, still 0 required failures. Nothing needed un-skipping in the runner's ignore list.Notes
main; independent of the sibling cache-conformance PRs fix: honor request cache-control max-age=0 in cache interceptor #5510 (fix/cache-request-max-age-zero), fix: enforce must-revalidate and proxy-revalidate over max-stale and stale-if-error #5511 (fix/cache-must-revalidate-max-stale), fix: send stored last-modified as if-modified-since when revalidating #5512 (fix/cache-if-modified-since-value) and fix: honor stale-if-error on connection errors during revalidation #5513 (fix/cache-stale-if-error-network), which touch the interceptor/revalidation paths rather than this invalidation hook.🤖 Generated with Claude Code