pub use crate::generated::route_registry::{
KnownRoute, RouteDescriptor, RouteFamily, RouteImplementation, RouteLocality, RouteMaturity,
KNOWN_ROUTE_DESCRIPTORS,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum RouteOptimization {
#[default]
Balanced,
LowestLatency,
}
pub const DEFAULT_ROUTE_PRIORITY: [KnownRoute; 5] = [
KnownRoute::IrohLan,
KnownRoute::IrohQuic,
KnownRoute::IrohWebRtc,
KnownRoute::IrohMoq,
KnownRoute::IrohRelay,
];
pub const LATENCY_MIN_IMPROVEMENT_MS: u64 = 20;
pub const LATENCY_CANDIDATE_STABLE_MS: i64 = 5_000;
pub const LATENCY_ROUTE_MIN_HOLD_MS: i64 = 30_000;
pub const LATENCY_SAMPLE_MAX_AGE_MS: i64 = 10_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LatencyObservation {
pub route: KnownRoute,
pub latency_ms: u64,
pub observed_at_ms: i64,
pub stable_since_ms: i64,
}
pub fn latency_switch(
current: LatencyObservation,
current_selected_at_ms: i64,
candidates: &[LatencyObservation],
now_ms: i64,
) -> Option<KnownRoute> {
if now_ms.saturating_sub(current_selected_at_ms) < LATENCY_ROUTE_MIN_HOLD_MS
|| now_ms.saturating_sub(current.observed_at_ms) > LATENCY_SAMPLE_MAX_AGE_MS
{
return None;
}
candidates
.iter()
.filter(|candidate| candidate.route != current.route)
.filter(|candidate| {
now_ms.saturating_sub(candidate.observed_at_ms) <= LATENCY_SAMPLE_MAX_AGE_MS
&& now_ms.saturating_sub(candidate.stable_since_ms) >= LATENCY_CANDIDATE_STABLE_MS
&& candidate
.latency_ms
.saturating_add(LATENCY_MIN_IMPROVEMENT_MS)
<= current.latency_ms
})
.min_by_key(|candidate| (candidate.latency_ms, candidate.route.default_rank()))
.map(|candidate| candidate.route)
}
pub fn rank_iroh_carriers(
configured_priority: &[KnownRoute],
supports_webrtc: bool,
supports_moq: bool,
relay_only: bool,
webrtc_relay_only: bool,
) -> Vec<KnownRoute> {
let candidates = [
(
KnownRoute::IrohWebRtc,
supports_webrtc && (!relay_only || webrtc_relay_only),
),
(KnownRoute::IrohMoq, supports_moq),
];
let priority = if configured_priority.is_empty() {
DEFAULT_ROUTE_PRIORITY.as_slice()
} else {
configured_priority
};
let mut ranked: Vec<KnownRoute> = candidates
.into_iter()
.filter_map(|(route, eligible)| eligible.then_some(route))
.collect();
ranked.sort_by_key(|route| {
priority
.iter()
.position(|candidate| candidate == route)
.map(|index| (0, index, route.default_rank()))
.unwrap_or((1, usize::MAX, route.default_rank()))
});
ranked
}
pub fn normalize_route(value: &str) -> Option<KnownRoute> {
let normalized = value.trim().to_ascii_lowercase();
KNOWN_ROUTE_DESCRIPTORS
.iter()
.find(|descriptor| descriptor.id == normalized)
.map(|descriptor| descriptor.route)
}
pub fn rank_routes(configured_priority: &[String], candidates: &[String]) -> Vec<String> {
let configured = unique_known(configured_priority);
let priority = if configured.is_empty() {
KnownRoute::DEFAULT_PRIORITY.to_vec()
} else {
configured
};
let candidates = unique_known(candidates);
let mut ranked: Vec<(KnownRoute, usize)> = candidates
.into_iter()
.enumerate()
.map(|(candidate_index, route)| (route, candidate_index))
.collect();
ranked.sort_by(|(left, left_index), (right, right_index)| {
route_sort_key(*left, *left_index, &priority).cmp(&route_sort_key(
*right,
*right_index,
&priority,
))
});
ranked
.into_iter()
.map(|(route, _)| route.as_str().to_string())
.collect()
}
fn unique_known(values: &[String]) -> Vec<KnownRoute> {
let mut result = Vec::new();
for value in values {
let Some(route) = normalize_route(value) else {
continue;
};
if !result.contains(&route) {
result.push(route);
}
}
result
}
fn route_sort_key(
route: KnownRoute,
candidate_index: usize,
configured_priority: &[KnownRoute],
) -> (u8, usize, u8, usize) {
match configured_priority
.iter()
.position(|candidate| *candidate == route)
{
Some(configured_index) => (0, configured_index, route.default_rank(), candidate_index),
None => (1, 0, route.default_rank(), candidate_index),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn carrier_ranking_is_privacy_first_and_skips_unsupported_routes() {
let priority = [KnownRoute::IrohMoq, KnownRoute::IrohWebRtc];
assert_eq!(
rank_iroh_carriers(&priority, true, true, false, false),
vec![KnownRoute::IrohMoq, KnownRoute::IrohWebRtc]
);
assert_eq!(
rank_iroh_carriers(&priority, true, true, true, false),
vec![KnownRoute::IrohMoq]
);
assert_eq!(
rank_iroh_carriers(&priority, true, false, true, true),
vec![KnownRoute::IrohWebRtc]
);
}
#[test]
fn latency_switch_requires_fresh_stable_meaningful_improvement() {
let now = 100_000;
let current = LatencyObservation {
route: KnownRoute::IrohRelay,
latency_ms: 80,
observed_at_ms: now - 1_000,
stable_since_ms: now - 60_000,
};
let candidate = LatencyObservation {
route: KnownRoute::IrohWebRtc,
latency_ms: 40,
observed_at_ms: now - 1_000,
stable_since_ms: now - 6_000,
};
assert_eq!(
latency_switch(current, now - 31_000, &[candidate], now),
Some(KnownRoute::IrohWebRtc)
);
assert_eq!(
latency_switch(current, now - 10_000, &[candidate], now),
None
);
assert_eq!(
latency_switch(
current,
now - 31_000,
&[LatencyObservation {
latency_ms: 65,
..candidate
}],
now,
),
None
);
assert_eq!(
latency_switch(
current,
now - 31_000,
&[LatencyObservation {
observed_at_ms: now - LATENCY_SAMPLE_MAX_AGE_MS - 1,
..candidate
}],
now,
),
None
);
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct PolicyVectors {
routes: Vec<RegistryRoute>,
default_priority: Vec<String>,
rank_routes: Vec<RankRoutesVector>,
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RegistryRoute {
id: String,
base_protocol: String,
family: String,
implementation: String,
locality: String,
maturity: String,
default_rank: u8,
browser: bool,
native: bool,
independently_instantiable: bool,
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RankRoutesVector {
configured_priority: Vec<String>,
candidates: Vec<String>,
expected: Vec<String>,
}
#[test]
fn known_route_descriptors_have_unique_stable_ranks() {
let mut ranks: Vec<_> = KNOWN_ROUTE_DESCRIPTORS
.iter()
.map(|descriptor| descriptor.default_rank)
.collect();
ranks.sort_unstable();
ranks.dedup();
assert_eq!(
ranks,
(0..KNOWN_ROUTE_DESCRIPTORS.len() as u8).collect::<Vec<_>>()
);
}
#[test]
fn generated_descriptors_match_the_canonical_registry() {
let registry: PolicyVectors =
serde_json::from_str(crate::generated::route_registry::TRANSPORT_REGISTRY_JSON)
.expect("transport registry");
assert_eq!(registry.routes.len(), KNOWN_ROUTE_DESCRIPTORS.len());
for descriptor in KNOWN_ROUTE_DESCRIPTORS {
let expected = registry
.routes
.iter()
.find(|route| route.id == descriptor.id)
.unwrap_or_else(|| panic!("missing registry route {}", descriptor.id));
assert_eq!(expected.base_protocol, descriptor.base_protocol);
assert_eq!(expected.default_rank, descriptor.default_rank);
assert_eq!(expected.browser, descriptor.browser);
assert_eq!(expected.native, descriptor.native);
assert_eq!(
expected.independently_instantiable,
descriptor.independently_instantiable
);
assert_eq!(expected.family, serialized_name(descriptor.family));
assert_eq!(
expected.implementation,
serialized_name(descriptor.implementation)
);
assert_eq!(expected.locality, serialized_name(descriptor.locality));
assert_eq!(expected.maturity, serialized_name(descriptor.maturity));
}
assert_eq!(
registry.default_priority,
KnownRoute::DEFAULT_PRIORITY
.iter()
.map(|route| route.as_str().to_string())
.collect::<Vec<_>>()
);
}
fn serialized_name<T: serde::Serialize>(value: T) -> String {
serde_json::to_value(value)
.expect("serialize registry enum")
.as_str()
.expect("registry enum serializes as string")
.to_string()
}
#[test]
fn normalization_is_case_and_whitespace_insensitive() {
assert_eq!(normalize_route(" IROH-QUIC "), Some(KnownRoute::IrohQuic));
assert_eq!(normalize_route("WebRTC-LAN"), Some(KnownRoute::WebRtcLan));
assert_eq!(normalize_route(" webtransport "), None);
}
#[test]
fn route_ids_round_trip_through_serde_without_renaming_acronyms() {
for route in KnownRoute::ALL_BY_DEFAULT_RANK {
let encoded = serde_json::to_string(&route).expect("serialize route");
assert_eq!(encoded, format!("\"{}\"", route.as_str()));
assert_eq!(
serde_json::from_str::<KnownRoute>(&encoded).expect("deserialize route"),
route,
);
}
}
#[test]
fn ranking_deduplicates_and_ignores_unknown_routes() {
let configured = strings(["webrtc", "iroh-lan"]);
let candidates = strings([" IROH-LAN ", "unknown", "webrtc", "webrtc", "moq"]);
assert_eq!(
rank_routes(&configured, &candidates),
strings(["webrtc", "iroh-lan", "moq"])
);
}
#[test]
fn empty_priority_preserves_the_public_default_order() {
let candidates = strings([
"iroh",
"moq",
"webrtc",
"ble",
"webrtc-lan",
"iroh-lan",
"iroh-quic",
"iroh-webrtc",
"iroh-moq",
"iroh-relay",
"webrtc-turn",
]);
assert_eq!(
rank_routes(&[], &candidates),
strings([
"iroh-lan",
"iroh-quic",
"iroh-webrtc",
"iroh-moq",
"iroh-relay",
"webrtc-lan",
"ble",
"webrtc",
"moq",
"iroh",
"webrtc-turn",
])
);
}
#[test]
fn vectors_are_shared_with_other_language_consumers() {
let vectors: PolicyVectors =
serde_json::from_str(crate::generated::route_registry::TRANSPORT_REGISTRY_JSON)
.expect("transport policy vectors");
for vector in vectors.rank_routes {
assert_eq!(
rank_routes(&vector.configured_priority, &vector.candidates),
vector.expected,
"configured={:?} candidates={:?}",
vector.configured_priority,
vector.candidates
);
}
}
fn strings<const N: usize>(values: [&str; N]) -> Vec<String> {
values.into_iter().map(str::to_string).collect()
}
}