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