Skip to main content

slim_config/
errors.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::auth::ConfigAuthError;
5use slim_auth::errors::AuthError;
6use thiserror::Error;
7
8/// Errors for Config.
9/// This is a custom error type for handling configuration-related errors.
10/// It is used to provide more context to the error messages.
11#[derive(Error, Debug)]
12pub enum ConfigError {
13    // Configuration / required fields
14    #[error("missing the grpc server service")]
15    MissingServices,
16    #[error("missing grpc endpoint")]
17    MissingEndpoint,
18
19    // Address / parsing
20    #[error("endpoint parse error")]
21    EndpointParse(#[from] std::net::AddrParseError),
22    #[error("URI parse error")]
23    UriParse(#[from] http::uri::InvalidUri),
24    #[error("invalid endpoint scheme")]
25    InvalidEndpointScheme,
26    #[error("websocket transport requires endpoint scheme ws:// or wss://")]
27    InvalidWebSocketEndpointScheme,
28    #[error("websocket client builder requires websocket transport")]
29    WebSocketClientUnsupportedTransport,
30    #[error("websocket server builder requires websocket transport")]
31    WebSocketServerUnsupportedTransport,
32    #[error("websocket transport TLS configuration is invalid")]
33    WebSocketTlsConfiguration,
34    #[error("websocket server TLS is required but no TLS configuration was provided")]
35    WebSocketServerTlsMissing,
36    #[error("HTTPS proxy URL requires a TLS configuration but none was provided")]
37    ProxyTlsMissing,
38    #[error("websocket server TLS configuration is set but endpoint scheme is not wss://")]
39    WebSocketServerTlsUnexpected,
40    #[error("websocket server SNI / certificate name is invalid")]
41    WebSocketInvalidServerName,
42    #[error("websocket TLS handshake error")]
43    WebSocketTlsHandshake(#[source] std::io::Error),
44    #[error("websocket TLS handshake timed out")]
45    WebSocketTlsHandshakeTimeout,
46    #[error("websocket protocol upgrade timed out")]
47    WebSocketHandshakeTimeout,
48    #[error("websocket handshake returned non-101 status: {0}")]
49    WebSocketHandshakeStatus(http::StatusCode),
50    #[error("websocket handshake response missing Sec-WebSocket-Accept header")]
51    WebSocketMissingAcceptHeader,
52    #[error("websocket handshake response Sec-WebSocket-Accept did not match the sent key")]
53    WebSocketAcceptMismatch,
54    #[error("websocket client send error")]
55    WebSocketClientSend(#[source] Box<dyn std::error::Error + Send + Sync>),
56
57    // Network / transport
58    #[error("transport error")]
59    TransportError(#[from] tonic::transport::Error),
60    #[error("gRPC channel builder does not support websocket transport")]
61    GrpcChannelUnsupportedTransport,
62    #[error("gRPC server builder does not support websocket transport")]
63    GrpcServerUnsupportedTransport,
64    #[error("bind error")]
65    Bind(#[from] std::io::Error),
66    #[error("websocket connection error")]
67    WebSocketConnection(#[source] std::io::Error),
68    #[error("websocket handshake error")]
69    WebSocketHandshake(#[source] fastwebsockets::WebSocketError),
70    #[error("websocket request error")]
71    WebSocketRequest(#[source] http::Error),
72
73    // Unix domain sockets
74    #[error("unix domain sockets are unsupported on this platform")]
75    UnixSocketUnsupported,
76    #[error("unix domain socket endpoint requires a socket path")]
77    UnixSocketMissingPath,
78    #[error("unix domain sockets require tls.insecure=true")]
79    UnixSocketTlsUnsupported,
80    #[error("invalid unix domain socket path")]
81    UnixSocketInvalidPath(#[source] http::Error),
82
83    #[cfg(target_family = "unix")]
84    #[error("failed to connect to unix domain socket")]
85    UnixSocketConnect(#[source] std::io::Error),
86
87    // Header parsing
88    #[error("header name parse error")]
89    HeaderNameParse(#[from] http::header::InvalidHeaderName),
90    #[error("header value parse error")]
91    HeaderValueParse(#[from] http::header::InvalidHeaderValue),
92
93    // Rate limiting
94    #[error("rate limit parse error")]
95    RateLimitParse(#[from] std::num::ParseIntError),
96
97    // TLS configuration
98    #[error("TLS config error")]
99    TlsConfig(#[from] crate::tls::errors::ConfigError),
100
101    // Authentication
102    #[error("auth error")]
103    AuthError(#[from] AuthError),
104    #[error("auth config error")]
105    AuthConfigError(#[from] ConfigAuthError),
106
107    // Resolution / operational
108    #[error("resolution error")]
109    ResolutionError,
110
111    // ServerHandler routing
112    #[error("server handler does not provide gRPC routes, but transport is gRPC")]
113    HandlerMissingGrpcSupport,
114    #[error("server handler does not provide a websocket callback, but transport is websocket")]
115    HandlerMissingWebSocketSupport,
116
117    // Unknown / catch-all
118    #[error("unknown error")]
119    Unknown,
120}
121
122impl ConfigError {
123    /// True if this error came from a transient transport-level failure that
124    /// a connect attempt could plausibly recover from on retry (TCP refused,
125    /// TLS handshake hiccup, websocket upgrade race, etc.). Used by the
126    /// shared connect-retry helper to decide whether to keep retrying.
127    ///
128    /// Add new variants here when introducing a transport — that's the single
129    /// switch that opts a new transport into the shared retry policy.
130    pub fn is_retryable_connect_error(&self) -> bool {
131        match self {
132            // gRPC / tonic
133            ConfigError::TransportError(_) => true,
134
135            // Unix domain socket
136            #[cfg(target_family = "unix")]
137            ConfigError::UnixSocketConnect(_) => true,
138
139            // WebSocket
140            ConfigError::WebSocketConnection(_)
141            | ConfigError::WebSocketTlsHandshake(_)
142            | ConfigError::WebSocketTlsHandshakeTimeout
143            | ConfigError::WebSocketHandshake(_)
144            | ConfigError::WebSocketHandshakeTimeout
145            | ConfigError::WebSocketHandshakeStatus(_)
146            | ConfigError::WebSocketMissingAcceptHeader
147            | ConfigError::WebSocketAcceptMismatch
148            | ConfigError::WebSocketClientSend(_) => true,
149
150            _ => false,
151        }
152    }
153}