Skip to main content

cdk_http_client/ws/
error.rs

1//! WebSocket error types
2
3/// Errors that can occur during WebSocket operations.
4#[derive(Debug, thiserror::Error)]
5pub enum WsError {
6    /// A temporary network or connection failure.
7    #[error("transient WebSocket error: {0}")]
8    Transient(String),
9    /// The remote endpoint does not support WebSocket subscriptions.
10    #[error("WebSocket subscriptions are not supported: {0}")]
11    NotSupported(String),
12    /// A permanent configuration, authentication, TLS, or protocol failure.
13    #[error("terminal WebSocket error: {0}")]
14    Terminal(String),
15}
16
17#[cfg(not(target_arch = "wasm32"))]
18impl WsError {
19    pub(crate) fn from_tungstenite(error: tokio_tungstenite::tungstenite::Error) -> Self {
20        use tokio_tungstenite::tungstenite::Error;
21
22        let status = match &error {
23            Error::Http(response) => Some(response.status().as_u16()),
24            _ => None,
25        };
26        let message = error.to_string();
27
28        match status {
29            Some(404 | 405 | 501) => Self::NotSupported(message),
30            Some(408 | 429 | 500..=599) => Self::Transient(message),
31            Some(_) => Self::Terminal(message),
32            None => match error {
33                Error::ConnectionClosed | Error::AlreadyClosed | Error::Io(_) => {
34                    Self::Transient(message)
35                }
36                Error::Tls(_)
37                | Error::Capacity(_)
38                | Error::Protocol(_)
39                | Error::WriteBufferFull(_)
40                | Error::Utf8
41                | Error::AttackAttempt
42                | Error::Url(_)
43                | Error::Http(_)
44                | Error::HttpFormat(_) => Self::Terminal(message),
45            },
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[cfg(not(target_arch = "wasm32"))]
55    #[test]
56    fn classifies_http_upgrade_statuses() {
57        use tokio_tungstenite::tungstenite::http::{Response, StatusCode};
58        use tokio_tungstenite::tungstenite::Error;
59
60        let error_for_status = |status| {
61            Error::Http(
62                Response::builder()
63                    .status(status)
64                    .body(None)
65                    .expect("valid HTTP response"),
66            )
67        };
68
69        assert!(matches!(
70            WsError::from_tungstenite(error_for_status(StatusCode::NOT_FOUND)),
71            WsError::NotSupported(_)
72        ));
73        assert!(matches!(
74            WsError::from_tungstenite(error_for_status(StatusCode::UNAUTHORIZED)),
75            WsError::Terminal(_)
76        ));
77        assert!(matches!(
78            WsError::from_tungstenite(error_for_status(StatusCode::SERVICE_UNAVAILABLE)),
79            WsError::Transient(_)
80        ));
81    }
82}