use std::net::SocketAddr;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ProxyError {
#[error("Failed to bind to {addr}: {reason}")]
BindFailed { addr: SocketAddr, reason: String },
#[error("Failed to connect to backend {backend}: {reason}")]
BackendConnectionFailed { backend: SocketAddr, reason: String },
#[error("No healthy backends available for service '{service}'")]
NoHealthyBackends { service: String },
#[error("Backend request failed: {0}")]
BackendRequestFailed(String),
#[error("No route found for host '{host}' path '{path}'")]
RouteNotFound { host: String, path: String },
#[error("Forbidden: {0}")]
Forbidden(String),
#[error("Invalid request: {0}")]
InvalidRequest(String),
#[error("TLS error: {0}")]
Tls(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("HTTP error: {0}")]
Hyper(#[from] hyper::Error),
#[error("Request timeout after {0:?}")]
Timeout(std::time::Duration),
#[error("Configuration error: {0}")]
Config(String),
#[error("Internal error: {0}")]
Internal(String),
}
pub type Result<T, E = ProxyError> = std::result::Result<T, E>;
impl ProxyError {
#[must_use]
pub fn status_code(&self) -> http::StatusCode {
match self {
ProxyError::RouteNotFound { .. } => http::StatusCode::NOT_FOUND,
ProxyError::NoHealthyBackends { .. } => http::StatusCode::SERVICE_UNAVAILABLE,
ProxyError::BackendConnectionFailed { .. } | ProxyError::BackendRequestFailed(_) => {
http::StatusCode::BAD_GATEWAY
}
ProxyError::Forbidden(_) => http::StatusCode::FORBIDDEN,
ProxyError::InvalidRequest(_) => http::StatusCode::BAD_REQUEST,
ProxyError::Timeout(_) => http::StatusCode::GATEWAY_TIMEOUT,
_ => http::StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_status_codes() {
let err = ProxyError::RouteNotFound {
host: "example.com".to_string(),
path: "/api".to_string(),
};
assert_eq!(err.status_code(), http::StatusCode::NOT_FOUND);
let err = ProxyError::NoHealthyBackends {
service: "api".to_string(),
};
assert_eq!(err.status_code(), http::StatusCode::SERVICE_UNAVAILABLE);
let err = ProxyError::BackendConnectionFailed {
backend: "127.0.0.1:8080".parse().unwrap(),
reason: "connection refused".to_string(),
};
assert_eq!(err.status_code(), http::StatusCode::BAD_GATEWAY);
}
}