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