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