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}