Skip to content

Commit 2885905

Browse files
authored
fix: clone request headers instead of mutating in single-flight (#2121)
1 parent 87f556c commit 2885905

File tree

4 files changed

+93
-2
lines changed

4 files changed

+93
-2
lines changed

.changeset/funny-pandas-clean.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solidjs/start": patch
3+
---
4+
5+
fix: clone request headers in single-flight to avoid mutating immutable headers
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { describe, expect, it, vi, beforeEach } from "vitest";
2+
import type { FetchEvent } from "./types.ts";
3+
4+
vi.mock("h3", () => ({
5+
parseCookies: vi.fn(() => ({})),
6+
parseSetCookie: vi.fn(),
7+
}));
8+
9+
vi.mock("solid-js/web", () => ({
10+
renderToString: vi.fn(),
11+
provideRequestEvent: vi.fn((_event, fn) => fn()),
12+
}));
13+
14+
vi.mock("solidstart:server-fn-manifest", () => ({
15+
getServerFnById: vi.fn(),
16+
}));
17+
18+
vi.mock("./handler.ts", () => ({
19+
createPageEvent: vi.fn(),
20+
}));
21+
22+
vi.mock("./fetchEvent.ts", () => ({
23+
getFetchEvent: vi.fn(),
24+
mergeResponseHeaders: vi.fn(),
25+
}));
26+
27+
function createMockFetchEvent(headers: Record<string, string> = {}): FetchEvent {
28+
return {
29+
request: new Request("http://localhost/test", { headers }),
30+
response: {
31+
headers: {
32+
getSetCookie: () => [],
33+
},
34+
},
35+
nativeEvent: {},
36+
locals: {},
37+
} as FetchEvent;
38+
}
39+
40+
describe("createSingleFlightHeaders", () => {
41+
let createSingleFlightHeaders: (sourceEvent: FetchEvent) => Headers;
42+
43+
beforeEach(async () => {
44+
vi.clearAllMocks();
45+
const module = await import("./server-functions-handler.ts");
46+
createSingleFlightHeaders = module.createSingleFlightHeaders;
47+
});
48+
49+
it("should create a new Headers object instead of returning the original", () => {
50+
const sourceEvent = createMockFetchEvent({
51+
"content-type": "application/json",
52+
});
53+
54+
const result = createSingleFlightHeaders(sourceEvent);
55+
56+
expect(result).not.toBe(sourceEvent.request.headers);
57+
});
58+
59+
it("should not mutate the original request headers", () => {
60+
const originalHeaders = new Headers({
61+
"content-type": "application/json",
62+
"cookie": "session=abc123",
63+
"cf-ray": "abc123",
64+
"cf-cache-status": "HIT",
65+
});
66+
const sourceEvent: FetchEvent = {
67+
request: new Request("http://localhost/test", { headers: originalHeaders }),
68+
response: {
69+
headers: {
70+
getSetCookie: () => [],
71+
},
72+
},
73+
nativeEvent: {},
74+
locals: {},
75+
} as FetchEvent;
76+
77+
const originalCookieHeader = sourceEvent.request.headers.get("cookie");
78+
const originalCfRay = sourceEvent.request.headers.get("cf-ray");
79+
80+
createSingleFlightHeaders(sourceEvent);
81+
82+
expect(sourceEvent.request.headers.get("cookie")).toBe(originalCookieHeader);
83+
expect(sourceEvent.request.headers.get("cf-ray")).toBe(originalCfRay);
84+
});
85+
});

packages/start/src/server/server-functions-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,12 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
195195
}
196196

197197
let App: any;
198-
function createSingleFlightHeaders(sourceEvent: FetchEvent) {
198+
export function createSingleFlightHeaders(sourceEvent: FetchEvent) {
199199
// cookie handling logic is pretty simplistic so this might be imperfect
200200
// unclear if h3 internals are available on all platforms but we need a way to
201201
// update request headers on the underlying H3 event.
202202

203-
const headers = sourceEvent.request.headers;
203+
const headers = new Headers(sourceEvent.request.headers);
204204
const cookies = parseCookies(sourceEvent.nativeEvent);
205205
const SetCookies = sourceEvent.response.headers.getSetCookie();
206206
headers.delete("cookie");

packages/start/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020
"rootDir": "./src",
2121
},
2222
"include": ["env.d.ts", "./src", "./src/env.d.ts"],
23+
"exclude": ["**/*.spec.ts"]
2324
}

0 commit comments

Comments
 (0)