From d4c89ee22c5cd53de31863cc66d8fda82488529f Mon Sep 17 00:00:00 2001 From: Nils Andreas Svee Date: Wed, 21 Oct 2026 14:56:04 +0700 Subject: [PATCH] feat(client): add --timeouts option for session-opening retry schedule --- crates/irtt-app/src/shared/client/args.rs | 5 +++++ crates/irtt-app/src/shared/client/config.rs | 21 +++++++++++++-------- docs/irtt-client.md | 6 ++++++ docs/irtt-tui.md | 6 +++--- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/crates/irtt-app/src/shared/client/args.rs b/crates/irtt-app/src/shared/client/args.rs index def4f9c..b69cf80 100644 --- a/crates/irtt-app/src/shared/client/args.rs +++ b/crates/irtt-app/src/shared/client/args.rs @@ -22,6 +22,10 @@ pub struct CommonClientArgs { #[arg(short = 'i', long, default_value = "1s", value_parser = parse_duration)] pub interval: Duration, + /// Comma-separated session-opening timeouts, one per attempt (each >= 200ms). + #[arg(long, value_name = "DURATIONS", value_delimiter = ',', value_parser = parse_duration, long_help = "Comma-separated session-opening receive timeouts, one per attempt (each at least 200ms), e.g. 1s,2s,4s,8s. Omit to use the library default retry schedule.")] + pub timeouts: Vec, + /// UDP payload length. #[arg(short = 'l', long, default_value_t = 0, value_parser = parse_length)] pub length: u32, @@ -116,6 +120,7 @@ impl fmt::Debug for CommonClientArgs { .field("ipv6", &self.ipv6) .field("dual_stack", &self.dual_stack) .field("interval", &self.interval) + .field("timeouts", &self.timeouts) .field("length", &self.length) .field("hmac", &self.hmac.as_ref().map(|_| "[REDACTED]")) .field("clock", &self.clock) diff --git a/crates/irtt-app/src/shared/client/config.rs b/crates/irtt-app/src/shared/client/config.rs index be01f26..8fa4b4b 100644 --- a/crates/irtt-app/src/shared/client/config.rs +++ b/crates/irtt-app/src/shared/client/config.rs @@ -22,16 +22,21 @@ impl CommonClientArgs { /// /// The config is endpoint-independent; managed targets own their addresses. pub fn to_client_config(&self, duration: Duration) -> Result { + let mut open = OpenPolicy { + negotiation: if self.loose { + NegotiationPolicy::Loose + } else { + NegotiationPolicy::Strict + }, + ..Default::default() + }; + if !self.timeouts.is_empty() { + open.timeouts.clone_from(&self.timeouts); + } + Ok(ClientConfig { address_family: self.address_family(), - open: OpenPolicy { - negotiation: if self.loose { - NegotiationPolicy::Loose - } else { - NegotiationPolicy::Strict - }, - ..Default::default() - }, + open, request: SessionRequest { duration: (!duration.is_zero()).then_some(duration), interval: self.interval, diff --git a/docs/irtt-client.md b/docs/irtt-client.md index 1b6b647..1a15d47 100644 --- a/docs/irtt-client.md +++ b/docs/irtt-client.md @@ -180,6 +180,12 @@ expansion: a hostname producing both families consumes two target slots. be greater than zero; a zero test duration, including `0s`, selects continuous mode. +`--timeouts 1s,2s,4s,8s` sets the session-opening retry schedule. Each +comma-separated duration is the receive timeout for one open attempt; attempts +stop when a valid open reply arrives. The list must be nonempty, with each +value at least `200ms`, and uses the same duration syntax as `--interval`. +Omitting the option preserves the library default schedule. + `--duration` controls the run length: - Any nonzero duration (default `10s`) is a **finite** run: probing stops diff --git a/docs/irtt-tui.md b/docs/irtt-tui.md index 4c6bc2d..bdfe52b 100644 --- a/docs/irtt-tui.md +++ b/docs/irtt-tui.md @@ -16,9 +16,9 @@ At least one target is required. Target syntax, multiple targets, and pacing are identical to `irtt-client(1)` — see that manual for `[LABEL=]TARGET` syntax and the -`--pacing staggered|burst` option. The full set of negotiation flags -(`--interval`, `--length`, `--hmac`, `--clock`, `--tstamp`, `--stats`, -`--sfill`, `--dscp`, `--ttl`, `--loose`) is shared with `irtt-client` as +`--pacing staggered|burst` option. The client configuration flags +(`--interval`, `--timeouts`, `--length`, `--hmac`, `--clock`, `--tstamp`, +`--stats`, `--sfill`, `--dscp`, `--ttl`, `--loose`) are shared with `irtt-client` as well; consult that manual for their meaning. Address-family controls are shared too: `-4` / `--ipv4` uses IPv4 only,