Tunneled WebSocket receives a second Close (1012) after a clean Close; browsers log "Close received after close"
ajoslin opened ·
## Reproduction (v0.4.1)
Two nodes on one bucket. Open a WebSocket through node B to a Durable Object cell owned by node A, then have the client send Close 1000. The client receives:
``` close code=1000 reason=<echoed> close code=1012 reason=owner unavailable ```
Chromium fails the channel with a protocol error and logs `WebSocket connection to ... failed: Close received after close` for every socket that closes through a tunnel. On a multi-node fleet that is most sockets.
## Cause
`crates/celld/main/peer_tunnel.rs::splice` copies bytes between client and owner and, when the owner side ends, always writes a 1012 Close to the client. The comment assumes a client that already received a clean Close ignores the extra frame; RFC 6455 clients do not (Chromium `WebSocketChannel::HandleFrameByState` fails the channel in `CLOSE_WAIT`).
## Fix
Fork commit https://github.com/ajoslin/celld/commit/9d43b8a tracks WebSocket frame boundaries on the owner-to-client copy (`WebSocketCloseScanner` in `celld-logic`, no payload buffering) and writes the 1012 only when no Close crossed. Verified with the two-node probe above and `cargo build --release --locked`. I could not open a PR (`CreatePullRequest` permission denied for external forks); happy to if you enable it or prefer to cherry-pick.
- RY
fix has been landed and will be released in 0.5.0 soon
ryclosedlast month on GitHub
ryreferenced e519694581dclast month on GitHub
ryreferenced 4250d366aceblast month on GitHub
ryreferenced 3127cf8e307elast month on GitHub
ryreferenced 12d5b6333fe5last month on GitHub