itty-durable: When trying to use websockets the connection hangs on trying to connect
I was attempting to use WebSockets and whenever using itty-durable the connection hangs on trying to connect. Below is a small little code sample of what I am using to test the WebSockets. When I use it outside the scope of itty-durable (just on a normal route) it works fine.
import { ThrowableRouter } from 'itty-router-extras'
import { createIttyDurable, withDurables } from 'itty-durable'
export class TestWebsocket extends createIttyDurable({ persistOnChange: false }) {
constructor(state, env) {
super(state, env)
}
websocket() {
const [client, websocket] = Object.values(new WebSocketPair())
websocket.accept()
websocket.send(JSON.stringify({ connected: true }))
websocket.addEventListener('message', ({ data }) => {
websocket.send(JSON.stringify({ data }))
})
return new Response(null, { status: 101, webSocket: client })
}
}
const router = ThrowableRouter()
router.all('*', withDurables())
// Doesnt work
router.get('/ws', ({ TestWebsocket }) => TestWebsocket.get('test').websocket())
// Works
router.get('/ws2', () => {
const [client, websocket] = Object.values(new WebSocketPair())
websocket.accept()
websocket.send(JSON.stringify({ connected: true }))
websocket.addEventListener('message', ({ data }) => {
websocket.send(JSON.stringify({ data }))
})
return new Response(null, { status: 101, webSocket: client })
})
export default {
fetch: router.handle
}
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 18 (7 by maintainers)
Ok, so yesterday I successfully got itty-durable (modified version) to play nicely with, and return a websocket.
A few things to note:
upgrade: websocketheader from the DO request causes a failure. Original Worker request headers will now be passed along to the DO to remedy.do-contentheader, rather than the body itself.catchErrors) within theproxy-durablehad to be modified, as a 101 response from within the DO would trigger a!response.okerror.withDurables({ parse: true })flag, allowing the Response from within the DO to be passed directly back to the original Worker request.Would also like to contribute to make this happen, and before doing any work I’d like to summarize what (I think?) the challenges might be:
Upgrade: websocketheader needs to be sent to the DO when making a proxy’d function fetch. In doing this, maybe it makes sense to forward all headers of the current request over to the DO? This is assuming users ofitty-durablewill write something likeDurableObject.connect({ connectionData }), whereconnectis a user defined DO method that returns a Response with awebSocketproperty.DurableObject.connect({ connectionData }), currently, is only ever aPOSTrequest. But, from what I can tell, WebSocket connections MUST be initiated with aGETrequest. It’s weird because on Miniflare (--local) aPOSTrequest works just fine, so long as I’ve got the header. On “non-local” dev mode with wrangler, using aPOSTrequest with this setup just crashes wrangler completely. This line inkjmight have to do with it? Not sure, maybe I’m just not doing it right. Either way, it seems like it’d be good to use aGETrequest since that’s standard practice.Going about implementing all this, forwarding all request headers through with the proxy’d function fetch request doesn’t seem too difficult IF that’s a sensible solution. Maybe forwarding all headers is a bad idea? I’m not sure.
With the
POSTvsGETissue, it seems like the proxy’d function fetches withPOSTare pretty ingrained in the system, so breaking out of that for WebSockets could be less pretty. Perhaps theproxyDurablefunction’sgethandler could detect theUpgrade: websocketheader, and thus perform aGETrequest instead of aPOST? This wouldn’t support a request body though, which could instead be turned into URL params and be picked up by theitty-durableDO internal router.Dug into this a bit and it would appear that there’s a header issue or maybe just a response issue where the
webSocket: clientisn’t being forwarded when responding. This is either before the pass back or actually in the pass in where theheaders.Upgrade: 'websocket'isn’t being passed to the method call into the DO.