From c7537b9c42250e825a2fe431e9589fee8a5a7810 Mon Sep 17 00:00:00 2001 From: Nils Andreas Svee Date: Thu, 13 Aug 2026 00:55:23 +0700 Subject: [PATCH] fix(server): take the idle interval cap before saturating --- crates/irtt-server/src/negotiate.rs | 8 +++++++- crates/irtt-server/src/tests/negotiation.rs | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/irtt-server/src/negotiate.rs b/crates/irtt-server/src/negotiate.rs index a776ecb..9c5b565 100644 --- a/crates/irtt-server/src/negotiate.rs +++ b/crates/irtt-server/src/negotiate.rs @@ -80,7 +80,13 @@ pub(crate) fn negotiate_params(mut requested: Params, config: &ServerConfig) -> if requested.interval_ns < minimum { requested.interval_ns = minimum; } - let idle_cap = saturating_ns(config.idle_timeout()) / 4; + // Divided as a `Duration`, before the conversion rather than after it. The + // two agree for every timeout `i64` nanoseconds can hold, but saturating + // first would cap an absurdly long timeout's quarter at `i64::MAX / 4` — + // reducing intervals the configured cap never meant to reach — where + // dividing first leaves every representable interval alone, which is what a + // timeout beyond the wire's range should do. + let idle_cap = saturating_ns(config.idle_timeout() / 4); if idle_cap > 0 && requested.interval_ns > idle_cap { requested.interval_ns = idle_cap; } diff --git a/crates/irtt-server/src/tests/negotiation.rs b/crates/irtt-server/src/tests/negotiation.rs index 3f1ca44..cc01fad 100644 --- a/crates/irtt-server/src/tests/negotiation.rs +++ b/crates/irtt-server/src/tests/negotiation.rs @@ -118,6 +118,17 @@ fn a_negotiated_interval_is_capped_at_a_quarter_of_the_idle_timeout() { negotiated(config, &requesting(3_000_000_000, 5_000_000_000)).interval_ns, 2_000_000_000 ); + + // A timeout whose quarter is past what the wire can express caps nothing, + // because no representable interval reaches it. The quarter is taken before + // the conversion to nanoseconds for exactly this reason: saturating first + // would put the cap at a quarter of `i64::MAX` and reduce a long interval + // the configured timeout never meant to touch. + let absurd = ServerConfig::default().with_idle_timeout(Duration::MAX); + assert_eq!( + negotiated(absurd, &requesting(3_000_000_000, i64::MAX)).interval_ns, + i64::MAX + ); } #[test]