Summary
Deno.upgradeWebSocket() does not work with requests originating from node:http server upgrade events. This makes it impossible to handle WebSocket upgrades in frameworks that use Node's http.Server under the hood (e.g. Vite's dev server).
Context
Fresh (Deno's web framework) uses Vite for its dev server. Vite creates a Node.js http.Server internally. When a WebSocket upgrade request comes in, Vite fires the upgrade event on the http.Server — but this never reaches Connect middleware, and there's no way to bridge it to Deno.upgradeWebSocket().
The user's Fresh handler code looks like:
export const handler = {
GET(ctx: FreshContext) {
const { socket, response } = Deno.upgradeWebSocket(ctx.req);
socket.addEventListener("message", (event) => {
socket.send(`echo: ${event.data}`);
});
return response;
},
};
This works in production (via Deno.serve()) but fails in dev because the request comes through node:http.
Current behavior
Calling Deno.upgradeWebSocket() with a request from a node:http upgrade event fails with upgrade unavailable. Even constructing a new Request() manually and passing it to Deno.upgradeWebSocket() returns a 101 response and WebSocket object, but the WebSocket never connects (readyState stays at 0) because there's no underlying transport.
Expected behavior
Deno.upgradeWebSocket() should be able to work with WebSocket upgrade requests from node:http servers, either by:
- Supporting binding the returned WebSocket to the raw
net.Socket from the upgrade event
- Having Deno's
node:http compat layer automatically bridge upgrade events to Deno's WebSocket infrastructure
- Providing an API like
Deno.upgradeWebSocket(req, { socket }) that accepts a Node socket
Impact
This affects any Deno project using a Node-based HTTP server that needs WebSocket support:
Current workarounds
The only working workaround is to spin up a second Deno.serve() on a random port and proxy upgrade requests to it via raw TCP socket forwarding (freshframework/fresh#3737), which is heavy-handed.
Related
Summary
Deno.upgradeWebSocket()does not work with requests originating fromnode:httpserverupgradeevents. This makes it impossible to handle WebSocket upgrades in frameworks that use Node'shttp.Serverunder the hood (e.g. Vite's dev server).Context
Fresh (Deno's web framework) uses Vite for its dev server. Vite creates a Node.js
http.Serverinternally. When a WebSocket upgrade request comes in, Vite fires theupgradeevent on the http.Server — but this never reaches Connect middleware, and there's no way to bridge it toDeno.upgradeWebSocket().The user's Fresh handler code looks like:
This works in production (via
Deno.serve()) but fails in dev because the request comes throughnode:http.Current behavior
Calling
Deno.upgradeWebSocket()with a request from anode:httpupgrade event fails withupgrade unavailable. Even constructing anew Request()manually and passing it toDeno.upgradeWebSocket()returns a 101 response and WebSocket object, but the WebSocket never connects (readyStatestays at 0) because there's no underlying transport.Expected behavior
Deno.upgradeWebSocket()should be able to work with WebSocket upgrade requests fromnode:httpservers, either by:net.Socketfrom theupgradeeventnode:httpcompat layer automatically bridge upgrade events to Deno's WebSocket infrastructureDeno.upgradeWebSocket(req, { socket })that accepts a Node socketImpact
This affects any Deno project using a Node-based HTTP server that needs WebSocket support:
node:httpunder the hoodCurrent workarounds
The only working workaround is to spin up a second
Deno.serve()on a random port and proxy upgrade requests to it via raw TCP socket forwarding (freshframework/fresh#3737), which is heavy-handed.Related
deno:requestevent (dead end)