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