diff --git a/packages/tre/src/http/websocket.ts b/packages/tre/src/http/websocket.ts index 2f1ab39f7..572c89569 100644 --- a/packages/tre/src/http/websocket.ts +++ b/packages/tre/src/http/websocket.ts @@ -267,6 +267,8 @@ export async function coupleWebSocket( pair.addEventListener("close", (e) => { if (e.code === 1005 /* No Status Received */) { ws.close(); + } else if (e.code === 1006 /* Abnormal Closure */) { + ws.terminate(); } else { ws.close(e.code, e.reason); } diff --git a/packages/tre/src/index.ts b/packages/tre/src/index.ts index d6329701c..e1e700deb 100644 --- a/packages/tre/src/index.ts +++ b/packages/tre/src/index.ts @@ -240,7 +240,13 @@ export class Miniflare { this.#log.debug(`Running workerd ${desc}...`); this.#liveReloadServer = new WebSocketServer({ noServer: true }); - this.#webSocketServer = new WebSocketServer({ noServer: true }); + this.#webSocketServer = new WebSocketServer({ + noServer: true, + // Disable automatic handling of `Sec-WebSocket-Protocol` header, + // Cloudflare Workers require users to include this header themselves in + // `Response`s: https://github.com/cloudflare/miniflare/issues/179 + handleProtocols: () => false, + }); // Add custom headers included in response to WebSocket upgrade requests this.#webSocketExtraHeaders = new WeakMap(); this.#webSocketServer.on("headers", (headers, req) => { diff --git a/packages/tre/test/index.spec.ts b/packages/tre/test/index.spec.ts index 725504bd2..90a5511f9 100644 --- a/packages/tre/test/index.spec.ts +++ b/packages/tre/test/index.spec.ts @@ -23,7 +23,13 @@ test("Miniflare: web socket kitchen sink", async (t) => { // Create WebSocket origin server const server = http.createServer(); - const wss = new WebSocketServer({ server }); + const wss = new WebSocketServer({ + server, + handleProtocols(protocols) { + t.deepEqual(protocols, new Set(["protocol1", "protocol2"])); + return "protocol2"; + }, + }); wss.on("connection", (ws, req) => { // Testing receiving additional headers sent from upgrade request t.is(req.headers["user-agent"], "Test"); @@ -63,7 +69,11 @@ test("Miniflare: web socket kitchen sink", async (t) => { // Testing dispatchFetch WebSocket coupling const res = await mf.dispatchFetch("http://localhost", { - headers: { Upgrade: "websocket", "User-Agent": "Test" }, + headers: { + Upgrade: "websocket", + "User-Agent": "Test", + "Sec-WebSocket-Protocol": "protocol1, protocol2", + }, cf: { country: "MF" }, }); @@ -74,6 +84,7 @@ test("Miniflare: web socket kitchen sink", async (t) => { res.webSocket.close(1000, "Test Closure"); // Test receiving additional headers from upgrade response t.is(res.headers.get("Set-Cookie"), "key=value"); + t.is(res.headers.get("Sec-WebSocket-Protocol"), "protocol2"); // Check event results const clientEvent = await clientEventPromise;