1use std::convert::Infallible;
2
3use crate::client::ExclusiveBody;
4
5pub enum BadRequestReason {
6 MissingHeader(&'static str),
7 InvalidHeader(&'static str),
8 NoPathOrQuery,
9 NoHostName,
10 UnsupportedUrlScheme,
11}
12
13pub type FaucetResult<T> = std::result::Result<T, FaucetError>;
14
15pub enum FaucetError {
16 PoolBuild(deadpool::managed::BuildError),
17 PoolTimeout(deadpool::managed::TimeoutType),
18 PoolPostCreateHook,
19 PoolClosed,
20 PoolNoRuntimeSpecified,
21 NoSocketsAvailable,
22 ConnectionClosed,
23 Io(std::io::Error),
24 Unknown(String),
25 HostParseError(std::net::AddrParseError),
26 Hyper(hyper::Error),
27 BadRequest(BadRequestReason),
28 InvalidHeaderValues(hyper::header::InvalidHeaderValue),
29 Http(hyper::http::Error),
30 MissingArgument(&'static str),
31 DuplicateRoute(String),
32 Utf8Coding,
33 BufferCapacity(tokio_tungstenite::tungstenite::error::CapacityError),
34 ProtocolViolation(tokio_tungstenite::tungstenite::error::ProtocolError),
35 WSWriteBufferFull(tokio_tungstenite::tungstenite::Message),
36 PostgreSQL(tokio_postgres::Error),
37 AttackAttempt,
38}
39
40impl From<tokio_postgres::Error> for FaucetError {
41 fn from(value: tokio_postgres::Error) -> Self {
42 Self::PostgreSQL(value)
43 }
44}
45
46impl From<tokio_tungstenite::tungstenite::Error> for FaucetError {
47 fn from(value: tokio_tungstenite::tungstenite::Error) -> Self {
48 use tokio_tungstenite::tungstenite::error::UrlError;
49 use tokio_tungstenite::tungstenite::Error;
50 match value {
51 Error::Io(err) => FaucetError::Io(err),
52 Error::Url(err) => match err {
53 UrlError::NoPathOrQuery => FaucetError::BadRequest(BadRequestReason::NoPathOrQuery),
54 UrlError::NoHostName | UrlError::EmptyHostName => {
55 FaucetError::BadRequest(BadRequestReason::NoHostName)
56 }
57 UrlError::TlsFeatureNotEnabled => panic!("TLS Not enabled"),
58 UrlError::UnableToConnect(err) => FaucetError::Unknown(err),
59 UrlError::UnsupportedUrlScheme => {
60 FaucetError::BadRequest(BadRequestReason::UnsupportedUrlScheme)
61 }
62 },
63 Error::Tls(err) => FaucetError::Unknown(err.to_string()),
64 Error::Utf8 => FaucetError::Utf8Coding,
65 Error::Http(_) => FaucetError::Unknown("Unknown HTTP error".to_string()),
66 Error::Capacity(err) => FaucetError::BufferCapacity(err),
67 Error::HttpFormat(err) => FaucetError::Http(err),
68 Error::Protocol(err) => FaucetError::ProtocolViolation(err),
69 Error::AlreadyClosed | Error::ConnectionClosed => FaucetError::ConnectionClosed,
70 Error::AttackAttempt => FaucetError::AttackAttempt,
71 Error::WriteBufferFull(msg) => FaucetError::WSWriteBufferFull(msg),
72 }
73 }
74}
75
76impl From<hyper::header::InvalidHeaderValue> for FaucetError {
77 fn from(e: hyper::header::InvalidHeaderValue) -> Self {
78 Self::InvalidHeaderValues(e)
79 }
80}
81
82impl From<hyper::http::Error> for FaucetError {
83 fn from(e: hyper::http::Error) -> Self {
84 Self::Http(e)
85 }
86}
87
88impl From<deadpool::managed::PoolError<FaucetError>> for FaucetError {
89 fn from(value: deadpool::managed::PoolError<FaucetError>) -> Self {
90 match value {
91 deadpool::managed::PoolError::Backend(e) => e,
92 deadpool::managed::PoolError::Timeout(e) => Self::PoolTimeout(e),
93 deadpool::managed::PoolError::Closed => Self::PoolClosed,
94 deadpool::managed::PoolError::PostCreateHook(_) => Self::PoolPostCreateHook,
95 deadpool::managed::PoolError::NoRuntimeSpecified => Self::PoolNoRuntimeSpecified,
96 }
97 }
98}
99
100impl From<Infallible> for FaucetError {
101 fn from(_: Infallible) -> Self {
102 unreachable!("Infallible error")
103 }
104}
105
106impl From<deadpool::managed::BuildError> for FaucetError {
107 fn from(e: deadpool::managed::BuildError) -> Self {
108 Self::PoolBuild(e)
109 }
110}
111
112impl From<std::io::Error> for FaucetError {
113 fn from(e: std::io::Error) -> Self {
114 Self::Io(e)
115 }
116}
117
118impl From<std::net::AddrParseError> for FaucetError {
119 fn from(e: std::net::AddrParseError) -> Self {
120 Self::HostParseError(e)
121 }
122}
123
124impl From<hyper::Error> for FaucetError {
125 fn from(e: hyper::Error) -> Self {
126 Self::Hyper(e)
127 }
128}
129
130impl std::fmt::Display for FaucetError {
131 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
132 match self {
133 Self::PoolBuild(e) => write!(f, "Pool build error: {}", e),
134 Self::PoolTimeout(e) => write!(f, "Pool timeout error: {:?}", e),
135 Self::PoolPostCreateHook => write!(f, "Pool post create hook error"),
136 Self::PoolClosed => write!(f, "Pool closed error"),
137 Self::PoolNoRuntimeSpecified => write!(f, "Pool no runtime specified error"),
138 Self::Io(e) => write!(f, "IO error: {}", e),
139 Self::Unknown(e) => write!(f, "Unknown error: {}", e),
140 Self::HostParseError(e) => write!(f, "Error parsing host address: {}", e),
141 Self::Hyper(e) => write!(f, "Hyper error: {}", e),
142 Self::Http(e) => write!(f, "Http error: {}", e),
143 Self::InvalidHeaderValues(e) => write!(f, "Invalid header values: {}", e),
144 Self::MissingArgument(s) => write!(f, "Missing argument: {}", s),
145 Self::DuplicateRoute(route) => write!(f, "Route '{route}' is duplicated"),
146 Self::AttackAttempt => write!(f, "Attack attempt detected"),
147 Self::ConnectionClosed => write!(f, "Connection closed"),
148 Self::ProtocolViolation(e) => write!(f, "Protocol violation: {e}"),
149 Self::Utf8Coding => write!(f, "Utf8 Coding error"),
150 Self::BufferCapacity(cap_err) => write!(f, "Buffer Capacity: {cap_err}"),
151 Self::WSWriteBufferFull(buf) => write!(f, "Web Socket Write buffer full, {buf}"),
152 Self::PostgreSQL(value) => write!(f, "PostgreSQL error: {value}"),
153 Self::BadRequest(r) => match r {
154 BadRequestReason::UnsupportedUrlScheme => {
155 write!(f, "UnsupportedUrlScheme use ws:// os wss://")
156 }
157 BadRequestReason::NoHostName => write!(f, "No Host Name"),
158 BadRequestReason::MissingHeader(header) => {
159 write!(f, "Missing header: {}", header)
160 }
161 BadRequestReason::InvalidHeader(header) => {
162 write!(f, "Invalid header: {}", header)
163 }
164 BadRequestReason::NoPathOrQuery => write!(f, "No path and/or query"),
165 },
166 Self::NoSocketsAvailable => write!(f, "No sockets available"),
167 }
168 }
169}
170
171impl std::fmt::Debug for FaucetError {
172 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
173 write!(f, "{}", self)
174 }
175}
176
177impl std::error::Error for FaucetError {}
178
179impl FaucetError {
180 pub fn no_sec_web_socket_key() -> Self {
181 Self::BadRequest(BadRequestReason::MissingHeader("Sec-WebSocket-Key"))
182 }
183 pub fn unknown(s: impl ToString) -> Self {
184 Self::Unknown(s.to_string())
185 }
186}
187
188impl From<FaucetError> for hyper::Response<ExclusiveBody> {
189 fn from(val: FaucetError) -> Self {
190 let mut resp = hyper::Response::new(ExclusiveBody::plain_text(val.to_string()));
191 *resp.status_mut() = hyper::StatusCode::INTERNAL_SERVER_ERROR;
192 resp
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_faucet_error() {
202 let err = FaucetError::unknown("test");
203 assert_eq!(err.to_string(), "Unknown error: test");
204 }
205
206 #[test]
207 fn test_faucet_error_debug() {
208 let err = FaucetError::unknown("test");
209 assert_eq!(format!("{:?}", err), r#"Unknown error: test"#);
210 }
211
212 #[test]
213 fn test_faucet_error_from_hyper_error() {
214 let err = hyper::Request::builder()
215 .uri("INVALID URI")
216 .body(())
217 .unwrap_err();
218
219 let _err: FaucetError = From::from(err);
220 }
221
222 #[test]
223 fn test_faucet_error_from_io_error() {
224 let err = std::io::Error::new(std::io::ErrorKind::Other, "test");
225
226 let _err: FaucetError = From::from(err);
227 }
228
229 #[test]
230 fn test_faucet_error_from_pool_error() {
231 let err = deadpool::managed::PoolError::Backend(FaucetError::unknown("test"));
232
233 let _err: FaucetError = From::from(err);
234 }
235
236 #[test]
237 fn test_faucet_error_from_pool_build_error() {
238 let err = deadpool::managed::BuildError::NoRuntimeSpecified;
239
240 let _err: FaucetError = From::from(err);
241 }
242
243 #[test]
244 fn test_faucet_error_from_pool_timeout_error() {
245 let err = deadpool::managed::PoolError::<FaucetError>::Timeout(
246 deadpool::managed::TimeoutType::Create,
247 );
248
249 let _err: FaucetError = From::from(err);
250 }
251
252 #[test]
253 fn test_faucet_error_from_pool_closed_error() {
254 let err = deadpool::managed::PoolError::<FaucetError>::Closed;
255
256 let _err: FaucetError = From::from(err);
257 }
258
259 #[test]
260 fn test_faucet_error_from_pool_post_create_hook_error() {
261 let err = deadpool::managed::PoolError::<FaucetError>::PostCreateHook(
262 deadpool::managed::HookError::message("test"),
263 );
264
265 let _err: FaucetError = From::from(err);
266 }
267
268 #[test]
269 fn test_faucet_error_from_pool_no_runtime_specified_error() {
270 let err = deadpool::managed::PoolError::<FaucetError>::NoRuntimeSpecified;
271
272 let _err: FaucetError = From::from(err);
273 }
274
275 #[test]
276 fn test_faucet_error_from_hyper_invalid_header_value_error() {
277 let err = hyper::header::HeaderValue::from_bytes([0x00].as_ref()).unwrap_err();
278
279 let _err: FaucetError = From::from(err);
280 }
281
282 #[test]
283 fn test_faucet_error_from_addr_parse_error() {
284 let err = "INVALID".parse::<std::net::SocketAddr>().unwrap_err();
285
286 let _err: FaucetError = From::from(err);
287 }
288
289 #[test]
290 fn test_faucet_error_displat_missing_header() {
291 let _err = FaucetError::BadRequest(BadRequestReason::MissingHeader("test"));
292 }
293
294 #[test]
295 fn test_faucet_error_displat_invalid_header() {
296 let _err = FaucetError::BadRequest(BadRequestReason::InvalidHeader("test"));
297 }
298
299 #[test]
300 fn test_from_fauct_error_to_hyper_response() {
301 let err = FaucetError::unknown("test");
302 let resp: hyper::Response<ExclusiveBody> = err.into();
303 assert_eq!(resp.status(), hyper::StatusCode::INTERNAL_SERVER_ERROR);
304 }
305}