Skip to main content

web_transport_trait/
lib.rs

1mod util;
2
3use std::future::Future;
4use std::time::Duration;
5
6pub use crate::util::{MaybeSend, MaybeSync};
7use bytes::{Buf, BufMut, Bytes, BytesMut};
8
9/// Connection-level statistics.
10///
11/// Methods return `Option` — `None` means the implementation doesn't track
12/// this metric, while `Some(0)` means actually zero.
13pub trait Stats {
14    /// Total bytes sent over the connection, including retransmissions and overhead.
15    fn bytes_sent(&self) -> Option<u64> {
16        None
17    }
18
19    /// Total bytes received over the connection, including duplicate and overhead.
20    fn bytes_received(&self) -> Option<u64> {
21        None
22    }
23
24    /// Total bytes lost (detected via retransmission or acknowledgement).
25    fn bytes_lost(&self) -> Option<u64> {
26        None
27    }
28
29    /// Total number of datagrams sent.
30    fn packets_sent(&self) -> Option<u64> {
31        None
32    }
33
34    /// Total number of datagrams received.
35    fn packets_received(&self) -> Option<u64> {
36        None
37    }
38
39    /// Total number of datagrams detected as lost.
40    fn packets_lost(&self) -> Option<u64> {
41        None
42    }
43
44    /// Smoothed round-trip time estimate.
45    fn rtt(&self) -> Option<Duration> {
46        None
47    }
48
49    /// Estimated available send bandwidth, in bits per second.
50    fn estimated_send_rate(&self) -> Option<u64> {
51        None
52    }
53}
54
55/// Default stats implementation that returns `None` for all metrics.
56pub struct StatsUnavailable;
57impl Stats for StatsUnavailable {}
58
59/// Error trait for WebTransport operations.
60///
61/// Implementations must be Send + Sync + 'static for use across async boundaries.
62pub trait Error: std::error::Error + MaybeSend + MaybeSync + 'static {
63    /// Returns the error code and reason if this was an application error.
64    ///
65    /// NOTE: Reason reasons are technically bytes on the wire, but we convert to a String for convenience.
66    fn session_error(&self) -> Option<(u32, String)>;
67
68    /// Returns the error code if this was a stream error.
69    fn stream_error(&self) -> Option<u32> {
70        None
71    }
72}
73
74/// A WebTransport Session, able to accept/create streams and send/recv datagrams.
75///
76/// The session can be cloned to create multiple handles.
77/// The session will be closed on drop.
78pub trait Session: Clone + MaybeSend + MaybeSync + 'static {
79    type SendStream: SendStream;
80    type RecvStream: RecvStream;
81    type Error: Error;
82
83    /// Block until the peer creates a new unidirectional stream.
84    fn accept_uni(&self)
85        -> impl Future<Output = Result<Self::RecvStream, Self::Error>> + MaybeSend;
86
87    /// Block until the peer creates a new bidirectional stream.
88    fn accept_bi(
89        &self,
90    ) -> impl Future<Output = Result<(Self::SendStream, Self::RecvStream), Self::Error>> + MaybeSend;
91
92    /// Open a new bidirectional stream, which may block when there are too many concurrent streams.
93    fn open_bi(
94        &self,
95    ) -> impl Future<Output = Result<(Self::SendStream, Self::RecvStream), Self::Error>> + MaybeSend;
96
97    /// Open a new unidirectional stream, which may block when there are too many concurrent streams.
98    fn open_uni(&self) -> impl Future<Output = Result<Self::SendStream, Self::Error>> + MaybeSend;
99
100    /// Send a datagram over the network.
101    ///
102    /// QUIC datagrams may be dropped for any reason:
103    /// - Network congestion.
104    /// - Random packet loss.
105    /// - Payload is larger than `max_datagram_size()`
106    /// - Peer is not receiving datagrams.
107    /// - Peer has too many outstanding datagrams.
108    /// - ???
109    fn send_datagram(&self, payload: Bytes) -> Result<(), Self::Error>;
110
111    /// Receive a datagram over the network.
112    fn recv_datagram(&self) -> impl Future<Output = Result<Bytes, Self::Error>> + MaybeSend;
113
114    /// The maximum size of a datagram that can be sent.
115    fn max_datagram_size(&self) -> usize;
116
117    /// Return the negotiated WebTransport subprotocol, if any.
118    fn protocol(&self) -> Option<&str> {
119        None
120    }
121
122    /// Close the connection immediately with a code and reason.
123    fn close(&self, code: u32, reason: &str);
124
125    /// Block until the connection is closed by either side.
126    fn closed(&self) -> impl Future<Output = Self::Error> + MaybeSend;
127
128    /// Return connection-level statistics, if supported.
129    fn stats(&self) -> impl Stats {
130        StatsUnavailable
131    }
132}
133
134/// An outgoing stream of bytes to the peer.
135///
136/// QUIC streams have flow control, which means the send rate is limited by the peer's receive window.
137/// The stream will be closed with a graceful FIN when dropped.
138pub trait SendStream: MaybeSend {
139    type Error: Error;
140
141    /// Write some of the buffer to the stream, returning how many bytes were
142    /// written. See [`write_buf`](Self::write_buf) for the cancel-safety contract,
143    /// which this shares.
144    fn write(&mut self, buf: &[u8])
145        -> impl Future<Output = Result<usize, Self::Error>> + MaybeSend;
146
147    /// Write some of the given buffer to the stream, advancing it by the number of
148    /// bytes written. This may be less than the whole buffer, so callers loop (or
149    /// use [`write_all`](Self::write_all)).
150    ///
151    /// # Cancel safety
152    ///
153    /// Implementations must be cancel safe: if the returned future is dropped
154    /// before it resolves, `buf` must not have been advanced past the bytes the
155    /// implementation accepted for sending. (Whether they reach the peer is a
156    /// separate matter — a reset or a dead connection can still discard accepted
157    /// bytes.) Callers race writes against other work, so a byte taken from `buf`
158    /// but never accepted becomes a silent hole in the stream, which the peer
159    /// decodes as a truncated or garbage frame. Wait for send capacity *before*
160    /// consuming from `buf`, never after.
161    fn write_buf<B: Buf + MaybeSend>(
162        &mut self,
163        buf: &mut B,
164    ) -> impl Future<Output = Result<usize, Self::Error>> + MaybeSend {
165        async move {
166            let chunk = buf.chunk();
167            let size = self.write(chunk).await?;
168            buf.advance(size);
169            Ok(size)
170        }
171    }
172
173    /// Write the entire [Bytes] chunk to the stream, potentially avoiding a copy.
174    fn write_chunk(
175        &mut self,
176        chunk: Bytes,
177    ) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend {
178        async move {
179            // Just so the arg isn't mut
180            let mut c = chunk;
181            self.write_buf(&mut c).await?;
182            Ok(())
183        }
184    }
185
186    /// A helper to write all the data in the buffer.
187    fn write_all(
188        &mut self,
189        buf: &[u8],
190    ) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend {
191        async move {
192            let mut pos = 0;
193            while pos < buf.len() {
194                pos += self.write(&buf[pos..]).await?;
195            }
196            Ok(())
197        }
198    }
199
200    /// A helper to write all of the data in the buffer.
201    fn write_all_buf<B: Buf + MaybeSend>(
202        &mut self,
203        buf: &mut B,
204    ) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend {
205        async move {
206            while buf.has_remaining() {
207                self.write_buf(buf).await?;
208            }
209            Ok(())
210        }
211    }
212
213    /// Set the stream's priority.
214    ///
215    /// Streams with higher values will be sent first, but are not guaranteed to arrive first.
216    /// This matches the W3C WebTransport `sendOrder` convention (and quinn's scheduler).
217    fn set_priority(&mut self, order: u8);
218
219    /// Mark the stream as finished, erroring on any future writes.
220    ///
221    /// [SendStream::reset] can still be called to abandon any queued data.
222    /// [SendStream::closed] should return when the FIN is acknowledged by the peer.
223    ///
224    /// NOTE: Quinn implicitly calls this on Drop, but it's a common footgun.
225    /// Implementations SHOULD [SendStream::reset] on Drop instead.
226    fn finish(&mut self) -> Result<(), Self::Error>;
227
228    /// Immediately closes the stream and discards any remaining data.
229    ///
230    /// This translates into a RESET_STREAM QUIC code.
231    /// The peer may not receive the reset code if the stream is already closed.
232    fn reset(&mut self, code: u32);
233
234    /// Block until the stream is closed by either side.
235    ///
236    /// This includes:
237    /// - We sent a RESET_STREAM via [SendStream::reset]
238    /// - We received a STOP_SENDING via [RecvStream::stop]
239    /// - A FIN is acknowledged by the peer via [SendStream::finish]
240    ///
241    /// Some implementations do not support FIN acknowledgement, in which case this will block until the FIN is sent.
242    ///
243    /// NOTE: This takes a &mut to match Quinn and to simplify the implementation.
244    fn closed(&mut self) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend;
245}
246
247/// An incoming stream of bytes from the peer.
248///
249/// All bytes are flushed in order and the stream is flow controlled.
250/// The stream will be closed with STOP_SENDING code=0 when dropped.
251pub trait RecvStream: MaybeSend {
252    type Error: Error;
253
254    /// Read the next chunk of data, up to the max size.
255    ///
256    /// This returns a chunk of data instead of copying, which may be more efficient.
257    fn read(
258        &mut self,
259        dst: &mut [u8],
260    ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + MaybeSend;
261
262    /// Read some data into the provided buffer.
263    ///
264    /// The number of bytes read is returned, or None if the stream is closed.
265    /// The buffer will be advanced by the number of bytes read.
266    fn read_buf<B: BufMut + MaybeSend>(
267        &mut self,
268        buf: &mut B,
269    ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + MaybeSend {
270        async move {
271            let dst = unsafe {
272                std::mem::transmute::<&mut bytes::buf::UninitSlice, &mut [u8]>(buf.chunk_mut())
273            };
274            let size = match self.read(dst).await? {
275                Some(size) if size > 0 => size,
276                _ => return Ok(None),
277            };
278
279            unsafe { buf.advance_mut(size) };
280
281            Ok(Some(size))
282        }
283    }
284
285    /// Read the next chunk of data, up to the max size.
286    ///
287    /// This returns a chunk of data instead of copying, which may be more efficient.
288    fn read_chunk(
289        &mut self,
290        max: usize,
291    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + MaybeSend {
292        async move {
293            // Don't allocate too much. Write your own if you want to increase this buffer.
294            let mut buf = BytesMut::with_capacity(max.min(8 * 1024));
295
296            // TODO Test this, I think it will work?
297            Ok(self.read_buf(&mut buf).await?.map(|_| buf.freeze()))
298        }
299    }
300
301    /// Send a `STOP_SENDING` QUIC code, informing the peer that no more data will be read.
302    ///
303    /// An implementation MUST do this on Drop otherwise flow control will be leaked.
304    /// Call this method manually if you want to specify a code yourself.
305    fn stop(&mut self, code: u32);
306
307    /// Block until the stream has been closed by either side.
308    ///
309    /// This includes:
310    /// - We received a RESET_STREAM via [SendStream::reset]
311    /// - We sent a STOP_SENDING via [RecvStream::stop]
312    /// - We received a FIN via [SendStream::finish] and read all data.
313    fn closed(&mut self) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend;
314
315    /// A helper to keep reading until the stream is closed.
316    fn read_all(&mut self) -> impl Future<Output = Result<Bytes, Self::Error>> + MaybeSend {
317        async move {
318            let mut buf = BytesMut::new();
319            self.read_all_buf(&mut buf).await?;
320            Ok(buf.freeze())
321        }
322    }
323
324    /// A helper to keep reading until the buffer is full.
325    fn read_all_buf<B: BufMut + MaybeSend>(
326        &mut self,
327        buf: &mut B,
328    ) -> impl Future<Output = Result<usize, Self::Error>> + MaybeSend {
329        async move {
330            let mut size = 0;
331            while buf.has_remaining_mut() {
332                match self.read_buf(buf).await? {
333                    Some(n) if n > 0 => size += n,
334                    _ => break,
335                }
336            }
337            Ok(size)
338        }
339    }
340}