Skip to main content

web_transport_trait/
lib.rs

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