binance_sdk/common/
errors.rs

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