Skip to content

Add handshake and idle read timeouts to WebSockets.listen! - #1491

Merged
quinnj merged 3 commits into
masterfrom
claude/ws-server-timeouts
Oct 22, 2026
Merged

quinnj merged 3 commits into
masterfrom
claude/ws-server-timeouts

Conversation

@quinnj

@quinnj quinnj commented Oct 22, 2026

Copy link
Copy Markdown
Member

WebSockets.listen! had no timeouts and no way to set one. A client that connected and never finished its handshake, or went silent after it, held a task and a socket for as long as it liked.

WebSockets.listen!, and WebSockets.serve! with it, now take read_header_timeout, read_timeout and idle_timeout, in seconds or as _ns values, with the meaning and off-by-default values they have in HTTP.serve!:

  • read_header_timeout sets a read deadline from accepting the connection to the end of the upgrade request. That covers the TLS handshake on a wss server.
  • idle_timeout becomes the session's read idle timeout. The read loop already supports that timeout, re-arming it before each read and closing with 1006 websocket read idle timeout when it expires.
  • Both fall back to read_timeout when they are 0, as in HTTP.serve!.

Passing a value in both seconds and _ns form throws ArgumentError, via the same helper serve! uses. The defaults stay 0 (off). Whether to turn them on is a separate decision.

Verification. New testset in test/http_websocket_server_tests.jl that uses already-expired 1 ns timeouts. With read_header_timeout_ns = 1, a client that sends nothing is disconnected. With idle_timeout_ns = 1, the handler's receive throws WebSocketError with reason websocket read idle timeout. On master the keywords do not exist. http_websocket_server_tests.jl (68) and http_websocket_integration_tests.jl pass locally.

Review. The handshake deadline is cleared right after the request is read, so a slow handler or the session is not cut by it. The idle timeout goes through the read_idle_timeout_ns field that the client's read_idle_timeout already uses, so both sides share one code path. HTTP.listen! with WebSockets.upgrade is unchanged: the HTTP server's own deadlines apply there until upgrade clears them. This PR and #1482 both edit _serve_ws_conn! around read_request, so whichever merges second needs a small rebase.

🤖 Generated with Claude Code

quinnj and others added 2 commits October 22, 2026 05:33
The WebSocket server had no timeouts and no way to set one, so a client that
connected and never finished the handshake, or went silent after it, held a
task and a socket for as long as it liked. WebSockets.listen! (and serve!) now
take read_header_timeout, read_timeout and idle_timeout, in seconds or as _ns
values, with HTTP.serve!'s meaning and its off-by-default values:
read_header_timeout bounds the handshake request, idle_timeout closes a
session on which nothing arrives for that long, and both fall back to
read_timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.68%. Comparing base (564fa63) to head (dae821f).
⚠️ Report is 21 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1491      +/-   ##
==========================================
+ Coverage   91.12%   91.68%   +0.56%     
==========================================
  Files          31       31              
  Lines       12535    13208     +673     
==========================================
+ Hits        11422    12110     +688     
+ Misses       1113     1098      -15     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@quinnj
quinnj merged commit dd4c418 into master Oct 22, 2026
0 of 8 checks passed
@quinnj
quinnj deleted the claude/ws-server-timeouts branch October 22, 2026 12:32
@quinnj quinnj mentioned this pull request Oct 22, 2026
quinnj referenced this pull request Oct 22, 2026
URIs 1.7.2 rejects "http://[::1]evil.com/" when building the URI, so the test now accepts either rejection; with older URIs the URI still parses and HTTP must throw ArgumentError. Test-only change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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