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