diff --git a/client/android/client.go b/client/android/client.go index eac8fa246..666cfe5c4 100644 --- a/client/android/client.go +++ b/client/android/client.go @@ -157,23 +157,6 @@ func NewClient(androidSDKVersion int, deviceName string, uiVersion string, tunAd } } -// SetNetworkAvailable feeds OS-reported network availability into the client. -// While unavailable, the internal reconnect loops suspend their attempts and -// the connection listener reports NoNetwork instead of Connecting; when -// availability returns, the loops resume immediately with a fresh backoff. -func (c *Client) SetNetworkAvailable(available bool) { - c.netState.Set(available) - c.recorder.SetNetworkAvailable(available) -} - -// NotifyNetworkChange cuts the management, signal and relay connections -// after the OS switched networks, so the reconnect loops redial immediately -// on the new one. The engine and the TUN device stay untouched. -func (c *Client) NotifyNetworkChange() { - n := c.sweeper.Sweep() - log.Infof("network change: swept %d connections", n) -} - // Run start the internal client. It is a blocker function func (c *Client) Run(platformFiles PlatformFiles, urlOpener URLOpener, isAndroidTV bool, dns *DNSList, dnsReadyListener DnsReadyListener, envList *EnvList) error { exportEnvList(envList) @@ -302,6 +285,23 @@ func (c *Client) GetTunSettings() (*TunSettings, error) { }, nil } +// SetNetworkAvailable feeds OS-reported network availability into the client. +// While unavailable, the internal reconnect loops suspend their attempts and +// the connection listener reports NoNetwork instead of Connecting; when +// availability returns, the loops resume immediately with a fresh backoff. +func (c *Client) SetNetworkAvailable(available bool) { + c.netState.Set(available) + c.recorder.SetNetworkAvailable(available) +} + +// NotifyNetworkChange cuts the management, signal and relay connections +// after the OS switched networks, so the reconnect loops redial immediately +// on the new one. The engine and the TUN device stay untouched. +func (c *Client) NotifyNetworkChange() { + n := c.sweeper.Sweep() + log.Infof("network change: swept %d connections", n) +} + // DebugBundle generates a debug bundle, uploads it, and returns the upload key. // It works both with and without a running engine. func (c *Client) DebugBundle(platformFiles PlatformFiles, anonymize bool) (string, error) { diff --git a/client/netsweep/netsweep.go b/client/netsweep/netsweep.go index 652e8b292..0eea8819e 100644 --- a/client/netsweep/netsweep.go +++ b/client/netsweep/netsweep.go @@ -20,6 +20,39 @@ import ( // as a failed dial and redial on the new network. var ErrSwept = errors.New("netsweep: connection swept by network change") +// Dial tracks one dial from start to connection registration. It hands the +// dialed connection to the sweeper atomically, so a sweep can never fall +// between the dial finishing and the connection being registered. +type Dial struct { + sweeper *Sweeper + ctx context.Context + cancel context.CancelFunc + id uint64 + done bool // set by Sweep, WrapConn or Release; guarded by sweeper.mu +} + +// Ctx returns the dial's context. Sweep cancels it, so a dial started on the +// old network aborts instead of waiting out its handshake timeout. +func (d *Dial) Ctx() context.Context { + return d.ctx +} + +// Release ends the dial's registration and cancels its context. It is +// idempotent and safe after WrapConn, so callers can defer it. +func (d *Dial) Release() { + s := d.sweeper + if s == nil { + return + } + + s.mu.Lock() + d.done = true + delete(s.dials, d.id) + s.mu.Unlock() + + d.cancel() +} + // sweptConn deregisters itself from the sweeper when closed. type sweptConn struct { net.Conn @@ -49,17 +82,6 @@ func New() *Sweeper { } } -// Dial tracks one dial from start to connection registration. It hands the -// dialed connection to the sweeper atomically, so a sweep can never fall -// between the dial finishing and the connection being registered. -type Dial struct { - sweeper *Sweeper - ctx context.Context - cancel context.CancelFunc - id uint64 - done bool // set by Sweep, WrapConn or Release; guarded by sweeper.mu -} - // StartDial registers an in-flight dial. Dial with Ctx, hand the result to // WrapConn, and Release the dial when the attempt is over, typically deferred. func (s *Sweeper) StartDial(ctx context.Context) *Dial { @@ -79,12 +101,6 @@ func (s *Sweeper) StartDial(ctx context.Context) *Dial { return d } -// Ctx returns the dial's context. Sweep cancels it, so a dial started on the -// old network aborts instead of waiting out its handshake timeout. -func (d *Dial) Ctx() context.Context { - return d.ctx -} - // WrapConn hands conn over to the sweeper. If a sweep ran since StartDial, // the connection belongs to the old network: it is closed and ErrSwept is // returned. Otherwise conn is registered against the next sweep and returned @@ -113,22 +129,6 @@ func (d *Dial) WrapConn(conn net.Conn) (net.Conn, error) { return &sweptConn{Conn: conn, sweeper: s, id: id}, nil } -// Release ends the dial's registration and cancels its context. It is -// idempotent and safe after WrapConn, so callers can defer it. -func (d *Dial) Release() { - s := d.sweeper - if s == nil { - return - } - - s.mu.Lock() - d.done = true - delete(s.dials, d.id) - s.mu.Unlock() - - d.cancel() -} - // Sweep closes every registered connection, aborts every in-flight dial, and // returns how many connections it closed. A dial whose connection was not // yet handed to WrapConn is marked, so the late WrapConn closes it instead