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