Skip to main content

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}