ts_http_util/error.rs
1use std::error::Error as StdError;
2
3/// General categories of error that can occur during any phase of an HTTP connection.
4#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
5pub enum Error {
6 /// A function argument or field value wasn't populated, or contained an invalid value, or user-
7 /// supplied code caused an error.
8 #[error("invalid parameter or other input")]
9 InvalidInput,
10
11 /// An underlying I/O error occurred that prevented a connection from being established, a
12 /// request from being sent, or a response from being read.
13 #[error("i/o error encountered")]
14 Io,
15
16 /// A timeout expired while waiting for the server to respond, or the client (us) didn't send
17 /// request headers within the timeframe the server expected.
18 #[error("timed out")]
19 Timeout,
20
21 /// The connection is no longer usable for some (probably unexpected) reason.
22 #[error("an error occurred and the connection must be re-established before retrying")]
23 ConnectionClosed,
24
25 /// An invalid status code or HTTP 2 message where HTTP 1 was expected.
26 #[error("received a response which was invalid in some way")]
27 InvalidResponse,
28
29 /// The response body exceeded the caller's size limit (see
30 /// [`ResponseExt::collect_bytes_limited`](crate::ResponseExt::collect_bytes_limited)).
31 ///
32 /// Distinct from [`Io`](Self::Io) so a caller can tell "the peer streamed an over-cap body" (an
33 /// attack/misconfiguration signal — terminal, not worth retrying) apart from a transient I/O
34 /// failure mid-read.
35 #[error("response body exceeded the size limit")]
36 BodyTooLarge,
37}
38
39impl From<hyper::Error> for Error {
40 fn from(e: hyper::Error) -> Self {
41 if io_error(&e) {
42 Error::Io
43 } else if e.is_timeout() {
44 Error::Timeout
45 } else if e.is_parse() || e.is_user() {
46 Error::InvalidInput
47 } else if e.is_parse_status() || e.is_parse_version_h2() {
48 Error::InvalidResponse
49 } else {
50 // A problem with the connection, or something occurred where we should do some kind of
51 // reset before retrying.
52 // e.is_canceled() || e.is_shutdown() || e.is_body_write_aborted() || e.is_closed() || e.is_incomplete_message
53 Error::ConnectionClosed
54 }
55 }
56}
57
58fn io_error(e: &hyper::Error) -> bool {
59 let mut e = e as &dyn StdError;
60 loop {
61 match e.source() {
62 None => return false,
63 Some(source) => {
64 let io = source.downcast_ref::<std::io::Error>();
65 if io.is_some() {
66 return true;
67 }
68 e = source;
69 }
70 }
71 }
72}