Skip to content

fix(sse): add a protocol-level ping/pong heartbeat to LiveWS - #10782

Merged
diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.50from
maxmad64bis:fix/ws-heartbeat-ping-pong
Aug 20, 2026
Merged

diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.50from
maxmad64bis:fix/ws-heartbeat-ping-pong

Conversation

@maxmad64bis

Copy link
Copy Markdown
Contributor

⚠️ base-red inherited: #9985

Summary

Related Issues

Validation

  • Change type: other (WebSocket server)
  • Focused tests and category gates from the golden path:
    tests/integration/live-ws-heartbeat-keepalive.test.ts against a real spawned server,
    npm run test:vitest (368/368)
  • npm run lint
  • Reconciled with the current active release base; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR
  • SonarQube is temporarily opt-in while the private project has no quota; it is not a PR gate.

Tests Added Or Updated

  • tests/integration/live-ws-heartbeat-keepalive.test.ts

The existing #10452 test gains a third connection mode, so both properties are proven together: a
socket that stopped reading frames is still reaped, and a quiet-but-alive subscriber survives.

Coverage Notes

  • src/server/ws/liveServer.ts gains two statements — the ping call and the pong handler. Both are
    exercised by the integration test above against a real server process, which is the only layer
    where a protocol-level control-frame round trip can be observed.

Reviewer Notes

  • The "stopped reading" case pauses the underlying socket through ws's private _socket. There
    is no public API to model a half-open socket, and terminate() would model a close instead. It
    fails loudly if ws ever changes that internal.
  • That client cannot observe its own closure either, so the assertion reads the server's disconnect
    log rather than readyState.
  • The test needs a ~50s window to cross HEARTBEAT_TIMEOUT_MS. It reuses the harness and timing of
    the already-merged fix(dashboard): send periodic WS heartbeat pings to stop live-dashboard reconnect churn #10452 test and adds no wall-clock cost — the three clients connect in
    parallel before the single wait.

startHeartbeat() only refreshed a client's liveness on inbound application
messages (diegosouzapw#10452), evicting a read-only subscriber that never sends anything.
Send a protocol-level ping (RFC 6455 5.5.2) on each tick; conformant clients
answer with a pong control frame automatically, refreshed in a new
ws.on("pong") handler. A socket that stopped reading still goes unanswered and
is reaped as before, so diegosouzapw#10452's protection is unchanged. The application-level
pong is kept for clients that still rely on it.

Extended the integration test with a third connection mode covering both
properties.
@maxmad64bis
maxmad64bis force-pushed the fix/ws-heartbeat-ping-pong branch from 2d54a97 to 856c292 Compare August 19, 2026 22:02
@diegosouzapw
diegosouzapw merged commit 404d3a4 into diegosouzapw:release/v3.8.50 Aug 20, 2026
5 checks passed
@maxmad64bis
maxmad64bis deleted the fix/ws-heartbeat-ping-pong branch September 24, 2026 21:16
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…uzapw#10782)

Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
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