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