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}