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 Report β€” Durable Object websocket not working with Hono #2468

Closed
Soabbar opened this issue Jul 31, 2024 · 2 comments
Closed

Comments

@Soabbar
Copy link

Soabbar commented Jul 31, 2024

What version of Hono are you using?

4.5.2

What runtime/platform is your app running on?

Cloudflare Worker

What steps can reproduce the bug?

Here is my route

route.get("/ws/:id", ...rideController.getAllWS);

Here is the controller

export const getAllWS = factory.createHandlers(
	upgradeWebSocket(async (c) => {
		const upgradeHeader = c.req.header("Upgrade");
		if (!upgradeHeader || upgradeHeader !== "websocket") {
			return new Response("Durable Object expected Upgrade: websocket", {
				status: 426,
			});
		}
		const id = c.env.WEBSOCKET_MANAGER.idFromName("global");
		const stub = c.env.WEBSOCKET_MANAGER.get(id);
		return await stub.fetch(c.req.raw);
	}),
);

Here is my durable Object coming from Cloudflare docs... https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server/

import { DurableObject } from "cloudflare:workers";

export class WebSocketManager extends DurableObject {
	async fetch(request) {
		// Creates two ends of a WebSocket connection.
		const webSocketPair = new WebSocketPair();
		const [client, server] = Object.values(webSocketPair);

		// Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
		// request within the Durable Object. It has the effect of "accepting" the connection,
		// and allowing the WebSocket to send and receive messages.
		// Unlike `ws.accept()`, `state.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
		// is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
		// the connection is open. During periods of inactivity, the Durable Object can be evicted
		// from memory, but the WebSocket connection will remain open. If at some later point the
		// WebSocket receives a message, the runtime will recreate the Durable Object
		// (run the `constructor`) and deliver the message to the appropriate handler.
		this.ctx.acceptWebSocket(server);

		return new Response(null, {
			status: 101,
			webSocket: client,
		});
	}

	async webSocketMessage(ws, message) {
		// Upon receiving a message from the client, reply with the same message,
		// but will prefix the message with "[Durable Object]: " and return the
		// total number of connections.
		ws.send(
			`[Durable Object] message: ${message}, connections: ${this.ctx.getWebSockets().length}`,
		);
	}

	async webSocketClose(ws, code, reason, wasClean) {
		// If the client closes the connection, the runtime will invoke the webSocketClose() handler.
		ws.close(code, "Durable Object is closing WebSocket");
	}
}

Here is my wrangler.toml

[durable_objects]
bindings = [{ name = "WEBSOCKET_MANAGER", class_name = "WebSocketManager" }]

[[migrations]]
tag = "v1"
new_classes = ["WebSocketManager"]

What is the expected behavior?

I am suppose to connect, send a message through websocket and receive an answer back from it.

What do you see instead?

I can connect to the websocket, but if I send anything, I have no answer back or any errors.
The main issue is that webSocketMessage is never triggered....

Additional information

I have deployed this Durable object online, but still having the same behaviour.
Does Hono go through some specific process I am not aware of?

Is the controller part correct?

@wa-hab
Copy link

wa-hab commented Aug 25, 2024

hey, did you manage to fix this?

@Soabbar
Copy link
Author

Soabbar commented Aug 26, 2024

Yes, a misunderstanding from my end. The initial call shouldn't be a websocket.
So the controller look like such

export const getAllWS = factory.createHandlers(async (c) => {
		const id = c.env.WEBSOCKET_MANAGER.idFromName("global");
		const stub = c.env.WEBSOCKET_MANAGER.get(id);
		return await stub.fetch(c.req.raw);
	}),
);

Then the ws connection will be done properly.

@Soabbar Soabbar closed this as completed Aug 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants