From 7a3d1196b42bf809dedc8cc5d024eb7672df91dc Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 1 Sep 2026 11:20:18 -0400 Subject: [PATCH] Add immediate ping methods for websocket connection liveness checks --- olm/olm.go | 14 ++++++++++++++ websocket/client.go | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/olm/olm.go b/olm/olm.go index 041fb3a..8391076 100644 --- a/olm/olm.go +++ b/olm/olm.go @@ -1174,6 +1174,20 @@ func (o *Olm) SetPowerMode(mode string) error { return nil } +// PokeConnection sends an immediate ping over the control websocket rather +// than waiting for the next scheduled ping interval or read-deadline expiry, +// so a live connection confirms itself in one round trip and a dead one - +// undetectable while the underlying host was asleep, since nothing runs +// during real system sleep to notice - starts reconnecting right away. This +// is meant to be driven by an actual "device woke up" hook, not a timer, so +// recovery stays tied to a real signal rather than a guess about how long +// reconnecting might take. No-op if the tunnel isn't running. +func (o *Olm) PokeConnection() { + if o.websocket != nil { + o.websocket.PingNow() + } +} + // RebindSocket recreates the UDP socket when network connectivity changes. // This is necessary on macOS/iOS when transitioning between WiFi and cellular, // as the old socket becomes stale and can no longer route packets. diff --git a/websocket/client.go b/websocket/client.go index f46e78c..feb795c 100644 --- a/websocket/client.go +++ b/websocket/client.go @@ -773,6 +773,21 @@ func (c *Client) sendPing() { } } +// PingNow sends a single ping immediately instead of waiting for the next +// scheduled tick of pingMonitor - useful as a fast liveness probe (e.g. right +// after a host wake from sleep) so a live connection confirms itself in one +// round trip and a dead one starts reconnecting immediately, rather than +// waiting for the earlier of the next scheduled ping or the read deadline to +// expire. Safe to call at any time, including before the ping monitor has +// started or while disconnected (sendPing is a no-op in that case). Runs +// asynchronously since sendPing can block up to writeDeadline. +func (c *Client) PingNow() { + if c == nil { + return + } + go c.sendPing() +} + // pingMonitor sends pings at a short interval and triggers reconnect on failure func (c *Client) pingMonitor() { ticker := time.NewTicker(c.pingInterval)