1use std::{fmt, sync::Arc};
4
5use crate::{frame, proto, qpack, quic};
6
7type Cause = Box<dyn std::error::Error + Send + Sync>;
9pub(crate) type TransportError = Box<dyn quic::Error>;
11
12#[derive(Clone)]
14pub struct Error {
15    inner: Box<ErrorImpl>,
16}
17
18#[derive(PartialEq, Eq, Hash, Clone, Copy)]
20pub struct Code(u64);
21
22impl Code {
23    pub fn value(&self) -> u64 {
24        self.0
25    }
26}
27
28impl PartialEq<u64> for Code {
29    fn eq(&self, other: &u64) -> bool {
30        *other == self.0
31    }
32}
33
34#[derive(Clone)]
35struct ErrorImpl {
36    kind: Kind,
37    cause: Option<Arc<Cause>>,
38}
39
40#[doc(hidden)]
43#[non_exhaustive]
44#[derive(Clone, Debug)]
45pub enum Kind {
46    #[non_exhaustive]
47    Application {
48        code: Code,
49        reason: Option<Box<str>>,
50    },
51    #[non_exhaustive]
52    HeaderTooBig {
53        actual_size: u64,
54        max_size: u64,
55    },
56    #[non_exhaustive]
58    Transport(Arc<TransportError>),
59    Closed,
61    Closing,
63    Timeout,
64}
65
66macro_rules! codes {
69    (
70        $(
71            $(#[$docs:meta])*
72            ($num:expr, $name:ident);
73        )+
74    ) => {
75        impl Code {
76        $(
77            $(#[$docs])*
78            pub const $name: Code = Code($num);
79        )+
80        }
81
82        impl fmt::Debug for Code {
83            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84                match self.0 {
85                $(
86                    $num => f.write_str(stringify!($name)),
87                )+
88                    other => write!(f, "{:#x}", other),
89                }
90            }
91        }
92    }
93}
94
95codes! {
96    (0x100, H3_NO_ERROR);
99
100    (0x101, H3_GENERAL_PROTOCOL_ERROR);
104
105    (0x102, H3_INTERNAL_ERROR);
107
108    (0x103, H3_STREAM_CREATION_ERROR);
111
112    (0x104, H3_CLOSED_CRITICAL_STREAM);
114
115    (0x105, H3_FRAME_UNEXPECTED);
118
119    (0x106, H3_FRAME_ERROR);
122
123    (0x107, H3_EXCESSIVE_LOAD);
126
127    (0x108, H3_ID_ERROR);
130
131    (0x109, H3_SETTINGS_ERROR);
133
134    (0x10a, H3_MISSING_SETTINGS);
136
137    (0x10b, H3_REQUEST_REJECTED);
140
141    (0x10c, H3_REQUEST_CANCELLED);
143
144    (0x10d, H3_REQUEST_INCOMPLETE);
147
148    (0x10e, H3_MESSAGE_ERROR);
150
151    (0x10f, H3_CONNECT_ERROR);
154
155    (0x110, H3_VERSION_FALLBACK);
158
159    (0x200, QPACK_DECOMPRESSION_FAILED);
162
163    (0x201, QPACK_ENCODER_STREAM_ERROR);
166
167    (0x202, QPACK_DECODER_STREAM_ERROR);
170}
171
172impl Code {
173    pub(crate) fn with_reason<S: Into<Box<str>>>(self, reason: S) -> Error {
174        Error::new(Kind::Application {
175            code: self,
176            reason: Some(reason.into()),
177        })
178    }
179
180    pub(crate) fn with_cause<E: Into<Cause>>(self, cause: E) -> Error {
181        Error::from(self).with_cause(cause)
182    }
183
184    pub(crate) fn with_transport<E: Into<Box<dyn quic::Error>>>(self, err: E) -> Error {
185        Error::new(Kind::Transport(Arc::new(err.into())))
186    }
187}
188
189impl From<Code> for u64 {
190    fn from(code: Code) -> u64 {
191        code.0
192    }
193}
194
195impl Error {
198    fn new(kind: Kind) -> Self {
199        Error {
200            inner: Box::new(ErrorImpl { kind, cause: None }),
201        }
202    }
203
204    pub(crate) fn header_too_big(actual_size: u64, max_size: u64) -> Self {
205        Error::new(Kind::HeaderTooBig {
206            actual_size,
207            max_size,
208        })
209    }
210
211    pub(crate) fn with_cause<E: Into<Cause>>(mut self, cause: E) -> Self {
212        self.inner.cause = Some(Arc::new(cause.into()));
213        self
214    }
215
216    pub(crate) fn closing() -> Self {
217        Self::new(Kind::Closing)
218    }
219
220    pub(crate) fn closed() -> Self {
221        Self::new(Kind::Closed)
222    }
223
224    pub(crate) fn is_closed(&self) -> bool {
225        if let Kind::Closed = self.inner.kind {
226            return true;
227        }
228        false
229    }
230
231    pub(crate) fn is_header_too_big(&self) -> bool {
232        matches!(&self.inner.kind, Kind::HeaderTooBig { .. })
233    }
234
235    #[cfg(feature = "test_helpers")]
236    pub fn kind(&self) -> Kind {
237        self.inner.kind.clone()
238    }
239}
240
241impl fmt::Debug for Error {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        let mut builder = f.debug_struct("h3::Error");
244
245        match self.inner.kind {
246            Kind::Closed => {
247                builder.field("connection closed", &true);
248            }
249            Kind::Closing => {
250                builder.field("closing", &true);
251            }
252            Kind::Timeout => {
253                builder.field("timeout", &true);
254            }
255            Kind::Application { code, ref reason } => {
256                builder.field("code", &code);
257                if let Some(reason) = reason {
258                    builder.field("reason", reason);
259                }
260            }
261            Kind::Transport(ref e) => {
262                builder.field("kind", &e);
263                builder.field("code: ", &e.err_code());
264            }
265            Kind::HeaderTooBig {
266                actual_size,
267                max_size,
268            } => {
269                builder.field("header_size", &actual_size);
270                builder.field("max_size", &max_size);
271            }
272        }
273
274        if let Some(ref cause) = self.inner.cause {
275            builder.field("cause", cause);
276        }
277
278        builder.finish()
279    }
280}
281
282impl fmt::Display for Error {
283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        match self.inner.kind {
285            Kind::Closed => write!(f, "connection is closed")?,
286            Kind::Closing => write!(f, "connection is gracefully closing")?,
287            Kind::Transport(ref e) => write!(f, "quic transport error: {}", e)?,
288            Kind::Timeout => write!(f, "timeout",)?,
289            Kind::Application { code, ref reason } => {
290                if let Some(reason) = reason {
291                    write!(f, "application error: {}", reason)?
292                } else {
293                    write!(f, "application error {:?}", code)?
294                }
295            }
296            Kind::HeaderTooBig {
297                actual_size,
298                max_size,
299            } => write!(
300                f,
301                "issued header size {} o is beyond peer's limit {} o",
302                actual_size, max_size
303            )?,
304        };
305        if let Some(ref cause) = self.inner.cause {
306            write!(f, "cause: {}", cause)?
307        }
308        Ok(())
309    }
310}
311
312impl std::error::Error for Error {
313    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
314        self.inner.cause.as_ref().map(|e| &***e as _)
315    }
316}
317
318impl From<Code> for Error {
319    fn from(code: Code) -> Error {
320        Error::new(Kind::Application { code, reason: None })
321    }
322}
323
324impl From<qpack::EncoderError> for Error {
325    fn from(e: qpack::EncoderError) -> Self {
326        Self::from(Code::QPACK_ENCODER_STREAM_ERROR).with_cause(e)
327    }
328}
329
330impl From<qpack::DecoderError> for Error {
331    fn from(e: qpack::DecoderError) -> Self {
332        Self::from(Code::QPACK_DECODER_STREAM_ERROR).with_cause(e)
333    }
334}
335
336impl From<proto::headers::Error> for Error {
337    fn from(e: proto::headers::Error) -> Self {
338        Self::from(Code::H3_MESSAGE_ERROR).with_cause(e)
339    }
340}
341
342impl From<frame::Error> for Error {
343    fn from(e: frame::Error) -> Self {
344        match e {
345            frame::Error::Quic(e) => e.into(),
346            frame::Error::UnexpectedEnd => {
347                Code::H3_FRAME_ERROR.with_reason("received incomplete frame")
348            }
349            frame::Error::Proto(e) => match e {
350                proto::frame::Error::InvalidStreamId(_) => Code::H3_ID_ERROR,
351                proto::frame::Error::Settings(_) => Code::H3_SETTINGS_ERROR,
352                proto::frame::Error::UnsupportedFrame(_) | proto::frame::Error::UnknownFrame(_) => {
353                    Code::H3_FRAME_UNEXPECTED
354                }
355                proto::frame::Error::Incomplete(_)
356                | proto::frame::Error::InvalidFrameValue
357                | proto::frame::Error::Malformed => Code::H3_FRAME_ERROR,
358            }
359            .with_cause(e),
360        }
361    }
362}
363
364impl From<Error> for Box<dyn std::error::Error + std::marker::Send> {
365    fn from(e: Error) -> Self {
366        Box::new(e)
367    }
368}
369
370impl<T> From<T> for Error
371where
372    T: Into<TransportError>,
373{
374    fn from(e: T) -> Self {
375        let quic_error: TransportError = e.into();
376        if quic_error.is_timeout() {
377            return Error::new(Kind::Timeout);
378        }
379
380        match quic_error.err_code() {
381            Some(c) if Code::H3_NO_ERROR == c => Error::new(Kind::Closed),
382            Some(c) => Error::new(Kind::Application {
383                code: Code(c),
384                reason: None,
385            }),
386            None => Error::new(Kind::Transport(Arc::new(quic_error))),
387        }
388    }
389}
390
391impl From<proto::stream::InvalidStreamId> for Error {
392    fn from(e: proto::stream::InvalidStreamId) -> Self {
393        Self::from(Code::H3_ID_ERROR).with_cause(format!("{}", e))
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::Error;
400    use std::mem;
401
402    #[test]
403    fn test_size_of() {
404        assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
405    }
406}