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 {
177 matches!(self.inner.kind, Kind::Parse(_))
178 }
179
180 #[cfg(all(feature = "http1", feature = "server"))]
182 pub fn is_parse_too_large(&self) -> bool {
183 matches!(
184 self.inner.kind,
185 Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong)
186 )
187 }
188
189 pub fn is_parse_status(&self) -> bool {
192 matches!(self.inner.kind, Kind::Parse(Parse::Status))
193 }
194
195 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
198 pub fn is_parse_version_h2(&self) -> bool {
199 matches!(self.inner.kind, Kind::Parse(Parse::VersionH2))
200 }
201
202 pub fn is_user(&self) -> bool {
204 matches!(self.inner.kind, Kind::User(_))
205 }
206
207 pub fn is_canceled(&self) -> bool {
209 matches!(self.inner.kind, Kind::Canceled)
210 }
211
212 pub fn is_closed(&self) -> bool {
214 #[cfg(not(any(
215 all(feature = "http1", any(feature = "client", feature = "server")),
216 all(feature = "http2", feature = "client")
217 )))]
218 return false;
219
220 #[cfg(any(
221 all(feature = "http1", any(feature = "client", feature = "server")),
222 all(feature = "http2", feature = "client")
223 ))]
224 matches!(self.inner.kind, Kind::ChannelClosed)
225 }
226
227 pub fn is_incomplete_message(&self) -> bool {
245 #[cfg(not(all(any(feature = "client", feature = "server"), feature = "http1")))]
246 return false;
247
248 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
249 matches!(self.inner.kind, Kind::IncompleteMessage)
250 }
251
252 pub fn is_body_write_aborted(&self) -> bool {
254 #[cfg(not(any(
255 all(feature = "http1", any(feature = "client", feature = "server")),
256 feature = "ffi"
257 )))]
258 return false;
259
260 #[cfg(any(
261 all(feature = "http1", any(feature = "client", feature = "server")),
262 feature = "ffi"
263 ))]
264 matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
265 }
266
267 pub fn is_shutdown(&self) -> bool {
269 #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
270 if matches!(self.inner.kind, Kind::Shutdown) {
271 return true;
272 }
273 false
274 }
275
276 pub fn is_timeout(&self) -> bool {
278 #[cfg(all(feature = "http1", feature = "server"))]
279 if matches!(self.inner.kind, Kind::HeaderTimeout) {
280 return true;
281 }
282 self.find_source::<TimedOut>().is_some()
283 }
284
285 pub(super) fn new(kind: Kind) -> Error {
286 Error {
287 inner: Box::new(ErrorImpl { kind, cause: None }),
288 }
289 }
290
291 pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
292 self.inner.cause = Some(cause.into());
293 self
294 }
295
296 #[cfg(any(all(feature = "http1", feature = "server"), feature = "ffi"))]
297 pub(super) fn kind(&self) -> &Kind {
298 &self.inner.kind
299 }
300
301 pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
302 let mut cause = self.source();
303 while let Some(err) = cause {
304 if let Some(typed) = err.downcast_ref() {
305 return Some(typed);
306 }
307 cause = err.source();
308 }
309
310 None
312 }
313
314 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
315 pub(super) fn h2_reason(&self) -> h2::Reason {
316 self.find_source::<h2::Error>()
319 .and_then(|h2_err| h2_err.reason())
320 .unwrap_or(h2::Reason::INTERNAL_ERROR)
321 }
322
323 pub(super) fn new_canceled() -> Error {
324 Error::new(Kind::Canceled)
325 }
326
327 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
328 pub(super) fn new_incomplete() -> Error {
329 Error::new(Kind::IncompleteMessage)
330 }
331
332 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
333 pub(super) fn new_too_large() -> Error {
334 Error::new(Kind::Parse(Parse::TooLarge))
335 }
336
337 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
338 pub(super) fn new_version_h2() -> Error {
339 Error::new(Kind::Parse(Parse::VersionH2))
340 }
341
342 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
343 pub(super) fn new_unexpected_message() -> Error {
344 Error::new(Kind::UnexpectedMessage)
345 }
346
347 #[cfg(all(
348 any(feature = "client", feature = "server"),
349 any(feature = "http1", feature = "http2")
350 ))]
351 pub(super) fn new_io(cause: std::io::Error) -> Error {
352 Error::new(Kind::Io).with(cause)
353 }
354
355 #[cfg(any(
356 all(feature = "http1", any(feature = "client", feature = "server")),
357 all(feature = "http2", feature = "client")
358 ))]
359 pub(super) fn new_closed() -> Error {
360 Error::new(Kind::ChannelClosed)
361 }
362
363 #[cfg(all(
364 any(feature = "client", feature = "server"),
365 any(feature = "http1", feature = "http2")
366 ))]
367 pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
368 Error::new(Kind::Body).with(cause)
369 }
370
371 #[cfg(all(
372 any(feature = "client", feature = "server"),
373 any(feature = "http1", feature = "http2")
374 ))]
375 pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
376 Error::new(Kind::BodyWrite).with(cause)
377 }
378
379 #[cfg(any(
380 all(feature = "http1", any(feature = "client", feature = "server")),
381 feature = "ffi"
382 ))]
383 pub(super) fn new_body_write_aborted() -> Error {
384 Error::new(Kind::User(User::BodyWriteAborted))
385 }
386
387 fn new_user(user: User) -> Error {
388 Error::new(Kind::User(user))
389 }
390
391 #[cfg(any(feature = "http1", feature = "http2"))]
392 #[cfg(feature = "server")]
393 pub(super) fn new_user_header() -> Error {
394 Error::new_user(User::UnexpectedHeader)
395 }
396
397 #[cfg(all(feature = "http1", feature = "server"))]
398 pub(super) fn new_header_timeout() -> Error {
399 Error::new(Kind::HeaderTimeout)
400 }
401
402 #[cfg(feature = "http1")]
403 #[cfg(feature = "server")]
404 pub(super) fn new_user_unsupported_status_code() -> Error {
405 Error::new_user(User::UnsupportedStatusCode)
406 }
407
408 pub(super) fn new_user_no_upgrade() -> Error {
409 Error::new_user(User::NoUpgrade)
410 }
411
412 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
413 pub(super) fn new_user_manual_upgrade() -> Error {
414 Error::new_user(User::ManualUpgrade)
415 }
416
417 #[cfg(any(
418 all(any(feature = "client", feature = "server"), feature = "http1"),
419 all(feature = "server", feature = "http2")
420 ))]
421 pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
422 Error::new_user(User::Service).with(cause)
423 }
424
425 #[cfg(all(
426 any(feature = "client", feature = "server"),
427 any(feature = "http1", feature = "http2")
428 ))]
429 pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
430 Error::new_user(User::Body).with(cause)
431 }
432
433 #[cfg(all(feature = "client", feature = "http2"))]
434 pub(super) fn new_user_invalid_connect() -> Error {
435 Error::new_user(User::InvalidConnectWithBody)
436 }
437
438 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
439 pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
440 Error::new(Kind::Shutdown).with(cause)
441 }
442
443 #[cfg(feature = "ffi")]
444 pub(super) fn new_user_aborted_by_callback() -> Error {
445 Error::new_user(User::AbortedByCallback)
446 }
447
448 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
449 pub(super) fn new_user_dispatch_gone() -> Error {
450 Error::new(Kind::User(User::DispatchGone))
451 }
452
453 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
454 pub(super) fn new_h2(cause: ::h2::Error) -> Error {
455 if cause.is_io() {
456 Error::new_io(cause.into_io().expect("h2::Error::is_io"))
457 } else {
458 Error::new(Kind::Http2).with(cause)
459 }
460 }
461
462 fn description(&self) -> &str {
463 match self.inner.kind {
464 Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
465 #[cfg(feature = "http1")]
466 Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
467 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
468 Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
469 Kind::Parse(Parse::Uri) => "invalid URI",
470 #[cfg(all(feature = "http1", feature = "server"))]
471 Kind::Parse(Parse::UriTooLong) => "URI too long",
472 #[cfg(feature = "http1")]
473 Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
474 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
475 Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
476 "invalid content-length parsed"
477 }
478 #[cfg(all(feature = "http1", feature = "server"))]
479 Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
480 "invalid transfer-encoding parsed"
481 }
482 #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
483 Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
484 "unexpected transfer-encoding parsed"
485 }
486 #[cfg(any(feature = "http1", feature = "http2"))]
487 Kind::Parse(Parse::TooLarge) => "message head is too large",
488 Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
489 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
490 Kind::Parse(Parse::Internal) => {
491 "internal error inside Hyper and/or its dependencies, please report"
492 }
493 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
494 Kind::IncompleteMessage => "connection closed before message completed",
495 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
496 Kind::UnexpectedMessage => "received unexpected message from connection",
497 #[cfg(any(
498 all(feature = "http1", any(feature = "client", feature = "server")),
499 all(feature = "http2", feature = "client")
500 ))]
501 Kind::ChannelClosed => "channel closed",
502 Kind::Canceled => "operation was canceled",
503 #[cfg(all(feature = "http1", feature = "server"))]
504 Kind::HeaderTimeout => "read header from client timeout",
505 #[cfg(all(
506 any(feature = "client", feature = "server"),
507 any(feature = "http1", feature = "http2")
508 ))]
509 Kind::Body => "error reading a body from connection",
510 #[cfg(all(
511 any(feature = "client", feature = "server"),
512 any(feature = "http1", feature = "http2")
513 ))]
514 Kind::BodyWrite => "error writing a body to connection",
515 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
516 Kind::Shutdown => "error shutting down connection",
517 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
518 Kind::Http2 => "http2 error",
519 #[cfg(all(
520 any(feature = "client", feature = "server"),
521 any(feature = "http1", feature = "http2")
522 ))]
523 Kind::Io => "connection error",
524
525 #[cfg(all(
526 any(feature = "client", feature = "server"),
527 any(feature = "http1", feature = "http2")
528 ))]
529 Kind::User(User::Body) => "error from user's Body stream",
530 #[cfg(any(
531 all(feature = "http1", any(feature = "client", feature = "server")),
532 feature = "ffi"
533 ))]
534 Kind::User(User::BodyWriteAborted) => "user body write aborted",
535 #[cfg(all(feature = "client", feature = "http2"))]
536 Kind::User(User::InvalidConnectWithBody) => {
537 "user sent CONNECT request with non-zero body"
538 }
539 #[cfg(any(
540 all(any(feature = "client", feature = "server"), feature = "http1"),
541 all(feature = "server", feature = "http2")
542 ))]
543 Kind::User(User::Service) => "error from user's Service",
544 #[cfg(any(feature = "http1", feature = "http2"))]
545 #[cfg(feature = "server")]
546 Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
547 #[cfg(feature = "http1")]
548 #[cfg(feature = "server")]
549 Kind::User(User::UnsupportedStatusCode) => {
550 "response has 1xx status code, not supported by server"
551 }
552 Kind::User(User::NoUpgrade) => "no upgrade available",
553 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
554 Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
555 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
556 Kind::User(User::DispatchGone) => "dispatch task is gone",
557 #[cfg(feature = "ffi")]
558 Kind::User(User::AbortedByCallback) => "operation aborted by an application callback",
559 }
560 }
561}
562
563impl fmt::Debug for Error {
564 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
565 let mut f = f.debug_tuple("hyper::Error");
566 f.field(&self.inner.kind);
567 if let Some(ref cause) = self.inner.cause {
568 f.field(cause);
569 }
570 f.finish()
571 }
572}
573
574impl fmt::Display for Error {
575 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576 f.write_str(self.description())
577 }
578}
579
580impl StdError for Error {
581 fn source(&self) -> Option<&(dyn StdError + 'static)> {
582 self.inner
583 .cause
584 .as_ref()
585 .map(|cause| &**cause as &(dyn StdError + 'static))
586 }
587}
588
589#[doc(hidden)]
590impl From<Parse> for Error {
591 fn from(err: Parse) -> Error {
592 Error::new(Kind::Parse(err))
593 }
594}
595
596#[cfg(feature = "http1")]
597impl Parse {
598 #[cfg(any(feature = "client", feature = "server"))]
599 pub(crate) fn content_length_invalid() -> Self {
600 Parse::Header(Header::ContentLengthInvalid)
601 }
602
603 #[cfg(feature = "server")]
604 pub(crate) fn transfer_encoding_invalid() -> Self {
605 Parse::Header(Header::TransferEncodingInvalid)
606 }
607
608 #[cfg(any(feature = "client", feature = "server"))]
609 pub(crate) fn transfer_encoding_unexpected() -> Self {
610 Parse::Header(Header::TransferEncodingUnexpected)
611 }
612}
613
614#[cfg(feature = "http1")]
615impl From<httparse::Error> for Parse {
616 fn from(err: httparse::Error) -> Parse {
617 match err {
618 httparse::Error::HeaderName
619 | httparse::Error::HeaderValue
620 | httparse::Error::NewLine
621 | httparse::Error::Token => Parse::Header(Header::Token),
622 httparse::Error::Status => Parse::Status,
623 httparse::Error::TooManyHeaders => Parse::TooLarge,
624 httparse::Error::Version => Parse::Version,
625 }
626 }
627}
628
629impl From<http::method::InvalidMethod> for Parse {
630 fn from(_: http::method::InvalidMethod) -> Parse {
631 Parse::Method
632 }
633}
634
635impl From<http::status::InvalidStatusCode> for Parse {
636 fn from(_: http::status::InvalidStatusCode) -> Parse {
637 Parse::Status
638 }
639}
640
641impl From<http::uri::InvalidUri> for Parse {
642 fn from(_: http::uri::InvalidUri) -> Parse {
643 Parse::Uri
644 }
645}
646
647impl From<http::uri::InvalidUriParts> for Parse {
648 fn from(_: http::uri::InvalidUriParts) -> Parse {
649 Parse::Uri
650 }
651}
652
653impl fmt::Display for TimedOut {
656 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
657 f.write_str("operation timed out")
658 }
659}
660
661impl StdError for TimedOut {}
662
663#[cfg(test)]
664mod tests {
665 use super::*;
666 use std::mem;
667
668 fn assert_send_sync<T: Send + Sync + 'static>() {}
669
670 #[test]
671 fn error_satisfies_send_sync() {
672 assert_send_sync::<Error>()
673 }
674
675 #[test]
676 fn error_size_of() {
677 assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
678 }
679
680 #[cfg(feature = "http2")]
681 #[test]
682 fn h2_reason_unknown() {
683 let closed = Error::new_closed();
684 assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR);
685 }
686
687 #[cfg(feature = "http2")]
688 #[test]
689 fn h2_reason_one_level() {
690 let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM));
691 assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM);
692 }
693
694 #[cfg(feature = "http2")]
695 #[test]
696 fn h2_reason_nested() {
697 let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED));
698 let svc_err = Error::new_user_service(recvd);
700 assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
701 }
702}