Skip to content

fix(server): don't close the handle twice after a failed handshake - #322

Draft
lucidstack wants to merge 1 commit into
coder:mainfrom
lucidstack:ar/fix-double-close-after-failed-handshake
Draft

lucidstack wants to merge 1 commit into
coder:mainfrom
lucidstack:ar/fix-double-close-after-failed-handshake

Conversation

@lucidstack

Copy link
Copy Markdown

What

Guard the scheduled tcp_handle:close() after a failed handshake with is_closing(), like close_client and tcp.lua's _remove_client already do, and add a spec for both orderings.

Why

I kept seeing this a few seconds after the server came up:

[ClaudeCode] [client] [WARN] WebSocket handshake failed for client uv_tcp_t: 0x08733fc640: Bad Request
…
vim.schedule callback: …/claudecode/server/client.lua:108: handle 0x08733fc640 is already closing

The culprit was a local dev-server discovery daemon sending GET / to every new loopback port. The WARN is fair enough — it isn't a WebSocket upgrade — but the error isn't: the daemon hangs up as soon as it reads the 400, so the EOF teardown in tcp.lua closes the handle before the scheduled close runs, and that one then raises 😅

Any client that hangs up straight after the response reproduces it (PORT from :ClaudeCodeStatus):

python3 -c 'import socket; s = socket.create_connection(("127.0.0.1", PORT)); s.sendall(b"GET / HTTP/1.1\r\nHost: x\r\n\r\n"); s.recv(200); s.close()'

curl didn't trigger it for me, as it hangs up a little later.. so it's a race, and fast clients win it.

A client that hangs up as soon as it reads the 400, like a local port
scanner probing for web servers, has its handle closed by the EOF
teardown before the scheduled close runs, which then raises "handle …
is already closing".
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

Successfully merging this pull request may close these issues.

1 participant