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>, |
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 | /// 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)] |
66 | pub(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)] |
82 | pub(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)] |
93 | pub(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)] |
151 | pub(super) struct TimedOut; |
152 | |
153 | impl 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 | |
503 | impl 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 | |
514 | impl 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 | |
524 | impl 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)] |
534 | impl From<Parse> for Error { |
535 | fn from(err: Parse) -> Error { |
536 | Error::new(Kind::Parse(err)) |
537 | } |
538 | } |
539 | |
540 | #[cfg (feature = "http1" )] |
541 | impl 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 | |
556 | impl 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 | |
570 | impl From<http::method::InvalidMethod> for Parse { |
571 | fn from(_: http::method::InvalidMethod) -> Parse { |
572 | Parse::Method |
573 | } |
574 | } |
575 | |
576 | impl From<http::status::InvalidStatusCode> for Parse { |
577 | fn from(_: http::status::InvalidStatusCode) -> Parse { |
578 | Parse::Status |
579 | } |
580 | } |
581 | |
582 | impl From<http::uri::InvalidUri> for Parse { |
583 | fn from(_: http::uri::InvalidUri) -> Parse { |
584 | Parse::Uri |
585 | } |
586 | } |
587 | |
588 | impl From<http::uri::InvalidUriParts> for Parse { |
589 | fn from(_: http::uri::InvalidUriParts) -> Parse { |
590 | Parse::Uri |
591 | } |
592 | } |
593 | |
594 | #[doc (hidden)] |
595 | trait AssertSendSync: Send + Sync + 'static {} |
596 | #[doc (hidden)] |
597 | impl AssertSendSync for Error {} |
598 | |
599 | // ===== impl TimedOut ==== |
600 | |
601 | impl 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 | |
607 | impl StdError for TimedOut {} |
608 | |
609 | #[cfg (test)] |
610 | mod 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 | |