Skip to main content

tungstenite/protocol/
message.rs

1use super::frame::{CloseFrame, Frame};
2use crate::{
3    error::{CapacityError, Error, Result},
4    protocol::frame::Utf8Bytes,
5};
6use std::{fmt, result::Result as StdResult, str};
7
8mod string_collect {
9    use utf8::DecodeError;
10
11    use crate::error::{Error, Result};
12
13    #[derive(Debug)]
14    pub struct StringCollector {
15        data: String,
16        incomplete: Option<utf8::Incomplete>,
17    }
18
19    impl StringCollector {
20        pub fn new() -> Self {
21            StringCollector { data: String::new(), incomplete: None }
22        }
23
24        pub fn len(&self) -> usize {
25            self.data
26                .len()
27                .saturating_add(self.incomplete.map(|i| i.buffer_len as usize).unwrap_or(0))
28        }
29
30        pub fn extend<T: AsRef<[u8]>>(&mut self, tail: T) -> Result<()> {
31            let mut input: &[u8] = tail.as_ref();
32
33            if let Some(mut incomplete) = self.incomplete.take() {
34                if let Some((result, rest)) = incomplete.try_complete(input) {
35                    input = rest;
36                    match result {
37                        Ok(text) => self.data.push_str(text),
38                        Err(result_bytes) => {
39                            return Err(Error::Utf8(String::from_utf8_lossy(result_bytes).into()))
40                        }
41                    }
42                } else {
43                    input = &[];
44                    self.incomplete = Some(incomplete);
45                }
46            }
47
48            if !input.is_empty() {
49                match utf8::decode(input) {
50                    Ok(text) => {
51                        self.data.push_str(text);
52                        Ok(())
53                    }
54                    Err(DecodeError::Incomplete { valid_prefix, incomplete_suffix }) => {
55                        self.data.push_str(valid_prefix);
56                        self.incomplete = Some(incomplete_suffix);
57                        Ok(())
58                    }
59                    Err(DecodeError::Invalid { valid_prefix, invalid_sequence, .. }) => {
60                        self.data.push_str(valid_prefix);
61                        Err(Error::Utf8(String::from_utf8_lossy(invalid_sequence).into()))
62                    }
63                }
64            } else {
65                Ok(())
66            }
67        }
68
69        pub fn into_string(self) -> Result<String> {
70            if let Some(incomplete) = self.incomplete {
71                Err(Error::Utf8(format!("incomplete string: {incomplete:?}")))
72            } else {
73                Ok(self.data)
74            }
75        }
76    }
77}
78
79use self::string_collect::StringCollector;
80use bytes::Bytes;
81
82/// A struct representing the incomplete message.
83#[derive(Debug)]
84pub struct IncompleteMessage {
85    collector: IncompleteMessageCollector,
86    #[cfg(feature = "deflate")]
87    compressed: bool,
88}
89
90#[derive(Debug)]
91enum IncompleteMessageCollector {
92    Text(StringCollector),
93    Binary(Vec<u8>),
94}
95
96impl IncompleteMessage {
97    /// Create new.
98    pub fn new(message_type: MessageType) -> Self {
99        IncompleteMessage {
100            collector: match message_type {
101                MessageType::Binary => IncompleteMessageCollector::Binary(Vec::new()),
102                MessageType::Text => IncompleteMessageCollector::Text(StringCollector::new()),
103            },
104            #[cfg(feature = "deflate")]
105            compressed: false,
106        }
107    }
108
109    /// Create new instance that will hold compressed data.
110    #[cfg(feature = "deflate")]
111    pub fn new_compressed(message_type: MessageType) -> Self {
112        IncompleteMessage {
113            collector: match message_type {
114                MessageType::Binary => IncompleteMessageCollector::Binary(Vec::new()),
115                MessageType::Text => IncompleteMessageCollector::Text(StringCollector::new()),
116            },
117            compressed: true,
118        }
119    }
120
121    pub fn compressed(&self) -> bool {
122        #[cfg(feature = "deflate")]
123        return self.compressed;
124        #[cfg(not(feature = "deflate"))]
125        return false;
126    }
127
128    /// Get the current filled size of the buffer.
129    pub fn len(&self) -> usize {
130        match self.collector {
131            IncompleteMessageCollector::Text(ref t) => t.len(),
132            IncompleteMessageCollector::Binary(ref b) => b.len(),
133        }
134    }
135
136    /// Add more data to an existing message.
137    pub fn extend<T: AsRef<[u8]>>(&mut self, tail: T, size_limit: Option<usize>) -> Result<()> {
138        // Always have a max size. This ensures an error in case of concatenating two buffers
139        // of more than `usize::max_value()` bytes in total.
140        let max_size = size_limit.unwrap_or_else(usize::max_value);
141        let my_size = self.len();
142        let portion_size = tail.as_ref().len();
143        // Be careful about integer overflows here.
144        if my_size > max_size || portion_size > max_size - my_size {
145            return Err(Error::Capacity(CapacityError::MessageTooLong {
146                size: my_size + portion_size,
147                max_size,
148            }));
149        }
150
151        match self.collector {
152            IncompleteMessageCollector::Binary(ref mut v) => {
153                v.extend(tail.as_ref());
154                Ok(())
155            }
156            IncompleteMessageCollector::Text(ref mut t) => t.extend(tail),
157        }
158    }
159
160    /// Convert an incomplete message into a complete one.
161    pub fn complete(self) -> Result<Message> {
162        match self.collector {
163            IncompleteMessageCollector::Binary(v) => Ok(Message::Binary(v.into())),
164            IncompleteMessageCollector::Text(t) => {
165                let text = t.into_string()?;
166                Ok(Message::text(text))
167            }
168        }
169    }
170}
171
172/// The type of incomplete message.
173pub enum MessageType {
174    Text,
175    Binary,
176}
177
178/// An enum representing the various forms of a WebSocket message.
179#[derive(Debug, Eq, PartialEq, Clone)]
180pub enum Message {
181    /// A text WebSocket message
182    Text(Utf8Bytes),
183    /// A binary WebSocket message
184    Binary(Bytes),
185    /// A ping message with the specified payload
186    ///
187    /// The payload here must have a length less than 125 bytes
188    Ping(Bytes),
189    /// A pong message with the specified payload
190    ///
191    /// The payload here must have a length less than 125 bytes
192    Pong(Bytes),
193    /// A close message with the optional close frame.
194    Close(Option<CloseFrame>),
195    /// Raw frame. Note, that you're not going to get this value while reading the message.
196    Frame(Frame),
197}
198
199impl Message {
200    /// Create a new text WebSocket message from a stringable.
201    pub fn text<S>(string: S) -> Message
202    where
203        S: Into<Utf8Bytes>,
204    {
205        Message::Text(string.into())
206    }
207
208    /// Create a new binary WebSocket message by converting to `Bytes`.
209    pub fn binary<B>(bin: B) -> Message
210    where
211        B: Into<Bytes>,
212    {
213        Message::Binary(bin.into())
214    }
215
216    /// Indicates whether a message is a text message.
217    pub fn is_text(&self) -> bool {
218        matches!(*self, Message::Text(_))
219    }
220
221    /// Indicates whether a message is a binary message.
222    pub fn is_binary(&self) -> bool {
223        matches!(*self, Message::Binary(_))
224    }
225
226    /// Indicates whether a message is a ping message.
227    pub fn is_ping(&self) -> bool {
228        matches!(*self, Message::Ping(_))
229    }
230
231    /// Indicates whether a message is a pong message.
232    pub fn is_pong(&self) -> bool {
233        matches!(*self, Message::Pong(_))
234    }
235
236    /// Indicates whether a message is a close message.
237    pub fn is_close(&self) -> bool {
238        matches!(*self, Message::Close(_))
239    }
240
241    /// Get the length of the WebSocket message.
242    pub fn len(&self) -> usize {
243        match *self {
244            Message::Text(ref string) => string.len(),
245            Message::Binary(ref data) | Message::Ping(ref data) | Message::Pong(ref data) => {
246                data.len()
247            }
248            Message::Close(ref data) => data.as_ref().map(|d| d.reason.len()).unwrap_or(0),
249            Message::Frame(ref frame) => frame.len(),
250        }
251    }
252
253    /// Returns true if the WebSocket message has no content.
254    /// For example, if the other side of the connection sent an empty string.
255    pub fn is_empty(&self) -> bool {
256        self.len() == 0
257    }
258
259    /// Consume the WebSocket and return it as binary data.
260    pub fn into_data(self) -> Bytes {
261        match self {
262            Message::Text(utf8) => utf8.into(),
263            Message::Binary(data) | Message::Ping(data) | Message::Pong(data) => data,
264            Message::Close(None) => <_>::default(),
265            Message::Close(Some(frame)) => frame.reason.into(),
266            Message::Frame(frame) => frame.into_payload(),
267        }
268    }
269
270    /// Attempt to consume the WebSocket message and convert it to a String.
271    pub fn into_text(self) -> Result<Utf8Bytes> {
272        match self {
273            Message::Text(txt) => Ok(txt),
274            Message::Binary(data) | Message::Ping(data) | Message::Pong(data) => {
275                Ok(data.try_into()?)
276            }
277            Message::Close(None) => Ok(<_>::default()),
278            Message::Close(Some(frame)) => Ok(frame.reason),
279            Message::Frame(frame) => Ok(frame.into_text()?),
280        }
281    }
282
283    /// Attempt to get a &str from the WebSocket message,
284    /// this will try to convert binary data to utf8.
285    pub fn to_text(&self) -> Result<&str> {
286        match *self {
287            Message::Text(ref string) => Ok(string.as_str()),
288            Message::Binary(ref data) | Message::Ping(ref data) | Message::Pong(ref data) => {
289                Ok(str::from_utf8(data)?)
290            }
291            Message::Close(None) => Ok(""),
292            Message::Close(Some(ref frame)) => Ok(&frame.reason),
293            Message::Frame(ref frame) => Ok(frame.to_text()?),
294        }
295    }
296}
297
298impl From<String> for Message {
299    #[inline]
300    fn from(string: String) -> Self {
301        Message::text(string)
302    }
303}
304
305impl<'s> From<&'s str> for Message {
306    #[inline]
307    fn from(string: &'s str) -> Self {
308        Message::text(string)
309    }
310}
311
312impl<'b> From<&'b [u8]> for Message {
313    #[inline]
314    fn from(data: &'b [u8]) -> Self {
315        Message::binary(Bytes::copy_from_slice(data))
316    }
317}
318
319impl From<Bytes> for Message {
320    fn from(data: Bytes) -> Self {
321        Message::binary(data)
322    }
323}
324
325impl From<Vec<u8>> for Message {
326    #[inline]
327    fn from(data: Vec<u8>) -> Self {
328        Message::binary(data)
329    }
330}
331
332impl From<Message> for Bytes {
333    #[inline]
334    fn from(message: Message) -> Self {
335        message.into_data()
336    }
337}
338
339impl fmt::Display for Message {
340    fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
341        if let Ok(string) = self.to_text() {
342            write!(f, "{string}")
343        } else {
344            write!(f, "Binary Data<length={}>", self.len())
345        }
346    }
347}
348
349#[cfg(test)]
350mod tests {
351    use super::*;
352
353    #[test]
354    fn display() {
355        let t = Message::text("test".to_owned());
356        assert_eq!(t.to_string(), "test".to_owned());
357
358        let bin = Message::binary(vec![0, 1, 3, 4, 241]);
359        assert_eq!(bin.to_string(), "Binary Data<length=5>".to_owned());
360    }
361
362    #[test]
363    fn binary_convert() {
364        let bin = [6u8, 7, 8, 9, 10, 241];
365        let msg = Message::from(&bin[..]);
366        assert!(msg.is_binary());
367        assert!(msg.into_text().is_err());
368    }
369
370    #[test]
371    fn binary_convert_bytes() {
372        let bin = Bytes::from_iter([6u8, 7, 8, 9, 10, 241]);
373        let msg = Message::from(bin);
374        assert!(msg.is_binary());
375        assert!(msg.into_text().is_err());
376    }
377
378    #[test]
379    fn binary_convert_vec() {
380        let bin = vec![6u8, 7, 8, 9, 10, 241];
381        let msg = Message::from(bin);
382        assert!(msg.is_binary());
383        assert!(msg.into_text().is_err());
384    }
385
386    #[test]
387    fn binary_convert_into_bytes() {
388        let bin = vec![6u8, 7, 8, 9, 10, 241];
389        let bin_copy = bin.clone();
390        let msg = Message::from(bin);
391        let serialized: Bytes = msg.into();
392        assert_eq!(bin_copy, serialized);
393    }
394
395    #[test]
396    fn text_convert() {
397        let s = "kiwotsukete";
398        let msg = Message::from(s);
399        assert!(msg.is_text());
400    }
401}