1use std::error::Error as StdError;
3use std::fmt;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8type Cause = Box<dyn StdError + Send + Sync>;
9
10pub 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 #[allow(unused)]
26 IncompleteMessage,
27 #[cfg(feature = "http1")]
29 UnexpectedMessage,
30 Canceled,
32 ChannelClosed,
34 #[cfg(any(feature = "http1", feature = "http2"))]
36 Io,
37 #[allow(unused)]
39 Connect,
40 #[cfg(all(feature = "tcp", feature = "server"))]
42 Listen,
43 #[cfg(any(feature = "http1", feature = "http2"))]
45 #[cfg(feature = "server")]
46 Accept,
47 #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
49 HeaderTimeout,
50 #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
52 Body,
53 #[cfg(any(feature = "http1", feature = "http2"))]
55 BodyWrite,
56 #[cfg(feature = "http1")]
58 Shutdown,
59
60 #[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 #[cfg(any(feature = "http1", feature = "http2"))]
96 Body,
97 BodyWriteAborted,
99 #[cfg(any(feature = "http1", feature = "http2"))]
101 #[cfg(feature = "server")]
102 MakeService,
103 #[cfg(any(feature = "http1", feature = "http2"))]
105 Service,
106 #[cfg(any(feature = "http1", feature = "http2"))]
110 #[cfg(feature = "server")]
111 UnexpectedHeader,
112 #[cfg(any(feature = "http1", feature = "http2"))]
114 #[cfg(feature = "client")]
115 UnsupportedVersion,
116 #[cfg(any(feature = "http1", feature = "http2"))]
118 #[cfg(feature = "client")]
119 UnsupportedRequestMethod,
120 #[cfg(feature = "http1")]
122 #[cfg(feature = "server")]
123 UnsupportedStatusCode,
124 #[cfg(any(feature = "http1", feature = "http2"))]
126 #[cfg(feature = "client")]
127 AbsoluteUriRequired,
128
129 NoUpgrade,
131
132 #[cfg(feature = "http1")]
134 ManualUpgrade,
135
136 #[cfg(feature = "server")]
138 WithoutShutdownNonHttp1,
139
140 #[cfg(feature = "ffi")]
142 AbortedByCallback,
143}
144
145#[derive(Debug)]
147pub(super) struct TimedOut;
148
149impl Error {
150 pub fn is_parse(&self) -> bool {
152 matches!(self.inner.kind, Kind::Parse(_))
153 }
154
155 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 pub fn is_parse_status(&self) -> bool {
166 matches!(self.inner.kind, Kind::Parse(Parse::Status))
167 }
168
169 pub fn is_user(&self) -> bool {
171 matches!(self.inner.kind, Kind::User(_))
172 }
173
174 pub fn is_canceled(&self) -> bool {
176 matches!(self.inner.kind, Kind::Canceled)
177 }
178
179 pub fn is_closed(&self) -> bool {
181 matches!(self.inner.kind, Kind::ChannelClosed)
182 }
183
184 pub fn is_connect(&self) -> bool {
186 matches!(self.inner.kind, Kind::Connect)
187 }
188
189 pub fn is_incomplete_message(&self) -> bool {
191 matches!(self.inner.kind, Kind::IncompleteMessage)
192 }
193
194 pub fn is_body_write_aborted(&self) -> bool {
196 matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
197 }
198
199 pub fn is_timeout(&self) -> bool {
201 self.find_source::<TimedOut>().is_some()
202 }
203
204 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 None
236 }
237
238 #[cfg(feature = "http2")]
239 pub(super) fn h2_reason(&self) -> h2::Reason {
240 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 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
588impl 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 let svc_err = Error::new_user_service(recvd);
628 assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
629 }
630}