Skip to main content

iroh_netbench/
flow.rs

1//! Host-owned session and stream boundary used by benchmark flows.
2
3use std::{sync::Arc, time::Duration};
4
5use async_trait::async_trait;
6
7use crate::{PathKind, Result};
8
9/// One transport telemetry snapshot supplied by the host session.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct NetBenchTelemetry {
12    /// Currently selected transport path.
13    pub path: PathKind,
14    /// QUIC packets declared lost.
15    pub lost_packets: u64,
16    /// QUIC bytes declared lost.
17    pub lost_bytes: u64,
18    /// Congestion events on the selected path.
19    pub congestion_events: u64,
20    /// Received transport datagrams.
21    pub rx_datagrams: u64,
22    /// Transmitted transport datagrams.
23    pub tx_datagrams: u64,
24    /// Current path MTU, or zero when unavailable.
25    pub current_mtu: u16,
26    /// Path black-hole detections.
27    pub black_holes_detected: u64,
28    /// Current smoothed transport RTT.
29    pub rtt: Duration,
30}
31
32impl Default for NetBenchTelemetry {
33    fn default() -> Self {
34        Self {
35            path: PathKind::Unknown,
36            lost_packets: 0,
37            lost_bytes: 0,
38            congestion_events: 0,
39            rx_datagrams: 0,
40            tx_datagrams: 0,
41            current_mtu: 0,
42            black_holes_detected: 0,
43            rtt: Duration::ZERO,
44        }
45    }
46}
47
48/// Sending half of one host-routed reliable stream.
49#[async_trait]
50pub trait NetBenchSendStream: Send {
51    /// Writes one buffer and returns its complete length.
52    async fn write(&mut self, bytes: &[u8]) -> Result<usize> {
53        self.write_all(bytes).await?;
54        Ok(bytes.len())
55    }
56    /// Writes the complete buffer.
57    async fn write_all(&mut self, bytes: &[u8]) -> Result<()>;
58    /// Finishes the sending half without closing the host session.
59    ///
60    /// # Errors
61    ///
62    /// Returns the host adapter's flow-local stream error.
63    fn finish(&mut self) -> Result<()>;
64    /// Cancels this stream's pending write without closing the host session.
65    fn cancel(&mut self);
66}
67
68#[async_trait]
69impl<T: NetBenchSendStream + ?Sized> NetBenchSendStream for Box<T> {
70    async fn write(&mut self, bytes: &[u8]) -> Result<usize> {
71        (**self).write(bytes).await
72    }
73
74    async fn write_all(&mut self, bytes: &[u8]) -> Result<()> {
75        (**self).write_all(bytes).await
76    }
77
78    fn finish(&mut self) -> Result<()> {
79        (**self).finish()
80    }
81
82    fn cancel(&mut self) {
83        (**self).cancel();
84    }
85}
86
87/// Receiving half of one host-routed reliable stream.
88#[async_trait]
89pub trait NetBenchReceiveStream: Send {
90    /// Reads available bytes, returning zero after the peer finishes.
91    async fn read(&mut self, bytes: &mut [u8]) -> Result<usize>;
92    /// Reads exactly the supplied buffer length.
93    async fn read_exact(&mut self, bytes: &mut [u8]) -> Result<()>;
94    /// Cancels this stream's pending read without closing the host session.
95    fn cancel(&mut self);
96}
97
98#[async_trait]
99impl<T: NetBenchReceiveStream + ?Sized> NetBenchReceiveStream for Box<T> {
100    async fn read(&mut self, bytes: &mut [u8]) -> Result<usize> {
101        (**self).read(bytes).await
102    }
103
104    async fn read_exact(&mut self, bytes: &mut [u8]) -> Result<()> {
105        (**self).read_exact(bytes).await
106    }
107
108    fn cancel(&mut self) {
109        (**self).cancel();
110    }
111}
112
113/// One host-routed reliable bidirectional stream.
114pub trait NetBenchBidirectionalStream: Send {
115    /// Splits the stream so sending and receiving can run independently.
116    fn into_split(self: Box<Self>)
117    -> (Box<dyn NetBenchSendStream>, Box<dyn NetBenchReceiveStream>);
118}
119
120/// A flow-scoped view of one caller-owned authenticated peer session.
121///
122/// Implementations perform typed Stream and Datagram demultiplexing. Therefore `accept_bi` and
123/// `read_datagram` must never return traffic belonging to another host business flow.
124#[async_trait]
125pub trait NetBenchSession: Send + Sync {
126    /// Stable, non-secret remote identity for the report.
127    fn remote_peer_id(&self) -> String;
128    /// Samples path and transport counters without active probing.
129    fn telemetry(&self) -> NetBenchTelemetry;
130    /// Current maximum application Datagram payload.
131    fn max_datagram_size(&self) -> Option<usize>;
132    /// Opens one stream already typed as part of this netbench flow.
133    async fn open_bi(&self) -> Result<Box<dyn NetBenchBidirectionalStream>>;
134    /// Accepts the next stream already routed to this netbench flow.
135    async fn accept_bi(&self) -> Result<Box<dyn NetBenchBidirectionalStream>>;
136    /// Sends one flow-scoped unreliable Datagram.
137    async fn send_datagram(&self, bytes: Vec<u8>) -> Result<()>;
138    /// Reads the next Datagram routed to this flow.
139    async fn read_datagram(&self) -> Result<Vec<u8>>;
140}
141
142/// One admitted benchmark business flow.
143pub struct NetBenchFlow {
144    session: Arc<dyn NetBenchSession>,
145    control_send: Box<dyn NetBenchSendStream>,
146    control_recv: Box<dyn NetBenchReceiveStream>,
147}
148
149impl std::fmt::Debug for NetBenchFlow {
150    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        formatter
152            .debug_struct("NetBenchFlow")
153            .field("remote_peer_id", &self.session.remote_peer_id())
154            .finish_non_exhaustive()
155    }
156}
157
158impl NetBenchFlow {
159    /// Creates a flow after the host has authenticated/admitted the peer and routed its control
160    /// stream and Datagram namespace.
161    #[must_use]
162    pub fn new(
163        session: Arc<dyn NetBenchSession>,
164        control_send: Box<dyn NetBenchSendStream>,
165        control_recv: Box<dyn NetBenchReceiveStream>,
166    ) -> Self {
167        Self {
168            session,
169            control_send,
170            control_recv,
171        }
172    }
173
174    pub(crate) fn into_parts(
175        self,
176    ) -> (
177        Arc<dyn NetBenchSession>,
178        Box<dyn NetBenchSendStream>,
179        Box<dyn NetBenchReceiveStream>,
180    ) {
181        (self.session, self.control_send, self.control_recv)
182    }
183}