diff --git a/examples/autobahn-client.rs b/examples/autobahn-client.rs index 8143175..2538d86 100644 --- a/examples/autobahn-client.rs +++ b/examples/autobahn-client.rs @@ -30,7 +30,7 @@ fn run_test(case: u32) -> Result<()> { msg @ Message::Text(_) | msg @ Message::Binary(_) => { socket.write_message(msg)?; } - Message::Ping(_) | Message::Pong(_) | Message::Close(_) => {} + Message::Ping(_) | Message::Pong(_) | Message::Close(_) | Message::Frame(_) => {} } } } diff --git a/examples/autobahn-server.rs b/examples/autobahn-server.rs index 3250b2c..f002efa 100644 --- a/examples/autobahn-server.rs +++ b/examples/autobahn-server.rs @@ -21,7 +21,7 @@ fn handle_client(stream: TcpStream) -> Result<()> { msg @ Message::Text(_) | msg @ Message::Binary(_) => { socket.write_message(msg)?; } - Message::Ping(_) | Message::Pong(_) | Message::Close(_) => {} + Message::Ping(_) | Message::Pong(_) | Message::Close(_) | Message::Frame(_) => {} } } } diff --git a/src/protocol/frame/frame.rs b/src/protocol/frame/frame.rs index 8222fa1..1298ebe 100644 --- a/src/protocol/frame/frame.rs +++ b/src/protocol/frame/frame.rs @@ -6,6 +6,7 @@ use std::{ fmt, io::{Cursor, ErrorKind, Read, Write}, result::Result as StdResult, + str::Utf8Error, string::{FromUtf8Error, String}, }; @@ -39,7 +40,7 @@ impl<'t> fmt::Display for CloseFrame<'t> { /// A struct representing a WebSocket frame header. #[allow(missing_copy_implementations)] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct FrameHeader { /// Indicates that the frame is the last one of a possibly fragmented message. pub is_final: bool, @@ -199,7 +200,7 @@ impl FrameHeader { } /// A struct representing a WebSocket frame. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Frame { header: FrameHeader, payload: Vec, @@ -280,6 +281,12 @@ impl Frame { String::from_utf8(self.payload) } + /// Get frame payload as `&str`. + #[inline] + pub fn to_text<'a>(&'a self) -> Result<&'a str, Utf8Error> { + std::str::from_utf8(&self.payload) + } + /// Consume the frame into a closing frame. #[inline] pub(crate) fn into_close(self) -> Result>> { diff --git a/src/protocol/message.rs b/src/protocol/message.rs index e8eb90e..72ee181 100644 --- a/src/protocol/message.rs +++ b/src/protocol/message.rs @@ -5,7 +5,7 @@ use std::{ str, }; -use super::frame::CloseFrame; +use super::frame::{CloseFrame, Frame}; use crate::error::{CapacityError, Error, Result}; mod string_collect { @@ -172,6 +172,8 @@ pub enum Message { Pong(Vec), /// A close message with the optional close frame. Close(Option>), + /// Raw frame. Note, that you're not going to get this value while reading the message. + Frame(Frame), } impl Message { @@ -224,6 +226,7 @@ impl Message { data.len() } Message::Close(ref data) => data.as_ref().map(|d| d.reason.len()).unwrap_or(0), + Message::Frame(ref frame) => frame.len(), } } @@ -240,6 +243,7 @@ impl Message { Message::Binary(data) | Message::Ping(data) | Message::Pong(data) => data, Message::Close(None) => Vec::new(), Message::Close(Some(frame)) => frame.reason.into_owned().into_bytes(), + Message::Frame(frame) => frame.into_data(), } } @@ -248,10 +252,11 @@ impl Message { match self { Message::Text(string) => Ok(string), Message::Binary(data) | Message::Ping(data) | Message::Pong(data) => { - Ok(String::from_utf8(data).map_err(|err| err.utf8_error())?) + Ok(String::from_utf8(data)?) } Message::Close(None) => Ok(String::new()), Message::Close(Some(frame)) => Ok(frame.reason.into_owned()), + Message::Frame(frame) => Ok(frame.into_string()?), } } @@ -265,6 +270,7 @@ impl Message { } Message::Close(None) => Ok(""), Message::Close(Some(ref frame)) => Ok(&frame.reason), + Message::Frame(ref frame) => Ok(frame.to_text()?), } } } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 0586dcd..54b8df1 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -356,6 +356,7 @@ impl WebSocketContext { return self.write_pending(stream); } Message::Close(code) => return self.close(stream, code), + Message::Frame(f) => f, }; self.send_queue.push_back(frame);