web_socket/
lib.rs

1#![doc(html_logo_url = "https://cdn.worldvectorlogo.com/logos/websocket.svg")]
2#![doc = include_str!("../README.md")]
3#![warn(missing_docs)]
4
5mod frame;
6mod ws;
7#[doc(hidden)]
8pub use frame::Frame;
9pub use ws::WebSocket;
10
11/// Two roles that can be played by a WebSocket connection: `Server` and `Client`.
12#[derive(Debug)]
13pub enum Role {
14    /// Represent websocket server instance.
15    Server,
16    /// Represent websocket client instance.
17    Client,
18}
19
20/// It represent the type of data that is being sent over the WebSocket connection.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum MessageType {
23    /// `Text` data is represented as a sequence of Unicode characters encoded using UTF-8 encoding.
24    Text = 1,
25    /// `Binary` data can be any sequence of bytes and is typically used for sending non-textual data, such as images, audio files etc...
26    Binary = 2,
27}
28
29impl MessageType {
30    /// Returns `true` if message type is text
31    #[inline]
32    pub fn is_text(&self) -> bool {
33        matches!(self, MessageType::Text)
34    }
35
36    /// Returns `true` if message type is binary
37    #[inline]
38    pub fn is_binary(&self) -> bool {
39        matches!(self, MessageType::Binary)
40    }
41}
42
43/// Represents a fragment of a WebSocket message.
44#[derive(Debug, Clone)]
45pub enum Stream {
46    /// Indicates tCopyhe start of a new message fragment of the given [MessageType].
47    Start(MessageType),
48    /// Indicates the continuation of the current message fragment.
49    Next(MessageType),
50    /// Indicates the end of the current message fragment.
51    End(MessageType),
52}
53
54impl Stream {
55    /// Get [MessageType] from [Stream]
56    #[inline]
57    pub fn ty(&self) -> MessageType {
58        match *self {
59            Stream::Start(ty) => ty,
60            Stream::Next(ty) => ty,
61            Stream::End(ty) => ty,
62        }
63    }
64}
65
66/// Data that is either complete or fragmented.
67#[derive(Debug, Clone)]
68pub enum DataType {
69    /// The message is split into fragments, each of which is sent as a separate
70    /// WebSocket message with the [Fragment] variant.
71    Stream(Stream),
72    /// A complete WebSocket message in a single transmission.
73    Complete(MessageType),
74}
75
76#[derive(Debug)]
77/// Represent a websocket event
78pub enum Event {
79    /// Websocket data frame.
80    Data {
81        /// Represents WebSocket [DataType], Either complete or fragmented
82        ty: DataType,
83        /// Payload, represented as bytes.
84        data: Box<[u8]>,
85    },
86
87    /// A Ping frame may serve either as a keepalive or as a means to verify that the remote endpoint is still responsive.
88    ///
89    /// And SHOULD respond with Pong frame as soon as is practical.
90    Ping(Box<[u8]>),
91
92    /// A Pong frame sent in response to a Ping frame must have identical
93    /// "Application data" as found in the message body of the Ping frame being replied to.
94    ///
95    /// If an endpoint receives a Ping frame and has not yet sent Pong frame(s) in response to previous Ping frame(s), the endpoint MAY
96    /// elect to send a Pong frame for only the most recently processed Ping frame.
97    ///
98    /// A Pong frame MAY be sent unsolicited.  This serves as a unidirectional heartbeat.  A response to an unsolicited Pong frame is not expected.
99    Pong(Box<[u8]>),
100
101    /// represents the websocket error message.
102    Error(&'static str),
103
104    /// represents a successful close event of the WebSocket connection.
105    Close {
106        /// represents the status [CloseCode] of the close event.
107        code: u16,
108        /// represents the reason for the close event
109        reason: Box<str>,
110    },
111}
112
113/// When closing an established connection an endpoint MAY indicate a reason for closure.
114#[derive(Debug, Clone, Copy)]
115pub enum CloseCode {
116    /// The purpose for which the connection was established has been fulfilled
117    Normal = 1000,
118    /// Server going down or a browser having navigated away from a page
119    Away = 1001,
120    /// An endpoint is terminating the connection due to a protocol error.
121    ProtocolError = 1002,
122    /// It has received a type of data it cannot accept
123    Unsupported = 1003,
124
125    // reserved 1004
126    /// MUST NOT be set as a status code in a Close control frame by an endpoint.
127    ///
128    /// No status code was actually present.
129    NoStatusRcvd = 1005,
130    /// MUST NOT be set as a status code in a Close control frame by an endpoint.
131    ///
132    /// Connection was closed abnormally.
133    Abnormal = 1006,
134    /// Application has received data within a message that was not consistent with the type of the message.
135    InvalidPayload = 1007,
136    /// This is a generic status code that can be returned when there is no other more suitable status code.
137    PolicyViolation = 1008,
138    /// Message that is too big for it to process.
139    MessageTooBig = 1009,
140    /// It has expected the server to negotiate one or more extension.
141    MandatoryExt = 1010,
142    /// The server has encountered an unexpected condition that prevented it from fulfilling the request.
143    InternalError = 1011,
144    /// MUST NOT be set as a status code in a Close control frame by an endpoint.
145    ///
146    /// The connection was closed due to a failure to perform a TLS handshake.
147    TLSHandshake = 1015,
148}
149
150impl From<CloseCode> for u16 {
151    #[inline]
152    fn from(code: CloseCode) -> Self {
153        code as u16
154    }
155}
156
157impl From<u16> for CloseCode {
158    #[inline]
159    fn from(value: u16) -> Self {
160        match value {
161            1000 => CloseCode::Normal,
162            1001 => CloseCode::Away,
163            1002 => CloseCode::ProtocolError,
164            1003 => CloseCode::Unsupported,
165            1005 => CloseCode::NoStatusRcvd,
166            1006 => CloseCode::Abnormal,
167            1007 => CloseCode::InvalidPayload,
168            1009 => CloseCode::MessageTooBig,
169            1010 => CloseCode::MandatoryExt,
170            1011 => CloseCode::InternalError,
171            1015 => CloseCode::TLSHandshake,
172            _ => CloseCode::PolicyViolation,
173        }
174    }
175}
176
177impl PartialEq<u16> for CloseCode {
178    #[inline]
179    fn eq(&self, other: &u16) -> bool {
180        (*self as u16) == *other
181    }
182}
183
184/// This trait is responsible for encoding websocket closed frame.
185pub trait CloseReason {
186    /// Encoded close reason as bytes
187    type Bytes;
188    /// Encode websocket close frame.
189    fn to_bytes(self) -> Self::Bytes;
190}
191
192impl CloseReason for () {
193    type Bytes = [u8; 0];
194    fn to_bytes(self) -> Self::Bytes {
195        [0; 0]
196    }
197}
198
199impl CloseReason for u16 {
200    type Bytes = [u8; 2];
201    fn to_bytes(self) -> Self::Bytes {
202        self.to_be_bytes()
203    }
204}
205
206impl CloseReason for CloseCode {
207    type Bytes = [u8; 2];
208    fn to_bytes(self) -> Self::Bytes {
209        (self as u16).to_be_bytes()
210    }
211}
212
213impl CloseReason for &str {
214    type Bytes = Vec<u8>;
215    fn to_bytes(self) -> Self::Bytes {
216        CloseReason::to_bytes((CloseCode::Normal, self))
217    }
218}
219
220impl<Code, Msg> CloseReason for (Code, Msg)
221where
222    Code: Into<u16>,
223    Msg: AsRef<[u8]>,
224{
225    type Bytes = Vec<u8>;
226    fn to_bytes(self) -> Self::Bytes {
227        let (code, reason) = (self.0.into(), self.1.as_ref());
228        let mut data = Vec::with_capacity(2 + reason.len());
229        data.extend_from_slice(&code.to_be_bytes());
230        data.extend_from_slice(reason);
231        data
232    }
233}