h3/error/
error.rs

1//! This is the public facing error types for the h3 crate
2use crate::quic::ConnectionErrorIncoming;
3
4use super::{codes::Code, internal_error::InternalConnectionError};
5
6/// This enum represents the closure of a connection because of an a closed quic connection
7/// This can be either from this endpoint because of a violation of the protocol or from the remote endpoint
8///
9/// When the code [`Code::H3_NO_ERROR`] is used bei this peer or the remote peer, the connection is closed without an error
10/// according to the [h3 spec](https://www.rfc-editor.org/rfc/rfc9114.html#name-http-3-error-codes)
11#[derive(Debug, Clone)]
12#[non_exhaustive]
13pub enum ConnectionError {
14    /// The error occurred on the local side of the connection
15    #[cfg_attr(
16        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
17        non_exhaustive
18    )]
19    Local {
20        /// The error
21        error: LocalError,
22    },
23    /// Error returned by the quic layer
24    /// I might be an quic error or the remote h3 connection closed the connection with an error
25    #[cfg_attr(
26        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
27        non_exhaustive
28    )]
29    Remote(ConnectionErrorIncoming),
30    /// Timeout occurred
31    #[cfg_attr(
32        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
33        non_exhaustive
34    )]
35    Timeout,
36}
37
38impl ConnectionError {
39    /// Returns if the error is H3_NO_ERROR local or remote
40    pub fn is_h3_no_error(&self) -> bool {
41        match self {
42            ConnectionError::Local {
43                error:
44                    LocalError::Application {
45                        code: Code::H3_NO_ERROR,
46                        ..
47                    },
48            } => true,
49            ConnectionError::Remote(ConnectionErrorIncoming::ApplicationClose { error_code })
50                if *error_code == Code::H3_NO_ERROR.value() =>
51            {
52                true
53            }
54            _ => false,
55        }
56    }
57}
58
59/// This enum represents a local error
60#[derive(Debug, Clone, Hash)]
61#[non_exhaustive]
62pub enum LocalError {
63    #[non_exhaustive]
64    /// The application closed the connection
65    Application {
66        /// The error code
67        code: Code,
68        /// The error reason
69        reason: String,
70    },
71    #[non_exhaustive]
72    /// Graceful closing of the connection initiated by the local peer
73    Closing,
74}
75
76/// This enum represents a stream error
77#[derive(Debug)]
78#[non_exhaustive]
79pub enum StreamError {
80    /// The error occurred on the stream
81    #[cfg_attr(
82        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
83        non_exhaustive
84    )]
85    StreamError {
86        /// The error code
87        code: Code,
88        /// The error reason
89        reason: String,
90    },
91    /// The remote peer terminated the corresponding stream side
92    ///
93    /// Either Reset on peers sending side or StopSending on peers receiving side
94    #[cfg_attr(
95        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
96        non_exhaustive
97    )]
98    RemoteTerminate {
99        /// Reset code received from the peer
100        code: Code,
101    },
102    /// The error occurred on the connection
103    #[cfg_attr(
104        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
105        non_exhaustive
106    )]
107    ConnectionError(ConnectionError),
108    /// Error is used when violating the MAX_FIELD_SECTION_SIZE
109    ///
110    /// This can mean different things depending on the context
111    /// When sending a request, this means, that the request cannot be sent because the header is larger then permitted by the server
112    /// When receiving a request, this means, that the server sent a
113    #[cfg_attr(
114        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
115        non_exhaustive
116    )]
117    HeaderTooBig {
118        /// The actual size of the header block
119        actual_size: u64,
120        /// The maximum size of the header block
121        max_size: u64,
122    },
123    /// Received a GoAway frame from the remote
124    ///
125    /// Stream operations cannot be performed
126    #[cfg_attr(
127        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
128        non_exhaustive
129    )]
130    RemoteClosing,
131    /// Undefined error propagated by the quic layer
132    #[cfg_attr(
133        not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
134        non_exhaustive
135    )]
136    Undefined(Box<dyn std::error::Error + Send + Sync>),
137}
138
139impl StreamError {
140    /// Returns if the error is H3_NO_ERROR
141    pub fn is_h3_no_error(&self) -> bool {
142        match self {
143            StreamError::StreamError {
144                code: Code::H3_NO_ERROR,
145                ..
146            } => true,
147            StreamError::ConnectionError(conn_error) => conn_error.is_h3_no_error(),
148            _ => false,
149        }
150    }
151}
152
153impl From<InternalConnectionError> for LocalError {
154    fn from(err: InternalConnectionError) -> Self {
155        LocalError::Application {
156            code: err.code,
157            reason: err.message,
158        }
159    }
160}
161
162impl std::fmt::Display for ConnectionError {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        match self {
165            ConnectionError::Local { error } => write!(f, "Local error: {:?}", error),
166            ConnectionError::Remote(err) => write!(f, "Remote error: {}", err),
167            ConnectionError::Timeout => write!(f, "Timeout"),
168        }
169    }
170}
171
172impl std::error::Error for ConnectionError {}
173
174impl std::fmt::Display for StreamError {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match self {
177            StreamError::StreamError { code, reason } => {
178                write!(f, "Stream error: {:?} - {}", code, reason)
179            }
180            StreamError::ConnectionError(err) => write!(f, "Connection error: {}", err),
181            StreamError::RemoteTerminate { code } => write!(f, "Remote reset: {}", code),
182            StreamError::HeaderTooBig {
183                actual_size,
184                max_size,
185            } => write!(
186                f,
187                "Header too big: actual size: {}, max size: {}",
188                actual_size, max_size
189            ),
190            StreamError::Undefined(err) => write!(f, "Undefined error: {}", err),
191            StreamError::RemoteClosing => write!(f, "Remote is closing the connection"),
192        }
193    }
194}
195
196impl std::error::Error for StreamError {}