Skip to main content

trillium_http/
error.rs

1use crate::{HeaderName, Status, Version};
2use std::{num::TryFromIntError, str::Utf8Error, time::Duration};
3use thiserror::Error;
4
5/// Concrete errors that occur within trillium's HTTP implementation
6#[derive(Error, Debug)]
7#[non_exhaustive]
8pub enum Error {
9    /// [`std::io::Error`]
10    #[error(transparent)]
11    Io(#[from] std::io::Error),
12
13    /// this error describes a malformed request with a path that does
14    /// not start with / or http:// or https://
15    #[error("Unexpected uri format")]
16    UnexpectedUriFormat,
17
18    /// the relevant HTTP protocol expected this header, but it was
19    /// not provided
20    #[error("Mandatory {0} header missing")]
21    HeaderMissing(HeaderName<'static>),
22
23    /// this error describes a request that does not specify a path
24    #[error("Request path missing")]
25    RequestPathMissing,
26
27    /// connection was closed
28    #[error("Connection closed by client")]
29    Closed,
30
31    /// [`TryFromIntError`]
32    #[error(transparent)]
33    TryFromIntError(#[from] TryFromIntError),
34
35    /// An incomplete or invalid HTTP head
36    #[error("Partial or invalid HTTP head")]
37    InvalidHead,
38
39    /// We were unable to parse a [`HeaderName`][crate::HeaderName]
40    #[error("Invalid or unparseable header name")]
41    InvalidHeaderName,
42
43    /// We were unable to parse a [`HeaderValue`][crate::HeaderValue]
44    #[error("Invalid or unparseable header value, header name: {0}")]
45    InvalidHeaderValue(HeaderName<'static>),
46
47    /// we were able to parse this [`Version`], but we do not support it
48    #[error("Unsupported version {0}")]
49    UnsupportedVersion(Version),
50
51    /// A request named a well-formed HTTP version that this implementation doesn't speak, such as
52    /// `HTTP/2.0` or `HTTP/0.9` in an HTTP/1.x request-line.
53    #[error("Unsupported HTTP version {major}.{minor}")]
54    UnsupportedVersionNumber {
55        /// The major version digit
56        major: u8,
57        /// The minor version digit
58        minor: u8,
59    },
60
61    /// We were unable to parse a [`Version`]
62    #[error("Invalid or missing version")]
63    InvalidVersion,
64
65    /// we were unable to parse this method
66    #[error("Unsupported method {0}")]
67    UnrecognizedMethod(String),
68
69    /// this request did not have a method
70    #[error("Missing method")]
71    MissingMethod,
72
73    /// the request carried an `Expect` value that we do not handle
74    #[error("Unsupported expectation")]
75    ExpectationFailed,
76
77    /// this request did not have a status code
78    #[error("Missing status code")]
79    MissingStatus,
80
81    /// we were unable to parse a [`Status`](crate::Status)
82    #[error("Invalid status code")]
83    InvalidStatus,
84
85    /// we expected utf8, but there was an encoding error
86    #[error(transparent)]
87    EncodingError(#[from] Utf8Error),
88
89    /// we either received a header that does not make sense in context
90    #[error("Unexpected header: {0}")]
91    UnexpectedHeader(HeaderName<'static>),
92
93    /// to mitigate against malicious HTTP clients, we do not allow request headers beyond this
94    /// length.
95    #[error("Headers were malformed or longer than allowed")]
96    HeadersTooLong,
97
98    /// to mitigate against malicious HTTP clients, we do not read received bodies beyond this
99    /// length to memory. If you need to receive longer bodies, use the Stream or `AsyncRead`
100    /// implementation on `ReceivedBody`
101    #[error("Received body too long. Maximum {0} bytes")]
102    ReceivedBodyTooLong(u64),
103
104    /// something took longer than was allowed
105    #[error("{0} took longer than {1:?}")]
106    TimedOut(&'static str, Duration),
107
108    /// HTTP/2 peer has not advertised `SETTINGS_ENABLE_CONNECT_PROTOCOL = 1`, so an
109    /// extended-CONNECT request cannot be sent on this connection.
110    #[error("HTTP/2 peer does not support extended CONNECT")]
111    ExtendedConnectUnsupported,
112
113    /// An error from middleware or other application-level code, type-erased into a boxed
114    /// error so it can flow through the protocol error type.
115    ///
116    /// Use `error.downcast_ref::<MyError>()` to recover the concrete type — for example,
117    /// `error.downcast_ref::<trillium_redirect::client::RedirectError>()` to inspect a
118    /// follow-redirects failure.
119    #[error(transparent)]
120    Other(Box<dyn std::error::Error + Send + Sync>),
121}
122
123impl From<&Error> for Status {
124    fn from(error: &Error) -> Self {
125        match error {
126            Error::UnrecognizedMethod(_) => Status::NotImplemented,
127            Error::ExpectationFailed => Status::ExpectationFailed,
128            Error::HeadersTooLong => Status::RequestHeaderFieldsTooLarge,
129            Error::UnsupportedVersion(_) | Error::UnsupportedVersionNumber { .. } => {
130                Status::HttpVersionNotSupported
131            }
132            Error::Other(_) => Status::InternalServerError,
133            _ => Status::BadRequest,
134        }
135    }
136}
137
138/// this crate's result type
139pub type Result<T, E = Error> = std::result::Result<T, E>;
140
141impl Error {
142    /// Construct an Other variant
143    pub fn other(error: impl std::error::Error + Send + Sync + 'static) -> Self {
144        Self::Other(Box::new(error))
145    }
146}