mirror of
https://github.com/fosrl/newt.git
synced 2026-07-15 21:16:40 -05:00
[PR #387] [MERGED] Detect dead/half-open control websocket (read deadline + protocol ping) #8584
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/fosrl/newt/pull/387
Author: @DanivosYoun
Created: 6/22/2026
Status: ✅ Merged
Merged: 6/23/2026
Merged by: @oschwartz10612
Base:
main← Head:fix/ws-half-open-detection📝 Commits (1)
855b2b2fix(websocket): detect dead/half-open control connection (read deadline + protocol ping)📊 Changes
1 file changed (+50 additions, -7 deletions)
View changed files
📝
websocket/client.go(+50 -7)📄 Description
Problem
When the backend API server has an outage and then recovers, newt does not reconnect on its own — it stays stuck and has to be restarted by hand before it works again. This reproduces reliably when newt connects through a cloud load balancer / reverse proxy in front of the API server.
Root cause
The control websocket (
websocket/client.go) has no read deadline, and the keepalive is an application-levelnewt/pingJSON message, not a protocol PING frame:readPumpWithDisconnectDetectioncallsconn.ReadMessage()with no deadline, so on a half-open connection it blocks forever.sendPingdoesWriteJSON({type:"newt/ping"}). On a half-open socket (e.g. an LB holding the front-end TCP open after the backend died/restarted) these writes are buffered and keep succeeding, so no write error is ever raised.SetPongHandleris effectively dead code, because newt never sends a protocol PING, so no PONG is ever received.Net effect: newt believes it is still connected to a peer that is gone, never tears the connection down, and never reconnects — until the process is restarted.
Fix
Standard gorilla websocket keepalive, plus two related cleanups:
SetReadDeadline(pongWait)on connect, wherepongWait = 2 × pingInterval(min 20s), and refresh it on every pong and on every inbound message (covers servers that answernewt/pingwith a message rather than a protocol pong).websocket.PingMessagealongside the existing app ping; a compliant server auto-replies with a PONG that refreshes the deadline. If the peer is gone, no pong arrives, the read deadline expires,ReadMessageerrors, and the read pump reconnects.sendPingand the read-pump defer both callingreconnect()(which could race two connections).No server-side changes required — protocol PING/PONG is handled automatically by any standards-compliant websocket server.
Testing
go build ./...(all non-example packages),go vet ./websocket/, andgo test ./websocket/pass.pongWaitand reconnect automatically — no manual restart.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.