Repository navigation
fix(daemon): bound local WS connections and time out stalled upgrades - #489
Conversation
Fixes the 'Rust fmt, clippy, tests' CI failure: - cargo fmt --check flagged three hand-formatted spots in the new ws_connection_limit_tests module; reformatted via cargo fmt. - clippy -D warnings flagged the unused 'futures_util::SinkExt' import and the unused 'url' binding in ws_upgrade_times_out_for_silent_clients; removed both.
|
CI failure root cause: Fix (commit 82a2e27):
No production code changes — everything is inside the |
iuyo5678
left a comment
There was a problem hiding this comment.
Thanks for addressing #421. The approach looks appropriate for the local server: acquire a connection permit before spawning the task, retain it for the full connection lifetime, and bound the HTTP Upgrade with a timeout. This should improve daemon resilience to stalled clients without changing normal browser or CLI workflows.
Reviewed commit 82a2e27. I did not find any blocking implementation issues. The permit lifetime covers both the upgrade and application-handshake phases, and the remote server path remains outside the scope of this change.
Two non-blocking suggestions:
- Tighten the regression tests. The over-cap rejection check currently allows 5 seconds, which is also the upgrade timeout, and the reconnect check allows 10 seconds. A shorter deadline, clearly below the handshake timeout, would better distinguish immediate rejection/release from timeout-driven cleanup. It would also be useful to cover incomplete HTTP headers, closing one fully registered connection while the other 63 remain connected, and recovering the entire pool after upgrade or first-frame timeouts.
- Rate-limit the capacity warning. Logging a warning for every rejected connection can produce substantial log volume under a sustained connection flood. The existing throttling in
daemon/remote/server.rswould be a useful precedent.
Validation on macOS:
- All 17 WebSocket unit tests passed on the PR head.
- After applying the patch to main at
b17abe9, all 18 selected integration tests passed, including three additional local boundary checks for incomplete upgrades, connection-slot reuse, and first-frame timeout cleanup.
The remaining CI failure is the video encoder pixel assertion (expected 254 to be less than 15). The failing encoder test does not exercise this local WebSocket server, so I have not found evidence linking it to this change. Please rerun that check and investigate if it persists before merging.
The implementation looks ready to merge once CI is resolved and the required reviews are satisfied. The two suggestions above can be handled as follow-ups.
) The 'Browser video recording regression' failure at 82a2e27 is unrelated to this PR: the commit only touches #[cfg(test)] code in daemon/ws.rs, which is not compiled into the shipped binary. The same job passed on the previous head (6e51387), on main, and on other PRs today. The failure was a pixel assertion in encoder.browser.test.ts (red/blue bar rendered white), a known browser-rendering flake on CI runners. Empty commit to rerun CI.
|
The only failing check after 82a2e27, |
Fixes #421
Problem
The default local WebSocket server spawned a task per TCP connection with no cap, and
accept_hdr_asynchad no timeout: a local client that opens a TCP connection and stalls before (or during) the HTTP Upgrade pinned a tokio task and a socket FD indefinitely. Opening many such connections exhausted daemon tasks and FDs.Change (
crates/bsk-cli/src/daemon/ws.rs)MAX_LOCAL_WS_CONNECTIONS = 64: the accept loop holds atokio::sync::Semaphore; at the cap the fresh TCP stream is closed immediately instead of queueing another task. The permit moves into the connection task, so the slot is released exactly when the connection ends — client close, upgrade timeout, or first-frame timeout.WS_UPGRADE_TIMEOUT = 5s:accept_hdr_asyncruns undertokio::time::timeout, aligned with the existingHANDSHAKE_FIRST_FRAME_TIMEOUTfor the post-upgrade phase. A stalled upgrade now logs and releases its slot like any dead connection.Validation
Windows x64, cargo 1.96 (
CARGO_INCREMENTAL=0):cargo test -p bsk --lib ws_connection_limit_tests→ 2 passedws_upgrade_times_out_for_silent_clients: a real TCP client that connects and sends nothing is dropped by the daemon withinWS_UPGRADE_TIMEOUT + slackws_connection_limit_rejects_then_releases_slots: 64 concurrent upgraded clients (valid extension origin) hold every slot; connection feat(install-skill): add Kimi Code harness support #65 is closed promptly; after all clients close, a fresh client is accepted again (slot released)cargo test -p bsk --lib→ 433 passed, 2 ignored (full suite, no regressions)cargo clippy -p bsk --lib -- -D warnings: the 3 reported errors are pre-existing on this base (file_transfer.rsunused vars,harness.rsunneeded return) and untouched by this change; the new code is clean