Network Connection Logic Enhancement
Nucleic-Session: 93A70648-2057-4092-9372-860E1BA151D8 Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -689,6 +689,72 @@ final class HostConnection {
|
|||||||
if !connectivity.isLive, pathMonitor.isSatisfied {
|
if !connectivity.isLive, pathMonitor.isSatisfied {
|
||||||
reconnectAttempts = 0
|
reconnectAttempts = 0
|
||||||
reconnect(to: host)
|
reconnect(to: host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Live over a fallback transport (tailnet/relay) and a LAN path just reappeared (rejoined the
|
||||||
|
// Mac's Wi-Fi) → the return leg of the switch above. The tunnel survives the interface change
|
||||||
|
// on its own, so nothing else would ever re-plan back down to the direct path — probe LAN and
|
||||||
|
// swap to it if the Mac actually answers here.
|
||||||
|
maybeUpgradeToLAN()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Probe whether the pinned Mac is reachable over LAN right now and, if so, switch back to the
|
||||||
|
/// direct connection — the tailnet/relay → LAN return leg. Only meaningful while live over a
|
||||||
|
/// non-LAN transport with a LAN-capable path available; a bare TCP connect to the pinned LAN
|
||||||
|
/// endpoint that reaches `.ready` is the "the Mac is on this network" signal. The probe runs
|
||||||
|
/// entirely off the live connection, so joining a *foreign* Wi-Fi (no Mac here) costs one short,
|
||||||
|
/// silent connect attempt and leaves the working tunnel untouched.
|
||||||
|
private func maybeUpgradeToLAN() {
|
||||||
|
guard let host = pinnedHost, connectivity.isLive, activeTransport != .lan,
|
||||||
|
pathMonitor.canUseLAN, lanUpgradeProbe == nil,
|
||||||
|
let endpoint = discovery.endpoint(
|
||||||
|
forFingerprint: host.fingerprint, lanHost: host.lanHost, lanPort: host.lanPort)
|
||||||
|
else { return }
|
||||||
|
lanUpgradeProbe = Task { [weak self] in
|
||||||
|
let reachable = await Self.probeReachable(endpoint)
|
||||||
|
guard let self else { return }
|
||||||
|
self.lanUpgradeProbe = nil
|
||||||
|
// Re-check the world didn't move under us during the probe (still live, still not on LAN,
|
||||||
|
// LAN still available) before tearing a working connection down.
|
||||||
|
guard !Task.isCancelled, reachable, self.connectivity.isLive,
|
||||||
|
self.activeTransport != .lan, self.pathMonitor.canUseLAN,
|
||||||
|
let host = self.pinnedHost
|
||||||
|
else { return }
|
||||||
|
self.reconnectAttempts = 0
|
||||||
|
self.reconnect(to: host) // rebuilds the candidate chain LAN-first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bare TCP reachability check for `maybeUpgradeToLAN`: does `endpoint` accept a connection within
|
||||||
|
/// `timeoutSeconds`? Tears the probe socket down regardless — it only tests reachability, never
|
||||||
|
/// carries traffic. A short timeout so a foreign Wi-Fi (Mac absent) doesn't stall the check.
|
||||||
|
private static func probeReachable(_ endpoint: NWEndpoint, timeoutSeconds: Double = 3) async -> Bool {
|
||||||
|
let params = NWParameters.tcp
|
||||||
|
params.includePeerToPeer = true
|
||||||
|
let connection = NWConnection(to: endpoint, using: params)
|
||||||
|
let queue = DispatchQueue(label: "nucleic.remote.lanprobe")
|
||||||
|
let once = ProbeOnce()
|
||||||
|
return await withTaskCancellationHandler {
|
||||||
|
await withCheckedContinuation { (cont: CheckedContinuation<Bool, Never>) in
|
||||||
|
connection.stateUpdateHandler = { state in
|
||||||
|
switch state {
|
||||||
|
case .ready:
|
||||||
|
if once.take() { cont.resume(returning: true) }
|
||||||
|
connection.cancel()
|
||||||
|
case .failed, .cancelled:
|
||||||
|
if once.take() { cont.resume(returning: false) }
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queue.asyncAfter(deadline: .now() + timeoutSeconds) {
|
||||||
|
if once.take() { cont.resume(returning: false) }
|
||||||
|
connection.cancel()
|
||||||
|
}
|
||||||
|
connection.start(queue: queue)
|
||||||
|
}
|
||||||
|
} onCancel: {
|
||||||
|
connection.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user