feat(client): add receive metadata scaffold
This commit is contained in:
1 parent
ed62a5c80c
commit
25d5c1db1c
5 files changed
+122
-6
No files matched your search
@@ -14,9 +14,10 @@ use crate::{
|
|||||||
config::{ClientConfig, RecvBudget, RunMode, MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES},
|
config::{ClientConfig, RecvBudget, RunMode, MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES},
|
||||||
error::ClientError,
|
error::ClientError,
|
||||||
event::{
|
event::{
|
||||||
ClientEvent, OneWayDelaySample, OpenOutcome, PacketMeta, ReceivedStatsSample, RttSample,
|
ClientEvent, OneWayDelaySample, OpenOutcome, ReceivedStatsSample, RttSample, ServerTiming,
|
||||||
ServerTiming, SignedDuration, WarningKind,
|
SignedDuration, WarningKind,
|
||||||
},
|
},
|
||||||
|
metadata::ReceiveMeta,
|
||||||
probe::{CompletedSet, PendingMap, PendingProbe, TimedOutMap},
|
probe::{CompletedSet, PendingMap, PendingProbe, TimedOutMap},
|
||||||
session::{validate_negotiated_params, ActiveSession, ClientPhase, NegotiatedParams},
|
session::{validate_negotiated_params, ActiveSession, ClientPhase, NegotiatedParams},
|
||||||
socket::{connect_udp_socket, resolve_remote, validate_open_timeouts},
|
socket::{connect_udp_socket, resolve_remote, validate_open_timeouts},
|
||||||
@@ -431,7 +432,7 @@ impl Client {
|
|||||||
one_way,
|
one_way,
|
||||||
received_stats,
|
received_stats,
|
||||||
bytes: packet.len(),
|
bytes: packet.len(),
|
||||||
packet_meta: PacketMeta::default(),
|
packet_meta: ReceiveMeta::default().into(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
events.push(ClientEvent::EchoReply {
|
events.push(ClientEvent::EchoReply {
|
||||||
@@ -445,7 +446,7 @@ impl Client {
|
|||||||
one_way,
|
one_way,
|
||||||
received_stats,
|
received_stats,
|
||||||
bytes: packet.len(),
|
bytes: packet.len(),
|
||||||
packet_meta: PacketMeta::default(),
|
packet_meta: ReceiveMeta::default().into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(events)
|
Ok(events)
|
||||||
@@ -478,7 +479,7 @@ impl Client {
|
|||||||
one_way,
|
one_way,
|
||||||
received_stats,
|
received_stats,
|
||||||
bytes: packet.len(),
|
bytes: packet.len(),
|
||||||
packet_meta: PacketMeta::default(),
|
packet_meta: ReceiveMeta::default().into(),
|
||||||
}])
|
}])
|
||||||
} else if session.highest_received_seq.is_some_and(|h| wire_seq < h) {
|
} else if session.highest_received_seq.is_some_and(|h| wire_seq < h) {
|
||||||
// TODO: u32 ordering is acceptable for finite tests but continuous
|
// TODO: u32 ordering is acceptable for finite tests but continuous
|
||||||
@@ -495,7 +496,7 @@ impl Client {
|
|||||||
one_way: None,
|
one_way: None,
|
||||||
received_stats: build_received_stats(&reply),
|
received_stats: build_received_stats(&reply),
|
||||||
bytes: packet.len(),
|
bytes: packet.len(),
|
||||||
packet_meta: PacketMeta::default(),
|
packet_meta: ReceiveMeta::default().into(),
|
||||||
}])
|
}])
|
||||||
} else {
|
} else {
|
||||||
Ok(vec![ClientEvent::Warning {
|
Ok(vec![ClientEvent::Warning {
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ fn recv_once_decodes_echo_reply_and_emits_event() {
|
|||||||
received_stats,
|
received_stats,
|
||||||
server_timing,
|
server_timing,
|
||||||
bytes,
|
bytes,
|
||||||
|
packet_meta,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(*seq, 0);
|
assert_eq!(*seq, 0);
|
||||||
@@ -298,6 +299,10 @@ fn recv_once_decodes_echo_reply_and_emits_event() {
|
|||||||
assert!(server_timing.is_some());
|
assert!(server_timing.is_some());
|
||||||
let st = server_timing.as_ref().unwrap();
|
let st = server_timing.as_ref().unwrap();
|
||||||
assert!(st.processing.is_some());
|
assert!(st.processing.is_some());
|
||||||
|
assert_eq!(packet_meta.traffic_class, None);
|
||||||
|
assert_eq!(packet_meta.dscp, None);
|
||||||
|
assert_eq!(packet_meta.ecn, None);
|
||||||
|
assert_eq!(packet_meta.kernel_rx_timestamp, None);
|
||||||
}
|
}
|
||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,10 @@ pub struct ReceivedStatsSample {
|
|||||||
pub window: Option<u64>,
|
pub window: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receive-side packet metadata observed outside the IRTT wire protocol.
|
||||||
|
///
|
||||||
|
/// `None` means the metadata was unavailable. `Some(0)` means the metadata was
|
||||||
|
/// observed and its value was zero.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub struct PacketMeta {
|
pub struct PacketMeta {
|
||||||
pub traffic_class: Option<u8>,
|
pub traffic_class: Option<u8>,
|
||||||
@@ -150,3 +154,18 @@ pub struct PacketMeta {
|
|||||||
pub ecn: Option<u8>,
|
pub ecn: Option<u8>,
|
||||||
pub kernel_rx_timestamp: Option<std::time::SystemTime>,
|
pub kernel_rx_timestamp: Option<std::time::SystemTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::PacketMeta;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn packet_meta_default_is_unavailable() {
|
||||||
|
let meta = PacketMeta::default();
|
||||||
|
|
||||||
|
assert_eq!(meta.traffic_class, None);
|
||||||
|
assert_eq!(meta.dscp, None);
|
||||||
|
assert_eq!(meta.ecn, None);
|
||||||
|
assert_eq!(meta.kernel_rx_timestamp, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ mod config;
|
|||||||
mod error;
|
mod error;
|
||||||
mod event;
|
mod event;
|
||||||
mod managed;
|
mod managed;
|
||||||
|
mod metadata;
|
||||||
mod probe;
|
mod probe;
|
||||||
mod session;
|
mod session;
|
||||||
mod socket;
|
mod socket;
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use crate::event::PacketMeta;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub(crate) struct ReceiveMeta {
|
||||||
|
pub(crate) traffic_class: Option<u8>,
|
||||||
|
pub(crate) kernel_rx_timestamp: Option<SystemTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReceiveMeta> for PacketMeta {
|
||||||
|
fn from(meta: ReceiveMeta) -> Self {
|
||||||
|
Self {
|
||||||
|
traffic_class: meta.traffic_class,
|
||||||
|
dscp: meta.traffic_class.map(|traffic_class| traffic_class >> 2),
|
||||||
|
ecn: meta.traffic_class.map(|traffic_class| traffic_class & 0b11),
|
||||||
|
kernel_rx_timestamp: meta.kernel_rx_timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::ReceiveMeta;
|
||||||
|
use crate::event::PacketMeta;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn metadata_default_is_unavailable() {
|
||||||
|
let meta = ReceiveMeta::default();
|
||||||
|
let packet_meta = PacketMeta::from(meta);
|
||||||
|
|
||||||
|
assert_eq!(packet_meta.traffic_class, None);
|
||||||
|
assert_eq!(packet_meta.dscp, None);
|
||||||
|
assert_eq!(packet_meta.ecn, None);
|
||||||
|
assert_eq!(packet_meta.kernel_rx_timestamp, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn metadata_observed_traffic_class_zero_preserves_observed_zero() {
|
||||||
|
let packet_meta = PacketMeta::from(ReceiveMeta {
|
||||||
|
traffic_class: Some(0),
|
||||||
|
kernel_rx_timestamp: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(packet_meta.traffic_class, Some(0));
|
||||||
|
assert_eq!(packet_meta.dscp, Some(0));
|
||||||
|
assert_eq!(packet_meta.ecn, Some(0));
|
||||||
|
assert_eq!(packet_meta.kernel_rx_timestamp, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn metadata_observed_traffic_class_derives_dscp_and_ecn() {
|
||||||
|
let packet_meta = PacketMeta::from(ReceiveMeta {
|
||||||
|
traffic_class: Some(184),
|
||||||
|
kernel_rx_timestamp: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(packet_meta.traffic_class, Some(184));
|
||||||
|
assert_eq!(packet_meta.dscp, Some(46));
|
||||||
|
assert_eq!(packet_meta.ecn, Some(0));
|
||||||
|
assert_eq!(packet_meta.kernel_rx_timestamp, None);
|
||||||
|
|
||||||
|
let packet_meta = PacketMeta::from(ReceiveMeta {
|
||||||
|
traffic_class: Some(186),
|
||||||
|
kernel_rx_timestamp: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(packet_meta.traffic_class, Some(186));
|
||||||
|
assert_eq!(packet_meta.dscp, Some(46));
|
||||||
|
assert_eq!(packet_meta.ecn, Some(2));
|
||||||
|
assert_eq!(packet_meta.kernel_rx_timestamp, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn metadata_mapping_preserves_none_vs_observed_zero() {
|
||||||
|
let unavailable = PacketMeta::from(ReceiveMeta::default());
|
||||||
|
let observed_zero = PacketMeta::from(ReceiveMeta {
|
||||||
|
traffic_class: Some(0),
|
||||||
|
kernel_rx_timestamp: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(unavailable.traffic_class, None);
|
||||||
|
assert_eq!(unavailable.dscp, None);
|
||||||
|
assert_eq!(unavailable.ecn, None);
|
||||||
|
|
||||||
|
assert_eq!(observed_zero.traffic_class, Some(0));
|
||||||
|
assert_eq!(observed_zero.dscp, Some(0));
|
||||||
|
assert_eq!(observed_zero.ecn, Some(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user