Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
2f9006e47f fix: preserve request_context when cloning/constructing Request objects
`server.upgrade()` relies on the internal `request_context` field to
perform WebSocket upgrades. When a Request was cloned via `req.clone()`
or constructed via `new Request(url, req)`, the `request_context` was
not copied to the new object, causing `server.upgrade()` to return
`false`.

Closes #11382

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-20 02:28:26 +00:00
2 changed files with 107 additions and 0 deletions

View File

@@ -703,6 +703,8 @@ pub fn constructInto(globalThis: *jsc.JSGlobalObject, arguments: []const jsc.JSV
},
}
}
req.request_context = request.request_context;
}
if (value.asDirect(Response)) |response| {
@@ -1067,6 +1069,7 @@ pub fn cloneInto(
.method = this.method,
.flags = this.flags,
.#headers = headers,
.request_context = this.request_context,
};
if (this.signal) |signal| {

View File

@@ -0,0 +1,104 @@
import { describe, expect, test } from "bun:test";
describe("server.upgrade() with non-original Request", () => {
test("new Request(req.url, req) should upgrade successfully", async () => {
using server = Bun.serve({
port: 0,
fetch(req, server) {
const newReq = new Request(req.url, req);
if (server.upgrade(newReq)) return;
return new Response("Upgrade failed", { status: 500 });
},
websocket: {
open(ws) {
ws.send("hello");
},
message() {},
},
});
const { promise, resolve, reject } = Promise.withResolvers<string>();
const ws = new WebSocket(`ws://localhost:${server.port}`);
ws.onmessage = e => resolve(e.data);
ws.onerror = () => reject(new Error("WebSocket error"));
expect(await promise).toBe("hello");
ws.close();
});
test("req.clone() should upgrade successfully", async () => {
using server = Bun.serve({
port: 0,
fetch(req, server) {
const cloned = req.clone();
if (server.upgrade(cloned)) return;
return new Response("Upgrade failed", { status: 500 });
},
websocket: {
open(ws) {
ws.send("hello");
},
message() {},
},
});
const { promise, resolve, reject } = Promise.withResolvers<string>();
const ws = new WebSocket(`ws://localhost:${server.port}`);
ws.onmessage = e => resolve(e.data);
ws.onerror = () => reject(new Error("WebSocket error"));
expect(await promise).toBe("hello");
ws.close();
});
test("new Request(req) should upgrade successfully", async () => {
using server = Bun.serve({
port: 0,
fetch(req, server) {
const newReq = new Request(req);
if (server.upgrade(newReq)) return;
return new Response("Upgrade failed", { status: 500 });
},
websocket: {
open(ws) {
ws.send("hello");
},
message() {},
},
});
const { promise, resolve, reject } = Promise.withResolvers<string>();
const ws = new WebSocket(`ws://localhost:${server.port}`);
ws.onmessage = e => resolve(e.data);
ws.onerror = () => reject(new Error("WebSocket error"));
expect(await promise).toBe("hello");
ws.close();
});
test("original req should still upgrade after clone", async () => {
using server = Bun.serve({
port: 0,
fetch(req, server) {
// Clone the request but upgrade the original
const _cloned = req.clone();
if (server.upgrade(req)) return;
return new Response("Upgrade failed", { status: 500 });
},
websocket: {
open(ws) {
ws.send("hello");
},
message() {},
},
});
const { promise, resolve, reject } = Promise.withResolvers<string>();
const ws = new WebSocket(`ws://localhost:${server.port}`);
ws.onmessage = e => resolve(e.data);
ws.onerror = () => reject(new Error("WebSocket error"));
expect(await promise).toBe("hello");
ws.close();
});
});