http3/quic.rs
1//! QUIC Transport traits
2//!
3//! This module includes traits and types meant to allow being generic over any
4//! QUIC implementation.
5
6use std::{
7 fmt::{Debug, Display},
8 sync::Arc,
9 task::{self, Poll},
10};
11
12use bytes::Buf;
13
14use crate::error::Code;
15pub use crate::{
16 proto::stream::{InvalidStreamId, StreamId},
17 stream::WriteBuf,
18};
19
20/// Error type to communicate that the quic connection was closed
21///
22/// This is used by to implement the quic abstraction traits
23#[derive(Clone)]
24pub enum ConnectionErrorIncoming {
25 /// Error from the http3 layer
26 ApplicationClose {
27 /// http3 error code
28 error_code: u64,
29 },
30 /// Quic connection timeout
31 Timeout,
32 /// This variant can be used to signal, that an internal error occurred within the trait
33 /// implementations HTTP/3 will close the connection with H3_INTERNAL_ERROR
34 InternalError(String),
35 /// An unknown error occurred outside the HTTP/3 layer
36 ///
37 /// For example when the quic implementation errors because of a protocol violation
38 Undefined(Arc<dyn std::error::Error + Send + Sync>),
39}
40
41// Display the HTTP/3 error name, such as H3_NO_ERROR, instead of its numeric code.
42impl Debug for ConnectionErrorIncoming {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Self::ApplicationClose { error_code } => {
46 let error_code = Code::from(*error_code);
47 write!(f, "ApplicationClose({})", error_code)
48 }
49 Self::Timeout => write!(f, "Timeout"),
50 Self::InternalError(arg0) => f.debug_tuple("InternalError").field(arg0).finish(),
51 Self::Undefined(arg0) => f.debug_tuple("Undefined").field(arg0).finish(),
52 }
53 }
54}
55
56/// Error type to communicate that the stream was closed
57///
58/// This is used by to implement the quic abstraction traits
59/// When an error within the quic trait implementation occurs, use ConnectionErrorIncoming variant
60/// with InternalError
61#[derive(Debug)]
62pub enum StreamErrorIncoming {
63 /// Stream is closed because the whole connection is closed
64 ConnectionErrorIncoming {
65 /// Connection error
66 connection_error: ConnectionErrorIncoming,
67 },
68 /// Stream side was closed by the peer
69 ///
70 /// This can mean a reset for peers sending side or a stop_sending for peers receiving side
71 StreamTerminated {
72 /// Error code sent by the peer
73 error_code: u64,
74 },
75 /// An unknown error occurred outside the HTTP/3 layer
76 ///
77 /// H3 will handle this exactly like a StreamTerminated
78 /// like closing the connection with an error if http3 forbids a stream end for example with the
79 /// control stream
80 Unknown(Box<dyn std::error::Error + Send + Sync>),
81}
82
83impl std::error::Error for StreamErrorIncoming {}
84
85impl Display for StreamErrorIncoming {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 // display enum with fields
88 match self {
89 StreamErrorIncoming::ConnectionErrorIncoming { connection_error } => {
90 write!(f, "ConnectionError: {}", connection_error)
91 }
92 StreamErrorIncoming::StreamTerminated { error_code } => {
93 let error_code = Code::from(*error_code);
94 write!(f, "StreamClosed: {}", error_code)
95 }
96 StreamErrorIncoming::Unknown(error) => {
97 write!(f, "Error undefined by HTTP/3: {}", error)
98 }
99 }
100 }
101}
102
103impl Display for ConnectionErrorIncoming {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 // display enum with fields
106 match self {
107 ConnectionErrorIncoming::ApplicationClose { error_code } => {
108 let error_code = Code::from(*error_code);
109 write!(f, "ApplicationClose: {}", error_code)
110 }
111 ConnectionErrorIncoming::Timeout => write!(f, "Timeout"),
112 ConnectionErrorIncoming::InternalError(error) => {
113 write!(
114 f,
115 "InternalError in the quic trait implementation: {}",
116 error
117 )
118 }
119 ConnectionErrorIncoming::Undefined(error) => {
120 write!(f, "Error undefined by HTTP/3: {}", error)
121 }
122 }
123 }
124}
125
126impl std::error::Error for ConnectionErrorIncoming {}
127
128/// Trait representing a QUIC connection.
129pub trait Connection<B: Buf>: OpenStreams<B> {
130 /// The type produced by `poll_accept_recv()`
131 type RecvStream: RecvStream;
132 /// A producer of outgoing Unidirectional and Bidirectional streams.
133 type OpenStreams: OpenStreams<B, SendStream = Self::SendStream, BidiStream = Self::BidiStream>;
134
135 /// Accept an incoming unidirectional stream
136 ///
137 /// Returning `None` implies the connection is closing or closed.
138 fn poll_accept_recv(
139 &mut self,
140 cx: &mut task::Context<'_>,
141 ) -> Poll<Result<Self::RecvStream, ConnectionErrorIncoming>>;
142
143 /// Accept an incoming bidirectional stream
144 ///
145 /// Returning `None` implies the connection is closing or closed.
146 fn poll_accept_bidi(
147 &mut self,
148 cx: &mut task::Context<'_>,
149 ) -> Poll<Result<Self::BidiStream, ConnectionErrorIncoming>>;
150
151 /// Get an object to open outgoing streams.
152 fn opener(&self) -> Self::OpenStreams;
153}
154
155/// Trait for opening outgoing streams
156pub trait OpenStreams<B: Buf> {
157 /// The type produced by `poll_open_bidi()`
158 type BidiStream: SendStream<B> + RecvStream;
159 /// The type produced by `poll_open_send()`
160 type SendStream: SendStream<B>;
161
162 /// Poll the connection to create a new bidirectional stream.
163 fn poll_open_bidi(
164 &mut self,
165 cx: &mut task::Context<'_>,
166 ) -> Poll<Result<Self::BidiStream, StreamErrorIncoming>>;
167
168 /// Poll the connection to create a new unidirectional stream.
169 fn poll_open_send(
170 &mut self,
171 cx: &mut task::Context<'_>,
172 ) -> Poll<Result<Self::SendStream, StreamErrorIncoming>>;
173
174 /// Close the connection immediately
175 fn close(&mut self, code: crate::error::Code, reason: &[u8]);
176}
177
178/// A trait describing the "send" actions of a QUIC stream.
179pub trait SendStream<B: Buf> {
180 /// Polls if the stream can send more data.
181 fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), StreamErrorIncoming>>;
182
183 /// Send more data on the stream.
184 fn send_data<T: Into<WriteBuf<B>>>(&mut self, data: T) -> Result<(), StreamErrorIncoming>;
185
186 /// Poll to finish the sending side of the stream.
187 fn poll_finish(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), StreamErrorIncoming>>;
188
189 /// Send a QUIC reset code.
190 fn reset(&mut self, reset_code: u64);
191
192 /// Get QUIC send stream id
193 fn send_id(&self) -> StreamId;
194}
195
196/// Allows sending unframed pure bytes to a stream. Similar to [`AsyncWrite`](https://docs.rs/tokio/latest/tokio/io/trait.AsyncWrite.html)
197pub trait SendStreamUnframed<B: Buf>: SendStream<B> {
198 /// Attempts to write data into the stream.
199 ///
200 /// Returns the number of bytes written.
201 /// When `buf` is non-empty, an implementation that cannot currently make
202 /// progress must return [`Poll::Pending`] and arrange to wake the task.
203 /// Returning `Poll::Ready(Ok(0))` means the stream cannot make progress.
204 ///
205 /// `buf` is advanced by the number of bytes written.
206 fn poll_send<D: Buf>(
207 &mut self,
208 cx: &mut task::Context<'_>,
209 buf: &mut D,
210 ) -> Poll<Result<usize, StreamErrorIncoming>>;
211}
212
213/// A trait describing the "receive" actions of a QUIC stream.
214pub trait RecvStream {
215 /// The type of `Buf` for data received on this stream.
216 type Buf: Buf;
217
218 /// Poll the stream for more data.
219 ///
220 /// When the receiving side will no longer receive more data (such as because
221 /// the peer closed their sending side), this should return `None`.
222 fn poll_data(
223 &mut self,
224 cx: &mut task::Context<'_>,
225 ) -> Poll<Result<Option<Self::Buf>, StreamErrorIncoming>>;
226
227 /// Send a `STOP_SENDING` QUIC code.
228 fn stop_sending(&mut self, error_code: u64);
229
230 /// Get QUIC send stream id
231 fn recv_id(&self) -> StreamId;
232}
233
234/// Optional trait to allow "splitting" a bidirectional stream into two sides.
235pub trait BidiStream<B: Buf>: SendStream<B> + RecvStream {
236 /// The type for the send half.
237 type SendStream: SendStream<B>;
238 /// The type for the receive half.
239 type RecvStream: RecvStream;
240
241 /// Split this stream into two halves.
242 fn split(self) -> (Self::SendStream, Self::RecvStream);
243}
244
245/// Trait for QUIC streams that support 0-RTT detection.
246///
247/// This allows detection of streams opened during the 0-RTT phase of a QUIC connection.
248/// 0-RTT data is vulnerable to replay attacks, so applications should be cautious when
249/// processing non-idempotent requests on such streams.
250///
251/// See [RFC 8470 Section 5.2](https://www.rfc-editor.org/rfc/rfc8470.html#section-5.2)
252/// for guidance on handling 0-RTT data in HTTP/3.
253pub trait Is0rtt {
254 /// Check if this stream was opened during 0-RTT.
255 ///
256 /// Returns `true` if the stream was opened during the 0-RTT phase,
257 /// `false` otherwise.
258 fn is_0rtt(&self) -> bool;
259}