mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 03:48:56 +00:00
fix: respect user-provided Connection header in fetch() requests (#21049)
Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: jarred-sumner-bot <220441119+jarred-sumner-bot@users.noreply.github.com> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Jarred Sumner <Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7f29446d9b
commit
5fe0c034e2
@@ -542,6 +542,7 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
|
||||
var override_accept_encoding = false;
|
||||
var override_accept_header = false;
|
||||
var override_host_header = false;
|
||||
var override_connection_header = false;
|
||||
var override_user_agent = false;
|
||||
var add_transfer_encoding = true;
|
||||
var original_content_length: ?string = null;
|
||||
@@ -560,8 +561,10 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
|
||||
continue;
|
||||
},
|
||||
hashHeaderConst("Connection") => {
|
||||
if (!this.flags.disable_keepalive) {
|
||||
continue;
|
||||
override_connection_header = true;
|
||||
const connection_value = this.headerStr(header_values[i]);
|
||||
if (std.ascii.eqlIgnoreCase(connection_value, "close")) {
|
||||
this.flags.disable_keepalive = true;
|
||||
}
|
||||
},
|
||||
hashHeaderConst("if-modified-since") => {
|
||||
@@ -609,7 +612,7 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
|
||||
header_count += 1;
|
||||
}
|
||||
|
||||
if (!this.flags.disable_keepalive) {
|
||||
if (!override_connection_header and !this.flags.disable_keepalive) {
|
||||
request_headers_buf[header_count] = connection_header;
|
||||
header_count += 1;
|
||||
}
|
||||
|
||||
93
test/js/web/fetch/fetch-connection-header.test.ts
Normal file
93
test/js/web/fetch/fetch-connection-header.test.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { serve } from "bun";
|
||||
import { describe, expect, it, test } from "bun:test";
|
||||
|
||||
describe("fetch Connection header", () => {
|
||||
// Helper function to capture headers from a request
|
||||
const captureHeadersFromRequest = async (fetchOptions: RequestInit): Promise<Record<string, string>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Create a temporary server to capture headers
|
||||
const tempServer = serve({
|
||||
port: 0,
|
||||
fetch(req) {
|
||||
const capturedHeaders: Record<string, string> = {};
|
||||
for (const [name, value] of req.headers.entries()) {
|
||||
capturedHeaders[name.toLowerCase()] = value;
|
||||
}
|
||||
tempServer.stop();
|
||||
resolve(capturedHeaders);
|
||||
return new Response("OK");
|
||||
},
|
||||
});
|
||||
|
||||
const tempPort = tempServer.port;
|
||||
const url = `http://localhost:${tempPort}/test`;
|
||||
|
||||
// Make the request to temp server
|
||||
fetch(url, fetchOptions)
|
||||
.then(response => {
|
||||
if (response.status !== 200) {
|
||||
tempServer.stop();
|
||||
reject(new Error(`Expected status 200, got ${response.status}`));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
tempServer.stop();
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
test.each([
|
||||
["close", "close"],
|
||||
["keep-alive", "keep-alive"],
|
||||
["upgrade", "upgrade"],
|
||||
["Upgrade", "Upgrade"], // Test case preservation
|
||||
])("should respect Connection: %s header", async (inputValue, expectedValue) => {
|
||||
const headers = await captureHeadersFromRequest({
|
||||
headers: { Connection: inputValue },
|
||||
});
|
||||
|
||||
expect(headers.connection).toBe(expectedValue);
|
||||
});
|
||||
|
||||
test.each([
|
||||
["connection", "close"],
|
||||
["Connection", "close"],
|
||||
["CONNECTION", "close"],
|
||||
])("should respect case-insensitive header name: %s", async (headerName, expectedValue) => {
|
||||
const headers = await captureHeadersFromRequest({
|
||||
headers: { [headerName]: expectedValue },
|
||||
});
|
||||
|
||||
expect(headers.connection).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should respect Connection header in Request object", async () => {
|
||||
const headers = await captureHeadersFromRequest({
|
||||
headers: { Connection: "close" },
|
||||
});
|
||||
expect(headers.connection).toBe("close");
|
||||
});
|
||||
|
||||
it("should default to keep-alive when no Connection header provided", async () => {
|
||||
const headers = await captureHeadersFromRequest({});
|
||||
expect(headers.connection).toBe("keep-alive");
|
||||
});
|
||||
|
||||
it("should handle multiple headers including Connection", async () => {
|
||||
const headers = await captureHeadersFromRequest({
|
||||
headers: {
|
||||
"accept": "application/json",
|
||||
"accept-encoding": "gzip, deflate",
|
||||
"accept-language": "en-US",
|
||||
"connection": "close",
|
||||
"user-agent": "test-agent",
|
||||
"x-test-header": "test-value",
|
||||
},
|
||||
});
|
||||
|
||||
expect(headers.connection).toBe("close");
|
||||
expect(headers.accept).toBe("application/json");
|
||||
expect(headers["x-test-header"]).toBe("test-value");
|
||||
});
|
||||
});
|
||||
@@ -519,6 +519,7 @@ test/js/web/fetch/client-fetch.test.ts
|
||||
test/js/web/fetch/content-length.test.js
|
||||
test/js/web/fetch/cookies.test.ts
|
||||
test/js/web/fetch/fetch-args.test.ts
|
||||
test/js/web/fetch/fetch-connection-header.test.ts
|
||||
test/js/web/fetch/fetch-gzip.test.ts
|
||||
test/js/web/fetch/fetch-preconnect.test.ts
|
||||
test/js/web/fetch/fetch-redirect.test.ts
|
||||
|
||||
Reference in New Issue
Block a user