1#[derive(Debug, thiserror::Error)]
2pub enum SessionOpenError {
3 #[error("connection timed out")]
4 Timeout,
5 #[error("connection refused")]
6 Refused,
7 #[error("network unreachable")]
8 NetworkUnreachable,
9 #[error("host unreachable")]
10 HostUnreachable,
11 #[error("DNS resolution failed")]
12 Dns,
13 #[error("upstream authentication failed")]
14 UpstreamAuthentication,
15 #[error("request rejected by policy")]
16 PolicyDenied,
17 #[error("no eligible upstream available (transient)")]
18 UpstreamUnavailable,
19 #[error("route failed at hop {hop}")]
20 Hop {
21 hop: usize,
22 source: Box<SessionOpenError>,
23 },
24 #[error("other connection error: {0}")]
25 Other(String),
26}
27
28impl From<eggress_core::ConnectError> for SessionOpenError {
29 fn from(e: eggress_core::ConnectError) -> Self {
30 match e {
31 eggress_core::ConnectError::ConnectionRefused => SessionOpenError::Refused,
32 eggress_core::ConnectError::Timeout => SessionOpenError::Timeout,
33 eggress_core::ConnectError::DnsResolution(_) => SessionOpenError::Dns,
34 eggress_core::ConnectError::TlsHandshake(_) => {
35 SessionOpenError::Other("TLS handshake failed".into())
36 }
37 eggress_core::ConnectError::Io(io) => SessionOpenError::Other(io.to_string()),
38 eggress_core::ConnectError::ReservedTarget(addr) => {
39 SessionOpenError::Other(format!("reserved target: {addr}"))
40 }
41 }
42 }
43}
44
45impl From<eggress_core::chain::ChainError> for SessionOpenError {
46 fn from(e: eggress_core::chain::ChainError) -> Self {
47 match e {
48 eggress_core::chain::ChainError::ConnectFailed {
49 hop_index, source, ..
50 } => SessionOpenError::Hop {
51 hop: hop_index,
52 source: Box::new(SessionOpenError::from(source)),
53 },
54 eggress_core::chain::ChainError::HandshakeFailed {
55 hop_index, source, ..
56 } => SessionOpenError::Hop {
57 hop: hop_index,
58 source: Box::new(SessionOpenError::Other(source.to_string())),
59 },
60 eggress_core::chain::ChainError::EmptyChain => {
61 SessionOpenError::Other("empty chain".into())
62 }
63 eggress_core::chain::ChainError::InvalidChain { reason } => {
64 SessionOpenError::Other(format!("invalid chain: {reason}"))
65 }
66 }
67 }
68}
69
70impl From<eggress_protocol_http::HttpError> for SessionOpenError {
71 fn from(e: eggress_protocol_http::HttpError) -> Self {
72 match e {
73 eggress_protocol_http::HttpError::AuthRequired
74 | eggress_protocol_http::HttpError::AuthFailed => {
75 SessionOpenError::UpstreamAuthentication
76 }
77 eggress_protocol_http::HttpError::ConnectionRefused => SessionOpenError::Refused,
78 eggress_protocol_http::HttpError::GatewayTimeout => SessionOpenError::Timeout,
79 other => SessionOpenError::Other(other.to_string()),
80 }
81 }
82}
83
84impl From<eggress_protocol_socks::Socks5Error> for SessionOpenError {
85 fn from(e: eggress_protocol_socks::Socks5Error) -> Self {
86 match e {
87 eggress_protocol_socks::Socks5Error::ConnectionRefused => SessionOpenError::Refused,
88 eggress_protocol_socks::Socks5Error::AuthFailed => {
89 SessionOpenError::UpstreamAuthentication
90 }
91 other => SessionOpenError::Other(other.to_string()),
92 }
93 }
94}