Skip to content

feature: WebSocket ping/pong heartbeat — Node server (PR 2)#585

Open
xerial wants to merge 2 commits into
mainfrom
feature/websocket-heartbeat-node
Open

feature: WebSocket ping/pong heartbeat — Node server (PR 2)#585
xerial wants to merge 2 commits into
mainfrom
feature/websocket-heartbeat-node

Conversation

@xerial

@xerial xerial commented Jun 19, 2026

Copy link
Copy Markdown
Member

Why

PR 2 of the WebSocket heartbeat series (after #584's shared core + Native backends). Adds the outbound ping/pong heartbeat to the Node.js WebSocket server so it reaps half-open client connections.

What

  • NodeWebSocket.accept — when pingIntervalMillis > 0, start a setInterval that drives the shared WebSocketHeartbeat (send a ping on an idle tick via ctx.sendPing; ctx.close(1011, "ping timeout") when a ping goes unanswered). The heartbeat is reset on any inbound frame via the onActivity hook on WebSocketDispatcher.dispatch (from feature: WebSocket ping/pong heartbeat — shared core + Native server & client #584), and the interval is cleared in notifyClose (every close path). sendPing added to NodeWebSocketContext.
  • NodeServerConfig.webSocketPingIntervalMillis (0 = off) + withWebSocketPingIntervalMillis, threaded through handleUpgradegateAndAcceptaccept.

Testing

A Node integration test: the server pings every 150 ms, the Node runtime auto-pongs (the global WebSocket answers protocol pings automatically), and the live-but-idle connection is not reaped. uniJS/test: 1406 tests, 0 failed. JS-only change (the shared core is already on main).

Up next

PR 3 — Netty server (IdleStateHandler). PR 4 — JVM client (ScheduledExecutorService + sendPing/onPong).

🤖 Generated with Claude Code

Add the heartbeat to the Node WebSocket server, building on the shared
WebSocketHeartbeat + dispatch onActivity hook (#584).

- NodeWebSocket.accept: when pingIntervalMillis > 0, start a setInterval that
  drives the heartbeat (sendPing on idle / ctx.close(1011) on unanswered);
  reset on any inbound frame via the dispatch onActivity hook; clearInterval in
  notifyClose. sendPing added to NodeWebSocketContext.
- NodeServerConfig.webSocketPingIntervalMillis (0 = off) + builder, threaded
  through handleUpgrade/gateAndAccept/accept.

Test: a Node integration test — server pings every 150ms, the global WebSocket
runtime auto-pongs, and the live-but-idle connection is not reaped. uniJS/test:
1406 tests, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@xerial

xerial commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@github-actions github-actions Bot added the feature New feature label Jun 19, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a WebSocket ping/pong heartbeat mechanism for the Node.js HTTP server backend, allowing idle connections to be kept alive or closed on timeout. A potential memory leak was identified in NodeWebSocket.scala where the heartbeat interval could be scheduled after the connection has already been closed during initialization, preventing the interval from being cleared. It is recommended to check if the connection is closed before starting the interval.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread uni/.js/src/main/scala/wvlet/uni/http/NodeWebSocket.scala Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces WebSocket ping/pong heartbeat support to the Node.js HTTP server implementation, allowing idle connections to be kept alive or reaped if the peer stops responding. A review comment points out a potential memory leak where a permanent timer could be scheduled if the connection is closed synchronously during initial head processing. It is recommended to add a check to ensure the connection is not already closed before setting up the heartbeat interval.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread uni/.js/src/main/scala/wvlet/uni/http/NodeWebSocket.scala Outdated
If onOpen/drive(head) already triggered notifyClose before the interval was
scheduled, the interval would leak (notifyClose ran before intervalHandle was
set, so it couldn't clear it). Guard the setInterval on `!closed`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant