1use std::error::Error as StdError;
3use std::fmt;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8type Cause = Box<dyn StdError + Send + Sync>;
9
10pub struct Error {
32 inner: Box<ErrorImpl>,
33}
34
35struct ErrorImpl {
36 kind: Kind,
37 cause: Option<Cause>,
38}
39
40#[derive(Debug)]
41pub(super) enum Kind {
42 Parse(Parse),
43 User(User),
44 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
46 IncompleteMessage,
47 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
49 UnexpectedMessage,
50 Canceled,
52 #[cfg(any(
54 all(feature = "http1", any(feature = "client", feature = "server")),
55 all(feature = "http2", feature = "client")
56 ))]
57 ChannelClosed,
58 #[cfg(all(
60 any(feature = "client", feature = "server"),
61 any(feature = "http1", feature = "http2")
62 ))]
63 Io,
64 #[cfg(all(feature = "http1", feature = "server"))]
66 HeaderTimeout,
67 #[cfg(all(
69 any(feature = "client", feature = "server"),
70 any(feature = "http1", feature = "http2")
71 ))]
72 Body,
73 #[cfg(all(
75 any(feature = "client", feature = "server"),
76 any(feature = "http1", feature = "http2")
77 ))]
78 BodyWrite,
79 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
81 Shutdown,
82
83 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
85 Http2,
86}
87
88#[derive(Debug)]
89pub(super) enum Parse {
90 Method,
91 #[cfg(feature = "http1")]
92 Version,
93 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
94 VersionH2,
95 Uri,
96 #[cfg(all(feature = "http1", feature = "server"))]
97 UriTooLong,
98 #[cfg(feature = "http1")]
99 Header(Header),
100 #[cfg(any(feature = "http1", feature = "http2"))]
101 #[cfg_attr(feature = "http2", allow(unused))]
102 TooLarge,
103 Status,
104 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
105 Internal,
106}
107
108#[derive(Debug)]
109#[cfg(feature = "http1")]
110pub(super) enum Header {
111 Token,
112 #[cfg(any(feature = "client", feature = "server"))]
113 ContentLengthInvalid,
114 #[cfg(feature = "server")]
115 TransferEncodingInvalid,
116 #[cfg(any(feature = "client", feature = "server"))]
117 TransferEncodingUnexpected,
118}
119
120#[derive(Debug)]
121pub(super) enum User {
122 #[cfg(all(
124 any(feature = "client", feature = "server"),
125 any(feature = "http1", feature = "http2")
126 ))]
127 Body,
128 #[cfg(any(
130 all(feature = "http1", any(feature = "client", feature = "server")),
131 feature = "ffi"
132 ))]
133 BodyWriteAborted,
134 #[cfg(all(feature = "client", feature = "http2"))]
136 InvalidConnectWithBody,
137 #[cfg(any(
139 all(any(feature = "client", feature = "server"), feature = "http1"),
140 all(feature = "server", feature = "http2")
141 ))]
142 Service,
143 #[cfg(any(feature = "http1", feature = "http2"))]
147 #[cfg(feature = "server")]
148 UnexpectedHeader,
149 #[cfg(feature = "http1")]
151 #[cfg(feature = "server")]
152 UnsupportedStatusCode,
153
154 NoUpgrade,
156
157 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
159 ManualUpgrade,
160
161 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
163 DispatchGone,
164
165 #[cfg(feature = "ffi")]
167 AbortedByCallback,
168}
169
170#[derive(Debug)]
172pub(super) struct TimedOut;
173
174impl Error {
175 pub fn is_parse(&self) -> bool {
182 matches!(self.inner.kind, Kind::Parse(_))
183 }
184
185 #[cfg(all(feature = "http1", feature = "server"))]
193 pub fn is_parse_too_large(&self) -> bool {
194 matches!(
195 self.inner.kind,
196 Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong)
197 )
198 }
199
200 pub fn is_parse_status(&self) -> bool {
203 matches!(self.inner.kind, Kind::Parse(Parse::Status))
204 }
205
206 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
212 pub fn is_parse_version_h2(&self) -> bool {
213 matches!(self.inner.kind, Kind::Parse(Parse::VersionH2))
214 }
215
216 pub fn is_user(&self) -> bool {
223 matches!(self.inner.kind, Kind::User(_))
224 }
225
226 pub fn is_canceled(&self) -> bool {
232 matches!(self.inner.kind, Kind::Canceled)
233 }
234
235 pub fn is_closed(&self) -> bool {
241 #[cfg(not(any(
242 all(feature = "http1", any(feature = "client", feature = "server")),
243 all(feature = "http2", feature = "client")
244 )))]
245 return false;
246
247 #[cfg(any(
248 all(feature = "http1", any(feature = "client", feature = "server")),
249 all(feature = "http2", feature = "client")
250 ))]
251 matches!(self.inner.kind, Kind::ChannelClosed)
252 }
253
254 pub fn is_incomplete_message(&self) -> bool {
272 #[cfg(not(all(any(feature = "client", feature = "server"), feature = "http1")))]
273 return false;
274
275 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
276 matches!(self.inner.kind, Kind::IncompleteMessage)
277 }
278
279 pub fn is_body_write_aborted(&self) -> bool {
285 #[cfg(not(any(
286 all(feature = "http1", any(feature = "client", feature = "server")),
287 feature = "ffi"
288 )))]
289 return false;
290
291 #[cfg(any(
292 all(feature = "http1", any(feature = "client", feature = "server")),
293 feature = "ffi"
294 ))]
295 matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
296 }
297
298 pub fn is_shutdown(&self) -> bool {
303 #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
304 if matches!(self.inner.kind, Kind::Shutdown) {
305 return true;
306 }
307 false
308 }
309
310 pub fn is_timeout(&self) -> bool {
316 #[cfg(all(feature = "http1", feature = "server"))]
317 if matches!(self.inner.kind, Kind::HeaderTimeout) {
318 return true;
319 }
320 self.find_source::<TimedOut>().is_some()
321 }
322
323 pub(super) fn new(kind: Kind) -> Error {
324 Error {
325 inner: Box::new(ErrorImpl { kind, cause: None }),
326 }
327 }
328
329 pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
330 self.inner.cause = Some(cause.into());
331 self
332 }
333
334 #[cfg(any(all(feature = "http1", feature = "server"), feature = "ffi"))]
335 pub(super) fn kind(&self) -> &Kind {
336 &self.inner.kind
337 }
338
339 pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
340 let mut cause = self.source();
341 while let Some(err) = cause {
342 if let Some(typed) = err.downcast_ref() {
343 return Some(typed);
344 }
345 cause = err.source();
346 }
347
348 None
350 }
351
352 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
353 pub(super) fn h2_reason(&self) -> h2::Reason {
354 self.find_source::<h2::Error>()
357 .and_then(|h2_err| h2_err.reason())
358 .unwrap_or(h2::Reason::INTERNAL_ERROR)
359 }
360
361 pub(super) fn new_canceled() -> Error {
362 Error::new(Kind::Canceled)
363 }
364
365 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
366 pub(super) fn new_incomplete() -> Error {
367 Error::new(Kind::IncompleteMessage)
368 }
369
370 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
371 pub(super) fn new_too_large() -> Error {
372 Error::new(Kind::Parse(Parse::TooLarge))
373 }
374
375 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
376 pub(super) fn new_version_h2() -> Error {
377 Error::new(Kind::Parse(Parse::VersionH2))
378 }
379
380 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
381 pub(super) fn new_unexpected_message() -> Error {
382 Error::new(Kind::UnexpectedMessage)
383 }
384
385 #[cfg(all(
386 any(feature = "client", feature = "server"),
387 any(feature = "http1", feature = "http2")
388 ))]
389 pub(super) fn new_io(cause: std::io::Error) -> Error {
390 Error::new(Kind::Io).with(cause)
391 }
392
393 #[cfg(any(
394 all(feature = "http1", any(feature = "client", feature = "server")),
395 all(feature = "http2", feature = "client")
396 ))]
397 pub(super) fn new_closed() -> Error {
398 Error::new(Kind::ChannelClosed)
399 }
400
401 #[cfg(all(
402 any(feature = "client", feature = "server"),
403 any(feature = "http1", feature = "http2")
404 ))]
405 pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
406 Error::new(Kind::Body).with(cause)
407 }
408
409 #[cfg(all(
410 any(feature = "client", feature = "server"),
411 any(feature = "http1", feature = "http2")
412 ))]
413 pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
414 Error::new(Kind::BodyWrite).with(cause)
415 }
416
417 #[cfg(any(
418 all(feature = "http1", any(feature = "client", feature = "server")),
419 feature = "ffi"
420 ))]
421 pub(super) fn new_body_write_aborted() -> Error {
422 Error::new(Kind::User(User::BodyWriteAborted))
423 }
424
425 fn new_user(user: User) -> Error {
426 Error::new(Kind::User(user))
427 }
428
429 #[cfg(any(feature = "http1", feature = "http2"))]
430 #[cfg(feature = "server")]
431 pub(super) fn new_user_header() -> Error {
432 Error::new_user(User::UnexpectedHeader)
433 }
434
435 #[cfg(all(feature = "http1", feature = "server"))]
436 pub(super) fn new_header_timeout() -> Error {
437 Error::new(Kind::HeaderTimeout)
438 }
439
440 #[cfg(feature = "http1")]
441 #[cfg(feature = "server")]
442 pub(super) fn new_user_unsupported_status_code() -> Error {
443 Error::new_user(User::UnsupportedStatusCode)
444 }
445
446 pub(super) fn new_user_no_upgrade() -> Error {
447 Error::new_user(User::NoUpgrade)
448 }
449
450 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
451 pub(super) fn new_user_manual_upgrade() -> Error {
452 Error::new_user(User::ManualUpgrade)
453 }
454
455 #[cfg(any(
456 all(any(feature = "client", feature = "server"), feature = "http1"),
457 all(feature = "server", feature = "http2")
458 ))]
459 pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
460 Error::new_user(User::Service).with(cause)
461 }
462
463 #[cfg(all(
464 any(feature = "client", feature = "server"),
465 any(feature = "http1", feature = "http2")
466 ))]
467 pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
468 Error::new_user(User::Body).with(cause)
469 }
470
471 #[cfg(all(feature = "client", feature = "http2"))]
472 pub(super) fn new_user_invalid_connect() -> Error {
473 Error::new_user(User::InvalidConnectWithBody)
474 }
475
476 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
477 pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
478 Error::new(Kind::Shutdown).with(cause)
479 }
480
481 #[cfg(feature = "ffi")]
482 pub(super) fn new_user_aborted_by_callback() -> Error {
483 Error::new_user(User::AbortedByCallback)
484 }
485
486 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
487 pub(super) fn new_user_dispatch_gone() -> Error {
488 Error::new(Kind::User(User::DispatchGone))
489 }
490
491 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
492 pub(super) fn new_h2(cause: ::h2::Error) -> Error {
493 if cause.is_io() {
494 Error::new_io(cause.into_io().expect("h2::Error::is_io"))
495 } else {
496 Error::new(Kind::Http2).with(cause)
497 }
498 }
499
500 fn description(&self) -> &str {
501 match self.inner.kind {
502 Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
503 #[cfg(feature = "http1")]
504 Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
505 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
506 Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
507 Kind::Parse(Parse::Uri) => "invalid URI",
508 #[cfg(all(feature = "http1", feature = "server"))]
509 Kind::Parse(Parse::UriTooLong) => "URI too long",
510 #[cfg(feature = "http1")]
511 Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
512 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
513 Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
514 "invalid content-length parsed"
515 }
516 #[cfg(all(feature = "http1", feature = "server"))]
517 Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
518 "invalid transfer-encoding parsed"
519 }
520 #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
521 Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
522 "unexpected transfer-encoding parsed"
523 }
524 #[cfg(any(feature = "http1", feature = "http2"))]
525 Kind::Parse(Parse::TooLarge) => "message head is too large",
526 Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
527 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
528 Kind::Parse(Parse::Internal) => {
529 "internal error inside Hyper and/or its dependencies, please report"
530 }
531 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
532 Kind::IncompleteMessage => "connection closed before message completed",
533 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
534 Kind::UnexpectedMessage => "received unexpected message from connection",
535 #[cfg(any(
536 all(feature = "http1", any(feature = "client", feature = "server")),
537 all(feature = "http2", feature = "client")
538 ))]
539 Kind::ChannelClosed => "channel closed",
540 Kind::Canceled => "operation was canceled",
541 #[cfg(all(feature = "http1", feature = "server"))]
542 Kind::HeaderTimeout => "read header from client timeout",
543 #[cfg(all(
544 any(feature = "client", feature = "server"),
545 any(feature = "http1", feature = "http2")
546 ))]
547 Kind::Body => "error reading a body from connection",
548 #[cfg(all(
549 any(feature = "client", feature = "server"),
550 any(feature = "http1", feature = "http2")
551 ))]
552 Kind::BodyWrite => "error writing a body to connection",
553 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
554 Kind::Shutdown => "error shutting down connection",
555 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
556 Kind::Http2 => "http2 error",
557 #[cfg(all(
558 any(feature = "client", feature = "server"),
559 any(feature = "http1", feature = "http2")
560 ))]
561 Kind::Io => "connection error",
562
563 #[cfg(all(
564 any(feature = "client", feature = "server"),
565 any(feature = "http1", feature = "http2")
566 ))]
567 Kind::User(User::Body) => "error from user's Body stream",
568 #[cfg(any(
569 all(feature = "http1", any(feature = "client", feature = "server")),
570 feature = "ffi"
571 ))]
572 Kind::User(User::BodyWriteAborted) => "user body write aborted",
573 #[cfg(all(feature = "client", feature = "http2"))]
574 Kind::User(User::InvalidConnectWithBody) => {
575 "user sent CONNECT request with non-zero body"
576 }
577 #[cfg(any(
578 all(any(feature = "client", feature = "server"), feature = "http1"),
579 all(feature = "server", feature = "http2")
580 ))]
581 Kind::User(User::Service) => "error from user's Service",
582 #[cfg(any(feature = "http1", feature = "http2"))]
583 #[cfg(feature = "server")]
584 Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
585 #[cfg(feature = "http1")]
586 #[cfg(feature = "server")]
587 Kind::User(User::UnsupportedStatusCode) => {
588 "response has 1xx status code, not supported by server"
589 }
590 Kind::User(User::NoUpgrade) => "no upgrade available",
591 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
592 Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
593 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
594 Kind::User(User::DispatchGone) => "dispatch task is gone",
595 #[cfg(feature = "ffi")]
596 Kind::User(User::AbortedByCallback) => "operation aborted by an application callback",
597 }
598 }
599}
600
601impl fmt::Debug for Error {
602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603 let mut f = f.debug_tuple("hyper::Error");
604 f.field(&self.inner.kind);
605 if let Some(ref cause) = self.inner.cause {
606 f.field(cause);
607 }
608 f.finish()
609 }
610}
611
612impl fmt::Display for Error {
613 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614 f.write_str(self.description())
615 }
616}
617
618impl StdError for Error {
619 fn source(&self) -> Option<&(dyn StdError + 'static)> {
620 self.inner
621 .cause
622 .as_ref()
623 .map(|cause| &**cause as &(dyn StdError + 'static))
624 }
625}
626
627#[doc(hidden)]
628impl From<Parse> for Error {
629 fn from(err: Parse) -> Error {
630 Error::new(Kind::Parse(err))
631 }
632}
633
634#[cfg(feature = "http1")]
635impl Parse {
636 #[cfg(any(feature = "client", feature = "server"))]
637 pub(crate) fn content_length_invalid() -> Self {
638 Parse::Header(Header::ContentLengthInvalid)
639 }
640
641 #[cfg(feature = "server")]
642 pub(crate) fn transfer_encoding_invalid() -> Self {
643 Parse::Header(Header::TransferEncodingInvalid)
644 }
645
646 #[cfg(any(feature = "client", feature = "server"))]
647 pub(crate) fn transfer_encoding_unexpected() -> Self {
648 Parse::Header(Header::TransferEncodingUnexpected)
649 }
650}
651
652#[cfg(feature = "http1")]
653impl From<httparse::Error> for Parse {
654 fn from(err: httparse::Error) -> Parse {
655 match err {
656 httparse::Error::HeaderName
657 | httparse::Error::HeaderValue
658 | httparse::Error::NewLine
659 | httparse::Error::Token => Parse::Header(Header::Token),
660 httparse::Error::Status => Parse::Status,
661 httparse::Error::TooManyHeaders => Parse::TooLarge,
662 httparse::Error::Version => Parse::Version,
663 }
664 }
665}
666
667impl From<http::method::InvalidMethod> for Parse {
668 fn from(_: http::method::InvalidMethod) -> Parse {
669 Parse::Method
670 }
671}
672
673impl From<http::status::InvalidStatusCode> for Parse {
674 fn from(_: http::status::InvalidStatusCode) -> Parse {
675 Parse::Status
676 }
677}
678
679impl From<http::uri::InvalidUri> for Parse {
680 fn from(_: http::uri::InvalidUri) -> Parse {
681 Parse::Uri
682 }
683}
684
685impl From<http::uri::InvalidUriParts> for Parse {
686 fn from(_: http::uri::InvalidUriParts) -> Parse {
687 Parse::Uri
688 }
689}
690
691impl fmt::Display for TimedOut {
694 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695 f.write_str("operation timed out")
696 }
697}
698
699impl StdError for TimedOut {}
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704 use std::mem;
705
706 fn assert_send_sync<T: Send + Sync + 'static>() {}
707
708 #[test]
709 fn error_satisfies_send_sync() {
710 assert_send_sync::<Error>()
711 }
712
713 #[test]
714 fn error_size_of() {
715 assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
716 }
717
718 #[cfg(feature = "http2")]
719 #[test]
720 fn h2_reason_unknown() {
721 let closed = Error::new_closed();
722 assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR);
723 }
724
725 #[cfg(feature = "http2")]
726 #[test]
727 fn h2_reason_one_level() {
728 let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM));
729 assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM);
730 }
731
732 #[cfg(feature = "http2")]
733 #[test]
734 fn h2_reason_nested() {
735 let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED));
736 let svc_err = Error::new_user_service(recvd);
738 assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
739 }
740}