Skip to content

Commit 185baef

Browse files
Copilotkitsonk
andauthored
Fix WebSocket upgrade failure when using app.fetch with deno serve (#710)
Fixes: #689 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kitsonk <1282577+kitsonk@users.noreply.github.com> Co-authored-by: Kitson Kelly <me@kitsonkelly.com>
1 parent 18cdfde commit 185baef

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

application.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,52 @@ Deno.test({
998998
},
999999
});
10001000

1001+
Deno.test({
1002+
name: "application .fetch() websocket upgrade",
1003+
ignore: isNode(),
1004+
sanitizeOps: false,
1005+
sanitizeResources: false,
1006+
async fn() {
1007+
const app = new Application();
1008+
let wsReceived = "";
1009+
app.use((ctx) => {
1010+
const ws = ctx.upgrade();
1011+
ws.onmessage = (event) => {
1012+
wsReceived = event.data;
1013+
ws.send("pong");
1014+
ws.close();
1015+
};
1016+
});
1017+
1018+
const controller = new AbortController();
1019+
let port = 0;
1020+
const { promise: listeningPromise, resolve: listeningResolve } =
1021+
createPromiseWithResolvers<void>();
1022+
const server = Deno.serve({
1023+
signal: controller.signal,
1024+
port: 0,
1025+
handler: app.fetch as (req: Request) => Promise<Response>,
1026+
onListen({ port: p }) {
1027+
port = p;
1028+
listeningResolve();
1029+
},
1030+
});
1031+
await listeningPromise;
1032+
1033+
const { promise, resolve, reject } = createPromiseWithResolvers<string>();
1034+
const ws = new WebSocket(`ws://localhost:${port}/`);
1035+
ws.onopen = () => ws.send("ping");
1036+
ws.onmessage = (event) => resolve(event.data);
1037+
ws.onerror = () => reject(new Error("WebSocket error"));
1038+
const reply = await promise;
1039+
assertEquals(reply, "pong");
1040+
assertEquals(wsReceived, "ping");
1041+
controller.abort();
1042+
await server.finished;
1043+
teardown();
1044+
},
1045+
});
1046+
10011047
function isBigInitValue(value: unknown): value is { __bigint: string } {
10021048
return value != null && typeof value === "object" && "__bigint" in value &&
10031049
typeof (value as any).__bigint === "string";

application.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,10 @@ export class Application<AS extends State = Record<string, any>>
718718
);
719719
try {
720720
await this.#getComposed()(context);
721+
if (context.respond === false) {
722+
context.response.destroy();
723+
return contextRequest.response;
724+
}
721725
const response = await context.response.toDomResponse();
722726
context.response.destroy(false);
723727
return response;

0 commit comments

Comments
 (0)