Skip to main content

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