websocket_web/
closed.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! WebSocket closing.

use std::{
    fmt,
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

use futures_util::FutureExt;

/// Reason for why a WebSocket connection is closed.
#[derive(Debug, Clone)]
pub struct ClosedReason {
    /// A number representing the closing code.
    pub code: CloseCode,
    /// A string representing a human-readable description of
    /// the reason why the socket connection was closed.
    pub reason: String,
    /// Indicates whether or not the connection was cleanly closed
    pub was_clean: bool,
}

impl fmt::Display for ClosedReason {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
        if self.reason.is_empty() {
            write!(f, "{}", self.code)
        } else {
            write!(f, "{} ({})", &self.reason, self.code)
        }
    }
}

/// A close code indicating why a WebSocket connection was closed.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u16)]
pub enum CloseCode {
    /// The connection successfully completed the purpose for which it was created.
    NormalClosure = 1000,
    /// The endpoint is going away, either because of a server failure or a navigation away.
    GoingAway = 1001,
    /// The endpoint is terminating the connection due to a protocol error.
    ProtocolError = 1002,
    /// The connection is being terminated because the endpoint received data of a type it cannot accept.
    UnsupportedData = 1003,
    /// Reserved: Indicates that no status code was provided although one was expected.
    NoStatusRcvd = 1005,
    /// Reserved: Indicates that a connection was closed abnormally when a status code was expected.
    AbnormalClosure = 1006,
    /// The endpoint is terminating the connection because a message has inconsistent data.
    InvalidFramePayloadData = 1007,
    /// The endpoint is terminating the connection because it received a message that violates its policy.
    PolicyViolation = 1008,
    /// The endpoint is terminating the connection because a data frame was received that is too large.
    MessageTooBig = 1009,
    /// The client is terminating the connection because it expected a server extension negotiation.
    MandatoryExt = 1010,
    /// The server is terminating the connection because it encountered an unexpected condition.
    InternalError = 1011,
    /// The server is terminating the connection because it is restarting.
    ServiceRestart = 1012,
    /// The server is terminating the connection due to a temporary condition (e.g., overloaded).
    TryAgainLater = 1013,
    /// The server was acting as a gateway or proxy and received an invalid response from upstream.
    BadGateway = 1014,
    /// Reserved: The connection was closed due to a failure in the TLS handshake.
    TlsHandshake = 1015,
    /// Other close code.
    Other(u16),
}

impl fmt::Display for CloseCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CloseCode::NormalClosure => write!(f, "normal closure"),
            CloseCode::GoingAway => write!(f, "going away"),
            CloseCode::ProtocolError => write!(f, "protocol error"),
            CloseCode::UnsupportedData => write!(f, "unsupported data"),
            CloseCode::NoStatusRcvd => write!(f, "no status rcvd"),
            CloseCode::AbnormalClosure => write!(f, "abnormal closure"),
            CloseCode::InvalidFramePayloadData => write!(f, "invalid frame payload data"),
            CloseCode::PolicyViolation => write!(f, "policy violation"),
            CloseCode::MessageTooBig => write!(f, "message too big"),
            CloseCode::MandatoryExt => write!(f, "mandatory ext"),
            CloseCode::InternalError => write!(f, "internal error"),
            CloseCode::ServiceRestart => write!(f, "service restart"),
            CloseCode::TryAgainLater => write!(f, "try again later"),
            CloseCode::BadGateway => write!(f, "bad gateway"),
            CloseCode::TlsHandshake => write!(f, "TLS handshake"),
            CloseCode::Other(code) => write!(f, "{code}"),
        }
    }
}

impl From<CloseCode> for u16 {
    fn from(code: CloseCode) -> Self {
        match code {
            CloseCode::NormalClosure => 1000,
            CloseCode::GoingAway => 1001,
            CloseCode::ProtocolError => 1002,
            CloseCode::UnsupportedData => 1003,
            CloseCode::NoStatusRcvd => 1005,
            CloseCode::AbnormalClosure => 1006,
            CloseCode::InvalidFramePayloadData => 1007,
            CloseCode::PolicyViolation => 1008,
            CloseCode::MessageTooBig => 1009,
            CloseCode::MandatoryExt => 1010,
            CloseCode::InternalError => 1011,
            CloseCode::ServiceRestart => 1012,
            CloseCode::TryAgainLater => 1013,
            CloseCode::BadGateway => 1014,
            CloseCode::TlsHandshake => 1015,
            CloseCode::Other(code) => code,
        }
    }
}

impl From<u16> for CloseCode {
    fn from(value: u16) -> Self {
        match value {
            1000 => CloseCode::NormalClosure,
            1001 => CloseCode::GoingAway,
            1002 => CloseCode::ProtocolError,
            1003 => CloseCode::UnsupportedData,
            1005 => CloseCode::NoStatusRcvd,
            1006 => CloseCode::AbnormalClosure,
            1007 => CloseCode::InvalidFramePayloadData,
            1008 => CloseCode::PolicyViolation,
            1009 => CloseCode::MessageTooBig,
            1010 => CloseCode::MandatoryExt,
            1011 => CloseCode::InternalError,
            1012 => CloseCode::ServiceRestart,
            1013 => CloseCode::TryAgainLater,
            1014 => CloseCode::BadGateway,
            1015 => CloseCode::TlsHandshake,
            other => CloseCode::Other(other),
        }
    }
}

impl CloseCode {
    /// Whether the close code can be specified when closing a WebSocket
    /// using [WebSocket::close_with_reason](crate::WebSocket::close_with_reason).
    ///
    /// The close code is valid if it is either [CloseCode::NormalClosure] or
    /// [CloseCode::Other] with a value between 3000 and 4999.
    pub fn is_valid(&self) -> bool {
        match self {
            Self::NormalClosure => true,
            Self::Other(other) if 3000 <= *other && *other < 5000 => true,
            _ => false,
        }
    }
}

/// A future that resolves once a WebSocket has been closed.
pub struct Closed(pub(crate) Pin<Box<dyn Future<Output = ClosedReason>>>);

impl fmt::Debug for Closed {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("Closed").finish()
    }
}

impl Future for Closed {
    type Output = ClosedReason;
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        self.0.poll_unpin(cx)
    }
}