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