1use crate::{Dir, Duration, frame::Frame};
11
12#[derive(Default, Debug, Copy, Clone)]
14#[non_exhaustive]
15pub struct UdpStats {
16 pub datagrams: u64,
18 pub bytes: u64,
20 pub ios: u64,
24}
25
26impl UdpStats {
27 pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
28 self.datagrams += datagrams;
29 self.bytes += bytes as u64;
30 self.ios += 1;
31 }
32}
33
34#[derive(Default, Copy, Clone)]
36#[non_exhaustive]
37#[allow(missing_docs)]
38pub struct FrameStats {
39 pub acks: u64,
40 pub ack_frequency: u64,
41 pub crypto: u64,
42 pub connection_close: u64,
43 pub data_blocked: u64,
44 pub datagram: u64,
45 pub handshake_done: u8,
46 pub immediate_ack: u64,
47 pub max_data: u64,
48 pub max_stream_data: u64,
49 pub max_streams_bidi: u64,
50 pub max_streams_uni: u64,
51 pub new_connection_id: u64,
52 pub new_token: u64,
53 pub path_challenge: u64,
54 pub path_response: u64,
55 pub ping: u64,
56 pub reset_stream: u64,
57 pub retire_connection_id: u64,
58 pub stream_data_blocked: u64,
59 pub streams_blocked_bidi: u64,
60 pub streams_blocked_uni: u64,
61 pub stop_sending: u64,
62 pub stream: u64,
63 pub add_address: u64,
64 pub punch_me_now: u64,
65 pub remove_address: u64,
66 pub observed_address: u64,
67 pub try_connect_to: u64,
68 pub try_connect_to_response: u64,
69}
70
71impl FrameStats {
72 pub(crate) fn record(&mut self, frame: &Frame) {
73 match frame {
74 Frame::Padding => {}
75 Frame::Ping => self.ping += 1,
76 Frame::Ack(_) => self.acks += 1,
77 Frame::ResetStream(_) => self.reset_stream += 1,
78 Frame::StopSending(_) => self.stop_sending += 1,
79 Frame::Crypto(_) => self.crypto += 1,
80 Frame::Datagram(_) => self.datagram += 1,
81 Frame::NewToken(_) => self.new_token += 1,
82 Frame::MaxData(_) => self.max_data += 1,
83 Frame::MaxStreamData { .. } => self.max_stream_data += 1,
84 Frame::MaxStreams { dir, .. } => {
85 if *dir == Dir::Bi {
86 self.max_streams_bidi += 1;
87 } else {
88 self.max_streams_uni += 1;
89 }
90 }
91 Frame::DataBlocked { .. } => self.data_blocked += 1,
92 Frame::Stream(_) => self.stream += 1,
93 Frame::StreamDataBlocked { .. } => self.stream_data_blocked += 1,
94 Frame::StreamsBlocked { dir, .. } => {
95 if *dir == Dir::Bi {
96 self.streams_blocked_bidi += 1;
97 } else {
98 self.streams_blocked_uni += 1;
99 }
100 }
101 Frame::NewConnectionId(_) => self.new_connection_id += 1,
102 Frame::RetireConnectionId { .. } => self.retire_connection_id += 1,
103 Frame::PathChallenge(_) => self.path_challenge += 1,
104 Frame::PathResponse(_) => self.path_response += 1,
105 Frame::Close(_) => self.connection_close += 1,
106 Frame::AckFrequency(_) => self.ack_frequency += 1,
107 Frame::ImmediateAck => self.immediate_ack += 1,
108 Frame::HandshakeDone => self.handshake_done = self.handshake_done.saturating_add(1),
109 Frame::AddAddress(_) => self.add_address += 1,
110 Frame::PunchMeNow(_) => self.punch_me_now += 1,
111 Frame::RemoveAddress(_) => self.remove_address += 1,
112 Frame::ObservedAddress(_) => self.observed_address += 1,
113 Frame::TryConnectTo(_) => self.try_connect_to += 1,
114 Frame::TryConnectToResponse(_) => self.try_connect_to_response += 1,
115 }
116 }
117}
118
119impl std::fmt::Debug for FrameStats {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 f.debug_struct("FrameStats")
122 .field("ACK", &self.acks)
123 .field("ACK_FREQUENCY", &self.ack_frequency)
124 .field("CONNECTION_CLOSE", &self.connection_close)
125 .field("CRYPTO", &self.crypto)
126 .field("DATA_BLOCKED", &self.data_blocked)
127 .field("DATAGRAM", &self.datagram)
128 .field("HANDSHAKE_DONE", &self.handshake_done)
129 .field("IMMEDIATE_ACK", &self.immediate_ack)
130 .field("MAX_DATA", &self.max_data)
131 .field("MAX_STREAM_DATA", &self.max_stream_data)
132 .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
133 .field("MAX_STREAMS_UNI", &self.max_streams_uni)
134 .field("NEW_CONNECTION_ID", &self.new_connection_id)
135 .field("NEW_TOKEN", &self.new_token)
136 .field("PATH_CHALLENGE", &self.path_challenge)
137 .field("PATH_RESPONSE", &self.path_response)
138 .field("PING", &self.ping)
139 .field("RESET_STREAM", &self.reset_stream)
140 .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
141 .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
142 .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
143 .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
144 .field("STOP_SENDING", &self.stop_sending)
145 .field("STREAM", &self.stream)
146 .field("ADD_ADDRESS", &self.add_address)
147 .field("PUNCH_ME_NOW", &self.punch_me_now)
148 .field("REMOVE_ADDRESS", &self.remove_address)
149 .field("OBSERVED_ADDRESS", &self.observed_address)
150 .finish()
151 }
152}
153
154#[derive(Debug, Default, Copy, Clone)]
156#[non_exhaustive]
157pub struct PathStats {
158 pub rtt: Duration,
160 pub cwnd: u64,
162 pub congestion_events: u64,
164 pub lost_packets: u64,
166 pub lost_bytes: u64,
168 pub sent_packets: u64,
170 pub sent_plpmtud_probes: u64,
172 pub lost_plpmtud_probes: u64,
175 pub black_holes_detected: u64,
177 pub current_mtu: u16,
179}
180
181#[derive(Debug, Default, Copy, Clone)]
183#[non_exhaustive]
184pub struct ConnectionStats {
185 pub udp_tx: UdpStats,
187 pub udp_rx: UdpStats,
189 pub frame_tx: FrameStats,
191 pub frame_rx: FrameStats,
193 pub path: PathStats,
195}