Skip to content

fix(devserver): Extend loopback candidates to IPv6 and mobile emulators#23218

Open
carldebilly wants to merge 2 commits intomasterfrom
dev/cdb/devserver-loopback-candidates
Open

fix(devserver): Extend loopback candidates to IPv6 and mobile emulators#23218
carldebilly wants to merge 2 commits intomasterfrom
dev/cdb/devserver-loopback-candidates

Conversation

@carldebilly
Copy link
Copy Markdown
Member

Summary

Closes #17757

The client-side loopback address list used to contain only 127.0.0.1 (IPv4), and was entirely skipped for mobile platforms. This left two gaps:

  • On dual-stack machines or environments where the dev-server binds to IPv6, [::1] was never tried.
  • iOS Simulator, Mac Catalyst, and Android Emulator can all reach the host via a loopback or host-alias address, but were treated the same as real devices.

Changes

RemoteControlClient.cs

  • Replaces the single 127.0.0.1 prepend with GetLoopbackCandidates(), a platform-specific helper using #if blocks (consistent with the rest of Uno.UI.RemoteControl):

    Target Candidates prepended
    WASM, Skia desktop 127.0.0.1, [::1]
    iOS Simulator / Mac Catalyst 127.0.0.1, [::1]
    Android Emulator (AVD, detected via Build.Fingerprint) 10.0.2.2, 127.0.0.1
    iOS / Android real devices (none — IDE injects the LAN address)

    IP literals are used instead of "localhost" to avoid DNS resolution on the startup-critical path and because ClientWebSocket.ConnectAsync does not guarantee Happy Eyeballs — passing a hostname picks a single address family.

  • 127.0.0.1 and [::1] are now treated as equivalent in the last-known-good preferred endpoint matching (introduced in fix(devSrv): Improve dev-server reliability #17781). A session that connected via 127.0.0.1 gives a zero delay to [::1] on the next start, and vice versa. Without this, switching families would silently reset the 3-second head-start.

  • Extracts BuildAddressListWithLoopback as an internal static method for unit testability.

Given_RemoteControlClient_LoopbackCandidates.cs — 7 unit tests covering:

  • IsLoopbackAddress for all recognized literals and non-loopback cases.
  • BuildAddressListWithLoopback: prepend order, deduplication, case-insensitive skip, port-scoped skip, empty candidate list (returns same instance).

Test plan

  • Build and run Uno.UI.RemoteControl.DevServer.Tests — new tests should pass.
  • Run Skia Desktop SamplesApp with devserver: confirm connection uses 127.0.0.1 or [::1] at first start (no LAN-address noise in logs).
  • Run on a machine with VPN or multiple NICs active: confirm reduction of first-chance WebSocket exceptions at startup.
  • Run on Android Emulator (AVD): confirm 10.0.2.2 appears first in the candidate list.
  • Run on iOS Simulator: confirm 127.0.0.1/[::1] appear in the candidate list.
  • Run on a real Android/iOS device: confirm no loopback candidates are added (LAN address injected by IDE is used as-is).

…ators

- Add [::1] alongside 127.0.0.1 on desktop and WASM so dual-stack
  environments try both families independently via the existing
  parallel-race connection strategy
- iOS Simulator and Mac Catalyst: add 127.0.0.1/[::1] (shared network
  stack reaches the host)
- Android Emulator (AVD fingerprint "generic"): prepend 10.0.2.2
  (canonical host alias) and 127.0.0.1 (adb-reverse support)
- Real mobile devices: no loopback prepend (IDE injects LAN address)
- Treat 127.0.0.1 and [::1] as equivalent in the last-known-good
  preferred-endpoint matching so switching between families does not
  reset the 3s boost
- Extract BuildAddressListWithLoopback as internal static for unit tests
Copilot AI review requested due to automatic review settings May 6, 2026 13:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves Uno dev-server client connection reliability by expanding and platform-specializing the “loopback-first” candidate address list (including IPv6 loopback and emulator host aliases), and by treating IPv4/IPv6 loopback as equivalent when applying the last-known-good endpoint head-start logic.

Changes:

  • Added a platform/runtime-specific loopback candidate list (IPv4 + IPv6 on desktop/WASM, emulator-specific host alias on Android) and prepends it to discovered server addresses.
  • Updated preferred-endpoint selection and delay logic to treat 127.0.0.1 and [::1] as equivalent.
  • Added unit tests covering loopback detection and list-building behavior (ordering, dedup/skip rules, and no-candidate fast path).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Uno.UI.RemoteControl/RemoteControlClient.cs Adds loopback candidate discovery, preferred loopback equivalence, and an extracted list-builder helper for testability.
src/Uno.UI.RemoteControl.DevServer.Tests/Given_RemoteControlClient_LoopbackCandidates.cs Adds unit tests for loopback literal recognition and loopback candidate prepending/dedup behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Uno.UI.RemoteControl/RemoteControlClient.cs Outdated
@unodevops
Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23218/wasm-skia-net9/index.html

- Rename GetLoopbackCandidates -> GetDevServerHostCandidates (10.0.2.2 is not a loopback)
- Expand Android emulator detection to multi-field heuristic covering Genymotion, WSA, and AVD
- Flatten #if __MACCATALYST__ before #if __IOS__||__TVOS__ to remove nested preprocessor nesting
- Extract IsPriorityCandidate and IsPreferredEndpointMatch as internal static testable helpers
- Add first-launch loopback-first: non-priority addresses wait 500 ms when no preferred is known
- Fix BuildAddressListWithLoopback to return original array when all candidates already present
- Use StringComparison.Ordinal for IP literal comparisons in IsLoopbackAddress
- Add testing seam comment on internal static helpers
- Add tests: IsPriorityCandidate, IsPreferredEndpointMatch, WhenAllCandidatesAlreadyPresent
- Fix test name typos and naming style consistency
@unodevops
Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23218/wasm-skia-net9/index.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DevServer] Investigate how to use localhost first on desktop

3 participants