Skip to content

Fix CPU spin on half-closed WebSocket / silent body truncation#828

Merged
ademar merged 2 commits into
masterfrom
fix-cpu-spin-half-open-websocket
May 30, 2026
Merged

Fix CPU spin on half-closed WebSocket / silent body truncation#828
ademar merged 2 commits into
masterfrom
fix-cpu-spin-half-open-websocket

Conversation

@ademar

@ademar ademar commented May 30, 2026

Copy link
Copy Markdown
Member

readPostData aborts silently on EOF or cancellation, leaving the caller with a zero-filled buffer and no indication that the read was short.

In WebSocket.readBytes / readBytesIntoByteSegment this caused the frame parser to see opcode=0 length=0 (a zero-length Continuation frame), return immediately, and loop forever - pegging a CPU core after the peer half-closed the connection.

In ConnectionFacade.getRawPostData the same bug silently truncated HTTP/1 request bodies, returning Ok with a partially-zeroed buffer instead of surfacing the broken connection.

The HTTP/2 reader (Http2.readBytes) already guards against this with an 'if offset = n' check; apply the same fix to the other three call sites.

Added a regression test in src/Suave.Tests/WebSocket.fs that opens a raw TCP socket, performs a valid WebSocket upgrade handshake, half-closes the client send side, and asserts the handler exits within a deadline.

readPostData aborts silently on EOF or cancellation, leaving the caller
with a zero-filled buffer and no indication that the read was short.

In WebSocket.readBytes / readBytesIntoByteSegment this caused the frame
parser to see opcode=0 length=0 (a zero-length Continuation frame),
return immediately, and loop forever - pegging a CPU core after the
peer half-closed the connection.

In ConnectionFacade.getRawPostData the same bug silently truncated
HTTP/1 request bodies, returning Ok with a partially-zeroed buffer
instead of surfacing the broken connection.

The HTTP/2 reader (Http2.readBytes) already guards against this with
an 'if offset = n' check; apply the same fix to the other three
call sites.
@ademar

ademar commented May 30, 2026

Copy link
Copy Markdown
Member Author

@copilot Can you produce a test that opens a TCP socket, sends a valid WebSocket upgrade, then half-closes — and asserts the handler exits within a deadline.

Copilot AI commented May 30, 2026

Copy link
Copy Markdown
Contributor

@copilot Can you produce a test that opens a TCP socket, sends a valid WebSocket upgrade, then half-closes — and asserts the handler exits within a deadline.

Added in 3ee47f6. There is now a regression test in /src/Suave.Tests/WebSocket.fs that opens a raw TcpClient, sends a valid WebSocket upgrade request, half-closes the socket (SocketShutdown.Send), and asserts the handler exits within 2 seconds.

Copilot finished work on behalf of ademar May 30, 2026 19:55
@ademar ademar merged commit 711a7f9 into master May 30, 2026
2 checks passed
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.

2 participants