rama_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    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    /// Error calling user's Body::poll_data().
122    #[cfg(all(
123        any(feature = "client", feature = "server"),
124        any(feature = "http1", feature = "http2")
125    ))]
126    Body,
127    /// The user aborted writing of the outgoing body.
128    #[cfg(any(
129        all(feature = "http1", any(feature = "client", feature = "server")),
130        feature = "ffi"
131    ))]
132    BodyWriteAborted,
133    /// Error from future of user's Service.
134    #[cfg(any(
135        all(any(feature = "client", feature = "server"), feature = "http1"),
136        all(feature = "server", feature = "http2")
137    ))]
138    Service,
139    /// User tried to send a certain header in an unexpected context.
140    ///
141    /// For example, sending both `content-length` and `transfer-encoding`.
142    #[cfg(any(feature = "http1", feature = "http2"))]
143    #[cfg(feature = "server")]
144    UnexpectedHeader,
145    /// User tried to respond with a 1xx (not 101) response code.
146    #[cfg(feature = "http1")]
147    #[cfg(feature = "server")]
148    UnsupportedStatusCode,
149
150    /// User tried polling for an upgrade that doesn't exist.
151    NoUpgrade,
152
153    /// User polled for an upgrade, but low-level API is not using upgrades.
154    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
155    ManualUpgrade,
156
157    /// The dispatch task is gone.
158    #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
159    DispatchGone,
160
161    /// User aborted in an FFI callback.
162    #[cfg(feature = "ffi")]
163    AbortedByCallback,
164}
165
166// Sentinel type to indicate the error was caused by a timeout.
167#[derive(Debug)]
168pub(super) struct TimedOut;
169
170impl Error {
171    /// Returns true if this was an HTTP parse error.
172    pub fn is_parse(&self) -> bool {
173        matches!(self.inner.kind, Kind::Parse(_))
174    }
175
176    /// Returns true if this was an HTTP parse error caused by a message that was too large.
177    #[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    /// Returns true if this was an HTTP parse error caused by an invalid response status code or
186    /// reason phrase.
187    pub fn is_parse_status(&self) -> bool {
188        matches!(self.inner.kind, Kind::Parse(Parse::Status))
189    }
190
191    /// Returns true if this error was caused by user code.
192    pub fn is_user(&self) -> bool {
193        matches!(self.inner.kind, Kind::User(_))
194    }
195
196    /// Returns true if this was about a `Request` that was canceled.
197    pub fn is_canceled(&self) -> bool {
198        matches!(self.inner.kind, Kind::Canceled)
199    }
200
201    /// Returns true if a sender's channel is closed.
202    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    /// Returns true if the connection closed before a message could complete.
217    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    /// Returns true if the body write was aborted.
226    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    /// Returns true if the error was caused by a timeout.
241    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        // else
271        None
272    }
273
274    #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
275    pub(super) fn h2_reason(&self) -> h2::Reason {
276        // Find an h2::Reason somewhere in the cause stack, if it exists,
277        // otherwise assume an INTERNAL_ERROR.
278        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
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    #[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        // Suppose a user were proxying the received error
648        let svc_err = Error::new_user_service(recvd);
649        assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
650    }
651}