Skip to content

fix: skip body read when Dial fails after a 101 response - #591

Draft
mafredri wants to merge 1 commit into
masterfrom
mafredri/dial-skip-101-body-read
Draft

mafredri wants to merge 1 commit into
masterfrom
mafredri/dial-skip-101-body-read

Conversation

@mafredri

Copy link
Copy Markdown
Member

When the server answers 101 Switching Protocols but the handshake still fails, for example on an invalid Sec-WebSocket-Accept or a subprotocol the client did not offer, Dial now closes the response body and returns at once.

Previously, every Dial that failed after receiving a response read up to 1024 bytes of the response body for debugging, with a 3 second timer. After a 101 the body is the upgraded connection, not an HTTP body, so when the server sent nothing more, each of these failures took 3 seconds.

For these failures the returned response keeps its status and headers, and resp.Body is empty (http.NoBody). Failures with any other status are unchanged: resp.Body holds up to the first 1024 bytes, read with the same 3 second limit.

The tradeoff: if a server answers 101 and then writes error text on the connection, resp.Body no longer contains that text. Bytes after a 101 belong to the upgraded protocol, not to an HTTP body.

TestDialFailedHandshakeBody covers a 101 with an invalid Sec-WebSocket-Accept from a server that then stays silent (fails on master after 3.0 s; passes with this change in about 0 s against a 1 s limit) and a 403 whose body is still returned. Lint, race tests and the WASM test pass.

Decision log
  • Skip the read only for status 101: other statuses have a real HTTP body, and its first bytes often explain the rejection.
  • Return http.NoBody instead of a nil resp.Body: when Dial returns a response with an error, resp.Body has never been nil, so callers that read it need no new nil check.
  • Rejected: a shorter timer for 101. Each such failure would still wait, and the body would still contain WebSocket bytes.
  • Rejected: changing the Dial doc. Its text ("you can only read the first 1024 bytes of the body") stays true with an empty body.
  • Test limit of 1 s: far from master's 3 s and from the fix's near 0 s, so a slow CI machine does not flip the result.

🤖 This PR was created with the help of Coder Agents.

When the server answered 101 but a header check failed, Dial read up to
1024 bytes from the response body before returning. After a 101 the body
is the upgraded connection, so a silent server made Dial wait for the 3
s timer. Close it instead and return an empty body; other statuses still
return the first 1024 bytes.

This branch has not been deployed

No deployments
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