tokio_quiche/http3/
stats.rs

1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use std::sync::atomic::AtomicI64;
28use std::sync::atomic::AtomicU64;
29use std::sync::atomic::Ordering;
30use std::time::Duration;
31
32use crossbeam::atomic::AtomicCell;
33use datagram_socket::StreamClosureKind;
34
35/// Stream-level HTTP/3 audit statistics recorded by
36/// [H3Driver](crate::http3::driver::H3Driver).
37#[derive(Debug)]
38pub struct H3AuditStats {
39    /// The stream ID of this session.
40    stream_id: u64,
41    /// The number of bytes sent over the stream.
42    downstream_bytes_sent: AtomicU64,
43    /// The number of bytes received over the stream.
44    downstream_bytes_recvd: AtomicU64,
45    /// A STOP_SENDING error code received from the peer.
46    ///
47    /// -1 indicates that this error code was not received yet.
48    recvd_stop_sending_error_code: AtomicI64,
49    /// A RESET_STREAM error code received from the peer.
50    ///
51    /// -1 indicates that this error code was not received yet.
52    recvd_reset_stream_error_code: AtomicI64,
53    /// A STOP_SENDING error code sent to the peer.
54    ///
55    /// -1 indicates that this error code was not received yet.
56    sent_stop_sending_error_code: AtomicI64,
57    /// A RESET_STREAM error code sent to the peer.
58    ///
59    /// -1 indicates that this error code was not received yet.
60    sent_reset_stream_error_code: AtomicI64,
61    /// Stream FIN received from the peer.
62    recvd_stream_fin: AtomicCell<StreamClosureKind>,
63    /// Stream FIN sent to the peer.
64    sent_stream_fin: AtomicCell<StreamClosureKind>,
65    /// Cumulative time between HEADERS failed flush and complete.
66    ///
67    /// Measured as the duration between the first moment a HEADERS frame was
68    /// not flushed in full, and the moment that it was completely flushed.
69    /// Measured across all HEADERS frames sent on the stream. A value of 0
70    /// indicates there was no failed flushing.
71    headers_flush_duration: AtomicCell<Duration>,
72}
73
74impl H3AuditStats {
75    pub fn new(stream_id: u64) -> Self {
76        Self {
77            stream_id,
78            downstream_bytes_sent: AtomicU64::new(0),
79            downstream_bytes_recvd: AtomicU64::new(0),
80            recvd_stop_sending_error_code: AtomicI64::new(-1),
81            recvd_reset_stream_error_code: AtomicI64::new(-1),
82            sent_stop_sending_error_code: AtomicI64::new(-1),
83            sent_reset_stream_error_code: AtomicI64::new(-1),
84            recvd_stream_fin: AtomicCell::new(StreamClosureKind::None),
85            sent_stream_fin: AtomicCell::new(StreamClosureKind::None),
86            headers_flush_duration: AtomicCell::new(Duration::from_secs(0)),
87        }
88    }
89
90    /// The stream ID of this session.
91    #[inline]
92    pub fn stream_id(&self) -> u64 {
93        self.stream_id
94    }
95
96    /// The number of bytes sent over the stream.
97    #[inline]
98    pub fn downstream_bytes_sent(&self) -> u64 {
99        self.downstream_bytes_sent.load(Ordering::SeqCst)
100    }
101
102    /// The number of bytes received over the stream.
103    #[inline]
104    pub fn downstream_bytes_recvd(&self) -> u64 {
105        self.downstream_bytes_recvd.load(Ordering::SeqCst)
106    }
107
108    /// A STOP_SENDING error code received from the peer.
109    ///
110    /// -1 indicates that this error code was not received yet.
111    #[inline]
112    pub fn recvd_stop_sending_error_code(&self) -> i64 {
113        self.recvd_stop_sending_error_code.load(Ordering::SeqCst)
114    }
115
116    /// A RESET_STREAM error code received from the peer.
117    ///
118    /// -1 indicates that this error code was not received yet.
119    #[inline]
120    pub fn recvd_reset_stream_error_code(&self) -> i64 {
121        self.recvd_reset_stream_error_code.load(Ordering::SeqCst)
122    }
123
124    /// A STOP_SENDING error code sent to the peer.
125    ///
126    /// -1 indicates that this error code was not received yet.
127    #[inline]
128    pub fn sent_stop_sending_error_code(&self) -> i64 {
129        self.sent_stop_sending_error_code.load(Ordering::SeqCst)
130    }
131
132    /// A RESET_STREAM error code sent to the peer.
133    ///
134    /// -1 indicates that this error code was not received yet.
135    #[inline]
136    pub fn sent_reset_stream_error_code(&self) -> i64 {
137        self.sent_reset_stream_error_code.load(Ordering::SeqCst)
138    }
139
140    /// Stream FIN received from the peer.
141    #[inline]
142    pub fn recvd_stream_fin(&self) -> StreamClosureKind {
143        self.recvd_stream_fin.load()
144    }
145
146    /// Stream FIN sent to the peer.
147    #[inline]
148    pub fn sent_stream_fin(&self) -> StreamClosureKind {
149        self.sent_stream_fin.load()
150    }
151
152    /// Cumulative time between HEADERS failed flush and complete.
153    ///
154    /// Measured as the duration between the first moment a HEADERS frame was
155    /// not flushed in full, and the moment that it was completely flushed.
156    /// Measured across all HEADERS frames sent on the stream. A value of 0
157    /// indicates there was no failed flushing.
158    #[inline]
159    pub fn headers_flush_duration(&self) -> Duration {
160        self.headers_flush_duration.load()
161    }
162
163    #[inline]
164    pub fn add_downstream_bytes_sent(&self, bytes_sent: u64) {
165        self.downstream_bytes_sent
166            .fetch_add(bytes_sent, Ordering::SeqCst);
167    }
168
169    #[inline]
170    pub fn add_downstream_bytes_recvd(&self, bytes_recvd: u64) {
171        self.downstream_bytes_recvd
172            .fetch_add(bytes_recvd, Ordering::SeqCst);
173    }
174
175    #[inline]
176    pub fn set_recvd_stop_sending_error_code(
177        &self, recvd_stop_sending_error_code: i64,
178    ) {
179        self.recvd_stop_sending_error_code
180            .store(recvd_stop_sending_error_code, Ordering::SeqCst);
181    }
182
183    #[inline]
184    pub fn set_recvd_reset_stream_error_code(
185        &self, recvd_reset_stream_error_code: i64,
186    ) {
187        self.recvd_reset_stream_error_code
188            .store(recvd_reset_stream_error_code, Ordering::SeqCst);
189    }
190
191    #[inline]
192    pub fn set_sent_stop_sending_error_code(
193        &self, sent_stop_sending_error_code: i64,
194    ) {
195        self.sent_stop_sending_error_code
196            .store(sent_stop_sending_error_code, Ordering::SeqCst);
197    }
198
199    #[inline]
200    pub fn set_sent_reset_stream_error_code(
201        &self, sent_reset_stream_error_code: i64,
202    ) {
203        self.sent_reset_stream_error_code
204            .store(sent_reset_stream_error_code, Ordering::SeqCst);
205    }
206
207    #[inline]
208    pub fn set_recvd_stream_fin(&self, recvd_stream_fin: StreamClosureKind) {
209        self.recvd_stream_fin.store(recvd_stream_fin);
210    }
211
212    #[inline]
213    pub fn set_sent_stream_fin(&self, sent_stream_fin: StreamClosureKind) {
214        self.sent_stream_fin.store(sent_stream_fin);
215    }
216
217    #[inline]
218    pub fn add_header_flush_duration(&self, duration: Duration) {
219        // NB: load and store may not be atomic but we aren't accessing the
220        // object from any other thread so things should be ok.
221        let current = self.headers_flush_duration.load();
222        self.headers_flush_duration.store(current + duration);
223    }
224}