use std::{
fmt,
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures_util::FutureExt;
#[derive(Debug, Clone)]
pub struct ClosedReason {
pub code: CloseCode,
pub reason: String,
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)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u16)]
pub enum CloseCode {
NormalClosure = 1000,
GoingAway = 1001,
ProtocolError = 1002,
UnsupportedData = 1003,
NoStatusRcvd = 1005,
AbnormalClosure = 1006,
InvalidFramePayloadData = 1007,
PolicyViolation = 1008,
MessageTooBig = 1009,
MandatoryExt = 1010,
InternalError = 1011,
ServiceRestart = 1012,
TryAgainLater = 1013,
BadGateway = 1014,
TlsHandshake = 1015,
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 {
pub fn is_valid(&self) -> bool {
match self {
Self::NormalClosure => true,
Self::Other(other) if 3000 <= *other && *other < 5000 => true,
_ => false,
}
}
}
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)
}
}