hyper/
error.rs

1//! Error and Result module.
2use std::error::Error as StdError;
3use std::fmt;
4
5/// Result type often returned from methods that can have hyper `Error`s.
6pub type Result<T> = std::result::Result<T, Error>;
7
8type Cause = Box<dyn StdError + Send + Sync>;
9
10/// Represents errors that can occur handling HTTP streams.
11///
12/// # Formatting
13///
14/// The `Display` implementation of this type will only print the details of
15/// this level of error, even though it may have been caused by another error
16/// and contain that error in its source. To print all the relevant
17/// information, including the source chain, using something like
18/// `std::error::Report`, or equivalent 3rd party types.
19///
20/// The contents of the formatted error message of this specific `Error` type
21/// is unspecified. **You must not depend on it.** The wording and details may
22/// change in any version, with the goal of improving error messages.
23///
24/// # Source
25///
26/// A `hyper::Error` may be caused by another error. To aid in debugging,
27/// those are exposed in `Error::source()` as erased types. While it is
28/// possible to check the exact type of the sources, they **can not be depended
29/// on**. They may come from private internal dependencies, and are subject to
30/// change at any moment.
31pub 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    /// A message reached EOF, but is not complete.
45    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
46    IncompleteMessage,
47    /// A connection received a message (or bytes) when not waiting for one.
48    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
49    UnexpectedMessage,
50    /// A pending item was dropped before ever being processed.
51    Canceled,
52    /// Indicates a channel (client or body sender) is closed.
53    #[cfg(any(
54        all(feature = "http1", any(feature = "client", feature = "server")),
55        all(feature = "http2", feature = "client")
56    ))]
57    ChannelClosed,
58    /// An `io::Error` that occurred while trying to read or write to a network stream.
59    #[cfg(all(
60        any(feature = "client", feature = "server"),
61        any(feature = "http1", feature = "http2")
62    ))]
63    Io,
64    /// User took too long to send headers
65    #[cfg(all(feature = "http1", feature = "server"))]
66    HeaderTimeout,
67    /// Error while reading a body from connection.
68    #[cfg(all(
69        any(feature = "client", feature = "server"),
70        any(feature = "http1", feature = "http2")
71    ))]
72    Body,
73    /// Error while writing a body to connection.
74    #[cfg(all(
75        any(feature = "client", feature = "server"),
76        any(feature = "http1", feature = "http2")
77    ))]
78    BodyWrite,
79    /// Error calling AsyncWrite::shutdown()
80    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
81    Shutdown,
82
83    /// A general error from h2.
84    #[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    /// Error calling user's Body::poll_data().
123    #[cfg(all(
124        any(feature = "client", feature = "server"),
125        any(feature = "http1", feature = "http2")
126    ))]
127    Body,
128    /// The user aborted writing of the outgoing body.
129    #[cfg(any(
130        all(feature = "http1", any(feature = "client", feature = "server")),
131        feature = "ffi"
132    ))]
133    BodyWriteAborted,
134    /// Error from future of user's Service.
135    #[cfg(any(
136        all(any(feature = "client", feature = "server"), feature = "http1"),
137        all(feature = "server", feature = "http2")
138    ))]
139    Service,
140    /// User tried to send a certain header in an unexpected context.
141    ///
142    /// For example, sending both `content-length` and `transfer-encoding`.
143    #[cfg(any(feature = "http1", feature = "http2"))]
144    #[cfg(feature = "server")]
145    UnexpectedHeader,
146    /// User tried to respond with a 1xx (not 101) response code.
147    #[cfg(feature = "http1")]
148    #[cfg(feature = "server")]
149    UnsupportedStatusCode,
150
151    /// User tried polling for an upgrade that doesn't exist.
152    NoUpgrade,
153
154    /// User polled for an upgrade, but low-level API is not using upgrades.
155    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
156    ManualUpgrade,
157
158    /// The dispatch task is gone.
159    #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
160    DispatchGone,
161
162    /// User aborted in an FFI callback.
163    #[cfg(feature = "ffi")]
164    AbortedByCallback,
165}
166
167// Sentinel type to indicate the error was caused by a timeout.
168#[derive(Debug)]
169pub(super) struct TimedOut;
170
171impl Error {
172    /// Returns true if this was an HTTP parse error.
173    pub fn is_parse(&self) -> bool {
174        matches!(self.inner.kind, Kind::Parse(_))
175    }
176
177    /// Returns true if this was an HTTP parse error caused by a message that was too large.
178    #[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    /// Returns true if this was an HTTP parse error caused by an invalid response status code or
187    /// reason phrase.
188    pub fn is_parse_status(&self) -> bool {
189        matches!(self.inner.kind, Kind::Parse(Parse::Status))
190    }
191
192    /// Returns true if this error was caused by user code.
193    pub fn is_user(&self) -> bool {
194        matches!(self.inner.kind, Kind::User(_))
195    }
196
197    /// Returns true if this was about a `Request` that was canceled.
198    pub fn is_canceled(&self) -> bool {
199        matches!(self.inner.kind, Kind::Canceled)
200    }
201
202    /// Returns true if a sender's channel is closed.
203    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    /// Returns true if the connection closed before a message could complete.
218    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    /// Returns true if the body write was aborted.
227    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    /// Returns true if the error was caused by a timeout.
242    pub fn is_timeout(&self) -> bool {
243        #[cfg(all(feature = "http1", feature = "server"))]
244        if matches!(self.inner.kind, Kind::HeaderTimeout) {
245            return true;
246        }
247        self.find_source::<TimedOut>().is_some()
248    }
249
250    pub(super) fn new(kind: Kind) -> Error {
251        Error {
252            inner: Box::new(ErrorImpl { kind, cause: None }),
253        }
254    }
255
256    pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
257        self.inner.cause = Some(cause.into());
258        self
259    }
260
261    #[cfg(any(all(feature = "http1", feature = "server"), feature = "ffi"))]
262    pub(super) fn kind(&self) -> &Kind {
263        &self.inner.kind
264    }
265
266    pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
267        let mut cause = self.source();
268        while let Some(err) = cause {
269            if let Some(typed) = err.downcast_ref() {
270                return Some(typed);
271            }
272            cause = err.source();
273        }
274
275        // else
276        None
277    }
278
279    #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
280    pub(super) fn h2_reason(&self) -> h2::Reason {
281        // Find an h2::Reason somewhere in the cause stack, if it exists,
282        // otherwise assume an INTERNAL_ERROR.
283        self.find_source::<h2::Error>()
284            .and_then(|h2_err| h2_err.reason())
285            .unwrap_or(h2::Reason::INTERNAL_ERROR)
286    }
287
288    pub(super) fn new_canceled() -> Error {
289        Error::new(Kind::Canceled)
290    }
291
292    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
293    pub(super) fn new_incomplete() -> Error {
294        Error::new(Kind::IncompleteMessage)
295    }
296
297    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
298    pub(super) fn new_too_large() -> Error {
299        Error::new(Kind::Parse(Parse::TooLarge))
300    }
301
302    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
303    pub(super) fn new_version_h2() -> Error {
304        Error::new(Kind::Parse(Parse::VersionH2))
305    }
306
307    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
308    pub(super) fn new_unexpected_message() -> Error {
309        Error::new(Kind::UnexpectedMessage)
310    }
311
312    #[cfg(all(
313        any(feature = "client", feature = "server"),
314        any(feature = "http1", feature = "http2")
315    ))]
316    pub(super) fn new_io(cause: std::io::Error) -> Error {
317        Error::new(Kind::Io).with(cause)
318    }
319
320    #[cfg(any(
321        all(feature = "http1", any(feature = "client", feature = "server")),
322        all(feature = "http2", feature = "client")
323    ))]
324    pub(super) fn new_closed() -> Error {
325        Error::new(Kind::ChannelClosed)
326    }
327
328    #[cfg(all(
329        any(feature = "client", feature = "server"),
330        any(feature = "http1", feature = "http2")
331    ))]
332    pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
333        Error::new(Kind::Body).with(cause)
334    }
335
336    #[cfg(all(
337        any(feature = "client", feature = "server"),
338        any(feature = "http1", feature = "http2")
339    ))]
340    pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
341        Error::new(Kind::BodyWrite).with(cause)
342    }
343
344    #[cfg(any(
345        all(feature = "http1", any(feature = "client", feature = "server")),
346        feature = "ffi"
347    ))]
348    pub(super) fn new_body_write_aborted() -> Error {
349        Error::new(Kind::User(User::BodyWriteAborted))
350    }
351
352    fn new_user(user: User) -> Error {
353        Error::new(Kind::User(user))
354    }
355
356    #[cfg(any(feature = "http1", feature = "http2"))]
357    #[cfg(feature = "server")]
358    pub(super) fn new_user_header() -> Error {
359        Error::new_user(User::UnexpectedHeader)
360    }
361
362    #[cfg(all(feature = "http1", feature = "server"))]
363    pub(super) fn new_header_timeout() -> Error {
364        Error::new(Kind::HeaderTimeout)
365    }
366
367    #[cfg(feature = "http1")]
368    #[cfg(feature = "server")]
369    pub(super) fn new_user_unsupported_status_code() -> Error {
370        Error::new_user(User::UnsupportedStatusCode)
371    }
372
373    pub(super) fn new_user_no_upgrade() -> Error {
374        Error::new_user(User::NoUpgrade)
375    }
376
377    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
378    pub(super) fn new_user_manual_upgrade() -> Error {
379        Error::new_user(User::ManualUpgrade)
380    }
381
382    #[cfg(any(
383        all(any(feature = "client", feature = "server"), feature = "http1"),
384        all(feature = "server", feature = "http2")
385    ))]
386    pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
387        Error::new_user(User::Service).with(cause)
388    }
389
390    #[cfg(all(
391        any(feature = "client", feature = "server"),
392        any(feature = "http1", feature = "http2")
393    ))]
394    pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
395        Error::new_user(User::Body).with(cause)
396    }
397
398    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
399    pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
400        Error::new(Kind::Shutdown).with(cause)
401    }
402
403    #[cfg(feature = "ffi")]
404    pub(super) fn new_user_aborted_by_callback() -> Error {
405        Error::new_user(User::AbortedByCallback)
406    }
407
408    #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
409    pub(super) fn new_user_dispatch_gone() -> Error {
410        Error::new(Kind::User(User::DispatchGone))
411    }
412
413    #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
414    pub(super) fn new_h2(cause: ::h2::Error) -> Error {
415        if cause.is_io() {
416            Error::new_io(cause.into_io().expect("h2::Error::is_io"))
417        } else {
418            Error::new(Kind::Http2).with(cause)
419        }
420    }
421
422    fn description(&self) -> &str {
423        match self.inner.kind {
424            Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
425            #[cfg(feature = "http1")]
426            Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
427            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
428            Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
429            Kind::Parse(Parse::Uri) => "invalid URI",
430            #[cfg(all(feature = "http1", feature = "server"))]
431            Kind::Parse(Parse::UriTooLong) => "URI too long",
432            #[cfg(feature = "http1")]
433            Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
434            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
435            Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
436                "invalid content-length parsed"
437            }
438            #[cfg(all(feature = "http1", feature = "server"))]
439            Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
440                "invalid transfer-encoding parsed"
441            }
442            #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
443            Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
444                "unexpected transfer-encoding parsed"
445            }
446            #[cfg(any(feature = "http1", feature = "http2"))]
447            Kind::Parse(Parse::TooLarge) => "message head is too large",
448            Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
449            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
450            Kind::Parse(Parse::Internal) => {
451                "internal error inside Hyper and/or its dependencies, please report"
452            }
453            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
454            Kind::IncompleteMessage => "connection closed before message completed",
455            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
456            Kind::UnexpectedMessage => "received unexpected message from connection",
457            #[cfg(any(
458                all(feature = "http1", any(feature = "client", feature = "server")),
459                all(feature = "http2", feature = "client")
460            ))]
461            Kind::ChannelClosed => "channel closed",
462            Kind::Canceled => "operation was canceled",
463            #[cfg(all(feature = "http1", feature = "server"))]
464            Kind::HeaderTimeout => "read header from client timeout",
465            #[cfg(all(
466                any(feature = "client", feature = "server"),
467                any(feature = "http1", feature = "http2")
468            ))]
469            Kind::Body => "error reading a body from connection",
470            #[cfg(all(
471                any(feature = "client", feature = "server"),
472                any(feature = "http1", feature = "http2")
473            ))]
474            Kind::BodyWrite => "error writing a body to connection",
475            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
476            Kind::Shutdown => "error shutting down connection",
477            #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
478            Kind::Http2 => "http2 error",
479            #[cfg(all(
480                any(feature = "client", feature = "server"),
481                any(feature = "http1", feature = "http2")
482            ))]
483            Kind::Io => "connection error",
484
485            #[cfg(all(
486                any(feature = "client", feature = "server"),
487                any(feature = "http1", feature = "http2")
488            ))]
489            Kind::User(User::Body) => "error from user's Body stream",
490            #[cfg(any(
491                all(feature = "http1", any(feature = "client", feature = "server")),
492                feature = "ffi"
493            ))]
494            Kind::User(User::BodyWriteAborted) => "user body write aborted",
495            #[cfg(any(
496                all(any(feature = "client", feature = "server"), feature = "http1"),
497                all(feature = "server", feature = "http2")
498            ))]
499            Kind::User(User::Service) => "error from user's Service",
500            #[cfg(any(feature = "http1", feature = "http2"))]
501            #[cfg(feature = "server")]
502            Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
503            #[cfg(feature = "http1")]
504            #[cfg(feature = "server")]
505            Kind::User(User::UnsupportedStatusCode) => {
506                "response has 1xx status code, not supported by server"
507            }
508            Kind::User(User::NoUpgrade) => "no upgrade available",
509            #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
510            Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
511            #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
512            Kind::User(User::DispatchGone) => "dispatch task is gone",
513            #[cfg(feature = "ffi")]
514            Kind::User(User::AbortedByCallback) => "operation aborted by an application callback",
515        }
516    }
517}
518
519impl fmt::Debug for Error {
520    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521        let mut f = f.debug_tuple("hyper::Error");
522        f.field(&self.inner.kind);
523        if let Some(ref cause) = self.inner.cause {
524            f.field(cause);
525        }
526        f.finish()
527    }
528}
529
530impl fmt::Display for Error {
531    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532        f.write_str(self.description())
533    }
534}
535
536impl StdError for Error {
537    fn source(&self) -> Option<&(dyn StdError + 'static)> {
538        self.inner
539            .cause
540            .as_ref()
541            .map(|cause| &**cause as &(dyn StdError + 'static))
542    }
543}
544
545#[doc(hidden)]
546impl From<Parse> for Error {
547    fn from(err: Parse) -> Error {
548        Error::new(Kind::Parse(err))
549    }
550}
551
552#[cfg(feature = "http1")]
553impl Parse {
554    #[cfg(any(feature = "client", feature = "server"))]
555    pub(crate) fn content_length_invalid() -> Self {
556        Parse::Header(Header::ContentLengthInvalid)
557    }
558
559    #[cfg(feature = "server")]
560    pub(crate) fn transfer_encoding_invalid() -> Self {
561        Parse::Header(Header::TransferEncodingInvalid)
562    }
563
564    #[cfg(any(feature = "client", feature = "server"))]
565    pub(crate) fn transfer_encoding_unexpected() -> Self {
566        Parse::Header(Header::TransferEncodingUnexpected)
567    }
568}
569
570#[cfg(feature = "http1")]
571impl From<httparse::Error> for Parse {
572    fn from(err: httparse::Error) -> Parse {
573        match err {
574            httparse::Error::HeaderName
575            | httparse::Error::HeaderValue
576            | httparse::Error::NewLine
577            | httparse::Error::Token => Parse::Header(Header::Token),
578            httparse::Error::Status => Parse::Status,
579            httparse::Error::TooManyHeaders => Parse::TooLarge,
580            httparse::Error::Version => Parse::Version,
581        }
582    }
583}
584
585impl From<http::method::InvalidMethod> for Parse {
586    fn from(_: http::method::InvalidMethod) -> Parse {
587        Parse::Method
588    }
589}
590
591impl From<http::status::InvalidStatusCode> for Parse {
592    fn from(_: http::status::InvalidStatusCode) -> Parse {
593        Parse::Status
594    }
595}
596
597impl From<http::uri::InvalidUri> for Parse {
598    fn from(_: http::uri::InvalidUri) -> Parse {
599        Parse::Uri
600    }
601}
602
603impl From<http::uri::InvalidUriParts> for Parse {
604    fn from(_: http::uri::InvalidUriParts) -> Parse {
605        Parse::Uri
606    }
607}
608
609// ===== impl TimedOut ====
610
611impl 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    fn assert_send_sync<T: Send + Sync + 'static>() {}
625
626    #[test]
627    fn error_satisfies_send_sync() {
628        assert_send_sync::<Error>()
629    }
630
631    #[test]
632    fn error_size_of() {
633        assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
634    }
635
636    #[cfg(feature = "http2")]
637    #[test]
638    fn h2_reason_unknown() {
639        let closed = Error::new_closed();
640        assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR);
641    }
642
643    #[cfg(feature = "http2")]
644    #[test]
645    fn h2_reason_one_level() {
646        let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM));
647        assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM);
648    }
649
650    #[cfg(feature = "http2")]
651    #[test]
652    fn h2_reason_nested() {
653        let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED));
654        // Suppose a user were proxying the received error
655        let svc_err = Error::new_user_service(recvd);
656        assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
657    }
658}