feat(proto): Implement TransportConfig::set_rtt_hint

This commit is contained in:
Philipp Krüger
2025-12-18 22:34:14 +01:00
parent 4f4b8b7f3d
commit 086ebf3bb7
2 changed files with 41 additions and 11 deletions
+27 -1
View File
@@ -8,7 +8,7 @@ use std::{
};
use crate::{
ConnectionId, Duration, INITIAL_MTU, Instant, MAX_UDP_PAYLOAD, Side, VarInt,
ConnectionId, Duration, FourTuple, INITIAL_MTU, Instant, MAX_UDP_PAYLOAD, Side, VarInt,
VarIntBoundsExceeded, address_discovery, congestion, connection::qlog::QlogSink,
};
#[cfg(feature = "qlog")]
@@ -83,6 +83,8 @@ pub struct TransportConfig {
pub(crate) max_remote_nat_traversal_addresses: Option<NonZeroU8>,
pub(crate) rtt_hint: Arc<dyn Fn(FourTuple) -> Option<Duration> + Send + Sync>,
#[cfg(feature = "qlog")]
pub(crate) qlog_factory: Option<Arc<dyn QlogFactory>>,
}
@@ -467,6 +469,27 @@ impl TransportConfig {
self
}
/// Sets the rtt hint function.
///
/// This function is consulted any time a new path is opened on a 4-tuple that we don't
/// yet have a good rtt estimate for yet.
///
/// If the function returns some RTT, then this RTT is used as the initial RTT in the path's
/// rtt estimator.
/// If the function returns None, then we use the default rtt estimator's initial RTT value
/// (see also [`Self::initial_rtt`]).
///
/// Be sure to always use conservative estimates of RTT, as the initial RTT value is used
/// for setting the path's timeout. If the RTT estimate is about 9x lower than the actual
/// RTT, then the path will be abandoned before a response could even be processed.
pub fn set_rtt_hint(
&mut self,
rtt_hint: impl Fn(FourTuple) -> Option<Duration> + Send + Sync + 'static,
) -> &mut Self {
self.rtt_hint = Arc::new(rtt_hint);
self
}
/// Configures qlog capturing by setting a [`QlogFactory`].
///
/// This assigns a [`QlogFactory`] that produces qlog capture configurations for
@@ -582,6 +605,8 @@ impl Default for TransportConfig {
// nat traversal disabled by default
max_remote_nat_traversal_addresses: None,
rtt_hint: Arc::new(|_| None),
#[cfg(feature = "qlog")]
qlog_factory: None,
}
@@ -621,6 +646,7 @@ impl fmt::Debug for TransportConfig {
default_path_max_idle_timeout,
default_path_keep_alive_interval,
max_remote_nat_traversal_addresses,
rtt_hint: _,
#[cfg(feature = "qlog")]
qlog_factory,
} = self;
+14 -10
View File
@@ -818,15 +818,6 @@ impl Connection {
&mut self.paths.get_mut(&path_id).expect("known path").data
}
/// Check if the 4-tuple path (as in RFC9000 Path, not multipath path) had already been validated.
fn is_path_validated(&self, addresses: FourTuple) -> bool {
self.paths
.values()
.any(|path_state| path_state.data.validated && path_state.data.addresses == addresses)
// TODO(@divma): we might want to ensure the path has been recently active to consider the
// address validated
}
fn ensure_path(
&mut self,
path_id: PathId,
@@ -834,7 +825,17 @@ impl Connection {
now: Instant,
pn: Option<u64>,
) -> &mut PathData {
let validated = self.is_path_validated(addresses);
let path_on_same_address = self
.paths
.iter()
.find(|(path_id, state)| {
addresses.is_probably_same_path(&state.data.addresses)
&& state.data.validated
&& !self.abandoned_paths.contains(path_id)
})
.clone();
let validated = path_on_same_address.is_some();
let initial_rtt = path_on_same_address.map(|(_, state)| state.data.rtt.conservative());
let vacant_entry = match self.paths.entry(path_id) {
btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
btree_map::Entry::Occupied(occupied_entry) => {
@@ -856,6 +857,9 @@ impl Connection {
);
data.validated = validated;
if let Some(rtt_hint) = initial_rtt.or_else(|| (self.config.rtt_hint)(addresses)) {
data.rtt.reset_initial_rtt(rtt_hint);
}
let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base();
self.timers.set(