1use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, WiseGateError>;
10
11#[derive(Debug, Error)]
29pub enum WiseGateError {
30 #[error("Invalid IP address: {0}")]
32 InvalidIp(String),
33
34 #[error("Configuration error: {0}")]
36 ConfigError(String),
37
38 #[error("Proxy error: {0}")]
40 ProxyError(String),
41
42 #[error("Rate limit exceeded for IP: {0}")]
44 RateLimitExceeded(String),
45
46 #[error("IP blocked: {0}")]
48 IpBlocked(String),
49
50 #[error("URL pattern blocked: {0}")]
52 PatternBlocked(String),
53
54 #[error("HTTP method blocked: {0}")]
56 MethodBlocked(String),
57
58 #[error("Upstream connection failed: {0}")]
60 UpstreamConnectionFailed(String),
61
62 #[error("Upstream timeout: {0}")]
64 UpstreamTimeout(String),
65
66 #[error("Request body too large: {size} bytes (max: {max} bytes)")]
68 BodyTooLarge {
69 size: usize,
71 max: usize,
73 },
74
75 #[error("Body read error: {0}")]
77 BodyReadError(String),
78
79 #[error("HTTP client error: {0}")]
81 HttpClientError(#[from] reqwest::Error),
82
83 #[error("Invalid header: {0}")]
85 InvalidHeader(String),
86}
87
88impl WiseGateError {
89 pub fn status_code(&self) -> hyper::StatusCode {
95 use hyper::StatusCode;
96
97 match self {
98 Self::InvalidIp(_) => StatusCode::BAD_REQUEST,
99 Self::ConfigError(_) => StatusCode::INTERNAL_SERVER_ERROR,
100 Self::ProxyError(_) => StatusCode::BAD_GATEWAY,
101 Self::RateLimitExceeded(_) => StatusCode::TOO_MANY_REQUESTS,
102 Self::IpBlocked(_) => StatusCode::FORBIDDEN,
103 Self::PatternBlocked(_) => StatusCode::NOT_FOUND,
104 Self::MethodBlocked(_) => StatusCode::METHOD_NOT_ALLOWED,
105 Self::UpstreamConnectionFailed(_) => StatusCode::BAD_GATEWAY,
106 Self::UpstreamTimeout(_) => StatusCode::GATEWAY_TIMEOUT,
107 Self::BodyTooLarge { .. } => StatusCode::PAYLOAD_TOO_LARGE,
108 Self::BodyReadError(_) => StatusCode::BAD_REQUEST,
109 Self::HttpClientError(_) => StatusCode::BAD_GATEWAY,
110 Self::InvalidHeader(_) => StatusCode::BAD_REQUEST,
111 }
112 }
113
114 pub fn user_message(&self) -> &str {
119 match self {
120 Self::InvalidIp(_) => "Invalid request",
121 Self::ConfigError(_) => "Internal server error",
122 Self::ProxyError(_) => "Bad gateway",
123 Self::RateLimitExceeded(_) => "Rate limit exceeded",
124 Self::IpBlocked(_) => "Access denied",
125 Self::PatternBlocked(_) => "Not found",
126 Self::MethodBlocked(_) => "Method not allowed",
127 Self::UpstreamConnectionFailed(_) => "Service unavailable",
128 Self::UpstreamTimeout(_) => "Gateway timeout",
129 Self::BodyTooLarge { .. } => "Request body too large",
130 Self::BodyReadError(_) => "Bad request",
131 Self::HttpClientError(_) => "Bad gateway",
132 Self::InvalidHeader(_) => "Bad request",
133 }
134 }
135
136 pub fn is_server_error(&self) -> bool {
141 matches!(
142 self,
143 Self::ConfigError(_)
144 | Self::ProxyError(_)
145 | Self::UpstreamConnectionFailed(_)
146 | Self::UpstreamTimeout(_)
147 | Self::HttpClientError(_)
148 )
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use hyper::StatusCode;
156
157 #[test]
158 fn test_error_display() {
159 let err = WiseGateError::InvalidIp("192.168.1.999".into());
160 assert_eq!(err.to_string(), "Invalid IP address: 192.168.1.999");
161
162 let err = WiseGateError::RateLimitExceeded("10.0.0.1".into());
163 assert_eq!(err.to_string(), "Rate limit exceeded for IP: 10.0.0.1");
164
165 let err = WiseGateError::BodyTooLarge {
166 size: 200,
167 max: 100,
168 };
169 assert_eq!(
170 err.to_string(),
171 "Request body too large: 200 bytes (max: 100 bytes)"
172 );
173 }
174
175 #[test]
176 fn test_status_codes() {
177 assert_eq!(
178 WiseGateError::InvalidIp("".into()).status_code(),
179 StatusCode::BAD_REQUEST
180 );
181 assert_eq!(
182 WiseGateError::RateLimitExceeded("".into()).status_code(),
183 StatusCode::TOO_MANY_REQUESTS
184 );
185 assert_eq!(
186 WiseGateError::IpBlocked("".into()).status_code(),
187 StatusCode::FORBIDDEN
188 );
189 assert_eq!(
190 WiseGateError::PatternBlocked("".into()).status_code(),
191 StatusCode::NOT_FOUND
192 );
193 assert_eq!(
194 WiseGateError::MethodBlocked("".into()).status_code(),
195 StatusCode::METHOD_NOT_ALLOWED
196 );
197 assert_eq!(
198 WiseGateError::UpstreamTimeout("".into()).status_code(),
199 StatusCode::GATEWAY_TIMEOUT
200 );
201 assert_eq!(
202 WiseGateError::BodyTooLarge { size: 0, max: 0 }.status_code(),
203 StatusCode::PAYLOAD_TOO_LARGE
204 );
205 }
206
207 #[test]
208 fn test_user_messages() {
209 assert_eq!(
210 WiseGateError::ConfigError("secret".into()).user_message(),
211 "Internal server error"
212 );
213 assert_eq!(
214 WiseGateError::IpBlocked("10.0.0.1".into()).user_message(),
215 "Access denied"
216 );
217 }
218
219 #[test]
220 fn test_is_server_error() {
221 assert!(WiseGateError::ConfigError("".into()).is_server_error());
222 assert!(WiseGateError::UpstreamConnectionFailed("".into()).is_server_error());
223 assert!(WiseGateError::UpstreamTimeout("".into()).is_server_error());
224
225 assert!(!WiseGateError::RateLimitExceeded("".into()).is_server_error());
226 assert!(!WiseGateError::IpBlocked("".into()).is_server_error());
227 assert!(!WiseGateError::MethodBlocked("".into()).is_server_error());
228 }
229}