Skip to content

fix(network): preserve dates in response headers - #42734

Closed
Devin Rousso (dcrousso) wants to merge 1 commit into
microsoft:mainfrom
dcrousso:fix-42687
Closed

Devin Rousso (dcrousso) wants to merge 1 commit into
microsoft:mainfrom
dcrousso:fix-42687

Conversation

@dcrousso

Copy link
Copy Markdown
Contributor

response header parsing corrupts HTTP dates and macOS WebKit Set-Cookie values

preserve date headers and only split combined cookies before another cookie pair

fixes #42687

response header parsing corrupts HTTP dates and macOS WebKit `Set-Cookie` values

preserve date headers and only split combined cookies before another cookie pair
for (const header of headers) {
const separator = header.name.toLowerCase() === 'set-cookie' ? '\n' : ',';
const tokens = header.value.split(separator).map(s => s.trim());
const tokens = splitHeaderValue(header.name, header.value, separator).map(s => s.trim());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Firefox we don't need to guess. nsIHttpChannel.visitOriginalResponseHeaders returns headers unmerged, in the order received from the peer. Synthesized headers from route.fulfill go through ParseHeaderLine -> SetHeaderFromNet, so they are visited too. Switching responseHead() in juggler NetworkObserver.js to it makes parseMultivalueHeaders unnecessary and also fixes WWW-Authenticate/Proxy-Authenticate, which Gecko merges with \n and we currently split on comma.

That needs a Firefox roll, so fine to land the heuristic first, but let's not treat this as the Firefox fix.

export function splitHeaderValue(name: string, value: string, separator: string): string[] {
if (separator === ',') {
switch (name.toLowerCase()) {
case 'date':

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use Chromium's non-coalescing list (HttpUtil::IsNonCoalescingHeader) instead: date, expires, last-modified, location, retry-after, set-cookie, www-authenticate, proxy-authenticate, strict-transport-security.

location (commas in query strings) and the auth challenges are real breakages that this list misses. if-modified-since, if-unmodified-since and if-range are request headers; every caller passing a separator handles response headers, so they are dead entries.

return result;
}

export function splitHeaderValue(name: string, value: string, separator: string): string[] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The separator === ',' guard makes the coupling between the name switch and the separator implicit. Suggest keeping plain split('\n') for the raw-header paths and giving the comma path its own splitCommaSeparatedHeader(name, value).

@yury-s Yury Semikhatsky (yury-s) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks risky overall for an issue that we haven't heard any complaints about.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

1 failed
❌ [firefox] › mcp/cli-session.spec.ts:54 › idle timeout shuts the session down @mcp-windows-latest-firefox

8563 passed, 1446 skipped


Merge workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "tests 1"

1 failed
❌ [firefox-library] › library/browsercontext-storage-state.spec.ts:453 › should work when service worker is intefering and the origin is not open @firefox-ubuntu-22.04-node20

9 flaky ⚠️ [chromium-library] › library/video.spec.ts:736 › screencast › should work with video+trace `@frozen-time-library-chromium-linux`
⚠️ [chromium-library] › library/video.spec.ts:736 › screencast › should work with video+trace `@realtime-time-library-chromium-linux`
⚠️ [chromium-library] › library/beforeunload.spec.ts:130 › should support dismissing the dialog multiple times `@chromium-ubuntu-22.04-node24`
⚠️ [chromium-library] › library/video.spec.ts:699 › screencast › should capture full viewport on hidpi `@chromium-ubuntu-22.04-node20`
⚠️ [firefox-library] › library/browsercontext-cookies-third-party.spec.ts:257 › third party 'Partitioned;' cookies `@firefox-ubuntu-22.04-node20`
⚠️ [firefox-library] › library/browsercontext-cookies-third-party.spec.ts:470 › top level 'Partitioned;' cookie and same origin iframe `@firefox-ubuntu-22.04-node20`
⚠️ [firefox-page] › page/page-event-request.spec.ts:181 › should return response body when Cross-Origin-Opener-Policy is set `@firefox-ubuntu-22.04-node20`
⚠️ [firefox-page] › page/page-goto.spec.ts:90 › should work with Cross-Origin-Opener-Policy `@firefox-ubuntu-22.04-node20`
⚠️ [playwright-test] › ui-mode-test-run.spec.ts:583 › should remove output folder before test run `@windows-latest-node22`

51734 passed, 1247 skipped


Merge workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

🟢 The one failure is a known Windows/Firefox flake — this PR is clear

Hi, I'm the Playwright bot and I triaged the failing CI run.

[firefox] › mcp/cli-session.spec.ts:54 › idle timeout shuts the session down (mcp-windows-latest-firefox) is the only failure, and it fails regularly on SHAs this PR can't be responsible for.

Details

Pre-existing flake / infra

This PR only changes splitHeaderValue in packages/isomorphic/headers.ts and its use in ffNetworkManager.ts::parseMultivalueHeaders. Nothing in the MCP CLI session lifecycle reads response headers, and the two tests the PR adds in page-network-response.spec.ts passed everywhere.

Triaged by the Playwright bot - agent run

@github-actions

Copy link
Copy Markdown
Contributor

🟢 Both failures are pre-existing — this PR is clear

Hi, I'm the Playwright bot and I triaged the two red tests. Neither touches the header-splitting change: the storage-state one fails on main too (34 of 34 runs in the CI results db), and the MCP session test flips verdict across unrelated SHAs.

Details

Pre-existing flake / infra

  • [firefox-library] › library/browsercontext-storage-state.spec.ts:453 › should work when service worker is intefering and the origin is not open — broken on trunk, not by this PR. In the aggregated CI results it has failed 34 of 34 runs, passed 0, with the identical expect(received).toEqual(expected) / - Expected - 0 / + Received + 4 diff — including push runs on main (cca996eb, a0241629) and PRs #42728, #42726, #42716, #42654. It also predates this branch, so the diff can't be responsible.

  • [firefox] › mcp/cli-session.spec.ts:54 › idle timeout shuts the session down (mcp-windows-latest-firefox) — timing flake. Same test, same bot: failed 5 of 39 runs, passed 34, including push runs on main (9ae53771, 52a0e720, d1ead3ec) and PR #42706. It flips in both directions — those runs expected (no browsers) and saw a live session, this run expected - default: and saw (no browsers) — which is what an idle-timeout race looks like on a slow Windows bot.

Why the diff can't reach either

splitHeaderValue only changes how already-received response headers are split on , for date-ish headers and Set-Cookie. The storage-state failure is a service-worker/origin assertion with no Expires date in play, and the MCP CLI session test never goes near response header parsing.

Triaged by the Playwright bot - agent run

@dcrousso

Copy link
Copy Markdown
Contributor Author

discussed offline and this should really be fixed upstream

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: headersArray() splits single header values on commas on Firefox and WebKit, corrupting every HTTP-date header

2 participants