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    /// We were unable to parse a [`Version`]
52    #[error("Invalid or missing version")]
53    InvalidVersion,
54
55    /// we were unable to parse this method
56    #[error("Unsupported method {0}")]
57    UnrecognizedMethod(String),
58
59    /// this request did not have a method
60    #[error("Missing method")]
61    MissingMethod,
62
63    /// the request carried an `Expect` value that we do not handle
64    #[error("Unsupported expectation")]
65    ExpectationFailed,
66
67    /// this request did not have a status code
68    #[error("Missing status code")]
69    MissingStatus,
70
71    /// we were unable to parse a [`Status`](crate::Status)
72    #[error("Invalid status code")]
73    InvalidStatus,
74
75    /// we expected utf8, but there was an encoding error
76    #[error(transparent)]
77    EncodingError(#[from] Utf8Error),
78
79    /// we either received a header that does not make sense in context
80    #[error("Unexpected header: {0}")]
81    UnexpectedHeader(HeaderName<'static>),
82
83    /// to mitigate against malicious HTTP clients, we do not allow request headers beyond this
84    /// length.
85    #[error("Headers were malformed or longer than allowed")]
86    HeadersTooLong,
87
88    /// to mitigate against malicious HTTP clients, we do not read received bodies beyond this
89    /// length to memory. If you need to receive longer bodies, use the Stream or `AsyncRead`
90    /// implementation on `ReceivedBody`
91    #[error("Received body too long. Maximum {0} bytes")]
92    ReceivedBodyTooLong(u64),
93
94    /// something took longer than was allowed
95    #[error("{0} took longer than {1:?}")]
96    TimedOut(&'static str, Duration),
97
98    /// HTTP/2 peer has not advertised `SETTINGS_ENABLE_CONNECT_PROTOCOL = 1`, so an
99    /// extended-CONNECT request cannot be sent on this connection.
100    #[error("HTTP/2 peer does not support extended CONNECT")]
101    ExtendedConnectUnsupported,
102
103    /// An error from middleware or other application-level code, type-erased into a boxed
104    /// error so it can flow through the protocol error type.
105    ///
106    /// Use `error.downcast_ref::<MyError>()` to recover the concrete type — for example,
107    /// `error.downcast_ref::<trillium_redirect::client::RedirectError>()` to inspect a
108    /// follow-redirects failure.
109    #[error(transparent)]
110    Other(Box<dyn std::error::Error + Send + Sync>),
111}
112
113impl From<&Error> for Status {
114    fn from(error: &Error) -> Self {
115        match error {
116            Error::UnrecognizedMethod(_) => Status::NotImplemented,
117            Error::ExpectationFailed => Status::ExpectationFailed,
118            Error::Other(_) => Status::InternalServerError,
119            _ => Status::BadRequest,
120        }
121    }
122}
123
124/// this crate's result type
125pub type Result<T, E = Error> = std::result::Result<T, E>;
126
127impl Error {
128    /// Construct an Other variant
129    pub fn other(error: impl std::error::Error + Send + Sync + 'static) -> Self {
130        Self::Other(Box::new(error))
131    }
132}