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 | /// |
12 | /// # Formatting |
13 | /// |
14 | /// The `Display` implementation of this type will only print the details of |
15 | /// this level of error, even though it may have been caused by another error |
16 | /// and contain that error in its source. To print all the relevant |
17 | /// information, including the source chain, using something like |
18 | /// `std::error::Report`, or equivalent 3rd party types. |
19 | /// |
20 | /// The contents of the formatted error message of this specific `Error` type |
21 | /// is unspecified. **You must not depend on it.** The wording and details may |
22 | /// change in any version, with the goal of improving error messages. |
23 | /// |
24 | /// # Source |
25 | /// |
26 | /// A `hyper::Error` may be caused by another error. To aid in debugging, |
27 | /// those are exposed in `Error::source()` as erased types. While it is |
28 | /// possible to check the exact type of the sources, they **can not be depended |
29 | /// on**. They may come from private internal dependencies, and are subject to |
30 | /// change at any moment. |
31 | pub struct Error { |
32 | inner: Box<ErrorImpl>, |
33 | } |
34 | |
35 | struct ErrorImpl { |
36 | kind: Kind, |
37 | cause: Option<Cause>, |
38 | } |
39 | |
40 | #[derive (Debug)] |
41 | pub(super) enum Kind { |
42 | Parse(Parse), |
43 | User(User), |
44 | /// A message reached EOF, but is not complete. |
45 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
46 | IncompleteMessage, |
47 | /// A connection received a message (or bytes) when not waiting for one. |
48 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
49 | UnexpectedMessage, |
50 | /// A pending item was dropped before ever being processed. |
51 | Canceled, |
52 | /// Indicates a channel (client or body sender) is closed. |
53 | #[cfg (any( |
54 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
55 | all(feature = "http2" , feature = "client" ) |
56 | ))] |
57 | ChannelClosed, |
58 | /// An `io::Error` that occurred while trying to read or write to a network stream. |
59 | #[cfg (all( |
60 | any(feature = "client" , feature = "server" ), |
61 | any(feature = "http1" , feature = "http2" ) |
62 | ))] |
63 | Io, |
64 | /// User took too long to send headers |
65 | #[cfg (all(feature = "http1" , feature = "server" ))] |
66 | HeaderTimeout, |
67 | /// Error while reading a body from connection. |
68 | #[cfg (all( |
69 | any(feature = "client" , feature = "server" ), |
70 | any(feature = "http1" , feature = "http2" ) |
71 | ))] |
72 | Body, |
73 | /// Error while writing a body to connection. |
74 | #[cfg (all( |
75 | any(feature = "client" , feature = "server" ), |
76 | any(feature = "http1" , feature = "http2" ) |
77 | ))] |
78 | BodyWrite, |
79 | /// Error calling AsyncWrite::shutdown() |
80 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
81 | Shutdown, |
82 | |
83 | /// A general error from h2. |
84 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http2" ))] |
85 | Http2, |
86 | } |
87 | |
88 | #[derive (Debug)] |
89 | pub(super) enum Parse { |
90 | Method, |
91 | #[cfg (feature = "http1" )] |
92 | Version, |
93 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
94 | VersionH2, |
95 | Uri, |
96 | #[cfg (all(feature = "http1" , feature = "server" ))] |
97 | UriTooLong, |
98 | #[cfg (feature = "http1" )] |
99 | Header(Header), |
100 | #[cfg (any(feature = "http1" , feature = "http2" ))] |
101 | #[cfg_attr (feature = "http2" , allow(unused))] |
102 | TooLarge, |
103 | Status, |
104 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
105 | Internal, |
106 | } |
107 | |
108 | #[derive (Debug)] |
109 | #[cfg (feature = "http1" )] |
110 | pub(super) enum Header { |
111 | Token, |
112 | #[cfg (any(feature = "client" , feature = "server" ))] |
113 | ContentLengthInvalid, |
114 | #[cfg (feature = "server" )] |
115 | TransferEncodingInvalid, |
116 | #[cfg (any(feature = "client" , feature = "server" ))] |
117 | TransferEncodingUnexpected, |
118 | } |
119 | |
120 | #[derive (Debug)] |
121 | pub(super) enum User { |
122 | /// Error calling user's Body::poll_data(). |
123 | #[cfg (all( |
124 | any(feature = "client" , feature = "server" ), |
125 | any(feature = "http1" , feature = "http2" ) |
126 | ))] |
127 | Body, |
128 | /// The user aborted writing of the outgoing body. |
129 | #[cfg (any( |
130 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
131 | feature = "ffi" |
132 | ))] |
133 | BodyWriteAborted, |
134 | /// Error from future of user's Service. |
135 | #[cfg (any( |
136 | all(any(feature = "client" , feature = "server" ), feature = "http1" ), |
137 | all(feature = "server" , feature = "http2" ) |
138 | ))] |
139 | Service, |
140 | /// User tried to send a certain header in an unexpected context. |
141 | /// |
142 | /// For example, sending both `content-length` and `transfer-encoding`. |
143 | #[cfg (any(feature = "http1" , feature = "http2" ))] |
144 | #[cfg (feature = "server" )] |
145 | UnexpectedHeader, |
146 | /// User tried to respond with a 1xx (not 101) response code. |
147 | #[cfg (feature = "http1" )] |
148 | #[cfg (feature = "server" )] |
149 | UnsupportedStatusCode, |
150 | |
151 | /// User tried polling for an upgrade that doesn't exist. |
152 | NoUpgrade, |
153 | |
154 | /// User polled for an upgrade, but low-level API is not using upgrades. |
155 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
156 | ManualUpgrade, |
157 | |
158 | /// The dispatch task is gone. |
159 | #[cfg (all(feature = "client" , any(feature = "http1" , feature = "http2" )))] |
160 | DispatchGone, |
161 | |
162 | /// User aborted in an FFI callback. |
163 | #[cfg (feature = "ffi" )] |
164 | AbortedByCallback, |
165 | } |
166 | |
167 | // Sentinel type to indicate the error was caused by a timeout. |
168 | #[derive (Debug)] |
169 | pub(super) struct TimedOut; |
170 | |
171 | impl Error { |
172 | /// Returns true if this was an HTTP parse error. |
173 | pub fn is_parse(&self) -> bool { |
174 | matches!(self.inner.kind, Kind::Parse(_)) |
175 | } |
176 | |
177 | /// Returns true if this was an HTTP parse error caused by a message that was too large. |
178 | #[cfg (all(feature = "http1" , feature = "server" ))] |
179 | pub fn is_parse_too_large(&self) -> bool { |
180 | matches!( |
181 | self.inner.kind, |
182 | Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong) |
183 | ) |
184 | } |
185 | |
186 | /// Returns true if this was an HTTP parse error caused by an invalid response status code or |
187 | /// reason phrase. |
188 | pub fn is_parse_status(&self) -> bool { |
189 | matches!(self.inner.kind, Kind::Parse(Parse::Status)) |
190 | } |
191 | |
192 | /// Returns true if this error was caused by user code. |
193 | pub fn is_user(&self) -> bool { |
194 | matches!(self.inner.kind, Kind::User(_)) |
195 | } |
196 | |
197 | /// Returns true if this was about a `Request` that was canceled. |
198 | pub fn is_canceled(&self) -> bool { |
199 | matches!(self.inner.kind, Kind::Canceled) |
200 | } |
201 | |
202 | /// Returns true if a sender's channel is closed. |
203 | pub fn is_closed(&self) -> bool { |
204 | #[cfg (not(any( |
205 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
206 | all(feature = "http2" , feature = "client" ) |
207 | )))] |
208 | return false; |
209 | |
210 | #[cfg (any( |
211 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
212 | all(feature = "http2" , feature = "client" ) |
213 | ))] |
214 | matches!(self.inner.kind, Kind::ChannelClosed) |
215 | } |
216 | |
217 | /// Returns true if the connection closed before a message could complete. |
218 | pub fn is_incomplete_message(&self) -> bool { |
219 | #[cfg (not(all(any(feature = "client" , feature = "server" ), feature = "http1" )))] |
220 | return false; |
221 | |
222 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
223 | matches!(self.inner.kind, Kind::IncompleteMessage) |
224 | } |
225 | |
226 | /// Returns true if the body write was aborted. |
227 | pub fn is_body_write_aborted(&self) -> bool { |
228 | #[cfg (not(any( |
229 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
230 | feature = "ffi" |
231 | )))] |
232 | return false; |
233 | |
234 | #[cfg (any( |
235 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
236 | feature = "ffi" |
237 | ))] |
238 | matches!(self.inner.kind, Kind::User(User::BodyWriteAborted)) |
239 | } |
240 | |
241 | /// Returns true if the error was caused by a timeout. |
242 | pub fn is_timeout(&self) -> bool { |
243 | #[cfg (all(feature = "http1" , feature = "server" ))] |
244 | if matches!(self.inner.kind, Kind::HeaderTimeout) { |
245 | return true; |
246 | } |
247 | self.find_source::<TimedOut>().is_some() |
248 | } |
249 | |
250 | pub(super) fn new(kind: Kind) -> Error { |
251 | Error { |
252 | inner: Box::new(ErrorImpl { kind, cause: None }), |
253 | } |
254 | } |
255 | |
256 | pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error { |
257 | self.inner.cause = Some(cause.into()); |
258 | self |
259 | } |
260 | |
261 | #[cfg (any(all(feature = "http1" , feature = "server" ), feature = "ffi" ))] |
262 | pub(super) fn kind(&self) -> &Kind { |
263 | &self.inner.kind |
264 | } |
265 | |
266 | pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> { |
267 | let mut cause = self.source(); |
268 | while let Some(err) = cause { |
269 | if let Some(typed) = err.downcast_ref() { |
270 | return Some(typed); |
271 | } |
272 | cause = err.source(); |
273 | } |
274 | |
275 | // else |
276 | None |
277 | } |
278 | |
279 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http2" ))] |
280 | pub(super) fn h2_reason(&self) -> h2::Reason { |
281 | // Find an h2::Reason somewhere in the cause stack, if it exists, |
282 | // otherwise assume an INTERNAL_ERROR. |
283 | self.find_source::<h2::Error>() |
284 | .and_then(|h2_err| h2_err.reason()) |
285 | .unwrap_or(h2::Reason::INTERNAL_ERROR) |
286 | } |
287 | |
288 | pub(super) fn new_canceled() -> Error { |
289 | Error::new(Kind::Canceled) |
290 | } |
291 | |
292 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
293 | pub(super) fn new_incomplete() -> Error { |
294 | Error::new(Kind::IncompleteMessage) |
295 | } |
296 | |
297 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
298 | pub(super) fn new_too_large() -> Error { |
299 | Error::new(Kind::Parse(Parse::TooLarge)) |
300 | } |
301 | |
302 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
303 | pub(super) fn new_version_h2() -> Error { |
304 | Error::new(Kind::Parse(Parse::VersionH2)) |
305 | } |
306 | |
307 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
308 | pub(super) fn new_unexpected_message() -> Error { |
309 | Error::new(Kind::UnexpectedMessage) |
310 | } |
311 | |
312 | #[cfg (all( |
313 | any(feature = "client" , feature = "server" ), |
314 | any(feature = "http1" , feature = "http2" ) |
315 | ))] |
316 | pub(super) fn new_io(cause: std::io::Error) -> Error { |
317 | Error::new(Kind::Io).with(cause) |
318 | } |
319 | |
320 | #[cfg (any( |
321 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
322 | all(feature = "http2" , feature = "client" ) |
323 | ))] |
324 | pub(super) fn new_closed() -> Error { |
325 | Error::new(Kind::ChannelClosed) |
326 | } |
327 | |
328 | #[cfg (all( |
329 | any(feature = "client" , feature = "server" ), |
330 | any(feature = "http1" , feature = "http2" ) |
331 | ))] |
332 | pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error { |
333 | Error::new(Kind::Body).with(cause) |
334 | } |
335 | |
336 | #[cfg (all( |
337 | any(feature = "client" , feature = "server" ), |
338 | any(feature = "http1" , feature = "http2" ) |
339 | ))] |
340 | pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error { |
341 | Error::new(Kind::BodyWrite).with(cause) |
342 | } |
343 | |
344 | #[cfg (any( |
345 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
346 | feature = "ffi" |
347 | ))] |
348 | pub(super) fn new_body_write_aborted() -> Error { |
349 | Error::new(Kind::User(User::BodyWriteAborted)) |
350 | } |
351 | |
352 | fn new_user(user: User) -> Error { |
353 | Error::new(Kind::User(user)) |
354 | } |
355 | |
356 | #[cfg (any(feature = "http1" , feature = "http2" ))] |
357 | #[cfg (feature = "server" )] |
358 | pub(super) fn new_user_header() -> Error { |
359 | Error::new_user(User::UnexpectedHeader) |
360 | } |
361 | |
362 | #[cfg (all(feature = "http1" , feature = "server" ))] |
363 | pub(super) fn new_header_timeout() -> Error { |
364 | Error::new(Kind::HeaderTimeout) |
365 | } |
366 | |
367 | #[cfg (feature = "http1" )] |
368 | #[cfg (feature = "server" )] |
369 | pub(super) fn new_user_unsupported_status_code() -> Error { |
370 | Error::new_user(User::UnsupportedStatusCode) |
371 | } |
372 | |
373 | pub(super) fn new_user_no_upgrade() -> Error { |
374 | Error::new_user(User::NoUpgrade) |
375 | } |
376 | |
377 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
378 | pub(super) fn new_user_manual_upgrade() -> Error { |
379 | Error::new_user(User::ManualUpgrade) |
380 | } |
381 | |
382 | #[cfg (any( |
383 | all(any(feature = "client" , feature = "server" ), feature = "http1" ), |
384 | all(feature = "server" , feature = "http2" ) |
385 | ))] |
386 | pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error { |
387 | Error::new_user(User::Service).with(cause) |
388 | } |
389 | |
390 | #[cfg (all( |
391 | any(feature = "client" , feature = "server" ), |
392 | any(feature = "http1" , feature = "http2" ) |
393 | ))] |
394 | pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error { |
395 | Error::new_user(User::Body).with(cause) |
396 | } |
397 | |
398 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
399 | pub(super) fn new_shutdown(cause: std::io::Error) -> Error { |
400 | Error::new(Kind::Shutdown).with(cause) |
401 | } |
402 | |
403 | #[cfg (feature = "ffi" )] |
404 | pub(super) fn new_user_aborted_by_callback() -> Error { |
405 | Error::new_user(User::AbortedByCallback) |
406 | } |
407 | |
408 | #[cfg (all(feature = "client" , any(feature = "http1" , feature = "http2" )))] |
409 | pub(super) fn new_user_dispatch_gone() -> Error { |
410 | Error::new(Kind::User(User::DispatchGone)) |
411 | } |
412 | |
413 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http2" ))] |
414 | pub(super) fn new_h2(cause: ::h2::Error) -> Error { |
415 | if cause.is_io() { |
416 | Error::new_io(cause.into_io().expect("h2::Error::is_io" )) |
417 | } else { |
418 | Error::new(Kind::Http2).with(cause) |
419 | } |
420 | } |
421 | |
422 | fn description(&self) -> &str { |
423 | match self.inner.kind { |
424 | Kind::Parse(Parse::Method) => "invalid HTTP method parsed" , |
425 | #[cfg (feature = "http1" )] |
426 | Kind::Parse(Parse::Version) => "invalid HTTP version parsed" , |
427 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
428 | Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)" , |
429 | Kind::Parse(Parse::Uri) => "invalid URI" , |
430 | #[cfg (all(feature = "http1" , feature = "server" ))] |
431 | Kind::Parse(Parse::UriTooLong) => "URI too long" , |
432 | #[cfg (feature = "http1" )] |
433 | Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed" , |
434 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
435 | Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => { |
436 | "invalid content-length parsed" |
437 | } |
438 | #[cfg (all(feature = "http1" , feature = "server" ))] |
439 | Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => { |
440 | "invalid transfer-encoding parsed" |
441 | } |
442 | #[cfg (all(feature = "http1" , any(feature = "client" , feature = "server" )))] |
443 | Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => { |
444 | "unexpected transfer-encoding parsed" |
445 | } |
446 | #[cfg (any(feature = "http1" , feature = "http2" ))] |
447 | Kind::Parse(Parse::TooLarge) => "message head is too large" , |
448 | Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed" , |
449 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
450 | Kind::Parse(Parse::Internal) => { |
451 | "internal error inside Hyper and/or its dependencies, please report" |
452 | } |
453 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
454 | Kind::IncompleteMessage => "connection closed before message completed" , |
455 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
456 | Kind::UnexpectedMessage => "received unexpected message from connection" , |
457 | #[cfg (any( |
458 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
459 | all(feature = "http2" , feature = "client" ) |
460 | ))] |
461 | Kind::ChannelClosed => "channel closed" , |
462 | Kind::Canceled => "operation was canceled" , |
463 | #[cfg (all(feature = "http1" , feature = "server" ))] |
464 | Kind::HeaderTimeout => "read header from client timeout" , |
465 | #[cfg (all( |
466 | any(feature = "client" , feature = "server" ), |
467 | any(feature = "http1" , feature = "http2" ) |
468 | ))] |
469 | Kind::Body => "error reading a body from connection" , |
470 | #[cfg (all( |
471 | any(feature = "client" , feature = "server" ), |
472 | any(feature = "http1" , feature = "http2" ) |
473 | ))] |
474 | Kind::BodyWrite => "error writing a body to connection" , |
475 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
476 | Kind::Shutdown => "error shutting down connection" , |
477 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http2" ))] |
478 | Kind::Http2 => "http2 error" , |
479 | #[cfg (all( |
480 | any(feature = "client" , feature = "server" ), |
481 | any(feature = "http1" , feature = "http2" ) |
482 | ))] |
483 | Kind::Io => "connection error" , |
484 | |
485 | #[cfg (all( |
486 | any(feature = "client" , feature = "server" ), |
487 | any(feature = "http1" , feature = "http2" ) |
488 | ))] |
489 | Kind::User(User::Body) => "error from user's Body stream" , |
490 | #[cfg (any( |
491 | all(feature = "http1" , any(feature = "client" , feature = "server" )), |
492 | feature = "ffi" |
493 | ))] |
494 | Kind::User(User::BodyWriteAborted) => "user body write aborted" , |
495 | #[cfg (any( |
496 | all(any(feature = "client" , feature = "server" ), feature = "http1" ), |
497 | all(feature = "server" , feature = "http2" ) |
498 | ))] |
499 | Kind::User(User::Service) => "error from user's Service" , |
500 | #[cfg (any(feature = "http1" , feature = "http2" ))] |
501 | #[cfg (feature = "server" )] |
502 | Kind::User(User::UnexpectedHeader) => "user sent unexpected header" , |
503 | #[cfg (feature = "http1" )] |
504 | #[cfg (feature = "server" )] |
505 | Kind::User(User::UnsupportedStatusCode) => { |
506 | "response has 1xx status code, not supported by server" |
507 | } |
508 | Kind::User(User::NoUpgrade) => "no upgrade available" , |
509 | #[cfg (all(any(feature = "client" , feature = "server" ), feature = "http1" ))] |
510 | Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use" , |
511 | #[cfg (all(feature = "client" , any(feature = "http1" , feature = "http2" )))] |
512 | Kind::User(User::DispatchGone) => "dispatch task is gone" , |
513 | #[cfg (feature = "ffi" )] |
514 | Kind::User(User::AbortedByCallback) => "operation aborted by an application callback" , |
515 | } |
516 | } |
517 | } |
518 | |
519 | impl fmt::Debug for Error { |
520 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
521 | let mut f: DebugTuple<'_, '_> = f.debug_tuple(name:"hyper::Error" ); |
522 | f.field(&self.inner.kind); |
523 | if let Some(ref cause: &Box) = self.inner.cause { |
524 | f.field(cause); |
525 | } |
526 | f.finish() |
527 | } |
528 | } |
529 | |
530 | impl fmt::Display for Error { |
531 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
532 | f.write_str(self.description()) |
533 | } |
534 | } |
535 | |
536 | impl StdError for Error { |
537 | fn source(&self) -> Option<&(dyn StdError + 'static)> { |
538 | self.inner |
539 | .cause |
540 | .as_ref() |
541 | .map(|cause: &Box| &**cause as &(dyn StdError + 'static)) |
542 | } |
543 | } |
544 | |
545 | #[doc (hidden)] |
546 | impl From<Parse> for Error { |
547 | fn from(err: Parse) -> Error { |
548 | Error::new(Kind::Parse(err)) |
549 | } |
550 | } |
551 | |
552 | #[cfg (feature = "http1" )] |
553 | impl Parse { |
554 | #[cfg (any(feature = "client" , feature = "server" ))] |
555 | pub(crate) fn content_length_invalid() -> Self { |
556 | Parse::Header(Header::ContentLengthInvalid) |
557 | } |
558 | |
559 | #[cfg (feature = "server" )] |
560 | pub(crate) fn transfer_encoding_invalid() -> Self { |
561 | Parse::Header(Header::TransferEncodingInvalid) |
562 | } |
563 | |
564 | #[cfg (any(feature = "client" , feature = "server" ))] |
565 | pub(crate) fn transfer_encoding_unexpected() -> Self { |
566 | Parse::Header(Header::TransferEncodingUnexpected) |
567 | } |
568 | } |
569 | |
570 | #[cfg (feature = "http1" )] |
571 | impl From<httparse::Error> for Parse { |
572 | fn from(err: httparse::Error) -> Parse { |
573 | match err { |
574 | httparse::Error::HeaderName |
575 | | httparse::Error::HeaderValue |
576 | | httparse::Error::NewLine |
577 | | httparse::Error::Token => Parse::Header(Header::Token), |
578 | httparse::Error::Status => Parse::Status, |
579 | httparse::Error::TooManyHeaders => Parse::TooLarge, |
580 | httparse::Error::Version => Parse::Version, |
581 | } |
582 | } |
583 | } |
584 | |
585 | impl From<http::method::InvalidMethod> for Parse { |
586 | fn from(_: http::method::InvalidMethod) -> Parse { |
587 | Parse::Method |
588 | } |
589 | } |
590 | |
591 | impl From<http::status::InvalidStatusCode> for Parse { |
592 | fn from(_: http::status::InvalidStatusCode) -> Parse { |
593 | Parse::Status |
594 | } |
595 | } |
596 | |
597 | impl From<http::uri::InvalidUri> for Parse { |
598 | fn from(_: http::uri::InvalidUri) -> Parse { |
599 | Parse::Uri |
600 | } |
601 | } |
602 | |
603 | impl From<http::uri::InvalidUriParts> for Parse { |
604 | fn from(_: http::uri::InvalidUriParts) -> Parse { |
605 | Parse::Uri |
606 | } |
607 | } |
608 | |
609 | // ===== impl TimedOut ==== |
610 | |
611 | impl fmt::Display for TimedOut { |
612 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
613 | f.write_str(data:"operation timed out" ) |
614 | } |
615 | } |
616 | |
617 | impl StdError for TimedOut {} |
618 | |
619 | #[cfg (test)] |
620 | mod tests { |
621 | use super::*; |
622 | use std::mem; |
623 | |
624 | fn assert_send_sync<T: Send + Sync + 'static>() {} |
625 | |
626 | #[test ] |
627 | fn error_satisfies_send_sync() { |
628 | assert_send_sync::<Error>() |
629 | } |
630 | |
631 | #[test ] |
632 | fn error_size_of() { |
633 | assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>()); |
634 | } |
635 | |
636 | #[cfg (feature = "http2" )] |
637 | #[test ] |
638 | fn h2_reason_unknown() { |
639 | let closed = Error::new_closed(); |
640 | assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR); |
641 | } |
642 | |
643 | #[cfg (feature = "http2" )] |
644 | #[test ] |
645 | fn h2_reason_one_level() { |
646 | let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM)); |
647 | assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM); |
648 | } |
649 | |
650 | #[cfg (feature = "http2" )] |
651 | #[test ] |
652 | fn h2_reason_nested() { |
653 | let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED)); |
654 | // Suppose a user were proxying the received error |
655 | let svc_err = Error::new_user_service(recvd); |
656 | assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED); |
657 | } |
658 | } |
659 | |