feat: smart LAN/tunnel URL switching for remote connections#179
Merged
feat: smart LAN/tunnel URL switching for remote connections#179
Conversation
2364276 to
5e29bef
Compare
When mobile connects to a desktop server, the app now stores both URLs (tunnel and LAN) and automatically picks the best one: - LAN first: On WiFi, probes the LAN address with a 2s HTTP timeout. If reachable, uses the fast local connection. - Tunnel fallback: If LAN probe fails or device is on cellular, uses the DevTunnel/ngrok URL. - Smart reconnect: On each reconnect attempt, re-resolves which URL to use based on current network state. Implementation: - ConnectionSettings: new LanUrl/LanToken fields - WsBridgeClient: ConnectSmartAsync with LAN probe + ResolveUrlAsync - QR codes: encode both URLs when both sharing modes are active - QR scanning: parse lanUrl/lanToken with backward compatibility - Connectivity monitor (iOS/Android): debounced network change events via MAUI Connectivity API - CopilotService: accepts LanUrl-only configs (no tunnel required) Bug fixes (code review): - StopConnectivityMonitoring: use Interlocked.Exchange to atomically clear _connectivityDebounce, preventing race with OnConnectivityChanged - OnConnectivityChanged: call _bridgeClient.Stop() on WiFi loss while using LAN, triggering immediate reconnect via tunnel instead of waiting for TCP keepalive timeout (~11 min) - ProbeLanAsync: use shared static HttpClient to avoid socket exhaustion from per-call instances under sustained reconnect failures Includes 25 new tests covering URL resolution, LAN probe, QR parsing, settings serialization, and backward compatibility. Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stop() cancels the CTS, which blocks the auto-reconnect guard in WiFi was lost while connected via LAN, the bridge would permanently disconnect with no recovery path. Add AbortForReconnect() to IWsBridgeClient / WsBridgeClient that calls _ws.Abort() without touching the CTS. The resulting WebSocketException causes ReceiveLoopAsync to exit with a live CTS, which triggers the auto-reconnect loop. The loop calls ResolveUrlAsync, which re-probes LAN (fails, no WiFi) and falls back to tunnel. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, QR backward compat - StartConnectivityMonitoring: call StopConnectivityMonitoring() first to prevent double-registration of ConnectivityChanged handler across reconnects - OnConnectivityChanged: add reverse case (gained WiFi while on tunnel → switch to LAN) - GenerateDirectQrCode: include 'url'+'token' fallback for old apps that don't understand lanUrl (backward compat when no tunnel is running) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
12494c9 to
0575b18
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When connecting a mobile device to a desktop PolyPilot server, users must choose between a LAN address (fast, only works on same network) or a DevTunnel URL (works everywhere, slower). If the user scans a LAN QR code and then leaves the house, the connection breaks with no fallback.
Solution
The app now stores both URLs and automatically picks the best one:
How it works
Connectivity.ConnectivityChangedevents detect WiFi↔cellular transitionsNew fields
ConnectionSettings.LanUrlhttp://192.168.1.5:4322)ConnectionSettings.LanTokenQR code format (backward compatible)
{ "url": "https://xxx.devtunnels.ms", "token": "tunnel-jwt", "lanUrl": "http://192.168.1.5:4322", "lanToken": "server-password" }Old QR codes without
lanUrlcontinue to work — single-URL mode, no probing overhead.Edge cases handled
Files changed (9 files)
ConnectionSettings.cs— LanUrl/LanToken propertiesWsBridgeClient.cs— ConnectSmartAsync, ResolveUrlAsync, ProbeLanAsync, IsCellularOnlyIWsBridgeClient.cs— new interface methodsCopilotService.Bridge.cs— ToWebSocketUrl helper, smart connect routing, connectivity monitorCopilotService.cs— accept LanUrl-only configsDashboard.razor— parse/save LanUrl from QR scanSettings.razor— QR generation includes both URLs, validation accepts LanUrlTestStubs.cs— stub updatesSmartUrlResolutionTests.cs— 25 new testsTests
All 1169 tests pass (25 new + 1144 existing). 1 pre-existing locale-dependent failure unrelated.