Skip to content

fix(session): re-send auto-login on automatic reconnect#20

Merged
HarryCordewener merged 2 commits into
masterfrom
fix/reconnect-auto-login
Jul 1, 2026
Merged

fix(session): re-send auto-login on automatic reconnect#20
HarryCordewener merged 2 commits into
masterfrom
fix/reconnect-auto-login

Conversation

@HarryCordewener

Copy link
Copy Markdown
Member

Problem

When a session drops and TelnetConnection reconnects 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, CanSend is false so the Send button stays disabled, and the user is stuck.

Fix

Move auto-login into the Session, fired on every transition into Connected (the initial connect and each automatic reconnect), via a provider that resolves the character's stored connect string from the secret store on demand:

  • Session gains an optional Func<ValueTask<string?>>? autoLoginProvider; on a non-ConnectedConnected transition it invokes the provider and sends the result.
  • TelnetSessionLauncher supplies the provider and no longer sends the credential itself.
  • The secret is fetched per-attempt rather than held in the 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 on Reconnecting → Connected.
  • SessionWithoutAutoLoginSendsNothingOnConnect — no provider ⇒ nothing sent.
  • Full Core suite: 196 pass.

🤖 Generated with Claude Code

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
Copilot AI review requested due to automatic review settings July 1, 2026 12:49

Copilot AI left a comment

Copy link
Copy Markdown

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 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 autoLoginProvider to Session and invoke it on non-ConnectedConnected transitions.
  • Update TelnetSessionLauncher to 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>
@HarryCordewener
HarryCordewener merged commit 208a65a into master Jul 1, 2026
2 checks passed
@HarryCordewener
HarryCordewener deleted the fix/reconnect-auto-login branch July 1, 2026 13:36
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.

2 participants