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