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)
}
}