Skip to content

feat(rust): Implementation of missing RUST feature#603

Open
thechandanbhagat wants to merge 12 commits intomicrosoft:mainfrom
thechandanbhagat:docs/rust-reconnection-support
Open

feat(rust): Implementation of missing RUST feature#603
thechandanbhagat wants to merge 12 commits intomicrosoft:mainfrom
thechandanbhagat:docs/rust-reconnection-support

Conversation

@thechandanbhagat
Copy link
Copy Markdown

@thechandanbhagat thechandanbhagat commented Apr 1, 2026

Summary

This PR implements automatic reconnection support in the Rust SDK, closing the feature gap with the TypeScript SDK's RelayTunnelConnector reconnect loop.

Previously the Rust SDK had no automatic reconnection — callers had to detect disconnection and call connect() manually. This PR adds:

  1. connect_persistent() — a new method on RelayTunnelHost that wraps the relay connection in a retry loop with exponential back-off
  2. Refactored internal helpers so both connect() and connect_persistent() share the same connection logic

New API

// Connect once (unchanged behaviour)
let handle = host.connect(&token).await?;

// Connect with automatic reconnection
let opts = ReconnectOptions {
    max_attempts: None,          // retry indefinitely
    initial_delay_ms: 1_000,     // 1 s initial delay
    max_delay_ms: 13_000,        // cap at 13 s (matches TypeScript SDK)
};
let persistent = host.connect_persistent(token, opts).await?;

// Watch connection-state changes
let mut state_rx = persistent.state.clone();
tokio::spawn(async move {
    while state_rx.changed().await.is_ok() {
        println!("State: {:?}", *state_rx.borrow());
    }
});

// Graceful shutdown
persistent.stop().await?;

New types

Type Description
RelayConnectionState Connected / Reconnecting { attempt, delay_ms } / Disconnected
ReconnectOptions Controls backoff: max_attempts, initial_delay_ms, max_delay_ms
PersistentRelayHandle Returned by connect_persistent() — holds state receiver + stop signal

Implementation Details

  • Fail-fast first connection: the first attempt is made eagerly inside connect_persistent() so callers surface configuration errors immediately (no background goroutine races)
  • Exponential backoff: delay doubles on each failed attempt, capped at max_delay_ms (default 13 s, matching TypeScript behaviour)
  • Clean shutdown: dropping PersistentRelayHandle (or calling .stop()) signals the reconnect loop to exit gracefully
  • Stop signal on backoff sleep: the select! during the sleep also listens for a stop signal so shutdown is responsive even while waiting
  • Shared connection logic: connect() now delegates to relay_connect_once(), and connect_persistent() calls it in a loop — no code duplication

Backoff behaviour (mirrors TypeScript RelayTunnelConnector)

attempt 1 → wait 1 s
attempt 2 → wait 2 s
attempt 3 → wait 4 s
attempt 4 → wait 8 s
attempt 5+ → wait 13 s (cap)

Files Changed

File Change
rs/src/connections/relay_tunnel_host.rs Added connect_persistent(), new types, extracted free functions
rs/src/connections/errors.rs Added MaxReconnectAttemptsExceeded(u32) error variant
README.md Updated feature matrix: Reconnection ✅ for Rust

Testing

Rust Analyzer (via VS Code) reports no errors. An OpenSSL system dependency prevents running cargo build locally on Windows without vendored OpenSSL — the logic has been cross-referenced against the equivalent TypeScript implementation in ts/src/connections/relayTunnelConnector.ts.


Code of Conduct

The Rust RelayTunnelHost supports reconnection — callers can reuse the
same RelayTunnelHost instance and call connect() again after a dropped
connection. Update the SDK Feature Matrix to reflect this.
Copilot AI review requested due to automatic review settings April 1, 2026 00:53
Copy link
Copy Markdown

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

Updates the repository’s SDK Feature Matrix documentation to accurately reflect that the Rust SDK supports reconnection (aligning README with the Rust RelayTunnelHost::connect() guidance mentioned in the PR description).

Changes:

  • Marked Reconnection as supported (✅) for the Rust SDK in the README feature matrix.

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

Add connect_persistent() to RelayTunnelHost with automatic reconnection:
- New connect_persistent() method wraps relay_connect_once() in a retry loop
- Exponential backoff: starts at 1s, doubles up to 13s cap (matching TS SDK)
- Returns PersistentRelayHandle with watch::Receiver<RelayConnectionState>
- Stop the reconnect loop cleanly by dropping or calling .stop() on the handle
- Fail-fast: first connection attempt is made eagerly before spawning the loop
- Max retry limit supported via ReconnectOptions.max_attempts (None = infinite)

New types:
- RelayConnectionState enum (Connected / Reconnecting / Disconnected)
- ReconnectOptions struct (max_attempts, initial_delay_ms, max_delay_ms)
- PersistentRelayHandle (state receiver, stop signal, join handle)

Refactored connect() to delegate to new relay_connect_once() free function,
and extracted create_websocket() to create_relay_websocket() free function
so both connect() and connect_persistent() can share connection logic.

Closes the feature gap with the TypeScript SDK's RelayTunnelConnector
reconnect loop.
@thechandanbhagat thechandanbhagat changed the title docs: mark Reconnection as supported for Rust SDK feat(rust): implement automatic reconnection with exponential backoff Apr 1, 2026
…nnection

- Add KeepAliveState enum (NotConfigured, Succeeded/Failed with count)
- Add keep_alive_interval and token_refresher fields to ReconnectOptions
- Add keep_alive: Receiver<KeepAliveState> to PersistentRelayHandle
- Rewrite connect_persistent() with:
  - SSH-level reconnect: skip delay and reset backoff on TunnelRelayDisconnected
  - Auto token refresh: retry on HTTP 401 via configurable token_refresher callback
  - Keep-alive channel plumbing to relay_connect_once
- Update relay_connect_once() with keep_alive param and background probe task
- Add TokenRefreshFailed error variant to TunnelError
- Update README rows 16-18 Rust column: SSH reconnection, token refresh, keep-alive
- Add unit tests: KeepAliveState variants, backoff cap, skip_delay, ReconnectOptions defaults
Copy link
Copy Markdown

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

Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.


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

thechandanbhagat and others added 8 commits April 4, 2026 01:56
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@thechandanbhagat thechandanbhagat changed the title feat(rust): implement automatic reconnection with exponential backoff feat(rust): Missing RUST Functionality Apr 9, 2026
@thechandanbhagat thechandanbhagat changed the title feat(rust): Missing RUST Functionality feat(rust): Implementation of missing RUST feature Apr 9, 2026
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