oak
oak copied to clipboard
Bug: `deno serve` causes websocket upgrade to fail
Code:
// main.ts
import { Application, Router } from '@oak/oak';
const router = new Router();
router.get('/wss', (context) => {
const socket = context.upgrade();
socket.onmessage = (event) => {
console.log(event.data);
socket.send('Hello, WebSocket! [Server]');
};
});
router.get('/', (context) => {
context.response.body = `
<h1>Hello, Oak!</h1>
<script>
const ws = new WebSocket('ws://localhost:8000/wss');
ws.onopen = () => {
console.log('WebSocket connection established!');
ws.send('Hello, WebSocket!');
};
ws.onmessage = (event) => {
console.log(event.data);
};
</script>
`;
context.response.type = 'text/html';
});
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
export default { fetch: app.fetch };
deno serve main.ts
Expected Result:
Success
Actual Result:
deno serve: Listening on http://0.0.0.0:8000/
Upgrade response was not returned from callback
Note:
This works correctly when using Hono and following their Deno instructions:
As a follow up, by using the underlying Deno APIs, this problem can be worked around:
// this works
import { Application, Router } from "@oak/oak";
const router = new Router();
router.get("/wss", (context) => {
const { socket, response } = Deno.upgradeWebSocket(context.request.source!);
socket.onmessage = (event) => {
console.log(event.data);
socket.send("Hello, WebSocket! [Server]");
};
context.response.with(response);
});
router.get("/", (context) => {
context.response.body = `
<h1>Hello, Oak!</h1>
<script>
const ws = new WebSocket('ws://localhost:8000/wss');
ws.onopen = () => {
console.log('WebSocket connection established!');
ws.send('Hello, WebSocket!');
};
ws.onmessage = (event) => {
console.log(event.data);
};
</script>
`;
context.response.type = "text/html";
});
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
export default {
fetch: app.fetch,
};
Thanks for the reproduction (and work around). I have never validated oak against a deno serve use case actually but need to do that.
FYI, I also tested SSE, and there is a similar issue with deno serve
import { Application, Context, Router, Status } from "@oak/oak";
const router = new Router();
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Server-Sent Events</title>
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/[email protected]/sse.js"></script>
</head>
<body>
<div hx-ext="sse" sse-connect="/sse" sse-swap="message"></div>
</body>
</html>
`;
router.get(
"/",
(ctx) => {
ctx.response.body = html;
},
).get(
"/sse",
async (ctx: Context) => {
ctx.assert(
ctx.request.accepts("text/event-stream"),
Status.UnsupportedMediaType,
);
const target = await ctx.sendEvents();
target.dispatchMessage("<h2>hello</h2>");
},
);
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
export default { fetch: app.fetch };
Expected Result
<h2> shows up in the DOM
Actual Result
404 Error
Notes
This example works fine when using app.listen