Reconnect on a real handover instead of waiting for a dead path
Build and test / test (macos-latest) (push) Waiting to run
Build and test / test (ubuntu-24.04) (push) Waiting to run
Build and test / test (windows-latest) (push) Waiting to run
Build and test / apple-client (push) Waiting to run

A Wi-Fi to cellular switch left the session visibly dropping: the media transport rebound
itself within a few seconds, but nothing noticed the blackholed control connection until an
unanswered keepalive proved it, and the teardown that followed announced a lost connection and
waited another second before dialling again.

Watch the system path on iOS and fail the control connection the moment the carrying interface
changes, which is the only path change TCP cannot survive. Roaming between access points and a
link that is merely unusable for a while keep the same interface and the same source address,
so ControlPathWatcher reports neither; an unsatisfied path holds the last signature rather than
reporting, so a reconnect is never started into a route that cannot carry it. Tighten the
keepalive window on the phone as the backstop for what the monitor cannot see, run the first
reconnect attempt immediately, and defer the lost-connection announcement until an attempt has
actually failed, so a sub-second handover is silent and only a real outage is announced.

A control reconnect still re-authenticates and rejoins: the media keys come from the TLS
exporter of the connection that was lost, so seamless handover needs control-plane session
resumption rather than a faster reconnect.
This commit is contained in:
2026-09-25 17:20:52 +02:00
parent 0b81b81c0c
commit 0dad40c9d7
7 changed files with 251 additions and 12 deletions
+8 -1
View File
@@ -46,7 +46,14 @@ possession of its media key from the new address with an authenticated `Rebind`
relay moves its endpoint, instead of the session dying silently in both directions; the client
rebuilds its UDP socket rather than retrying on one pinned to a vanished interface. The control
connection is judged live by server traffic rather than assumed live, so a blackholed TCP path
is detected in 30 s instead of waiting minutes for the OS. The receive jitter buffer keeps a
is detected in 30 s instead of waiting minutes for the OS, and 12 s on iOS. iOS also watches the
system path and fails the control connection the moment the carrying interface changes, so a
handover reconnects in about a second instead of waiting out the silence timeout; access-point
roaming and an unusable-but-unchanged link are deliberately not handovers and are ridden out.
The first reconnect attempt is immediate, and a loss is only announced once an attempt has
actually failed, so a handover reads as a hiccup rather than a dropped call. A control reconnect
still re-authenticates and rejoins: seamless handover needs control-plane session resumption,
because the media keys come from the TLS exporter of the connection that was lost. The receive jitter buffer keeps a
one-frame depth floor, measures late and reordered arrivals, and can deepen mid-call, and a
stalled consumer now costs bounded audio rather than the live talkspurt.