Skip to main content

zlayer_proxy/
error.rs

1//! Proxy error types
2//!
3//! This module defines all error types for the proxy crate.
4
5use std::net::SocketAddr;
6use thiserror::Error;
7
8/// Errors that can occur in the proxy
9#[derive(Debug, Error)]
10pub enum ProxyError {
11    /// Failed to bind to address
12    #[error("Failed to bind to {addr}: {reason}")]
13    BindFailed { addr: SocketAddr, reason: String },
14
15    /// Failed to connect to backend
16    #[error("Failed to connect to backend {backend}: {reason}")]
17    BackendConnectionFailed { backend: SocketAddr, reason: String },
18
19    /// No healthy backends available
20    #[error("No healthy backends available for service '{service}'")]
21    NoHealthyBackends { service: String },
22
23    /// Backend request failed
24    #[error("Backend request failed: {0}")]
25    BackendRequestFailed(String),
26
27    /// Route not found
28    #[error("No route found for host '{host}' path '{path}'")]
29    RouteNotFound { host: String, path: String },
30
31    /// Forbidden - source not allowed to access internal endpoint
32    #[error("Forbidden: {0}")]
33    Forbidden(String),
34
35    /// Invalid request
36    #[error("Invalid request: {0}")]
37    InvalidRequest(String),
38
39    /// TLS error
40    #[error("TLS error: {0}")]
41    Tls(String),
42
43    /// IO error
44    #[error("IO error: {0}")]
45    Io(#[from] std::io::Error),
46
47    /// Hyper error
48    #[error("HTTP error: {0}")]
49    Hyper(#[from] hyper::Error),
50
51    /// Timeout
52    #[error("Request timeout after {0:?}")]
53    Timeout(std::time::Duration),
54
55    /// Configuration error
56    #[error("Configuration error: {0}")]
57    Config(String),
58
59    /// Internal error
60    #[error("Internal error: {0}")]
61    Internal(String),
62}
63
64/// Result type alias for proxy operations
65pub type Result<T, E = ProxyError> = std::result::Result<T, E>;
66
67impl ProxyError {
68    /// Returns the HTTP status code for this error
69    #[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}