From 67620024a2d27280cdd977ea0e2d2673f8b7395b Mon Sep 17 00:00:00 2001 From: Ephraim Duncan <55143799+ephraimduncan@users.noreply.github.com> Date: Thu, 10 Sep 2026 10:33:59 +0000 Subject: [PATCH] fix(header-analysis): report the last cache-status member (#2681) --- .../src/cdn/normalize-cache-status.test.ts | 19 +++++++++++++++++++ .../src/cdn/normalize-cache-status.ts | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/header-analysis/src/cdn/normalize-cache-status.test.ts b/packages/header-analysis/src/cdn/normalize-cache-status.test.ts index 1e736d9e..f6f3743c 100644 --- a/packages/header-analysis/src/cdn/normalize-cache-status.test.ts +++ b/packages/header-analysis/src/cdn/normalize-cache-status.test.ts @@ -47,6 +47,7 @@ describe("cloudfront / fastly / akamai (x-cache)", () => { ["RefreshHit from cloudfront", "EXPIRED"], ["HIT", "HIT"], ["MISS, HIT", "HIT"], + ["HIT, MISS", "HIT"], [ "TCP_HIT from a23-45-67-89.deploy.akamaitechnologies.com (AkamaiGHost)", "HIT", @@ -70,13 +71,31 @@ describe("rfc 9211 cache-status", () => { ["ExampleCache; fwd=miss; stored", "MISS"], ["ExampleCache; fwd=stale", "EXPIRED"], ["ExampleCache; fwd=bypass", "BYPASS"], + ["OriginCache; hit, EdgeCache; fwd=miss", "MISS"], + ["OriginCache; fwd=miss, EdgeCache; hit", "HIT"], + ["OriginCache; fwd=bypass, EdgeCache; fwd=stale", "EXPIRED"], + ['OriginCache; hit, "Edge, Cache"; fwd=miss', "MISS"], + ['OriginCache; hit, EdgeCache; fwd=miss; detail="a,b; hit"', "MISS"], + [ + String.raw`OriginCache; hit, EdgeCache; fwd=miss; detail="a\",b; hit"`, + "MISS", + ], + [String.raw`OriginCache; detail="a\\"; hit, EdgeCache; fwd=miss`, "MISS"], ] as const) { test(`cache-status: ${raw} -> ${status}`, () => { const result = normalizeCacheStatus({ "Cache-Status": raw }); expect(result.status).toBe(status); expect(result.source).toBe("cache-status"); + expect(result.raw).toBe(raw); }); } + + test("does not use an upstream hit when the last cache has no status", () => { + expect( + normalizeCacheStatus({ "Cache-Status": "OriginCache; hit, EdgeCache" }) + .status, + ).toBe("UNKNOWN"); + }); }); describe("vendor header priority", () => { diff --git a/packages/header-analysis/src/cdn/normalize-cache-status.ts b/packages/header-analysis/src/cdn/normalize-cache-status.ts index 54a5c923..cc89cfaf 100644 --- a/packages/header-analysis/src/cdn/normalize-cache-status.ts +++ b/packages/header-analysis/src/cdn/normalize-cache-status.ts @@ -38,9 +38,11 @@ function fromToken(value: string): CacheStatus | null { return null; } -// RFC 9211, e.g. `"Netlify Edge"; hit` or `ExampleCache; fwd=miss; stored` +// RFC 9211 lists the cache closest to the user last. function fromCacheStatusHeader(value: string): CacheStatus | null { - const lower = value.toLowerCase(); + // Quoted strings can contain commas and text that resembles parameters. + const unquoted = value.replace(/"(?:[^"\\]|\\.)*"/g, '""'); + const lower = unquoted.slice(unquoted.lastIndexOf(",") + 1).toLowerCase(); if (/;\s*hit/.test(lower)) return "HIT"; const fwd = lower.match(/fwd=([a-z-]+)/)?.[1]; if (!fwd) return null; -- 2.51.2