Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: deno serve causes websocket upgrade to fail #689

Open
jkonowitch opened this issue Jan 9, 2025 · 3 comments
Open

Bug: deno serve causes websocket upgrade to fail #689

jkonowitch opened this issue Jan 9, 2025 · 3 comments
Labels
bug Something isn't working needs investigation Something that needs to be looked at

Comments

@jkonowitch
Copy link

jkonowitch commented Jan 9, 2025

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:

image

@jkonowitch
Copy link
Author

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,
};

@kitsonk kitsonk added bug Something isn't working needs investigation Something that needs to be looked at labels Jan 9, 2025
@kitsonk
Copy link
Collaborator

kitsonk commented Jan 9, 2025

Thanks for the reproduction (and work around). I have never validated oak against a deno serve use case actually but need to do that.

@jkonowitch
Copy link
Author

jkonowitch commented Jan 19, 2025

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs investigation Something that needs to be looked at
Projects
None yet
Development

No branches or pull requests

2 participants