|
|
|
@ -2,24 +2,29 @@ |
|
|
|
|
|
|
|
|
|
pub mod frame; |
|
|
|
|
|
|
|
|
|
pub(crate) mod message; |
|
|
|
|
mod message; |
|
|
|
|
|
|
|
|
|
pub use self::frame::CloseFrame; |
|
|
|
|
pub use self::message::Message; |
|
|
|
|
pub use self::{frame::CloseFrame, message::Message}; |
|
|
|
|
|
|
|
|
|
use log::*; |
|
|
|
|
use std::collections::VecDeque; |
|
|
|
|
use std::io::{ErrorKind as IoErrorKind, Read, Write}; |
|
|
|
|
use std::mem::replace; |
|
|
|
|
|
|
|
|
|
use self::frame::coding::{CloseCode, Control as OpCtl, Data as OpData, OpCode}; |
|
|
|
|
use self::frame::{Frame, FrameCodec}; |
|
|
|
|
use self::message::IncompleteMessage; |
|
|
|
|
use crate::error::{Error, Result}; |
|
|
|
|
use crate::extensions::compression::{CompressionSwitcher, WsCompression}; |
|
|
|
|
use crate::extensions::WebSocketExtension; |
|
|
|
|
use crate::protocol::frame::coding::Data; |
|
|
|
|
use crate::util::NonBlockingResult; |
|
|
|
|
use std::{ |
|
|
|
|
collections::VecDeque, |
|
|
|
|
io::{ErrorKind as IoErrorKind, Read, Write}, |
|
|
|
|
mem::replace, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use self::{ |
|
|
|
|
frame::{ |
|
|
|
|
coding::{CloseCode, Control as OpCtl, Data as OpData, OpCode}, |
|
|
|
|
Frame, FrameCodec, |
|
|
|
|
}, |
|
|
|
|
message::{IncompleteMessage, IncompleteMessageType}, |
|
|
|
|
extensions::{WebSocketExtension, compression::{CompressionSwitcher, WsCompression}}; |
|
|
|
|
}; |
|
|
|
|
use crate::{ |
|
|
|
|
error::{Error, Result}, |
|
|
|
|
util::NonBlockingResult, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
pub(crate) const MAX_MESSAGE_SIZE: usize = 64 << 20; |
|
|
|
|
|
|
|
|
@ -33,7 +38,7 @@ pub enum Role { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// The configuration for WebSocket connection.
|
|
|
|
|
#[derive(Debug, Copy, Clone)] |
|
|
|
|
#[derive(Debug, Clone, Copy)] |
|
|
|
|
pub struct WebSocketConfig { |
|
|
|
|
/// The size of the send queue. You can use it to turn on/off the backpressure features. `None`
|
|
|
|
|
/// means here that the size of the queue is unlimited. The default value is the unlimited
|
|
|
|
@ -77,10 +82,7 @@ impl<Stream> WebSocket<Stream> { |
|
|
|
|
/// or together with an existing one. If you need an initial handshake, use
|
|
|
|
|
/// `connect()` or `accept()` functions of the crate to construct a websocket.
|
|
|
|
|
pub fn from_raw_socket(stream: Stream, role: Role, config: Option<WebSocketConfig>) -> Self { |
|
|
|
|
WebSocket { |
|
|
|
|
socket: stream, |
|
|
|
|
context: WebSocketContext::new(role, config), |
|
|
|
|
} |
|
|
|
|
WebSocket { socket: stream, context: WebSocketContext::new(role, config) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Convert a raw socket into a WebSocket without performing a handshake.
|
|
|
|
@ -136,10 +138,7 @@ impl<Stream> WebSocket<Stream> { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<Stream> WebSocket<Stream> |
|
|
|
|
where |
|
|
|
|
Stream: Read + Write, |
|
|
|
|
{ |
|
|
|
|
impl<Stream: Read + Write> WebSocket<Stream> { |
|
|
|
|
/// Read a message from stream, if possible.
|
|
|
|
|
///
|
|
|
|
|
/// This will queue responses to ping and close messages to be sent. It will call
|
|
|
|
@ -333,9 +332,7 @@ impl WebSocketContext { |
|
|
|
|
|
|
|
|
|
// Do not write after sending a close frame.
|
|
|
|
|
if !self.state.is_active() { |
|
|
|
|
return Err(Error::Protocol( |
|
|
|
|
"Sending after closing is not allowed".into(), |
|
|
|
|
)); |
|
|
|
|
return Err(Error::Protocol("Sending after closing is not allowed".into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if let Some(max_send_queue) = self.config.max_send_queue { |
|
|
|
@ -457,9 +454,7 @@ impl WebSocketContext { |
|
|
|
|
Role::Client => { |
|
|
|
|
if frame.is_masked() { |
|
|
|
|
// A client MUST close a connection if it detects a masked frame. (RFC 6455)
|
|
|
|
|
return Err(Error::Protocol( |
|
|
|
|
"Received a masked frame from server".into(), |
|
|
|
|
)); |
|
|
|
|
return Err(Error::Protocol("Received a masked frame from server".into())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -476,9 +471,9 @@ impl WebSocketContext { |
|
|
|
|
Err(Error::Protocol("Control frame too big".into())) |
|
|
|
|
} |
|
|
|
|
OpCtl::Close => Ok(self.do_close(frame.into_close()?).map(Message::Close)), |
|
|
|
|
OpCtl::Reserved(i) => Err(Error::Protocol( |
|
|
|
|
format!("Unknown control frame type {}", i).into(), |
|
|
|
|
)), |
|
|
|
|
OpCtl::Reserved(i) => { |
|
|
|
|
Err(Error::Protocol(format!("Unknown control frame type {}", i).into())) |
|
|
|
|
} |
|
|
|
|
OpCtl::Ping => { |
|
|
|
|
let data = frame.into_data(); |
|
|
|
|
// No ping processing after we sent a close frame.
|
|
|
|
@ -568,43 +563,8 @@ impl WebSocketContext { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if frame.header().is_final { |
|
|
|
|
frame = self.decoder.on_send_frame(frame)?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let max_frame_size = self.config.max_frame_size.unwrap_or_else(usize::max_value); |
|
|
|
|
if frame.payload().len() > max_frame_size { |
|
|
|
|
let mut chunks = frame.payload().chunks(max_frame_size).peekable(); |
|
|
|
|
let data_frame = Frame::message( |
|
|
|
|
Vec::from(chunks.next().unwrap()), |
|
|
|
|
frame.header().opcode, |
|
|
|
|
false, |
|
|
|
|
); |
|
|
|
|
self.frame |
|
|
|
|
.write_frame(stream, data_frame) |
|
|
|
|
.check_connection_reset(self.state)?; |
|
|
|
|
|
|
|
|
|
while let Some(chunk) = chunks.next() { |
|
|
|
|
let frame = Frame::message( |
|
|
|
|
Vec::from(chunk), |
|
|
|
|
OpCode::Data(Data::Continue), |
|
|
|
|
chunks.peek().is_none(), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
trace!("Sending frame: {:?}", frame); |
|
|
|
|
|
|
|
|
|
self.frame |
|
|
|
|
.write_frame(stream, frame) |
|
|
|
|
.check_connection_reset(self.state)?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} else { |
|
|
|
|
trace!("Sending frame: {:?}", frame); |
|
|
|
|
self.frame |
|
|
|
|
.write_frame(stream, frame) |
|
|
|
|
.check_connection_reset(self.state) |
|
|
|
|
} |
|
|
|
|
trace!("Sending frame: {:?}", frame); |
|
|
|
|
self.frame.write_frame(stream, frame).check_connection_reset(self.state) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -626,20 +586,14 @@ enum WebSocketState { |
|
|
|
|
impl WebSocketState { |
|
|
|
|
/// Tell if we're allowed to process normal messages.
|
|
|
|
|
fn is_active(self) -> bool { |
|
|
|
|
match self { |
|
|
|
|
WebSocketState::Active => true, |
|
|
|
|
_ => false, |
|
|
|
|
} |
|
|
|
|
matches!(self, WebSocketState::Active) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Tell if we should process incoming data. Note that if we send a close frame
|
|
|
|
|
/// but the remote hasn't confirmed, they might have sent data before they receive our
|
|
|
|
|
/// close frame, so we should still pass those to client code, hence ClosedByUs is valid.
|
|
|
|
|
fn can_read(self) -> bool { |
|
|
|
|
match self { |
|
|
|
|
WebSocketState::Active | WebSocketState::ClosedByUs => true, |
|
|
|
|
_ => false, |
|
|
|
|
} |
|
|
|
|
matches!(self, WebSocketState::Active | WebSocketState::ClosedByUs) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Check if the state is active, return error if not.
|
|
|
|
@ -675,11 +629,7 @@ impl<T> CheckConnectionReset for Result<T> { |
|
|
|
|
mod tests { |
|
|
|
|
use super::{Message, Role, WebSocket, WebSocketConfig}; |
|
|
|
|
|
|
|
|
|
use crate::extensions::compression::WsCompression; |
|
|
|
|
use crate::protocol::frame::coding::{Data, OpCode}; |
|
|
|
|
use crate::protocol::frame::Frame; |
|
|
|
|
use std::io; |
|
|
|
|
use std::io::Cursor; |
|
|
|
|
use std::{io, io::Cursor}; |
|
|
|
|
|
|
|
|
|
struct WriteMoc<Stream>(Stream); |
|
|
|
|
|
|
|
|
@ -708,14 +658,8 @@ mod tests { |
|
|
|
|
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, None); |
|
|
|
|
assert_eq!(socket.read_message().unwrap(), Message::Ping(vec![1, 2])); |
|
|
|
|
assert_eq!(socket.read_message().unwrap(), Message::Pong(vec![3])); |
|
|
|
|
assert_eq!( |
|
|
|
|
socket.read_message().unwrap(), |
|
|
|
|
Message::Text("Hello, World!".into()) |
|
|
|
|
); |
|
|
|
|
assert_eq!( |
|
|
|
|
socket.read_message().unwrap(), |
|
|
|
|
Message::Binary(vec![0x01, 0x02, 0x03]) |
|
|
|
|
); |
|
|
|
|
assert_eq!(socket.read_message().unwrap(), Message::Text("Hello, World!".into())); |
|
|
|
|
assert_eq!(socket.read_message().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03])); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
@ -724,11 +668,7 @@ mod tests { |
|
|
|
|
0x01, 0x07, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x80, 0x06, 0x57, 0x6f, 0x72, |
|
|
|
|
0x6c, 0x64, 0x21, |
|
|
|
|
]); |
|
|
|
|
let limit = WebSocketConfig { |
|
|
|
|
max_send_queue: None, |
|
|
|
|
max_frame_size: Some(16 << 20), |
|
|
|
|
compression: WsCompression::None(Some(10)), |
|
|
|
|
}; |
|
|
|
|
let limit = WebSocketConfig { max_message_size: Some(10), ..WebSocketConfig::default() }; |
|
|
|
|
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit)); |
|
|
|
|
assert_eq!( |
|
|
|
|
socket.read_message().unwrap_err().to_string(), |
|
|
|
@ -739,80 +679,11 @@ mod tests { |
|
|
|
|
#[test] |
|
|
|
|
fn size_limiting_binary() { |
|
|
|
|
let incoming = Cursor::new(vec![0x82, 0x03, 0x01, 0x02, 0x03]); |
|
|
|
|
let limit = WebSocketConfig { |
|
|
|
|
max_send_queue: None, |
|
|
|
|
max_frame_size: Some(16 << 20), |
|
|
|
|
compression: WsCompression::None(Some(2)), |
|
|
|
|
}; |
|
|
|
|
let limit = WebSocketConfig { max_message_size: Some(2), ..WebSocketConfig::default() }; |
|
|
|
|
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit)); |
|
|
|
|
assert_eq!( |
|
|
|
|
socket.read_message().unwrap_err().to_string(), |
|
|
|
|
"Space limit exceeded: Message too big: 0 + 3 > 2" |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn fragmented_tx() { |
|
|
|
|
let max_message_size = 2; |
|
|
|
|
let input_str = "hello unit test"; |
|
|
|
|
|
|
|
|
|
let limit = WebSocketConfig { |
|
|
|
|
max_send_queue: None, |
|
|
|
|
max_frame_size: Some(2), |
|
|
|
|
compression: WsCompression::None(Some(max_message_size)), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let mut socket = |
|
|
|
|
WebSocket::from_raw_socket(Cursor::new(Vec::new()), Role::Client, Some(limit)); |
|
|
|
|
|
|
|
|
|
socket.write_message(Message::text(input_str)).unwrap(); |
|
|
|
|
socket.socket.set_position(0); |
|
|
|
|
|
|
|
|
|
let WebSocket { |
|
|
|
|
mut socket, |
|
|
|
|
mut context, |
|
|
|
|
} = socket; |
|
|
|
|
|
|
|
|
|
let vec = input_str.chars().collect::<Vec<_>>(); |
|
|
|
|
let mut iter = vec |
|
|
|
|
.chunks(max_message_size) |
|
|
|
|
.map(|c| c.iter().collect::<String>()) |
|
|
|
|
.into_iter() |
|
|
|
|
.peekable(); |
|
|
|
|
|
|
|
|
|
let frame_eq = |expected: Frame, actual: Frame| { |
|
|
|
|
assert_eq!(expected.payload(), actual.payload()); |
|
|
|
|
assert_eq!(expected.header().opcode, actual.header().opcode); |
|
|
|
|
assert_eq!( |
|
|
|
|
expected.header().ext_headers.rsv1, |
|
|
|
|
actual.header().ext_headers.rsv1 |
|
|
|
|
); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let expected = Frame::message(iter.next().unwrap().into(), OpCode::Data(Data::Text), false); |
|
|
|
|
frame_eq( |
|
|
|
|
expected, |
|
|
|
|
context |
|
|
|
|
.frame |
|
|
|
|
.read_frame(&mut socket, Some(max_message_size)) |
|
|
|
|
.unwrap() |
|
|
|
|
.unwrap(), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
while let Some(chars) = iter.next() { |
|
|
|
|
let expected = Frame::message( |
|
|
|
|
chars.into(), |
|
|
|
|
OpCode::Data(Data::Continue), |
|
|
|
|
iter.peek().is_none(), |
|
|
|
|
); |
|
|
|
|
frame_eq( |
|
|
|
|
expected, |
|
|
|
|
context |
|
|
|
|
.frame |
|
|
|
|
.read_frame(&mut socket, Some(max_message_size)) |
|
|
|
|
.unwrap() |
|
|
|
|
.unwrap(), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|