Skip to main content

binance_sdk/common/
errors.rs

1use thiserror::Error;
2use tokio_tungstenite::tungstenite::error::ProtocolError;
3
4/// Represents different types of WebSocket connection failures and their reconnection eligibility
5#[derive(Debug, Clone, Copy)]
6pub enum WebsocketConnectionFailureReason {
7    /// Network-level interruption (should reconnect)
8    NetworkInterruption,
9    /// Connection reset by peer (should reconnect)
10    ConnectionReset,
11    /// Server temporary error (should reconnect)
12    ServerTemporaryError,
13    /// Unexpected connection close (should reconnect)
14    UnexpectedClose,
15    /// Stream ended unexpectedly (should reconnect)
16    StreamEnded,
17    /// Authentication/authorization failure (should not reconnect)
18    AuthenticationFailure,
19    /// Protocol violation (should not reconnect)
20    ProtocolViolation,
21    /// Configuration error (should not reconnect)
22    ConfigurationError,
23    /// User initiated close (should not reconnect)
24    UserInitiatedClose,
25    /// Permanent server error (should not reconnect)
26    PermanentServerError,
27    /// Normal close (should not reconnect)
28    NormalClose,
29}
30
31impl WebsocketConnectionFailureReason {
32    /// Classifies a tungstenite error into a failure reason
33    pub fn from_tungstenite_error(error: &tokio_tungstenite::tungstenite::Error) -> Self {
34        use tokio_tungstenite::tungstenite::Error;
35        match error {
36            Error::ConnectionClosed | Error::AlreadyClosed => Self::ConnectionReset,
37            Error::Io(io_error) => {
38                use std::io::ErrorKind;
39                match io_error.kind() {
40                    ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted => {
41                        Self::ConnectionReset
42                    }
43                    ErrorKind::UnexpectedEof => Self::StreamEnded,
44                    ErrorKind::PermissionDenied => Self::AuthenticationFailure,
45                    _ => Self::NetworkInterruption,
46                }
47            }
48            Error::Tls(_) | Error::Capacity(_) | Error::Url(_) => Self::ConfigurationError,
49            Error::Protocol(ProtocolError::ResetWithoutClosingHandshake) => Self::UnexpectedClose,
50            Error::Protocol(_) | Error::Utf8 | Error::HttpFormat(_) => Self::ProtocolViolation,
51            Error::Http(_) => Self::ServerTemporaryError,
52            _ => Self::NetworkInterruption,
53        }
54    }
55
56    /// Classifies a WebSocket close code into a failure reason
57    #[must_use]
58    pub fn from_close_code(code: u16, user_initiated: bool) -> Self {
59        if user_initiated {
60            return Self::UserInitiatedClose;
61        }
62        match code {
63            1000 => Self::NormalClose,                               // Normal closure
64            1001 | 1011 | 1012 | 1013 => Self::ServerTemporaryError, // Server temporary issues
65            1002 | 1003 | 1007 | 1009 | 1010 => Self::ProtocolViolation, // Protocol violations
66            1008 | 1014 | 4000..=4999 => Self::PermanentServerError, // Permanent server errors
67            1015 => Self::ConfigurationError,                        // TLS handshake failure
68            _ => Self::UnexpectedClose,                              // Unknown codes including 1006
69        }
70    }
71
72    /// Determines if this failure type warrants a reconnection attempt
73    #[must_use]
74    pub fn should_reconnect(&self) -> bool {
75        match self {
76            // Reconnectable failures
77            Self::NetworkInterruption
78            | Self::ConnectionReset
79            | Self::ServerTemporaryError
80            | Self::UnexpectedClose
81            | Self::StreamEnded => true,
82            // Non-reconnectable failures
83            Self::AuthenticationFailure
84            | Self::ProtocolViolation
85            | Self::ConfigurationError
86            | Self::UserInitiatedClose
87            | Self::PermanentServerError
88            | Self::NormalClose => false,
89        }
90    }
91}
92
93#[derive(Error, Debug)]
94pub enum ConnectorError {
95    #[error("Connector client error: {msg}")]
96    ConnectorClientError { msg: String, code: Option<i64> },
97
98    #[error("Unauthorized access. Authentication required. {msg}")]
99    UnauthorizedError { msg: String, code: Option<i64> },
100
101    #[error("Access to the requested resource is forbidden. {msg}")]
102    ForbiddenError { msg: String, code: Option<i64> },
103
104    #[error("Too many requests. You are being rate-limited. {msg}")]
105    TooManyRequestsError { msg: String, code: Option<i64> },
106
107    #[error("The IP address has been banned for exceeding rate limits. {msg}")]
108    RateLimitBanError { msg: String, code: Option<i64> },
109
110    #[error("Internal server error: {msg} (status code: {status_code:?})")]
111    ServerError {
112        msg: String,
113        status_code: Option<u16>,
114    },
115
116    #[error("Network error: {0}")]
117    NetworkError(String),
118
119    #[error("The requested resource was not found. {msg}")]
120    NotFoundError { msg: String, code: Option<i64> },
121
122    #[error("Bad request: {msg}")]
123    BadRequestError { msg: String, code: Option<i64> },
124}
125
126#[derive(Debug, Error)]
127pub enum WebsocketError {
128    #[error("WebSocket timeout error")]
129    Timeout,
130    #[error("WebSocket protocol error: {0}")]
131    Protocol(String),
132    #[error("URL parse error: {0}")]
133    Url(#[from] url::ParseError),
134    #[error("WebSocket handshake error: {0}")]
135    Handshake(String),
136    #[error("Network error: {0}")]
137    NetworkError(String),
138    #[error("No active WebSocket connection error.")]
139    NotConnected,
140    #[error("Server error: {0}")]
141    ServerError(String),
142    #[error("No response error.")]
143    NoResponse,
144    #[error("Server‐side response error (code {code}): {message}")]
145    ResponseError { code: i64, message: String },
146}