Fix: duplicate connect toggle tears down the winning VPN attempt - #8959
Draft
david-lant wants to merge 1 commit into
Draft
Fix: duplicate connect toggle tears down the winning VPN attempt#8959david-lant wants to merge 1 commit into
david-lant wants to merge 1 commit into
Conversation
Auto-diagnosed from Freshdesk ticket #181166. The ConnectToServer method-channel branch never posted VPNStatus.Connecting, so the Dart toggle guard (vpn_notifier.dart, which only debounces on connecting/disconnecting) stayed un-armed for the whole multi-second connect and a second tap dispatched a duplicate connect. The duplicate was then rejected by connectInFlight via a throw into launchVPN's shared failure handler, so the losing attempt unregistered DefaultNetworkMonitor (a process singleton, so it deregisters the listener outright) and posted VPNStatus.Error for the attempt that was about to succeed — leaving the tunnel up but the UI in error and sing-box bound to a stale interface across Wi-Fi/cellular roams. Post Connecting before dispatch (and reset to Disconnected on failure) to match the sibling Methods.Start branch, and make the in-flight rejection an early return placed before the network-monitor setup so a rejected attempt tears down nothing and posts no status. Co-Authored-By: Claude <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Summary
Auto-fix for Freshdesk ticket #181166 (Android 9.1.18, China / AS4134, Pro user: "打开软件跟没打开一样始终连接不上").
Root cause:
Methods.ConnectToServerposts noVPNStatus.Connecting, so the duplicate connect it lets through is rejected in a way that tears down the network listener owned by the attempt that is still running.Chain, as observed in the ticket's logs (36 occurrences over the capture; the reported session at
03:44:22.612–03:44:27.068):MethodHandler.ktMethods.ConnectToServerdispatched without postingVPNStatus.Connecting, unlike its siblingMethods.Start. The only debounce for the toggle is the Dart guard inlib/features/vpn/provider/vpn_notifier.dart, which returns early only onconnecting/disconnecting— so the state stayeddisconnectedfor the entire ~4.4 s connect and a second tap 1.48 s later dispatched a duplicateACTION_CONNECT_TO_SERVER.connectInFlightsingle-flight gate inLanternVpnService.launchVPN, which rejected it bythrowing into the sharedrunCatching { }.onFailure { }. That failure path belongs to the in-flight, ultimately successful attempt, so the rejected attempt: postedVPNStatus.Error(UI showed Error 202 ms before the tunnel reportedConnected), calledDefaultNetworkMonitor.setNetworkChangeCallback(null)+stop(), and — for thestart_vpnvariant, which setscleanUpOnFailure = true— ranserviceCleanUp(), unregistering the VPN status receiver.DefaultNetworkListener.listenersis keyed by theDefaultNetworkMonitorsingleton, so both attempts share one entry: the loser'sstop()unregisters the connectivity callback outright. The winner never re-registers, soupdateUnderlyingNetworks()stops firing on Wi-Fi/cellular roam — the tunnel reports Connected while outbound dials fail (14,611dial wlan0 (42)/rmnet0 (8): i/o timeoutin the capture, only 25updated default interfaceevents over three days).What changed
android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.ktMethods.ConnectToServernow postsVPNStatus.Connectingbefore dispatching and resets toVPNStatus.DisconnectedinonFailure, matchingMethods.Start. This arms the Dart guard for the whole connect so the duplicate is never dispatched.android/app/src/main/kotlin/org/getlantern/lantern/service/LanternVpnService.ktconnectInFlightcheck moves ahead of theDefaultNetworkMonitorsetup and exits withreturn@withContextinstead ofthrow, so a rejected attempt performs no teardown and posts no status. It stays aftershowStartingVPNConnectedNotification()because the OS still expects astartForeground()for the duplicatestartForegroundService().setupRadiance()/ the monitor setup,onFailurereleases it when the connect coroutine was never created (connectLaunched), so a failure in that window can't wedge every later attempt.Diagnosis was written against
v9.1.18-beta(dcd5bbb1b); the change is rebased onto currentmain, wherelaunchVPNhas since moved the foreground-notification call insiderunCatching.Test plan
setupRadiance()runs insidelaunchVPN), select a specific (non-auto) server so the Dart path isconnectToServer, tap the toggle twice ~1.5 s apart. Expect: oneACTION_CONNECT_TO_SERVERdispatch, second tap filtered by the Dart guard, noprevious VPN connect attempt still in flight, noErrorbeforeConnected.Ignoring VPN operation (...), leavesDefaultNetworkListenerregistered, and posts no status.setupRadiance()and confirm a subsequent connect attempt still runs (flag released).connecting(VPN consent denial should still land onMissingPermission).updated default interfacecontinues to fire.Auto-generated by
/issue-fixfrom an interactive/ticket-autodiagnoserun.