websocket_web/
closed.rs

1//! WebSocket closing.
2
3use std::{
4    fmt,
5    future::Future,
6    pin::Pin,
7    task::{Context, Poll},
8};
9
10use futures_util::FutureExt;
11
12/// Reason for why a WebSocket connection is closed.
13#[derive(Debug, Clone)]
14pub struct ClosedReason {
15    /// A number representing the closing code.
16    pub code: CloseCode,
17    /// A string representing a human-readable description of
18    /// the reason why the socket connection was closed.
19    pub reason: String,
20    /// Indicates whether or not the connection was cleanly closed
21    pub was_clean: bool,
22}
23
24impl fmt::Display for ClosedReason {
25    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
26        if self.reason.is_empty() {
27            write!(f, "{}", self.code)
28        } else {
29            write!(f, "{} ({})", &self.reason, self.code)
30        }
31    }
32}
33
34/// A close code indicating why a WebSocket connection was closed.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
36#[repr(u16)]
37pub enum CloseCode {
38    /// The connection successfully completed the purpose for which it was created.
39    NormalClosure = 1000,
40    /// The endpoint is going away, either because of a server failure or a navigation away.
41    GoingAway = 1001,
42    /// The endpoint is terminating the connection due to a protocol error.
43    ProtocolError = 1002,
44    /// The connection is being terminated because the endpoint received data of a type it cannot accept.
45    UnsupportedData = 1003,
46    /// Reserved: Indicates that no status code was provided although one was expected.
47    NoStatusRcvd = 1005,
48    /// Reserved: Indicates that a connection was closed abnormally when a status code was expected.
49    AbnormalClosure = 1006,
50    /// The endpoint is terminating the connection because a message has inconsistent data.
51    InvalidFramePayloadData = 1007,
52    /// The endpoint is terminating the connection because it received a message that violates its policy.
53    PolicyViolation = 1008,
54    /// The endpoint is terminating the connection because a data frame was received that is too large.
55    MessageTooBig = 1009,
56    /// The client is terminating the connection because it expected a server extension negotiation.
57    MandatoryExt = 1010,
58    /// The server is terminating the connection because it encountered an unexpected condition.
59    InternalError = 1011,
60    /// The server is terminating the connection because it is restarting.
61    ServiceRestart = 1012,
62    /// The server is terminating the connection due to a temporary condition (e.g., overloaded).
63    TryAgainLater = 1013,
64    /// The server was acting as a gateway or proxy and received an invalid response from upstream.
65    BadGateway = 1014,
66    /// Reserved: The connection was closed due to a failure in the TLS handshake.
67    TlsHandshake = 1015,
68    /// Other close code.
69    Other(u16),
70}
71
72impl fmt::Display for CloseCode {
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        match self {
75            CloseCode::NormalClosure => write!(f, "normal closure"),
76            CloseCode::GoingAway => write!(f, "going away"),
77            CloseCode::ProtocolError => write!(f, "protocol error"),
78            CloseCode::UnsupportedData => write!(f, "unsupported data"),
79            CloseCode::NoStatusRcvd => write!(f, "no status rcvd"),
80            CloseCode::AbnormalClosure => write!(f, "abnormal closure"),
81            CloseCode::InvalidFramePayloadData => write!(f, "invalid frame payload data"),
82            CloseCode::PolicyViolation => write!(f, "policy violation"),
83            CloseCode::MessageTooBig => write!(f, "message too big"),
84            CloseCode::MandatoryExt => write!(f, "mandatory ext"),
85            CloseCode::InternalError => write!(f, "internal error"),
86            CloseCode::ServiceRestart => write!(f, "service restart"),
87            CloseCode::TryAgainLater => write!(f, "try again later"),
88            CloseCode::BadGateway => write!(f, "bad gateway"),
89            CloseCode::TlsHandshake => write!(f, "TLS handshake"),
90            CloseCode::Other(code) => write!(f, "{code}"),
91        }
92    }
93}
94
95impl From<CloseCode> for u16 {
96    fn from(code: CloseCode) -> Self {
97        match code {
98            CloseCode::NormalClosure => 1000,
99            CloseCode::GoingAway => 1001,
100            CloseCode::ProtocolError => 1002,
101            CloseCode::UnsupportedData => 1003,
102            CloseCode::NoStatusRcvd => 1005,
103            CloseCode::AbnormalClosure => 1006,
104            CloseCode::InvalidFramePayloadData => 1007,
105            CloseCode::PolicyViolation => 1008,
106            CloseCode::MessageTooBig => 1009,
107            CloseCode::MandatoryExt => 1010,
108            CloseCode::InternalError => 1011,
109            CloseCode::ServiceRestart => 1012,
110            CloseCode::TryAgainLater => 1013,
111            CloseCode::BadGateway => 1014,
112            CloseCode::TlsHandshake => 1015,
113            CloseCode::Other(code) => code,
114        }
115    }
116}
117
118impl From<u16> for CloseCode {
119    fn from(value: u16) -> Self {
120        match value {
121            1000 => CloseCode::NormalClosure,
122            1001 => CloseCode::GoingAway,
123            1002 => CloseCode::ProtocolError,
124            1003 => CloseCode::UnsupportedData,
125            1005 => CloseCode::NoStatusRcvd,
126            1006 => CloseCode::AbnormalClosure,
127            1007 => CloseCode::InvalidFramePayloadData,
128            1008 => CloseCode::PolicyViolation,
129            1009 => CloseCode::MessageTooBig,
130            1010 => CloseCode::MandatoryExt,
131            1011 => CloseCode::InternalError,
132            1012 => CloseCode::ServiceRestart,
133            1013 => CloseCode::TryAgainLater,
134            1014 => CloseCode::BadGateway,
135            1015 => CloseCode::TlsHandshake,
136            other => CloseCode::Other(other),
137        }
138    }
139}
140
141impl CloseCode {
142    /// Whether the close code can be specified when closing a WebSocket
143    /// using [WebSocket::close_with_reason](crate::WebSocket::close_with_reason).
144    ///
145    /// The close code is valid if it is either [CloseCode::NormalClosure] or
146    /// [CloseCode::Other] with a value between 3000 and 4999.
147    pub fn is_valid(&self) -> bool {
148        match self {
149            Self::NormalClosure => true,
150            Self::Other(other) if 3000 <= *other && *other < 5000 => true,
151            _ => false,
152        }
153    }
154}
155
156/// A future that resolves once a WebSocket has been closed.
157pub struct Closed(pub(crate) Pin<Box<dyn Future<Output = ClosedReason>>>);
158
159impl fmt::Debug for Closed {
160    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        f.debug_tuple("Closed").finish()
162    }
163}
164
165impl Future for Closed {
166    type Output = ClosedReason;
167    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
168        self.0.poll_unpin(cx)
169    }
170}