Commit 3ef9daa
feat(config+session): M18.2 — plumb ConnectTimeout/ConnectionAttempts; wrap connect with retry (FR-80) (#29)
Closes the M12.6 ConnectTimeout / ConnectionAttempts deferral loop.
The primary `AnvilSession::connect` path now retries transient TCP
failures with jittered exponential backoff per the M18.1 retry
module; per-attempt connect timeouts wrap russh's `client::connect`
in `tokio::time::timeout` when configured.
src/config.rs:
- AnvilConfig: three new public fields:
connect_timeout: Option<Duration>
connection_attempts: Option<u32>
max_retry_window: Option<Duration>
Each None falls through to retry::RetryPolicy::default() at
session-build time.
- AnvilConfigBuilder: matching three setters. CLI applies them
AFTER apply_ssh_config so flags beat config (matches OpenSSH
precedence).
- apply_ssh_config: now consumes resolved.connect_timeout and
resolved.connection_attempts, but only if the builder field is
still None (preserves CLI-wins precedence). max_retry_window
is CLI-only — not in OpenSSH's grammar.
- warn_unhonored_directives helper REMOVED. Every ssh_config(5)
directive Anvil's resolver parses today is now consumed:
HostKeyAlgorithms / KexAlgorithms / Ciphers / MACs landed in
M17; ConnectTimeout / ConnectionAttempts in M18.
src/session.rs:
- AnvilSession: new private field `retry_history:
Vec<RetryAttempt>` capturing per-attempt failures during the
connect path. Surfaced via the new
pub fn AnvilSession::retry_history(&self) -> &[RetryAttempt]
accessor for `gitway --test --json`'s data.retry_attempts
envelope (FR-83). Empty when first attempt succeeded.
- connect(): wrapped in retry::run. Each attempt:
1. Build fresh HandlerPieces (russh consumes the handler).
2. Call client::connect(...), wrapped in tokio::time::timeout
when policy.connect_timeout is Some. Elapsed → mapped to
AnvilErrorKind::Io(io::ErrorKind::TimedOut) which the
FR-82 classifier treats as Retry.
3. Return (handle, ConnectArtifacts).
Retry history flows from retry::run into the AnvilSession
field. Auth / host-key / protocol errors are fatal per FR-82
and surface immediately without retry.
- New private struct ConnectArtifacts and helper
retry_policy_from_config(config) extracted to keep the closure
signature concise.
- connect_via_proxy_command and connect_via_jump_hosts (final
hop + per-hop) construct AnvilSession with retry_history:
Vec::new() for now. The plan PR body documents this
scope-narrowing: the ProxyCommand subprocess lifecycle and
per-hop direct-tcpip channels make retry semantics murkier
than the primary path; deferred to a follow-up sub-milestone.
FR-80..FR-83 are met for `gitway --test` against a direct
target host today; the proxy / jump paths fall back to the
pre-M18 single-attempt behaviour and surface the same opaque
IO error they did before.
Public API: pure additive (three new pub fields on AnvilConfig +
three new builder setters + new retry_history accessor).
build_russh_config signature unchanged.
207 lib tests + integration tests still pass.
Plan: M18.2 of anvil-gitway-milestone-plan.md.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent b143f5e commit 3ef9daa
2 files changed
Lines changed: 184 additions & 39 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
108 | 108 | | |
109 | 109 | | |
110 | 110 | | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
111 | 124 | | |
112 | 125 | | |
113 | 126 | | |
| |||
191 | 204 | | |
192 | 205 | | |
193 | 206 | | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
194 | 210 | | |
195 | 211 | | |
196 | 212 | | |
| |||
219 | 235 | | |
220 | 236 | | |
221 | 237 | | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
222 | 247 | | |
223 | 248 | | |
224 | 249 | | |
| |||
351 | 376 | | |
352 | 377 | | |
353 | 378 | | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
354 | 403 | | |
355 | 404 | | |
356 | 405 | | |
| |||
432 | 481 | | |
433 | 482 | | |
434 | 483 | | |
435 | | - | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
436 | 501 | | |
437 | 502 | | |
438 | 503 | | |
| |||
484 | 549 | | |
485 | 550 | | |
486 | 551 | | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
487 | 555 | | |
488 | 556 | | |
489 | 557 | | |
490 | 558 | | |
491 | | - | |
492 | | - | |
493 | | - | |
494 | | - | |
495 | | - | |
496 | | - | |
497 | | - | |
498 | | - | |
499 | | - | |
500 | | - | |
501 | | - | |
502 | | - | |
503 | | - | |
504 | | - | |
505 | | - | |
506 | | - | |
507 | | - | |
508 | | - | |
509 | | - | |
510 | | - | |
511 | | - | |
512 | | - | |
513 | | - | |
514 | | - | |
515 | | - | |
516 | | - | |
517 | | - | |
518 | | - | |
519 | | - | |
520 | | - | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
521 | 566 | | |
522 | 567 | | |
523 | 568 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
245 | 252 | | |
246 | 253 | | |
247 | 254 | | |
| |||
345 | 352 | | |
346 | 353 | | |
347 | 354 | | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
348 | 364 | | |
349 | 365 | | |
350 | | - | |
351 | | - | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
352 | 369 | | |
353 | | - | |
| 370 | + | |
354 | 371 | | |
355 | 372 | | |
356 | 373 | | |
357 | | - | |
358 | | - | |
359 | | - | |
360 | | - | |
361 | | - | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
362 | 404 | | |
363 | 405 | | |
364 | 406 | | |
| |||
367 | 409 | | |
368 | 410 | | |
369 | 411 | | |
| 412 | + | |
370 | 413 | | |
371 | 414 | | |
372 | 415 | | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
373 | 429 | | |
374 | 430 | | |
375 | 431 | | |
| |||
508 | 564 | | |
509 | 565 | | |
510 | 566 | | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
511 | 572 | | |
512 | 573 | | |
513 | 574 | | |
| |||
563 | 624 | | |
564 | 625 | | |
565 | 626 | | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
566 | 633 | | |
567 | 634 | | |
568 | 635 | | |
| |||
629 | 696 | | |
630 | 697 | | |
631 | 698 | | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
632 | 704 | | |
633 | 705 | | |
634 | 706 | | |
| |||
1021 | 1093 | | |
1022 | 1094 | | |
1023 | 1095 | | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
| 1123 | + | |
1024 | 1124 | | |
1025 | 1125 | | |
1026 | 1126 | | |
| |||
0 commit comments