mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 05:12:29 +00:00
## Summary Fixes issue #21677 where `Bun.serve()` was adding redundant Date headers when users provided their own Date header in the response. The root cause was that the HTTP server was writing user-provided Date headers and then µWebSockets was automatically adding its own Date header without checking if one already existed. ## Changes - **Added Date header detection in `NodeHTTP.cpp`**: When a user provides a Date header (either in common or uncommon headers), the code now sets the `HTTP_WROTE_DATE_HEADER` flag to prevent µWebSockets from automatically adding another Date header - **Case-insensitive header matching**: Uses `WTF::equalIgnoringASCIICase` for proper header name comparison in uncommon headers - **Comprehensive test coverage**: Added regression tests that verify no duplicate Date headers in all scenarios (static responses, dynamic responses, proxy responses) ## Test Plan - [x] Added comprehensive regression test in `test/regression/issue/21677.test.ts` - [x] Tests verify only one Date header exists in all response scenarios - [x] Tests fail with current main branch (confirms bug exists) - [x] Tests pass with this fix (confirms bug is resolved) - [x] Existing Date header tests still pass (no regression) ## Testing The reproduction case from the issue now works correctly: **Before (multiple Date headers):** ``` HTTP/1.1 200 OK Date: Thu, 07 Aug 2025 17:02:24 GMT content-type: text/plain;charset=utf-8 Date: Thu, 07 Aug 2025 17:02:23 GMT ``` **After (single Date header):** ``` HTTP/1.1 200 OK Date: Thu, 07 Aug 2025 17:02:23 GMT content-type: text/plain;charset=utf-8 ``` 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1037,6 +1037,11 @@ static void writeFetchHeadersToUWSResponse(WebCore::FetchHeaders& headers, uWS::
|
||||
res->writeMark();
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent automatic Date header insertion when user provides one
|
||||
if (header.key == WebCore::HTTPHeaderName::Date) {
|
||||
data->state |= uWS::HttpResponseData<isSSL>::HTTP_WROTE_DATE_HEADER;
|
||||
}
|
||||
writeResponseHeader<isSSL>(res, name, value);
|
||||
}
|
||||
|
||||
|
||||
100
test/regression/issue/21677.test.ts
Normal file
100
test/regression/issue/21677.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
test("issue #21677 - should not add redundant Date headers", async () => {
|
||||
const testDate1 = new Date("2025-08-07T17:01:47.000Z").toUTCString();
|
||||
const testDate2 = new Date("2025-08-07T17:02:23.000Z").toUTCString();
|
||||
const testDate3 = new Date("2025-08-07T17:03:06.000Z").toUTCString();
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
routes: {
|
||||
"/static": () =>
|
||||
new Response(`date test`, {
|
||||
headers: { date: testDate1 },
|
||||
}),
|
||||
"/proxy": async () => {
|
||||
// Create a simple server response with a Date header to proxy
|
||||
const simpleResponse = new Response("proxied content", {
|
||||
headers: {
|
||||
"Date": testDate3,
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
});
|
||||
return simpleResponse;
|
||||
},
|
||||
},
|
||||
fetch: () =>
|
||||
new Response(`date test`, {
|
||||
headers: { date: testDate2 },
|
||||
}),
|
||||
});
|
||||
|
||||
// Test dynamic route (default fetch handler)
|
||||
{
|
||||
const response = await fetch(server.url);
|
||||
|
||||
// Should only have one Date header, not multiple
|
||||
const dateHeaders = [...response.headers.entries()].filter(([key]) => key.toLowerCase() === "date");
|
||||
expect(dateHeaders).toHaveLength(1);
|
||||
expect(dateHeaders[0][1]).toBe(testDate2);
|
||||
}
|
||||
|
||||
// Test static route
|
||||
{
|
||||
const response = await fetch(new URL("/static", server.url));
|
||||
|
||||
// Should only have one Date header, not multiple
|
||||
const dateHeaders = [...response.headers.entries()].filter(([key]) => key.toLowerCase() === "date");
|
||||
expect(dateHeaders).toHaveLength(1);
|
||||
expect(dateHeaders[0][1]).toBe(testDate1);
|
||||
}
|
||||
|
||||
// Test proxy route
|
||||
{
|
||||
const response = await fetch(new URL("/proxy", server.url));
|
||||
|
||||
// Should only have one Date header, not multiple
|
||||
const dateHeaders = [...response.headers.entries()].filter(([key]) => key.toLowerCase() === "date");
|
||||
expect(dateHeaders).toHaveLength(1);
|
||||
expect(dateHeaders[0][1]).toBe(testDate3);
|
||||
}
|
||||
});
|
||||
|
||||
test("issue #21677 - reproduce with raw HTTP to verify duplicate headers", async () => {
|
||||
const testDate = new Date("2025-08-07T17:02:23.000Z").toUTCString();
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: () =>
|
||||
new Response(`date test`, {
|
||||
headers: { date: testDate },
|
||||
}),
|
||||
});
|
||||
|
||||
// Use TCP socket to get raw HTTP response and check for duplicate headers
|
||||
await new Promise((resolve, reject) => {
|
||||
const socket = Bun.connect({
|
||||
hostname: "localhost",
|
||||
port: server.port,
|
||||
socket: {
|
||||
data(socket, data) {
|
||||
const response = data.toString();
|
||||
// Should NOT contain multiple Date headers
|
||||
const lines = response.split("\r\n");
|
||||
const dateHeaderLines = lines.filter(line => line.toLowerCase().startsWith("date:"));
|
||||
|
||||
expect(dateHeaderLines).toHaveLength(1);
|
||||
expect(dateHeaderLines[0]).toBe(`Date: ${testDate}`);
|
||||
socket.end();
|
||||
resolve(undefined);
|
||||
},
|
||||
error(socket, error) {
|
||||
reject(error);
|
||||
},
|
||||
open(socket) {
|
||||
socket.write("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n");
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user