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