#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum NativePeerSendOrder {
OptionalTransportsFirst,
IrohFirstAllowOptionalFallback,
}
pub(crate) fn peer_send_order(
active_transport: Option<&str>,
_iroh_transport: Option<&str>,
) -> NativePeerSendOrder {
if active_transport
.map(crate::transport_label::is_optional)
.unwrap_or(false)
{
return NativePeerSendOrder::OptionalTransportsFirst;
}
NativePeerSendOrder::IrohFirstAllowOptionalFallback
}
pub(crate) fn latency_optimized_send_order(
active_transport: &str,
iroh_transport: &str,
iroh_latency_ms: u64,
webrtc_latency_ms: u64,
current_selected_at_ms: i64,
now_ms: i64,
) -> Option<NativePeerSendOrder> {
let active = crate::route_policy::normalize_route(active_transport)?;
let iroh = crate::route_policy::normalize_route(iroh_transport)?;
let (current, candidate) = if crate::transport_label::is_webrtc(active.as_str()) {
(
crate::route_policy::RouteLatencyObservation {
route: active,
latency_ms: webrtc_latency_ms,
observed_at_ms: now_ms,
stable_since_ms: current_selected_at_ms,
},
crate::route_policy::RouteLatencyObservation {
route: iroh,
latency_ms: iroh_latency_ms,
observed_at_ms: now_ms,
stable_since_ms: current_selected_at_ms,
},
)
} else if crate::transport_label::is_iroh_base(active.as_str()) {
(
crate::route_policy::RouteLatencyObservation {
route: iroh,
latency_ms: iroh_latency_ms,
observed_at_ms: now_ms,
stable_since_ms: current_selected_at_ms,
},
crate::route_policy::RouteLatencyObservation {
route: crate::route_policy::KnownRoute::WebRtc,
latency_ms: webrtc_latency_ms,
observed_at_ms: now_ms,
stable_since_ms: current_selected_at_ms,
},
)
} else {
return None;
};
crate::route_policy::lowest_latency_switch(
current,
current_selected_at_ms,
&[candidate],
now_ms,
)
.map(|route| {
if crate::transport_label::is_webrtc(route.as_str()) {
NativePeerSendOrder::OptionalTransportsFirst
} else {
NativePeerSendOrder::IrohFirstAllowOptionalFallback
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct TransportPolicyVectors {
send_order: Vec<SendOrderVector>,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct SendOrderVector {
active_transport: Option<String>,
iroh_transport: Option<String>,
order: String,
}
fn parse_order(value: &str) -> NativePeerSendOrder {
match value {
"optionalTransportsFirst" => NativePeerSendOrder::OptionalTransportsFirst,
"irohFirstAllowOptionalFallback" => NativePeerSendOrder::IrohFirstAllowOptionalFallback,
other => panic!("unknown send-order vector: {other}"),
}
}
#[test]
fn active_optional_transport_stays_first() {
assert_eq!(
peer_send_order(Some("webrtc"), Some("iroh-quic")),
NativePeerSendOrder::OptionalTransportsFirst
);
assert_eq!(
peer_send_order(Some("moq"), Some("iroh-lan")),
NativePeerSendOrder::OptionalTransportsFirst
);
}
#[test]
fn direct_iroh_paths_are_primary() {
assert_eq!(
peer_send_order(Some("iroh-quic"), Some("iroh-quic")),
NativePeerSendOrder::IrohFirstAllowOptionalFallback
);
assert_eq!(
peer_send_order(Some("iroh-lan"), Some("iroh-lan")),
NativePeerSendOrder::IrohFirstAllowOptionalFallback
);
assert_eq!(
peer_send_order(Some("ble"), Some("ble")),
NativePeerSendOrder::IrohFirstAllowOptionalFallback
);
}
#[test]
fn relay_or_unknown_paths_keep_product_data_on_proven_iroh() {
assert_eq!(
peer_send_order(Some("iroh-relay"), Some("iroh-relay")),
NativePeerSendOrder::IrohFirstAllowOptionalFallback
);
assert_eq!(
peer_send_order(Some("iroh"), Some("iroh")),
NativePeerSendOrder::IrohFirstAllowOptionalFallback
);
assert_eq!(
peer_send_order(None, None),
NativePeerSendOrder::IrohFirstAllowOptionalFallback
);
}
#[test]
fn latency_optimization_uses_passive_samples_with_hysteresis() {
let now = 100_000;
assert_eq!(
latency_optimized_send_order("iroh-relay", "iroh-relay", 90, 40, now - 31_000, now,),
Some(NativePeerSendOrder::OptionalTransportsFirst),
);
assert_eq!(
latency_optimized_send_order("webrtc", "iroh-quic", 20, 70, now - 31_000, now,),
Some(NativePeerSendOrder::IrohFirstAllowOptionalFallback),
);
assert_eq!(
latency_optimized_send_order("iroh-relay", "iroh-relay", 90, 40, now - 10_000, now,),
None,
);
}
#[test]
fn matches_shared_transport_policy_vectors() {
let vectors: TransportPolicyVectors =
serde_json::from_str(crate::generated::route_registry::TRANSPORT_REGISTRY_JSON)
.expect("transport policy vectors");
for vector in vectors.send_order {
assert_eq!(
peer_send_order(
vector.active_transport.as_deref(),
vector.iroh_transport.as_deref()
),
parse_order(&vector.order),
"active={:?} iroh={:?}",
vector.active_transport,
vector.iroh_transport
);
}
}
}