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