webtrans_trait/
lib.rs

1mod bounds;
2
3use std::future::Future;
4
5pub use crate::bounds::{MaybeSend, MaybeSync};
6use bytes::{Buf, BufMut, Bytes, BytesMut};
7
8/// Error trait for WebTransport operations.
9///
10/// Implementations must be Send + Sync + 'static to cross async boundaries.
11pub trait Error: std::error::Error + MaybeSend + MaybeSync + 'static {
12    /// Return the error code and reason if this was an application error.
13    ///
14    /// NOTE: Reasons are bytes on the wire, but are converted to `String` for convenience.
15    fn session_error(&self) -> Option<(u32, String)>;
16
17    /// Return the error code if this was a stream error.
18    fn stream_error(&self) -> Option<u32> {
19        None
20    }
21}
22
23/// A WebTransport session that can accept/create streams and send/receive datagrams.
24///
25/// The session can be cloned to create multiple handles.
26/// The session will be closed on drop.
27pub trait Session: Clone + MaybeSend + MaybeSync + 'static {
28    type SendStream: SendStream;
29    type RecvStream: RecvStream;
30    type Error: Error;
31
32    /// Block until the peer creates a new unidirectional stream.
33    fn accept_uni(&self)
34    -> impl Future<Output = Result<Self::RecvStream, Self::Error>> + MaybeSend;
35
36    /// Block until the peer creates a new bidirectional stream.
37    fn accept_bi(
38        &self,
39    ) -> impl Future<Output = Result<(Self::SendStream, Self::RecvStream), Self::Error>> + MaybeSend;
40
41    /// Open a new bidirectional stream, which may block if too many streams are open.
42    fn open_bi(
43        &self,
44    ) -> impl Future<Output = Result<(Self::SendStream, Self::RecvStream), Self::Error>> + MaybeSend;
45
46    /// Open a new unidirectional stream, which may block if too many streams are open.
47    fn open_uni(&self) -> impl Future<Output = Result<Self::SendStream, Self::Error>> + MaybeSend;
48
49    /// Send a datagram over the network.
50    ///
51    /// QUIC datagrams may be dropped for any reason:
52    /// - Network congestion.
53    /// - Random packet loss.
54    /// - Payload is larger than `max_datagram_size()`.
55    /// - Peer is not receiving datagrams.
56    /// - Peer has too many outstanding datagrams.
57    /// - Implementation-specific limits.
58    fn send_datagram(&self, payload: Bytes) -> Result<(), Self::Error>;
59
60    /// Receive a datagram over the network.
61    fn recv_datagram(&self) -> impl Future<Output = Result<Bytes, Self::Error>> + MaybeSend;
62
63    /// Return the maximum size of a datagram that can be sent.
64    fn max_datagram_size(&self) -> usize;
65
66    /// Close the connection immediately with a code and reason.
67    fn close(&self, code: u32, reason: &str);
68
69    /// Block until the connection is closed by either side.
70    fn closed(&self) -> impl Future<Output = Self::Error> + MaybeSend;
71}
72
73/// An outgoing stream of bytes to the peer.
74///
75/// QUIC streams have flow control, which means the send rate is limited by the peer's receive window.
76/// The stream is closed with a graceful FIN when dropped.
77pub trait SendStream: MaybeSend {
78    type Error: Error;
79
80    /// Write some of the buffer to the stream.
81    fn write(&mut self, buf: &[u8])
82    -> impl Future<Output = Result<usize, Self::Error>> + MaybeSend;
83
84    /// Write the given buffer to the stream, advancing the internal position.
85    fn write_buf<B: Buf + MaybeSend>(
86        &mut self,
87        buf: &mut B,
88    ) -> impl Future<Output = Result<usize, Self::Error>> + MaybeSend {
89        async move {
90            let chunk = buf.chunk();
91            let size = self.write(chunk).await?;
92            buf.advance(size);
93            Ok(size)
94        }
95    }
96
97    /// Write the entire [Bytes] chunk to the stream, potentially avoiding a copy.
98    fn write_chunk(
99        &mut self,
100        chunk: Bytes,
101    ) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend {
102        async move {
103            // Avoid a mutable binding for the argument.
104            let mut c = chunk;
105            self.write_buf(&mut c).await?;
106            Ok(())
107        }
108    }
109
110    /// Helper to write all data in the buffer.
111    fn write_all(
112        &mut self,
113        buf: &[u8],
114    ) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend {
115        async move {
116            let mut pos = 0;
117            while pos < buf.len() {
118                pos += self.write(&buf[pos..]).await?;
119            }
120            Ok(())
121        }
122    }
123
124    /// Helper to write all data in the buffer.
125    fn write_all_buf<B: Buf + MaybeSend>(
126        &mut self,
127        buf: &mut B,
128    ) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend {
129        async move {
130            while buf.has_remaining() {
131                self.write_buf(buf).await?;
132            }
133            Ok(())
134        }
135    }
136
137    /// Set the stream's priority.
138    ///
139    /// Streams with lower values are sent first, but arrival order is not guaranteed.
140    fn set_priority(&mut self, order: u8);
141
142    /// Mark the stream as finished, erroring on any future writes.
143    ///
144    /// [SendStream::reset] can still be called to abandon queued data.
145    /// [SendStream::closed] should return when the FIN is acknowledged by the peer.
146    ///
147    /// NOTE: Quinn implicitly calls this on drop, but it is a common footgun.
148    /// Implementations should call [SendStream::reset] on drop instead.
149    fn finish(&mut self) -> Result<(), Self::Error>;
150
151    /// Immediately close the stream and discard any remaining data.
152    ///
153    /// This translates into a RESET_STREAM QUIC code.
154    /// The peer may not receive the reset code if the stream is already closed.
155    fn reset(&mut self, code: u32);
156
157    /// Block until the stream is closed by either side.
158    ///
159    /// This includes:
160    /// - We sent a RESET_STREAM via [SendStream::reset]
161    /// - We received a STOP_SENDING via [RecvStream::stop]
162    /// - A FIN is acknowledged by the peer via [SendStream::finish]
163    ///
164    /// Some implementations do not support FIN acknowledgement, in which case this blocks until the FIN is sent.
165    ///
166    /// NOTE: This takes `&mut` to match Quinn and simplify the implementation.
167    fn closed(&mut self) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend;
168}
169
170/// An incoming stream of bytes from the peer.
171///
172/// All bytes are flushed in order and the stream is flow controlled.
173/// The stream is closed with STOP_SENDING code=0 when dropped.
174pub trait RecvStream: MaybeSend {
175    type Error: Error;
176
177    /// Read the next chunk of data, up to the max size.
178    ///
179    /// This returns a chunk of data instead of copying, which can be more efficient.
180    fn read(
181        &mut self,
182        dst: &mut [u8],
183    ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + MaybeSend;
184
185    /// Read some data into the provided buffer.
186    ///
187    /// The number of bytes read is returned, or `None` if the stream is closed.
188    /// The buffer is advanced by the number of bytes read.
189    fn read_buf<B: BufMut + MaybeSend>(
190        &mut self,
191        buf: &mut B,
192    ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + MaybeSend {
193        async move {
194            let dst = unsafe {
195                std::mem::transmute::<&mut bytes::buf::UninitSlice, &mut [u8]>(buf.chunk_mut())
196            };
197            let size = match self.read(dst).await? {
198                Some(size) => size,
199                None => return Ok(None),
200            };
201
202            unsafe { buf.advance_mut(size) };
203
204            Ok(Some(size))
205        }
206    }
207
208    /// Read the next chunk of data, up to the max size.
209    ///
210    /// This returns a chunk of data instead of copying, which can be more efficient.
211    fn read_chunk(
212        &mut self,
213        max: usize,
214    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + MaybeSend {
215        async move {
216            // Avoid excessive allocation; provide your own buffer to increase this limit.
217            let mut buf = BytesMut::with_capacity(max.min(8 * 1024));
218
219            Ok(self.read_buf(&mut buf).await?.map(|_| buf.freeze()))
220        }
221    }
222
223    /// Send a `STOP_SENDING` QUIC code, informing the peer that no more data will be read.
224    ///
225    /// Implementations must do this on drop to avoid leaking flow control.
226    /// Call this method manually to specify a custom code.
227    fn stop(&mut self, code: u32);
228
229    /// Block until the stream has been closed by either side.
230    ///
231    /// This includes:
232    /// - We received a RESET_STREAM via [SendStream::reset]
233    /// - We sent a STOP_SENDING via [RecvStream::stop]
234    /// - We received a FIN via [SendStream::finish] and read all data.
235    fn closed(&mut self) -> impl Future<Output = Result<(), Self::Error>> + MaybeSend;
236
237    /// Helper to keep reading until the stream is closed.
238    fn read_all(&mut self) -> impl Future<Output = Result<Bytes, Self::Error>> + MaybeSend {
239        async move {
240            let mut buf = BytesMut::new();
241            self.read_all_buf(&mut buf).await?;
242            Ok(buf.freeze())
243        }
244    }
245
246    /// Helper to keep reading until the buffer is full.
247    fn read_all_buf<B: BufMut + MaybeSend>(
248        &mut self,
249        buf: &mut B,
250    ) -> impl Future<Output = Result<usize, Self::Error>> + MaybeSend {
251        async move {
252            let mut size = 0;
253            while buf.has_remaining_mut() {
254                match self.read_buf(buf).await? {
255                    Some(n) => size += n,
256                    None => break,
257                }
258            }
259            Ok(size)
260        }
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use super::{Error, MaybeSend, RecvStream};
267    use bytes::{Bytes, BytesMut};
268    use futures::executor::block_on;
269    use std::fmt;
270
271    #[derive(Debug)]
272    struct TestError;
273
274    impl fmt::Display for TestError {
275        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276            write!(f, "test error")
277        }
278    }
279
280    impl std::error::Error for TestError {}
281
282    impl Error for TestError {
283        fn session_error(&self) -> Option<(u32, String)> {
284            None
285        }
286    }
287
288    struct TestRecvStream {
289        data: Vec<u8>,
290        pos: usize,
291    }
292
293    impl TestRecvStream {
294        fn new(data: &[u8]) -> Self {
295            Self {
296                data: data.to_vec(),
297                pos: 0,
298            }
299        }
300    }
301
302    impl RecvStream for TestRecvStream {
303        type Error = TestError;
304
305        fn read(
306            &mut self,
307            dst: &mut [u8],
308        ) -> impl std::future::Future<Output = Result<Option<usize>, Self::Error>> + MaybeSend
309        {
310            async move {
311                let available = self.data.len().saturating_sub(self.pos);
312                if available == 0 {
313                    return Ok(None);
314                }
315
316                let size = available.min(dst.len());
317                let end = self.pos + size;
318                dst[..size].copy_from_slice(&self.data[self.pos..end]);
319                self.pos = end;
320
321                Ok(Some(size))
322            }
323        }
324
325        fn stop(&mut self, _code: u32) {}
326
327        fn closed(
328            &mut self,
329        ) -> impl std::future::Future<Output = Result<(), Self::Error>> + MaybeSend {
330            async { Ok(()) }
331        }
332    }
333
334    #[test]
335    fn read_chunk_respects_max_and_eof() {
336        let mut stream = TestRecvStream::new(b"hello world");
337
338        let first = block_on(stream.read_chunk(5)).unwrap().unwrap();
339        assert_eq!(first, Bytes::from_static(b"hello"));
340
341        let second = block_on(stream.read_chunk(1024)).unwrap().unwrap();
342        assert_eq!(second, Bytes::from_static(b" world"));
343
344        let end = block_on(stream.read_chunk(1)).unwrap();
345        assert!(end.is_none());
346    }
347
348    #[test]
349    fn read_buf_advances_buffer() {
350        let mut stream = TestRecvStream::new(b"test");
351        let mut buf = BytesMut::with_capacity(4);
352
353        let size = block_on(stream.read_buf(&mut buf)).unwrap().unwrap();
354        assert_eq!(size, 4);
355        assert_eq!(&buf[..], b"test");
356    }
357}