M13.2: AnvilSession::connect_via_proxy_command (FR-55)#7
Closed
UnbreakableMJ wants to merge 1 commit into
Closed
Conversation
Adds the public constructor that consumes the ProxyCommand template
captured into ResolvedSshConfig in M12 and the building blocks landed
in M13.1 (token expansion + ChildStdio adapter).
session.rs:
- New private HandlerPieces struct + Self::build_handler_pieces helper
factor out the russh config + GitwayHandler + auth_banner +
verified_fingerprint mutex setup that connect() and the new
connect_via_proxy_command both need. M13.4's connect_via_jump_hosts
will reuse the same helper. The host-key fingerprint lookup
(with the StrictHostKeyChecking::AcceptNew tolerance shipped in
M12.5) lives there now, in one place.
- pub async fn connect_via_proxy_command(config, template, alias):
1. Reject the literal `none` (case-insensitive) — that's the FR-59
disable sentinel; callers should reach for connect() instead.
The ssh_config resolver preserves `none` as a literal so first-
wins protects it; the dispatcher in M13.6 turns that into a
call to connect() rather than this method.
2. Build the handler via build_handler_pieces.
3. Spawn the ProxyCommand child via crate::proxy::command::
spawn_proxy_command (token-expanded against config.host /
config.port / config.username / alias).
4. Hand the resulting ChildStdio (AsyncRead+AsyncWrite+Unpin+Send)
to russh::client::connect_stream. russh drives the SSH
handshake over the child's stdio.
5. Return the AnvilSession with the same shape as connect() —
indistinguishable to callers thereafter.
- The existing connect() now calls build_handler_pieces too; behavior
is identical (same russh::client::connect call, same handler).
proxy/mod.rs:
- Removed the M13.1-era #![allow(dead_code)]; spawn_proxy_command and
ChildStdio::new are now used by session.rs.
Tests: 173 lib + 1 matrix + 6 integration; 0 failures.
Plan: M13.2 of let-us-plan-on-bright-cosmos.md. Stacked on M13.1 (PR #6).
This was referenced May 4, 2026
3 tasks
UnbreakableMJ
added a commit
that referenced
this pull request
May 4, 2026
Adds the public `proxy::jump` submodule: JumpHost (struct): - pub fields: host: String, port: u16, user: Option<String>, identity_files: Vec<PathBuf>. - The chain manager (M13.4) populates identity_files by re-running ssh_config::resolve against each hop's name; M13.3 leaves it empty. parse_jump_chain(raw: &str) -> Result<Vec<JumpHost>, AnvilError>: - Accepts the OpenSSH `-J` / `ProxyJump` syntax: `[user@]host[:port]` comma-separated, whitespace-around-commas tolerated. - Defaults the port to 22, leaves user as None when omitted. - Rejects empty strings, trailing/double commas, empty user prefix, empty host, invalid port, and the literal `none` (case-insensitive) with clear actionable error messages. MAX_JUMP_HOPS = 8: - Matches OpenSSH's READCONF_MAX_DEPTH for ProxyJump chains (FR-57). - Enforced at parse time, not chain-construction time, so callers bail out before any network I/O. IPv6 literals (`[::1]:22`) are not in scope for M13 — none of the listed FRs require them and the OpenSSH convention is rare in ProxyJump strings; documented as a follow-up. Tests: +16 unit tests (single-hop bare, single-hop user@host:port, two-hop chain, whitespace tolerance, empty-string/whitespace-only, trailing/double commas, empty user, empty host, invalid port, out-of-range port, none literal in three case variants, max-length chain accepted, length+1 chain rejected, struct PartialEq+Clone sanity). 189 lib tests green, 0 failures. Plan: M13.3 of let-us-plan-on-bright-cosmos.md. Stacked on M13.2 (PR #7).
UnbreakableMJ
added a commit
that referenced
this pull request
May 4, 2026
Adds the public `proxy::jump` submodule: JumpHost (struct): - pub fields: host: String, port: u16, user: Option<String>, identity_files: Vec<PathBuf>. - The chain manager (M13.4) populates identity_files by re-running ssh_config::resolve against each hop's name; M13.3 leaves it empty. parse_jump_chain(raw: &str) -> Result<Vec<JumpHost>, AnvilError>: - Accepts the OpenSSH `-J` / `ProxyJump` syntax: `[user@]host[:port]` comma-separated, whitespace-around-commas tolerated. - Defaults the port to 22, leaves user as None when omitted. - Rejects empty strings, trailing/double commas, empty user prefix, empty host, invalid port, and the literal `none` (case-insensitive) with clear actionable error messages. MAX_JUMP_HOPS = 8: - Matches OpenSSH's READCONF_MAX_DEPTH for ProxyJump chains (FR-57). - Enforced at parse time, not chain-construction time, so callers bail out before any network I/O. IPv6 literals (`[::1]:22`) are not in scope for M13 — none of the listed FRs require them and the OpenSSH convention is rare in ProxyJump strings; documented as a follow-up. Tests: +16 unit tests (single-hop bare, single-hop user@host:port, two-hop chain, whitespace tolerance, empty-string/whitespace-only, trailing/double commas, empty user, empty host, invalid port, out-of-range port, none literal in three case variants, max-length chain accepted, length+1 chain rejected, struct PartialEq+Clone sanity). 189 lib tests green, 0 failures. Plan: M13.3 of let-us-plan-on-bright-cosmos.md. Stacked on M13.2 (PR #7).
UnbreakableMJ
added a commit
that referenced
this pull request
May 4, 2026
Adds the public `proxy::jump` submodule: JumpHost (struct): - pub fields: host: String, port: u16, user: Option<String>, identity_files: Vec<PathBuf>. - The chain manager (M13.4) populates identity_files by re-running ssh_config::resolve against each hop's name; M13.3 leaves it empty. parse_jump_chain(raw: &str) -> Result<Vec<JumpHost>, AnvilError>: - Accepts the OpenSSH `-J` / `ProxyJump` syntax: `[user@]host[:port]` comma-separated, whitespace-around-commas tolerated. - Defaults the port to 22, leaves user as None when omitted. - Rejects empty strings, trailing/double commas, empty user prefix, empty host, invalid port, and the literal `none` (case-insensitive) with clear actionable error messages. MAX_JUMP_HOPS = 8: - Matches OpenSSH's READCONF_MAX_DEPTH for ProxyJump chains (FR-57). - Enforced at parse time, not chain-construction time, so callers bail out before any network I/O. IPv6 literals (`[::1]:22`) are not in scope for M13 — none of the listed FRs require them and the OpenSSH convention is rare in ProxyJump strings; documented as a follow-up. Tests: +16 unit tests (single-hop bare, single-hop user@host:port, two-hop chain, whitespace tolerance, empty-string/whitespace-only, trailing/double commas, empty user, empty host, invalid port, out-of-range port, none literal in three case variants, max-length chain accepted, length+1 chain rejected, struct PartialEq+Clone sanity). 189 lib tests green, 0 failures. Plan: M13.3 of let-us-plan-on-bright-cosmos.md. Stacked on M13.2 (PR #7).
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
Adds the public constructor that consumes the ProxyCommand template captured in M12 and the building blocks landed in M13.1.
Stacks on #6. Will rebase onto main after #6 merges.
Public surface (additive)
AnvilSession::connect_via_proxy_command(config: &AnvilConfig, template: &str, alias: &str) -> Result<Self, AnvilError>%h %p %r %n %%againstconfig.host,config.port,config.username, andalias.russh::client::connect_stream.none(case-insensitive) — the FR-59 disable sentinel; callers should fall back toconnect()instead.Internal refactor
HandlerPiecesstruct +Self::build_handler_pieceshelper centralises the russh config + GitwayHandler + auth_banner + verified_fingerprint mutex setup thatconnect(),connect_via_proxy_command, and (M13.4)connect_via_jump_hostsall need.connect()now calls the same helper; behavior is unchanged.#![allow(dead_code)]fromproxy/mod.rssincespawn_proxy_commandandChildStdio::neware now consumed.Tests
test_proxy_jump.rs(gated byGITWAY_INTEGRATION_TESTS=1).Test plan
cargo fmt/cargo clippy --all-targets -- -D warningsclean.cargo test --lib --tests --lockedgreen.Plan: M13.2 of let-us-plan-on-bright-cosmos.md.
🤖 Generated with Claude Code