fix(session): re-send auto-login on automatic reconnect#20
Merged
Conversation
When a session dropped and TelnetConnection reconnected on its own, the stored connect string was never re-sent — the launcher only sent it once, on the initial connect. So an auto-reconnected session was left sitting at the server's login screen (unauthenticated), and with an empty input box the Send button stays disabled, leaving the user stuck. Move the auto-login into the Session: it now runs on every transition into Connected (initial connect AND each auto-reconnect) via a provider that resolves the character's stored connect string from the secret store on demand. The launcher supplies the provider and no longer sends the credential itself. Adds SessionSendsAutoLoginOnConnectAndReconnect and SessionWithoutAutoLoginSendsNothingOnConnect; full Core suite (196) passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHCRc5CJ6595iMzEgYYdQp
There was a problem hiding this comment.
Pull request overview
This PR fixes auto-reconnect sessions getting stuck unauthenticated by moving “auto-login” responsibility into Session, so the character’s stored connect string is re-sent on every transition into Connected (initial connect + automatic reconnects).
Changes:
- Add an optional
autoLoginProvidertoSessionand invoke it on non-Connected→Connectedtransitions. - Update
TelnetSessionLauncherto supply the provider (and stop sending the secret itself). - Add session-state tests covering auto-login on connect/reconnect and the no-provider case.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tests/SharpClient.Tests/Sessions/SessionStateTests.cs | Adds tests validating auto-login is sent on connect and resend on reconnect. |
| src/SharpClient.Core/Sessions/TelnetSessionLauncher.cs | Provides an auto-login provider to Session instead of sending the secret in the launcher. |
| src/SharpClient.Core/Sessions/Session.cs | Triggers best-effort auto-login on transitions into Connected. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+230
to
+234
| var command = await _autoLoginProvider!().ConfigureAwait(false); | ||
| if (!string.IsNullOrWhiteSpace(command)) | ||
| { | ||
| await SendAsync(command).ConfigureAwait(false); | ||
| } |
Comment on lines
+43
to
+48
| await session.ConnectAsync("host", 1); // fake raises Connected | ||
| await Task.Delay(50); | ||
| // Simulate an unexpected drop + automatic reconnect. | ||
| fake.RaiseState(ConnectionState.Reconnecting); | ||
| fake.RaiseState(ConnectionState.Connected); | ||
| await Task.Delay(50); |
Comment on lines
+61
to
+62
| await session.ConnectAsync("host", 1); | ||
| await Task.Delay(50); |
Comment on lines
+53
to
+59
| // Resolve the character's stored connect string on demand. The Session invokes this on the | ||
| // initial connect AND on every automatic reconnect, so a dropped session re-authenticates | ||
| // itself instead of being left at the server's login screen. The secret is fetched from the | ||
| // store each time rather than held in the Session. | ||
| Func<ValueTask<string?>>? autoLoginProvider = character.ConnectSecretKey is { } key | ||
| ? async () => await _secrets.GetAsync(key) | ||
| : null; |
…ect button (#21) Makes a session survive the network churn of a long drive (WiFi↔cellular, tower handoffs, dead zones) and adds a manual escape hatch. Core: - ForceReconnectAsync() on TelnetConnection/ITelnetConnection — reconnect now to the last endpoint, tearing down the dead socket and any backoff; resumes even after auto-reconnect gave up (Error). No-op after an intentional disconnect. - Tune TCP keepalive (30s idle / 10s interval / 3 probes) so a silently-dead peer surfaces in ~1 min instead of the OS default (~2h). - Application-level IAC NOP heartbeat (45s) that keeps the server session alive and drops+reconnects when a write fails. - ReconnectOptions.Default retries far longer (MaxAttempts 6 → 60, ~30 min) so a long dead zone doesn't permanently give up. - Intentional DisconnectAsync + ForceReconnectAsync on ISession/Session. Android: - Keep-alive service now watches the DEFAULT network: on a change it pins the process to the new network (BindProcessToNetwork) and signals a reconnect; ConnectionKeepAliveCoordinator turns that into an immediate ForceReconnect on every session. NetworkChangeSignal bridges the two without the coordinator referencing Android-only types. UI: - Disconnect button in the session header (drops the connection / stops auto-reconnect) for when the client thinks it's connected but the socket is dead. Builds off the reconnect auto-login fix so a reconnected session re-authenticates. Core 198 / UI 47 / Data 17 tests pass; Android head builds clean. Claude-Session: https://claude.ai/code/session_01HHCRc5CJ6595iMzEgYYdQp Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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 a session drops and
TelnetConnectionreconnects automatically, the stored connect string is never re-sent — the launcher only sent it once, on the initial connect. So the auto-reconnected session lands at the server's login screen, unauthenticated. With an empty input box,CanSendis false so the Send button stays disabled, and the user is stuck.Fix
Move auto-login into the
Session, fired on every transition intoConnected(the initial connect and each automatic reconnect), via a provider that resolves the character's stored connect string from the secret store on demand:Sessiongains an optionalFunc<ValueTask<string?>>? autoLoginProvider; on a non-Connected→Connectedtransition it invokes the provider and sends the result.TelnetSessionLaunchersupplies the provider and no longer sends the credential itself.Session.Result: a reconnected session re-authenticates itself; once
Connected, the input/Send work normally.Tests (TDD)
SessionSendsAutoLoginOnConnectAndReconnect— login is sent on the initial connect and re-sent onReconnecting → Connected.SessionWithoutAutoLoginSendsNothingOnConnect— no provider ⇒ nothing sent.🤖 Generated with Claude Code