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.
11pub struct Error {
12    inner: Box<ErrorImpl>,
13}
14
15struct ErrorImpl {
16    kind: Kind,
17    cause: Option<Cause>,
18}
19
20#[derive(Debug)]
21pub(super) enum Kind {
22    Parse(Parse),
23    User(User),
24    /// A message reached EOF, but is not complete.
25    #[allow(unused)]
26    IncompleteMessage,
27    /// A connection received a message (or bytes) when not waiting for one.
28    #[cfg(feature = "http1")]
29    UnexpectedMessage,
30    /// A pending item was dropped before ever being processed.
31    Canceled,
32    /// Indicates a channel (client or body sender) is closed.
33    ChannelClosed,
34    /// An `io::Error` that occurred while trying to read or write to a network stream.
35    #[cfg(any(feature = "http1", feature = "http2"))]
36    Io,
37    /// Error occurred while connecting.
38    #[allow(unused)]
39    Connect,
40    /// Error creating a TcpListener.
41    #[cfg(all(feature = "tcp", feature = "server"))]
42    Listen,
43    /// Error accepting on an Incoming stream.
44    #[cfg(any(feature = "http1", feature = "http2"))]
45    #[cfg(feature = "server")]
46    Accept,
47    /// User took too long to send headers
48    #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
49    HeaderTimeout,
50    /// Error while reading a body from connection.
51    #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
52    Body,
53    /// Error while writing a body to connection.
54    #[cfg(any(feature = "http1", feature = "http2"))]
55    BodyWrite,
56    /// Error calling AsyncWrite::shutdown()
57    #[cfg(feature = "http1")]
58    Shutdown,
59
60    /// A general error from h2.
61    #[cfg(feature = "http2")]
62    Http2,
63}
64
65#[derive(Debug)]
66pub(super) enum Parse {
67    Method,
68    Version,
69    #[cfg(feature = "http1")]
70    VersionH2,
71    Uri,
72    #[cfg_attr(not(all(feature = "http1", feature = "server")), allow(unused))]
73    UriTooLong,
74    Header(Header),
75    TooLarge,
76    Status,
77    #[cfg_attr(debug_assertions, allow(unused))]
78    Internal,
79}
80
81#[derive(Debug)]
82pub(super) enum Header {
83    Token,
84    #[cfg(feature = "http1")]
85    ContentLengthInvalid,
86    #[cfg(all(feature = "http1", feature = "server"))]
87    TransferEncodingInvalid,
88    #[cfg(feature = "http1")]
89    TransferEncodingUnexpected,
90}
91
92#[derive(Debug)]
93pub(super) enum User {
94    /// Error calling user's HttpBody::poll_data().
95    #[cfg(any(feature = "http1", feature = "http2"))]
96    Body,
97    /// The user aborted writing of the outgoing body.
98    BodyWriteAborted,
99    /// Error calling user's MakeService.
100    #[cfg(any(feature = "http1", feature = "http2"))]
101    #[cfg(feature = "server")]
102    MakeService,
103    /// Error from future of user's Service.
104    #[cfg(any(feature = "http1", feature = "http2"))]
105    Service,
106    /// User tried to send a certain header in an unexpected context.
107    ///
108    /// For example, sending both `content-length` and `transfer-encoding`.
109    #[cfg(any(feature = "http1", feature = "http2"))]
110    #[cfg(feature = "server")]
111    UnexpectedHeader,
112    /// User tried to create a Request with bad version.
113    #[cfg(any(feature = "http1", feature = "http2"))]
114    #[cfg(feature = "client")]
115    UnsupportedVersion,
116    /// User tried to create a CONNECT Request with the Client.
117    #[cfg(any(feature = "http1", feature = "http2"))]
118    #[cfg(feature = "client")]
119    UnsupportedRequestMethod,
120    /// User tried to respond with a 1xx (not 101) response code.
121    #[cfg(feature = "http1")]
122    #[cfg(feature = "server")]
123    UnsupportedStatusCode,
124    /// User tried to send a Request with Client with non-absolute URI.
125    #[cfg(any(feature = "http1", feature = "http2"))]
126    #[cfg(feature = "client")]
127    AbsoluteUriRequired,
128
129    /// User tried polling for an upgrade that doesn't exist.
130    NoUpgrade,
131
132    /// User polled for an upgrade, but low-level API is not using upgrades.
133    #[cfg(feature = "http1")]
134    ManualUpgrade,
135
136    /// User called `server::Connection::without_shutdown()` on an HTTP/2 conn.
137    #[cfg(feature = "server")]
138    WithoutShutdownNonHttp1,
139
140    /// User aborted in an FFI callback.
141    #[cfg(feature = "ffi")]
142    AbortedByCallback,
143}
144
145// Sentinel type to indicate the error was caused by a timeout.
146#[derive(Debug)]
147pub(super) struct TimedOut;
148
149impl Error {
150    /// Returns true if this was an HTTP parse error.
151    pub fn is_parse(&self) -> bool {
152        matches!(self.inner.kind, Kind::Parse(_))
153    }
154
155    /// Returns true if this was an HTTP parse error caused by a message that was too large.
156    pub fn is_parse_too_large(&self) -> bool {
157        matches!(
158            self.inner.kind,
159            Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong)
160        )
161    }
162
163    /// Returns true if this was an HTTP parse error caused by an invalid response status code or
164    /// reason phrase.
165    pub fn is_parse_status(&self) -> bool {
166        matches!(self.inner.kind, Kind::Parse(Parse::Status))
167    }
168
169    /// Returns true if this error was caused by user code.
170    pub fn is_user(&self) -> bool {
171        matches!(self.inner.kind, Kind::User(_))
172    }
173
174    /// Returns true if this was about a `Request` that was canceled.
175    pub fn is_canceled(&self) -> bool {
176        matches!(self.inner.kind, Kind::Canceled)
177    }
178
179    /// Returns true if a sender's channel is closed.
180    pub fn is_closed(&self) -> bool {
181        matches!(self.inner.kind, Kind::ChannelClosed)
182    }
183
184    /// Returns true if this was an error from `Connect`.
185    pub fn is_connect(&self) -> bool {
186        matches!(self.inner.kind, Kind::Connect)
187    }
188
189    /// Returns true if the connection closed before a message could complete.
190    pub fn is_incomplete_message(&self) -> bool {
191        matches!(self.inner.kind, Kind::IncompleteMessage)
192    }
193
194    /// Returns true if the body write was aborted.
195    pub fn is_body_write_aborted(&self) -> bool {
196        matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
197    }
198
199    /// Returns true if the error was caused by a timeout.
200    pub fn is_timeout(&self) -> bool {
201        self.find_source::<TimedOut>().is_some()
202    }
203
204    /// Consumes the error, returning its cause.
205    pub fn into_cause(self) -> Option<Box<dyn StdError + Send + Sync>> {
206        self.inner.cause
207    }
208
209    pub(super) fn new(kind: Kind) -> Error {
210        Error {
211            inner: Box::new(ErrorImpl { kind, cause: None }),
212        }
213    }
214
215    pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
216        self.inner.cause = Some(cause.into());
217        self
218    }
219
220    #[cfg(any(all(feature = "http1", feature = "server"), feature = "ffi"))]
221    pub(super) fn kind(&self) -> &Kind {
222        &self.inner.kind
223    }
224
225    pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
226        let mut cause = self.source();
227        while let Some(err) = cause {
228            if let Some(ref typed) = err.downcast_ref() {
229                return Some(typed);
230            }
231            cause = err.source();
232        }
233
234        // else
235        None
236    }
237
238    #[cfg(feature = "http2")]
239    pub(super) fn h2_reason(&self) -> h2::Reason {
240        // Find an h2::Reason somewhere in the cause stack, if it exists,
241        // otherwise assume an INTERNAL_ERROR.
242        self.find_source::<h2::Error>()
243            .and_then(|h2_err| h2_err.reason())
244            .unwrap_or(h2::Reason::INTERNAL_ERROR)
245    }
246
247    pub(super) fn new_canceled() -> Error {
248        Error::new(Kind::Canceled)
249    }
250
251    #[cfg(feature = "http1")]
252    pub(super) fn new_incomplete() -> Error {
253        Error::new(Kind::IncompleteMessage)
254    }
255
256    #[cfg(feature = "http1")]
257    pub(super) fn new_too_large() -> Error {
258        Error::new(Kind::Parse(Parse::TooLarge))
259    }
260
261    #[cfg(feature = "http1")]
262    pub(super) fn new_version_h2() -> Error {
263        Error::new(Kind::Parse(Parse::VersionH2))
264    }
265
266    #[cfg(feature = "http1")]
267    pub(super) fn new_unexpected_message() -> Error {
268        Error::new(Kind::UnexpectedMessage)
269    }
270
271    #[cfg(any(feature = "http1", feature = "http2"))]
272    pub(super) fn new_io(cause: std::io::Error) -> Error {
273        Error::new(Kind::Io).with(cause)
274    }
275
276    #[cfg(all(feature = "server", feature = "tcp"))]
277    pub(super) fn new_listen<E: Into<Cause>>(cause: E) -> Error {
278        Error::new(Kind::Listen).with(cause)
279    }
280
281    #[cfg(any(feature = "http1", feature = "http2"))]
282    #[cfg(feature = "server")]
283    pub(super) fn new_accept<E: Into<Cause>>(cause: E) -> Error {
284        Error::new(Kind::Accept).with(cause)
285    }
286
287    #[cfg(any(feature = "http1", feature = "http2"))]
288    #[cfg(feature = "client")]
289    pub(super) fn new_connect<E: Into<Cause>>(cause: E) -> Error {
290        Error::new(Kind::Connect).with(cause)
291    }
292
293    pub(super) fn new_closed() -> Error {
294        Error::new(Kind::ChannelClosed)
295    }
296
297    #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
298    pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
299        Error::new(Kind::Body).with(cause)
300    }
301
302    #[cfg(any(feature = "http1", feature = "http2"))]
303    pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
304        Error::new(Kind::BodyWrite).with(cause)
305    }
306
307    pub(super) fn new_body_write_aborted() -> Error {
308        Error::new(Kind::User(User::BodyWriteAborted))
309    }
310
311    fn new_user(user: User) -> Error {
312        Error::new(Kind::User(user))
313    }
314
315    #[cfg(any(feature = "http1", feature = "http2"))]
316    #[cfg(feature = "server")]
317    pub(super) fn new_user_header() -> Error {
318        Error::new_user(User::UnexpectedHeader)
319    }
320
321    #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
322    pub(super) fn new_header_timeout() -> Error {
323        Error::new(Kind::HeaderTimeout)
324    }
325
326    #[cfg(any(feature = "http1", feature = "http2"))]
327    #[cfg(feature = "client")]
328    pub(super) fn new_user_unsupported_version() -> Error {
329        Error::new_user(User::UnsupportedVersion)
330    }
331
332    #[cfg(any(feature = "http1", feature = "http2"))]
333    #[cfg(feature = "client")]
334    pub(super) fn new_user_unsupported_request_method() -> Error {
335        Error::new_user(User::UnsupportedRequestMethod)
336    }
337
338    #[cfg(feature = "http1")]
339    #[cfg(feature = "server")]
340    pub(super) fn new_user_unsupported_status_code() -> Error {
341        Error::new_user(User::UnsupportedStatusCode)
342    }
343
344    #[cfg(any(feature = "http1", feature = "http2"))]
345    #[cfg(feature = "client")]
346    pub(super) fn new_user_absolute_uri_required() -> Error {
347        Error::new_user(User::AbsoluteUriRequired)
348    }
349
350    pub(super) fn new_user_no_upgrade() -> Error {
351        Error::new_user(User::NoUpgrade)
352    }
353
354    #[cfg(feature = "http1")]
355    pub(super) fn new_user_manual_upgrade() -> Error {
356        Error::new_user(User::ManualUpgrade)
357    }
358
359    #[cfg(any(feature = "http1", feature = "http2"))]
360    #[cfg(feature = "server")]
361    pub(super) fn new_user_make_service<E: Into<Cause>>(cause: E) -> Error {
362        Error::new_user(User::MakeService).with(cause)
363    }
364
365    #[cfg(any(feature = "http1", feature = "http2"))]
366    pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
367        Error::new_user(User::Service).with(cause)
368    }
369
370    #[cfg(any(feature = "http1", feature = "http2"))]
371    pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
372        Error::new_user(User::Body).with(cause)
373    }
374
375    #[cfg(feature = "server")]
376    pub(super) fn new_without_shutdown_not_h1() -> Error {
377        Error::new(Kind::User(User::WithoutShutdownNonHttp1))
378    }
379
380    #[cfg(feature = "http1")]
381    pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
382        Error::new(Kind::Shutdown).with(cause)
383    }
384
385    #[cfg(feature = "ffi")]
386    pub(super) fn new_user_aborted_by_callback() -> Error {
387        Error::new_user(User::AbortedByCallback)
388    }
389
390    #[cfg(feature = "http2")]
391    pub(super) fn new_h2(cause: ::h2::Error) -> Error {
392        if cause.is_io() {
393            Error::new_io(cause.into_io().expect("h2::Error::is_io"))
394        } else {
395            Error::new(Kind::Http2).with(cause)
396        }
397    }
398
399    /// The error's standalone message, without the message from the source.
400    pub fn message(&self) -> impl fmt::Display + '_ {
401        self.description()
402    }
403
404    fn description(&self) -> &str {
405        match self.inner.kind {
406            Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
407            Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
408            #[cfg(feature = "http1")]
409            Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
410            Kind::Parse(Parse::Uri) => "invalid URI",
411            Kind::Parse(Parse::UriTooLong) => "URI too long",
412            Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
413            #[cfg(feature = "http1")]
414            Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
415                "invalid content-length parsed"
416            }
417            #[cfg(all(feature = "http1", feature = "server"))]
418            Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
419                "invalid transfer-encoding parsed"
420            }
421            #[cfg(feature = "http1")]
422            Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
423                "unexpected transfer-encoding parsed"
424            }
425            Kind::Parse(Parse::TooLarge) => "message head is too large",
426            Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
427            Kind::Parse(Parse::Internal) => {
428                "internal error inside Hyper and/or its dependencies, please report"
429            }
430            Kind::IncompleteMessage => "connection closed before message completed",
431            #[cfg(feature = "http1")]
432            Kind::UnexpectedMessage => "received unexpected message from connection",
433            Kind::ChannelClosed => "channel closed",
434            Kind::Connect => "error trying to connect",
435            Kind::Canceled => "operation was canceled",
436            #[cfg(all(feature = "server", feature = "tcp"))]
437            Kind::Listen => "error creating server listener",
438            #[cfg(any(feature = "http1", feature = "http2"))]
439            #[cfg(feature = "server")]
440            Kind::Accept => "error accepting connection",
441            #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
442            Kind::HeaderTimeout => "read header from client timeout",
443            #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
444            Kind::Body => "error reading a body from connection",
445            #[cfg(any(feature = "http1", feature = "http2"))]
446            Kind::BodyWrite => "error writing a body to connection",
447            #[cfg(feature = "http1")]
448            Kind::Shutdown => "error shutting down connection",
449            #[cfg(feature = "http2")]
450            Kind::Http2 => "http2 error",
451            #[cfg(any(feature = "http1", feature = "http2"))]
452            Kind::Io => "connection error",
453
454            #[cfg(any(feature = "http1", feature = "http2"))]
455            Kind::User(User::Body) => "error from user's HttpBody stream",
456            Kind::User(User::BodyWriteAborted) => "user body write aborted",
457            #[cfg(any(feature = "http1", feature = "http2"))]
458            #[cfg(feature = "server")]
459            Kind::User(User::MakeService) => "error from user's MakeService",
460            #[cfg(any(feature = "http1", feature = "http2"))]
461            Kind::User(User::Service) => "error from user's Service",
462            #[cfg(any(feature = "http1", feature = "http2"))]
463            #[cfg(feature = "server")]
464            Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
465            #[cfg(any(feature = "http1", feature = "http2"))]
466            #[cfg(feature = "client")]
467            Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
468            #[cfg(any(feature = "http1", feature = "http2"))]
469            #[cfg(feature = "client")]
470            Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
471            #[cfg(feature = "http1")]
472            #[cfg(feature = "server")]
473            Kind::User(User::UnsupportedStatusCode) => {
474                "response has 1xx status code, not supported by server"
475            }
476            #[cfg(any(feature = "http1", feature = "http2"))]
477            #[cfg(feature = "client")]
478            Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
479            Kind::User(User::NoUpgrade) => "no upgrade available",
480            #[cfg(feature = "http1")]
481            Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
482            #[cfg(feature = "server")]
483            Kind::User(User::WithoutShutdownNonHttp1) => {
484                "without_shutdown() called on a non-HTTP/1 connection"
485            }
486            #[cfg(feature = "ffi")]
487            Kind::User(User::AbortedByCallback) => "operation aborted by an application callback",
488        }
489    }
490}
491
492impl fmt::Debug for Error {
493    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494        let mut f = f.debug_tuple("hyper::Error");
495        f.field(&self.inner.kind);
496        if let Some(ref cause) = self.inner.cause {
497            f.field(cause);
498        }
499        f.finish()
500    }
501}
502
503impl fmt::Display for Error {
504    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505        if let Some(ref cause) = self.inner.cause {
506            write!(f, "{}: {}", self.description(), cause)
507        } else {
508            f.write_str(self.description())
509        }
510    }
511}
512
513impl StdError for Error {
514    fn source(&self) -> Option<&(dyn StdError + 'static)> {
515        self.inner
516            .cause
517            .as_ref()
518            .map(|cause| &**cause as &(dyn StdError + 'static))
519    }
520}
521
522#[doc(hidden)]
523impl From<Parse> for Error {
524    fn from(err: Parse) -> Error {
525        Error::new(Kind::Parse(err))
526    }
527}
528
529#[cfg(feature = "http1")]
530impl Parse {
531    pub(crate) fn content_length_invalid() -> Self {
532        Parse::Header(Header::ContentLengthInvalid)
533    }
534
535    #[cfg(all(feature = "http1", feature = "server"))]
536    pub(crate) fn transfer_encoding_invalid() -> Self {
537        Parse::Header(Header::TransferEncodingInvalid)
538    }
539
540    pub(crate) fn transfer_encoding_unexpected() -> Self {
541        Parse::Header(Header::TransferEncodingUnexpected)
542    }
543}
544
545impl From<httparse::Error> for Parse {
546    fn from(err: httparse::Error) -> Parse {
547        match err {
548            httparse::Error::HeaderName
549            | httparse::Error::HeaderValue
550            | httparse::Error::NewLine
551            | httparse::Error::Token => Parse::Header(Header::Token),
552            httparse::Error::Status => Parse::Status,
553            httparse::Error::TooManyHeaders => Parse::TooLarge,
554            httparse::Error::Version => Parse::Version,
555        }
556    }
557}
558
559impl From<http::method::InvalidMethod> for Parse {
560    fn from(_: http::method::InvalidMethod) -> Parse {
561        Parse::Method
562    }
563}
564
565impl From<http::status::InvalidStatusCode> for Parse {
566    fn from(_: http::status::InvalidStatusCode) -> Parse {
567        Parse::Status
568    }
569}
570
571impl From<http::uri::InvalidUri> for Parse {
572    fn from(_: http::uri::InvalidUri) -> Parse {
573        Parse::Uri
574    }
575}
576
577impl From<http::uri::InvalidUriParts> for Parse {
578    fn from(_: http::uri::InvalidUriParts) -> Parse {
579        Parse::Uri
580    }
581}
582
583#[doc(hidden)]
584trait AssertSendSync: Send + Sync + 'static {}
585#[doc(hidden)]
586impl AssertSendSync for Error {}
587
588// ===== impl TimedOut ====
589
590impl fmt::Display for TimedOut {
591    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592        f.write_str("operation timed out")
593    }
594}
595
596impl StdError for TimedOut {}
597
598#[cfg(test)]
599mod tests {
600    use super::*;
601    use std::mem;
602
603    #[test]
604    fn error_size_of() {
605        assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
606    }
607
608    #[cfg(feature = "http2")]
609    #[test]
610    fn h2_reason_unknown() {
611        let closed = Error::new_closed();
612        assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR);
613    }
614
615    #[cfg(feature = "http2")]
616    #[test]
617    fn h2_reason_one_level() {
618        let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM));
619        assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM);
620    }
621
622    #[cfg(feature = "http2")]
623    #[test]
624    fn h2_reason_nested() {
625        let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED));
626        // Suppose a user were proxying the received error
627        let svc_err = Error::new_user_service(recvd);
628        assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
629    }
630}