1use std::net::SocketAddr;
6use thiserror::Error;
7
8#[derive(Debug, Error)]
10pub enum ProxyError {
11 #[error("Failed to bind to {addr}: {reason}")]
13 BindFailed { addr: SocketAddr, reason: String },
14
15 #[error("Failed to connect to backend {backend}: {reason}")]
17 BackendConnectionFailed { backend: SocketAddr, reason: String },
18
19 #[error("No healthy backends available for service '{service}'")]
21 NoHealthyBackends { service: String },
22
23 #[error("Backend request failed: {0}")]
25 BackendRequestFailed(String),
26
27 #[error("No route found for host '{host}' path '{path}'")]
29 RouteNotFound { host: String, path: String },
30
31 #[error("Forbidden: {0}")]
33 Forbidden(String),
34
35 #[error("Invalid request: {0}")]
37 InvalidRequest(String),
38
39 #[error("TLS error: {0}")]
41 Tls(String),
42
43 #[error("IO error: {0}")]
45 Io(#[from] std::io::Error),
46
47 #[error("HTTP error: {0}")]
49 Hyper(#[from] hyper::Error),
50
51 #[error("Request timeout after {0:?}")]
53 Timeout(std::time::Duration),
54
55 #[error("Configuration error: {0}")]
57 Config(String),
58
59 #[error("Internal error: {0}")]
61 Internal(String),
62}
63
64pub type Result<T, E = ProxyError> = std::result::Result<T, E>;
66
67impl ProxyError {
68 #[must_use]
70 pub fn status_code(&self) -> http::StatusCode {
71 match self {
72 ProxyError::RouteNotFound { .. } => http::StatusCode::NOT_FOUND,
73 ProxyError::NoHealthyBackends { .. } => http::StatusCode::SERVICE_UNAVAILABLE,
74 ProxyError::BackendConnectionFailed { .. } | ProxyError::BackendRequestFailed(_) => {
75 http::StatusCode::BAD_GATEWAY
76 }
77 ProxyError::Forbidden(_) => http::StatusCode::FORBIDDEN,
78 ProxyError::InvalidRequest(_) => http::StatusCode::BAD_REQUEST,
79 ProxyError::Timeout(_) => http::StatusCode::GATEWAY_TIMEOUT,
80 _ => http::StatusCode::INTERNAL_SERVER_ERROR,
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_error_status_codes() {
91 let err = ProxyError::RouteNotFound {
92 host: "example.com".to_string(),
93 path: "/api".to_string(),
94 };
95 assert_eq!(err.status_code(), http::StatusCode::NOT_FOUND);
96
97 let err = ProxyError::NoHealthyBackends {
98 service: "api".to_string(),
99 };
100 assert_eq!(err.status_code(), http::StatusCode::SERVICE_UNAVAILABLE);
101
102 let err = ProxyError::BackendConnectionFailed {
103 backend: "127.0.0.1:8080".parse().unwrap(),
104 reason: "connection refused".to_string(),
105 };
106 assert_eq!(err.status_code(), http::StatusCode::BAD_GATEWAY);
107 }
108}