perf: fast relay connect on mobile and desktop + connect splash + edit-safe instances
Relay connect used to serialize a dead LAN probe (up to 8s per stale address on mobile, 2-4s on desktop) in front of the relay attempt, then paid a second WebSocket connect + E2EE handshake because the probe tunnel was thrown away. - mobile probeConnectionCandidates: race the relay probe against the direct chain with a 1.5s direct headstart; a live LAN still wins, a dead one no longer delays startup - relay probes adopt their tunnel as the runtime tunnel (adoptRelayTunnel) instead of dialing a fresh one — applies to auto-connect, pairing redeem, password login, and the desktop host switcher's relay fallback - relay probe drops the /health round-trip: the E2EE handshake already proves the server identity, /auth/session alone proves liveness and auth - desktop restoreDesktopRelayRuntime: same headstart race; a late direct success hot-switches back (stable runtimeKey); startup probe now passes expectedServerId so a re-leased LAN address never sees the token - launch splash shows 'Connecting to device: <label>' with animated dots under the (still centered) logo, translated in all locales - editing a saved instance no longer rebuilds it from the URL field alone: the id is passed through, relay/https candidates are preserved, and a token-key change migrates the Keychain token instead of orphaning it
This commit is contained in:
@@ -299,13 +299,20 @@ const RELAY_PROBE_TIMEOUT_MS = 8_000;
|
||||
* leaves the tunnel in `connecting` forever — the probe must report
|
||||
* unreachable instead of hanging every status/switch flow with it.
|
||||
*/
|
||||
export const probeRelayDesktopHost = async (relay: DesktopHostRelay): Promise<HostProbeResult> => {
|
||||
export const probeRelayDesktopHost = async (
|
||||
relay: DesktopHostRelay,
|
||||
// With `keepTunnel`, an 'ok' probe RETURNS its live tunnel (the caller owns
|
||||
// it — typically adopting it as the runtime tunnel, skipping a second
|
||||
// WebSocket connect + E2EE handshake); every other outcome closes it.
|
||||
options?: { keepTunnel?: boolean },
|
||||
): Promise<HostProbeResult & { tunnel?: ReturnType<typeof createRelayTunnelClient> }> => {
|
||||
const tunnel = createRelayTunnelClient({
|
||||
relayUrl: relay.relayUrl,
|
||||
serverId: relay.serverId,
|
||||
hostEncPubJwk: relay.hostEncPubJwk,
|
||||
});
|
||||
const startedAt = Date.now();
|
||||
let keep = false;
|
||||
try {
|
||||
const response = await Promise.race([
|
||||
tunnel.fetch('/health'),
|
||||
@@ -316,12 +323,13 @@ export const probeRelayDesktopHost = async (relay: DesktopHostRelay): Promise<Ho
|
||||
}
|
||||
}),
|
||||
]);
|
||||
if (!response) return { status: 'unreachable', latencyMs: 0 };
|
||||
return { status: response.ok ? 'ok' : 'unreachable', latencyMs: Math.max(0, Date.now() - startedAt) };
|
||||
if (!response?.ok) return { status: 'unreachable', latencyMs: 0 };
|
||||
keep = options?.keepTunnel === true;
|
||||
return { status: 'ok', latencyMs: Math.max(0, Date.now() - startedAt), ...(keep ? { tunnel } : {}) };
|
||||
} catch {
|
||||
return { status: 'unreachable', latencyMs: 0 };
|
||||
} finally {
|
||||
tunnel.close();
|
||||
if (!keep) tunnel.close();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user