Skip to main content

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}