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 pub(crate) inner: Box<ErrorImpl>,
17}
18
19#[derive(PartialEq, Eq, Hash, Clone, Copy)]
21pub struct Code {
22 code: u64,
23}
24
25impl Code {
26 pub fn value(&self) -> u64 {
31 self.code
32 }
33}
34
35impl PartialEq<u64> for Code {
36 fn eq(&self, other: &u64) -> bool {
37 *other == self.code
38 }
39}
40
41#[derive(Clone)]
43pub struct ErrorImpl {
44 pub(crate) kind: Kind,
45 cause: Option<Arc<Cause>>,
46}
47
48impl ErrorImpl {
49 pub fn kind(&self) -> &Kind {
51 &self.kind
52 }
53}
54
55#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
58pub enum ErrorLevel {
59 ConnectionError,
61 StreamError,
63}
64
65#[doc(hidden)]
68#[non_exhaustive]
69#[derive(Clone, Debug)]
70pub enum Kind {
71 #[non_exhaustive]
72 Application {
73 code: Code,
74 reason: Option<Box<str>>,
75 level: ErrorLevel,
76 },
77 #[non_exhaustive]
78 HeaderTooBig {
79 actual_size: u64,
80 max_size: u64,
81 },
82 #[non_exhaustive]
84 Transport(Arc<TransportError>),
85 Closed,
87 Closing,
89 Timeout,
90}
91
92macro_rules! codes {
95 (
96 $(
97 $(#[$docs:meta])*
98 ($num:expr, $name:ident);
99 )+
100 ) => {
101 impl Code {
102 $(
103 $(#[$docs])*
104 pub const $name: Code = Code{code: $num};
105 )+
106 }
107
108 impl fmt::Debug for Code {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 match self.code {
111 $(
112 $num => f.write_str(stringify!($name)),
113 )+
114 other => write!(f, "{:#x}", other),
115 }
116 }
117 }
118 }
119}
120
121codes! {
122 (0x33, H3_DATAGRAM_ERROR);
125 (0x100, H3_NO_ERROR);
128
129 (0x101, H3_GENERAL_PROTOCOL_ERROR);
133
134 (0x102, H3_INTERNAL_ERROR);
136
137 (0x103, H3_STREAM_CREATION_ERROR);
140
141 (0x104, H3_CLOSED_CRITICAL_STREAM);
143
144 (0x105, H3_FRAME_UNEXPECTED);
147
148 (0x106, H3_FRAME_ERROR);
151
152 (0x107, H3_EXCESSIVE_LOAD);
155
156 (0x108, H3_ID_ERROR);
159
160 (0x109, H3_SETTINGS_ERROR);
162
163 (0x10a, H3_MISSING_SETTINGS);
165
166 (0x10b, H3_REQUEST_REJECTED);
169
170 (0x10c, H3_REQUEST_CANCELLED);
172
173 (0x10d, H3_REQUEST_INCOMPLETE);
176
177 (0x10e, H3_MESSAGE_ERROR);
179
180 (0x10f, H3_CONNECT_ERROR);
183
184 (0x110, H3_VERSION_FALLBACK);
187
188 (0x200, QPACK_DECOMPRESSION_FAILED);
191
192 (0x201, QPACK_ENCODER_STREAM_ERROR);
195
196 (0x202, QPACK_DECODER_STREAM_ERROR);
199}
200
201impl Code {
202 pub(crate) fn with_reason<S: Into<Box<str>>>(self, reason: S, level: ErrorLevel) -> Error {
203 Error::new(Kind::Application {
204 code: self,
205 reason: Some(reason.into()),
206 level,
207 })
208 }
209
210 pub(crate) fn with_cause<E: Into<Cause>>(self, cause: E) -> Error {
211 Error::from(self).with_cause(cause)
212 }
213
214 pub(crate) fn with_transport<E: Into<Box<dyn quic::Error>>>(self, err: E) -> Error {
215 Error::new(Kind::Transport(Arc::new(err.into())))
216 }
217}
218
219impl From<Code> for u64 {
220 fn from(code: Code) -> u64 {
221 code.code
222 }
223}
224
225impl Error {
228 fn new(kind: Kind) -> Self {
229 Error {
230 inner: Box::new(ErrorImpl { kind, cause: None }),
231 }
232 }
233
234 pub fn try_get_code(&self) -> Option<Code> {
236 match self.inner.kind {
237 Kind::Application { code, .. } => Some(code),
238 _ => None,
239 }
240 }
241
242 pub fn get_error_level(&self) -> ErrorLevel {
245 match self.inner.kind {
246 Kind::Application {
247 code: _,
248 reason: _,
249 level,
250 } => level,
251 _ => ErrorLevel::ConnectionError,
253 }
254 }
255
256 pub(crate) fn header_too_big(actual_size: u64, max_size: u64) -> Self {
257 Error::new(Kind::HeaderTooBig {
258 actual_size,
259 max_size,
260 })
261 }
262
263 pub(crate) fn with_cause<E: Into<Cause>>(mut self, cause: E) -> Self {
264 self.inner.cause = Some(Arc::new(cause.into()));
265 self
266 }
267
268 pub(crate) fn closing() -> Self {
269 Self::new(Kind::Closing)
270 }
271
272 pub(crate) fn closed() -> Self {
273 Self::new(Kind::Closed)
274 }
275
276 pub(crate) fn is_closed(&self) -> bool {
277 if let Kind::Closed = self.inner.kind {
278 return true;
279 }
280 false
281 }
282
283 pub(crate) fn is_header_too_big(&self) -> bool {
284 matches!(&self.inner.kind, Kind::HeaderTooBig { .. })
285 }
286
287 #[doc(hidden)]
288 pub fn kind(&self) -> Kind {
289 self.inner.kind.clone()
290 }
291}
292
293impl fmt::Debug for Error {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 let mut builder = f.debug_struct("h3::Error");
296
297 match self.inner.kind {
298 Kind::Closed => {
299 builder.field("connection closed", &true);
300 }
301 Kind::Closing => {
302 builder.field("closing", &true);
303 }
304 Kind::Timeout => {
305 builder.field("timeout", &true);
306 }
307 Kind::Application {
308 code, ref reason, ..
309 } => {
310 builder.field("code", &code);
311 if let Some(reason) = reason {
312 builder.field("reason", reason);
313 }
314 }
315 Kind::Transport(ref e) => {
316 builder.field("kind", &e);
317 builder.field("code: ", &e.err_code());
318 }
319 Kind::HeaderTooBig {
320 actual_size,
321 max_size,
322 } => {
323 builder.field("header_size", &actual_size);
324 builder.field("max_size", &max_size);
325 }
326 }
327
328 if let Some(ref cause) = self.inner.cause {
329 builder.field("cause", cause);
330 }
331
332 builder.finish()
333 }
334}
335
336impl fmt::Display for Error {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 match self.inner.kind {
339 Kind::Closed => write!(f, "connection is closed")?,
340 Kind::Closing => write!(f, "connection is gracefully closing")?,
341 Kind::Transport(ref e) => write!(f, "quic transport error: {}", e)?,
342 Kind::Timeout => write!(f, "timeout",)?,
343 Kind::Application {
344 code, ref reason, ..
345 } => {
346 if let Some(reason) = reason {
347 write!(f, "application error: {}", reason)?
348 } else {
349 write!(f, "application error {:?}", code)?
350 }
351 }
352 Kind::HeaderTooBig {
353 actual_size,
354 max_size,
355 } => write!(
356 f,
357 "issued header size {} o is beyond peer's limit {} o",
358 actual_size, max_size
359 )?,
360 };
361 if let Some(ref cause) = self.inner.cause {
362 write!(f, "cause: {}", cause)?
363 }
364 Ok(())
365 }
366}
367
368impl std::error::Error for Error {
369 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
370 self.inner.cause.as_ref().map(|e| &***e as _)
371 }
372}
373
374impl From<Code> for Error {
375 fn from(code: Code) -> Error {
376 Error::new(Kind::Application {
377 code,
378 reason: None,
379 level: ErrorLevel::ConnectionError,
380 })
381 }
382}
383
384impl From<qpack::EncoderError> for Error {
385 fn from(e: qpack::EncoderError) -> Self {
386 Self::from(Code::QPACK_ENCODER_STREAM_ERROR).with_cause(e)
387 }
388}
389
390impl From<qpack::DecoderError> for Error {
391 fn from(e: qpack::DecoderError) -> Self {
392 match e {
393 qpack::DecoderError::InvalidStaticIndex(_) => {
394 Self::from(Code::QPACK_DECOMPRESSION_FAILED).with_cause(e)
395 }
396 _ => Self::from(Code::QPACK_DECODER_STREAM_ERROR).with_cause(e),
397 }
398 }
399}
400
401impl From<proto::headers::HeaderError> for Error {
402 fn from(e: proto::headers::HeaderError) -> Self {
403 Error::new(Kind::Application {
404 code: Code::H3_MESSAGE_ERROR,
405 reason: None,
406 level: ErrorLevel::StreamError,
407 })
408 .with_cause(e)
409 }
410}
411
412impl From<frame::FrameStreamError> for Error {
413 fn from(e: frame::FrameStreamError) -> Self {
414 match e {
415 frame::FrameStreamError::Quic(e) => e.into(),
416
417 frame::FrameStreamError::UnexpectedEnd => Code::H3_FRAME_ERROR
422 .with_reason("received incomplete frame", ErrorLevel::ConnectionError),
423
424 frame::FrameStreamError::Proto(e) => match e {
425 proto::frame::FrameError::InvalidStreamId(_)
426 | proto::frame::FrameError::InvalidPushId(_) => Code::H3_ID_ERROR,
427 proto::frame::FrameError::Settings(_) => Code::H3_SETTINGS_ERROR,
428 proto::frame::FrameError::UnsupportedFrame(_)
429 | proto::frame::FrameError::UnknownFrame(_) => Code::H3_FRAME_UNEXPECTED,
430
431 proto::frame::FrameError::Incomplete(_)
441 | proto::frame::FrameError::InvalidFrameValue
442 | proto::frame::FrameError::Malformed => Code::H3_FRAME_ERROR,
443 }
444 .with_cause(e),
445 }
446 }
447}
448
449impl From<Error> for Box<dyn std::error::Error + std::marker::Send> {
450 fn from(e: Error) -> Self {
451 Box::new(e)
452 }
453}
454
455impl<T> From<T> for Error
456where
457 T: Into<TransportError>,
458{
459 fn from(e: T) -> Self {
460 let quic_error: TransportError = e.into();
461 if quic_error.is_timeout() {
462 return Error::new(Kind::Timeout);
463 }
464
465 match quic_error.err_code() {
466 Some(c) if Code::H3_NO_ERROR == c => Error::new(Kind::Closed),
467 Some(c) => Error::new(Kind::Application {
468 code: Code { code: c },
469 reason: None,
470 level: ErrorLevel::ConnectionError,
471 }),
472 None => Error::new(Kind::Transport(Arc::new(quic_error))),
473 }
474 }
475}
476
477impl From<proto::stream::InvalidStreamId> for Error {
478 fn from(e: proto::stream::InvalidStreamId) -> Self {
479 Self::from(Code::H3_ID_ERROR).with_cause(format!("{}", e))
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::Error;
486 use std::mem;
487
488 #[test]
489 fn test_size_of() {
490 assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
491 }
492}