Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 9519ccc

Browse files
committed
Wait for worker response before opening WebSocket in client, closes #88
1 parent d99b0d3 commit 9519ccc

File tree

2 files changed

+49
-51
lines changed

2 files changed

+49
-51
lines changed

packages/http-server/src/index.ts

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { randomHex } from "@miniflare/shared";
1919
import { coupleWebSocket } from "@miniflare/web-sockets";
2020
import { BodyInit, Headers } from "undici";
21-
import StandardWebSocket, { WebSocketServer } from "ws";
21+
import { WebSocketServer } from "ws";
2222
import { getAccessibleHosts } from "./helpers";
2323
import { HTTPPlugin, RequestMeta } from "./plugin";
2424

@@ -305,38 +305,6 @@ export function createRequestListener<Plugins extends HTTPPluginSignatures>(
305305
};
306306
}
307307

308-
export type WebSocketUpgradeListener = (
309-
ws: StandardWebSocket,
310-
req: http.IncomingMessage
311-
) => void;
312-
313-
export function createWebSocketUpgradeListener<
314-
Plugins extends CorePluginSignatures
315-
>(
316-
mf: MiniflareCore<Plugins>,
317-
listener: RequestListener
318-
): WebSocketUpgradeListener {
319-
return async (ws, req) => {
320-
// Handle request in worker
321-
const response = await listener(req);
322-
323-
// Check web socket response was returned
324-
const webSocket = response?.webSocket;
325-
if (response?.status !== 101 || !webSocket) {
326-
ws.close(1002, "Protocol Error");
327-
mf.log.error(
328-
new TypeError(
329-
"Web Socket request did not return status 101 Switching Protocols response with Web Socket"
330-
)
331-
);
332-
return;
333-
}
334-
335-
// Couple the web socket here
336-
await coupleWebSocket(ws, webSocket);
337-
};
338-
}
339-
340308
export async function createServer<Plugins extends HTTPPluginSignatures>(
341309
mf: MiniflareCore<Plugins>,
342310
options?: http.ServerOptions & https.ServerOptions
@@ -355,17 +323,39 @@ export async function createServer<Plugins extends HTTPPluginSignatures>(
355323
}
356324

357325
// Setup WebSocket servers
358-
const upgrader = createWebSocketUpgradeListener(mf, listener);
359326
const webSocketServer = new WebSocketServer({ noServer: true });
360-
webSocketServer.on("connection", upgrader);
361327
const liveReloadServer = new WebSocketServer({ noServer: true });
362-
server.on("upgrade", (request, socket, head) => {
328+
server.on("upgrade", async (request, socket, head) => {
329+
// Only interested in pathname so base URL doesn't matter
363330
const { pathname } = new URL(request.url ?? "", "http://localhost");
364-
const server =
365-
pathname === "/cdn-cgi/mf/reload" ? liveReloadServer : webSocketServer;
366-
server.handleUpgrade(request, socket as any, head, (ws) =>
367-
server.emit("connection", ws, request)
368-
);
331+
if (pathname === "/cdn-cgi/mf/reload") {
332+
// If this is the for live-reload, handle the request ourselves
333+
liveReloadServer.handleUpgrade(request, socket as any, head, (ws) => {
334+
liveReloadServer.emit("connection", ws, request);
335+
});
336+
} else {
337+
// Otherwise, handle the request in the worker
338+
const response = await listener(request);
339+
340+
// Check web socket response was returned
341+
const webSocket = response?.webSocket;
342+
if (response?.status !== 101 || !webSocket) {
343+
socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
344+
socket.destroy();
345+
mf.log.error(
346+
new TypeError(
347+
"Web Socket request did not return status 101 Switching Protocols response with Web Socket"
348+
)
349+
);
350+
return;
351+
}
352+
353+
// Accept and couple the Web Socket
354+
webSocketServer.handleUpgrade(request, socket as any, head, (ws) => {
355+
void coupleWebSocket(ws, webSocket);
356+
webSocketServer.emit("connection", ws, request);
357+
});
358+
}
369359
});
370360
const reloadListener = () => {
371361
// Reload all connected live reload clients

packages/http-server/test/index.spec.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ import {
3232
} from "@miniflare/shared-test";
3333
import { MessageEvent, WebSocketPlugin } from "@miniflare/web-sockets";
3434
import test, { ExecutionContext, Macro } from "ava";
35-
import StandardWebSocket, { Data, Event as WebSocketEvent } from "ws";
35+
import StandardWebSocket, {
36+
Data,
37+
CloseEvent as WebSocketCloseEvent,
38+
ErrorEvent as WebSocketErrorEvent,
39+
Event as WebSocketEvent,
40+
} from "ws";
3641

3742
function listen(
3843
t: ExecutionContext,
@@ -487,7 +492,10 @@ test("createServer: handles web socket upgrades", async (t) => {
487492
const mf = useMiniflareWithHandler(
488493
{ HTTPPlugin, WebSocketPlugin },
489494
{},
490-
(globals) => {
495+
async (globals) => {
496+
// Simulate slow response, WebSocket must not open until worker responds
497+
await new Promise((resolve) => globals.setTimeout(resolve, 1000));
498+
491499
const [client, worker] = Object.values(new globals.WebSocketPair());
492500
worker.accept();
493501
worker.addEventListener("message", (e: MessageEvent) => {
@@ -523,18 +531,18 @@ test("createServer: expects status 101 and web socket response for upgrades", as
523531
const port = await listen(t, await createServer(mf));
524532

525533
const ws = new StandardWebSocket(`ws://localhost:${port}`);
526-
const [eventTrigger, eventPromise] = triggerPromise<{
527-
code: number;
528-
reason: string;
529-
}>();
530-
ws.addEventListener("close", eventTrigger);
531-
const event = await eventPromise;
534+
const [closeTrigger, closePromise] = triggerPromise<WebSocketCloseEvent>();
535+
const [errorTrigger, errorPromise] = triggerPromise<WebSocketErrorEvent>();
536+
ws.addEventListener("close", closeTrigger);
537+
ws.addEventListener("error", errorTrigger);
538+
const closeEvent = await closePromise;
539+
const errorEvent = await errorPromise;
532540

533541
t.deepEqual(log.logsAtLevel(LogLevel.ERROR), [
534542
"TypeError: Web Socket request did not return status 101 Switching Protocols response with Web Socket",
535543
]);
536-
t.is(event.code, 1002);
537-
t.is(event.reason, "Protocol Error");
544+
t.is(closeEvent.code, 1006);
545+
t.is(errorEvent.message, "Unexpected server response: 500");
538546
});
539547
test("createServer: notifies connected live reload clients on reload", async (t) => {
540548
const mf = useMiniflareWithHandler({ HTTPPlugin }, {}, (globals) => {

0 commit comments

Comments
 (0)