Bound WebSocket and SSE client handshakes by read_timeout - #482
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a liveness bug in Elio’s HTTP-based clients by ensuring WebSocket and SSE connect() handshake/header reads are bounded by base_client_config::read_timeout, preventing indefinite hangs when a peer accepts TCP/TLS but stays silent.
Changes:
- Added a shared fd-shutdown watchdog helper to enforce read deadlines during protocol header reads.
- Applied an absolute
read_timeoutdeadline to WebSocket upgrade response parsing and SSE response header reads (preservingETIMEDOUTon timeout). - Added regression tests and updated wiki/API docs to clarify
connect_timeoutvsread_timeoutbehavior.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| wiki/WebSocket-SSE.md | Documents that WebSocket/SSE connect() header/upgrade reads are bounded by read_timeout. |
| wiki/API-Reference.md | Clarifies base_client_config timeout semantics for WebSocket/SSE clients. |
| tests/unit/test_http_client.cpp | Adds regression tests that stalled WS/SSE handshakes fail with ETIMEDOUT. |
| include/elio/net/stream.hpp | Adds net::stream::mark_externally_shut_down() for TLS-safe cleanup after watchdog shutdown. |
| include/elio/http/client_base.hpp | Introduces shared arm_fd_shutdown_watchdog() helper for HTTP-based clients. |
| include/elio/http/websocket_client.hpp | Enforces read_timeout for WS upgrade response reads and sets deterministic error codes. |
| include/elio/http/sse_client.hpp | Enforces read_timeout for SSE response header reads and sets deterministic error codes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Addressed the failed-handshake cleanup comments in 7b96d3c: WebSocket connect failures now close the stored stream and move to closed state, and SSE connect failure paths now release the stored stream while preserving errno. Re-ran local validation: incremental out-of-source build with --parallel 2, the WebSocket/SSE timeout tests on normal/ASAN/TSAN binaries, the existing [http][client] tests, and git diff --check. @copilot please re-review only; do not make direct code modifications. |
Re-reviewed the current branch. |
Description
Fixes WebSocket and SSE client
connect()paths so protocol response/header reads after TCP/TLS setup are bounded byread_timeoutinstead of waiting indefinitely for a silent peer.Type of Change
Related Issues
Fixes #426
Changes Made
Core Changes
net::stream::mark_externally_shut_down()for timeout-driven fd shutdowns.read_timeoutdeadline.read_timeoutdeadline.ETIMEDOUTon these timeout paths.API Changes (if applicable)
No breaking API changes. Existing
base_client_config::read_timeoutnow applies consistently to WebSocket upgrade responses and SSE response headers duringconnect().Migration Guide (if breaking change)
Not applicable.
Testing
Unit Tests
Integration Tests
Sanitizer Testing
Test Results
Checklist
Code Quality
Documentation
Testing
Compatibility
Performance (if applicable)
Screenshots / Diagrams
Not applicable.
Additional Notes
The configure/build were performed out of source in
/tmp/elio-build-426; build parallelism was limited to 2. The build command was run with the configured HTTP(S) proxy for dependency fetches.Reviewer Guidance
Areas requiring special attention:
read_timeoutshould remain an absolute deadline for the full protocol header read rather than a per-read timeout.Questions for reviewers: