1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 17:59:35 +00:00
actix-web/src/ws/frame.rs

526 lines
16 KiB
Rust
Raw Normal View History

2018-06-23 04:29:23 +00:00
use byteorder::{ByteOrder, LittleEndian, NetworkEndian};
2018-04-13 23:02:01 +00:00
use bytes::{BufMut, Bytes, BytesMut};
2018-02-26 21:58:23 +00:00
use futures::{Async, Poll, Stream};
2018-02-10 04:43:14 +00:00
use rand;
2018-06-23 04:29:23 +00:00
use std::fmt;
2017-10-08 04:48:00 +00:00
use body::Binary;
2018-04-13 23:02:01 +00:00
use error::PayloadError;
2018-02-26 21:58:23 +00:00
use payload::PayloadHelper;
2018-02-27 18:09:24 +00:00
use ws::mask::apply_mask;
use ws::proto::{CloseCode, CloseReason, OpCode};
2018-04-29 05:55:47 +00:00
use ws::ProtocolError;
2017-10-08 04:48:00 +00:00
/// A struct representing a `WebSocket` frame.
#[derive(Debug)]
2018-03-09 21:03:15 +00:00
pub struct Frame {
2017-10-08 04:48:00 +00:00
finished: bool,
opcode: OpCode,
payload: Binary,
2017-10-08 04:48:00 +00:00
}
impl Frame {
2018-01-15 21:47:25 +00:00
/// Destruct frame
pub fn unpack(self) -> (bool, OpCode, Binary) {
2017-10-08 04:48:00 +00:00
(self.finished, self.opcode, self.payload)
}
/// Create a new Close control frame.
#[inline]
pub fn close(reason: Option<CloseReason>, genmask: bool) -> Binary {
2018-04-22 15:43:47 +00:00
let payload = match reason {
None => Vec::new(),
Some(reason) => {
let mut code_bytes = [0; 2];
NetworkEndian::write_u16(&mut code_bytes, reason.code.into());
let mut payload = Vec::from(&code_bytes[..]);
2018-04-29 05:55:47 +00:00
if let Some(description) = reason.description {
2018-04-22 15:43:47 +00:00
payload.extend(description.as_bytes());
}
payload
}
};
Frame::message(payload, OpCode::Close, true, genmask)
2017-10-08 04:48:00 +00:00
}
2018-04-13 23:02:01 +00:00
#[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
fn read_copy_md<S>(
2018-04-29 05:55:47 +00:00
pl: &mut PayloadHelper<S>, server: bool, max_size: usize,
2018-03-09 04:39:05 +00:00
) -> Poll<Option<(usize, bool, OpCode, usize, Option<u32>)>, ProtocolError>
2018-04-13 23:02:01 +00:00
where
S: Stream<Item = Bytes, Error = PayloadError>,
2018-02-26 21:58:23 +00:00
{
2017-10-08 04:48:00 +00:00
let mut idx = 2;
2018-02-26 21:58:23 +00:00
let buf = match pl.copy(2)? {
Async::Ready(Some(buf)) => buf,
Async::Ready(None) => return Ok(Async::Ready(None)),
Async::NotReady => return Ok(Async::NotReady),
};
let first = buf[0];
let second = buf[1];
let finished = first & 0x80 != 0;
2018-02-10 04:43:14 +00:00
// check masking
let masked = second & 0x80 != 0;
if !masked && server {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::UnmaskedFrame);
2018-02-10 04:43:14 +00:00
} else if masked && !server {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::MaskedFrame);
2018-02-10 04:43:14 +00:00
}
2018-03-09 04:39:05 +00:00
// Op code
let opcode = OpCode::from(first & 0x0F);
2018-03-09 04:39:05 +00:00
if let OpCode::Bad = opcode {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::InvalidOpcode(first & 0x0F));
2018-03-09 04:39:05 +00:00
}
let len = second & 0x7F;
let length = if len == 126 {
2018-02-26 21:58:23 +00:00
let buf = match pl.copy(4)? {
Async::Ready(Some(buf)) => buf,
Async::Ready(None) => return Ok(Async::Ready(None)),
Async::NotReady => return Ok(Async::NotReady),
};
2018-02-10 04:43:14 +00:00
let len = NetworkEndian::read_uint(&buf[idx..], 2) as usize;
idx += 2;
len
} else if len == 127 {
2018-02-26 21:58:23 +00:00
let buf = match pl.copy(10)? {
Async::Ready(Some(buf)) => buf,
Async::Ready(None) => return Ok(Async::Ready(None)),
Async::NotReady => return Ok(Async::NotReady),
};
2018-02-10 04:43:14 +00:00
let len = NetworkEndian::read_uint(&buf[idx..], 8) as usize;
idx += 8;
len
} else {
2018-02-10 04:43:14 +00:00
len as usize
};
2017-10-08 04:48:00 +00:00
2018-02-27 18:09:24 +00:00
// check for max allowed size
if length > max_size {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::Overflow);
2018-02-27 18:09:24 +00:00
}
2018-02-10 04:43:14 +00:00
let mask = if server {
2018-02-26 21:58:23 +00:00
let buf = match pl.copy(idx + 4)? {
Async::Ready(Some(buf)) => buf,
Async::Ready(None) => return Ok(Async::Ready(None)),
Async::NotReady => return Ok(Async::NotReady),
};
2018-04-13 23:02:01 +00:00
let mask: &[u8] = &buf[idx..idx + 4];
2018-06-23 04:29:23 +00:00
let mask_u32 = LittleEndian::read_u32(mask);
2018-02-26 21:58:23 +00:00
idx += 4;
2018-03-09 04:39:05 +00:00
Some(mask_u32)
} else {
None
};
2017-10-08 04:48:00 +00:00
2018-05-17 19:20:20 +00:00
Ok(Async::Ready(Some((idx, finished, opcode, length, mask))))
2018-03-09 04:39:05 +00:00
}
2018-04-13 23:02:01 +00:00
fn read_chunk_md(
2018-04-29 05:55:47 +00:00
chunk: &[u8], server: bool, max_size: usize,
2018-04-13 23:02:01 +00:00
) -> Poll<(usize, bool, OpCode, usize, Option<u32>), ProtocolError> {
2018-03-09 04:39:05 +00:00
let chunk_len = chunk.len();
let mut idx = 2;
if chunk_len < 2 {
2018-04-13 23:02:01 +00:00
return Ok(Async::NotReady);
2018-03-09 04:39:05 +00:00
}
let first = chunk[0];
let second = chunk[1];
let finished = first & 0x80 != 0;
// check masking
let masked = second & 0x80 != 0;
if !masked && server {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::UnmaskedFrame);
2018-03-09 04:39:05 +00:00
} else if masked && !server {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::MaskedFrame);
2018-03-09 04:39:05 +00:00
}
// Op code
let opcode = OpCode::from(first & 0x0F);
if let OpCode::Bad = opcode {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::InvalidOpcode(first & 0x0F));
2018-03-09 04:39:05 +00:00
}
let len = second & 0x7F;
let length = if len == 126 {
if chunk_len < 4 {
2018-04-13 23:02:01 +00:00
return Ok(Async::NotReady);
2018-03-09 04:39:05 +00:00
}
let len = NetworkEndian::read_uint(&chunk[idx..], 2) as usize;
idx += 2;
len
} else if len == 127 {
if chunk_len < 10 {
2018-04-13 23:02:01 +00:00
return Ok(Async::NotReady);
2018-03-09 04:39:05 +00:00
}
let len = NetworkEndian::read_uint(&chunk[idx..], 8) as usize;
idx += 8;
len
} else {
len as usize
};
// check for max allowed size
if length > max_size {
2018-04-13 23:02:01 +00:00
return Err(ProtocolError::Overflow);
2018-03-09 04:39:05 +00:00
}
let mask = if server {
if chunk_len < idx + 4 {
2018-04-13 23:02:01 +00:00
return Ok(Async::NotReady);
2018-03-09 04:39:05 +00:00
}
2018-04-13 23:02:01 +00:00
let mask: &[u8] = &chunk[idx..idx + 4];
2018-06-23 04:29:23 +00:00
let mask_u32 = LittleEndian::read_u32(mask);
2018-03-09 04:39:05 +00:00
idx += 4;
Some(mask_u32)
} else {
None
};
Ok(Async::Ready((idx, finished, opcode, length, mask)))
}
/// Parse the input stream into a frame.
2018-04-13 23:02:01 +00:00
pub fn parse<S>(
2018-04-29 05:55:47 +00:00
pl: &mut PayloadHelper<S>, server: bool, max_size: usize,
2018-04-13 23:02:01 +00:00
) -> Poll<Option<Frame>, ProtocolError>
where
S: Stream<Item = Bytes, Error = PayloadError>,
2018-03-09 04:39:05 +00:00
{
2018-03-09 13:36:40 +00:00
// try to parse ws frame md from one chunk
2018-03-09 04:39:05 +00:00
let result = match pl.get_chunk()? {
Async::NotReady => return Ok(Async::NotReady),
Async::Ready(None) => return Ok(Async::Ready(None)),
2018-03-09 21:03:15 +00:00
Async::Ready(Some(chunk)) => Frame::read_chunk_md(chunk, server, max_size)?,
2018-03-09 04:39:05 +00:00
};
let (idx, finished, opcode, length, mask) = match result {
2018-03-09 13:36:40 +00:00
// we may need to join several chunks
2018-03-09 04:39:05 +00:00
Async::NotReady => match Frame::read_copy_md(pl, server, max_size)? {
Async::Ready(Some(item)) => item,
2018-03-09 21:03:15 +00:00
Async::NotReady => return Ok(Async::NotReady),
2018-03-09 04:39:05 +00:00
Async::Ready(None) => return Ok(Async::Ready(None)),
},
Async::Ready(item) => item,
};
2018-03-09 01:19:50 +00:00
match pl.can_read(idx + length)? {
Async::Ready(Some(true)) => (),
2018-02-26 21:58:23 +00:00
Async::Ready(None) => return Ok(Async::Ready(None)),
2018-03-09 01:19:50 +00:00
Async::Ready(Some(false)) | Async::NotReady => return Ok(Async::NotReady),
}
// remove prefix
pl.drop_payload(idx);
2018-03-09 21:03:15 +00:00
// no need for body
2018-03-09 02:19:46 +00:00
if length == 0 {
return Ok(Async::Ready(Some(Frame {
2018-04-13 23:02:01 +00:00
finished,
opcode,
payload: Binary::from(""),
})));
2018-03-09 02:19:46 +00:00
}
2018-03-09 13:36:40 +00:00
let data = match pl.read_exact(length)? {
2018-03-09 01:19:50 +00:00
Async::Ready(Some(buf)) => buf,
Async::Ready(None) => return Ok(Async::Ready(None)),
Async::NotReady => panic!(),
};
// control frames must have length <= 125
match opcode {
OpCode::Ping | OpCode::Pong if length > 125 => {
return Err(ProtocolError::InvalidLength(length))
2017-10-08 04:48:00 +00:00
}
OpCode::Close if length > 125 => {
debug!("Received close frame with payload length exceeding 125. Morphing to protocol close frame.");
2018-04-13 23:02:01 +00:00
return Ok(Async::Ready(Some(Frame::default())));
2017-10-08 04:48:00 +00:00
}
2018-04-13 23:02:01 +00:00
_ => (),
}
2017-10-08 04:48:00 +00:00
// unmask
2018-03-09 04:39:05 +00:00
if let Some(mask) = mask {
2018-04-13 23:02:01 +00:00
let p: &mut [u8] = unsafe {
let ptr: &[u8] = &data;
2018-04-29 16:09:08 +00:00
&mut *(ptr as *const _ as *mut _)
2018-04-13 23:02:01 +00:00
};
2018-03-09 01:19:50 +00:00
apply_mask(p, mask);
}
2017-10-08 04:48:00 +00:00
2018-02-26 21:58:23 +00:00
Ok(Async::Ready(Some(Frame {
2018-04-13 23:02:01 +00:00
finished,
opcode,
payload: data.into(),
})))
2017-10-08 04:48:00 +00:00
}
/// Parse the payload of a close frame.
pub fn parse_close_payload(payload: &Binary) -> Option<CloseReason> {
if payload.len() >= 2 {
let raw_code = NetworkEndian::read_uint(payload.as_ref(), 2) as u16;
let code = CloseCode::from(raw_code);
let description = if payload.len() > 2 {
Some(String::from_utf8_lossy(&payload.as_ref()[2..]).into())
} else {
None
};
2018-05-17 19:20:20 +00:00
Some(CloseReason { code, description })
} else {
None
}
}
2018-02-10 04:43:14 +00:00
/// Generate binary representation
2018-04-13 23:02:01 +00:00
pub fn message<B: Into<Binary>>(
2018-04-29 05:55:47 +00:00
data: B, code: OpCode, finished: bool, genmask: bool,
2018-04-13 23:02:01 +00:00
) -> Binary {
2018-02-10 08:05:20 +00:00
let payload = data.into();
let one: u8 = if finished {
0x80 | Into::<u8>::into(code)
2018-02-10 04:43:14 +00:00
} else {
2018-02-10 08:05:20 +00:00
code.into()
};
let payload_len = payload.len();
let (two, p_len) = if genmask {
(0x80, payload_len + 4)
} else {
(0, payload_len)
2018-02-10 04:43:14 +00:00
};
2017-10-08 04:48:00 +00:00
2018-02-10 08:05:20 +00:00
let mut buf = if payload_len < 126 {
let mut buf = BytesMut::with_capacity(p_len + 2);
buf.put_slice(&[one, two | payload_len as u8]);
2018-02-10 04:43:14 +00:00
buf
} else if payload_len <= 65_535 {
2018-02-10 08:05:20 +00:00
let mut buf = BytesMut::with_capacity(p_len + 4);
buf.put_slice(&[one, two | 126]);
buf.put_u16_be(payload_len as u16);
2018-02-10 04:43:14 +00:00
buf
2017-10-08 04:48:00 +00:00
} else {
2018-02-23 09:45:33 +00:00
let mut buf = BytesMut::with_capacity(p_len + 10);
2018-02-10 08:05:20 +00:00
buf.put_slice(&[one, two | 127]);
buf.put_u64_be(payload_len as u64);
2018-02-10 04:43:14 +00:00
buf
};
2017-10-08 04:48:00 +00:00
2018-02-10 04:43:14 +00:00
if genmask {
2018-03-09 04:39:05 +00:00
let mask = rand::random::<u32>();
2018-06-23 04:29:23 +00:00
buf.put_u32_le(mask);
buf.extend_from_slice(payload.as_ref());
let pos = buf.len() - payload_len;
apply_mask(&mut buf[pos..], mask);
2018-02-10 08:05:20 +00:00
buf.into()
} else {
2018-02-10 08:05:20 +00:00
buf.put_slice(payload.as_ref());
buf.into()
2017-10-08 04:48:00 +00:00
}
}
}
impl Default for Frame {
fn default() -> Frame {
Frame {
finished: true,
opcode: OpCode::Close,
payload: Binary::from(&b""[..]),
2017-10-08 04:48:00 +00:00
}
}
}
impl fmt::Display for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2018-04-13 23:02:01 +00:00
write!(
f,
2017-10-08 04:48:00 +00:00
"
<FRAME>
final: {}
opcode: {}
payload length: {}
payload: 0x{}
</FRAME>",
2018-04-13 23:02:01 +00:00
self.finished,
self.opcode,
self.payload.len(),
self.payload
.as_ref()
.iter()
.map(|byte| format!("{:x}", byte))
.collect::<String>()
)
2017-10-08 04:48:00 +00:00
}
}
2017-10-23 20:31:22 +00:00
#[cfg(test)]
mod tests {
use super::*;
2018-02-26 21:58:23 +00:00
use futures::stream::once;
2018-04-05 03:24:09 +00:00
fn is_none(frm: &Poll<Option<Frame>, ProtocolError>) -> bool {
match *frm {
2018-02-26 21:58:23 +00:00
Ok(Async::Ready(None)) => true,
_ => false,
}
}
2018-04-13 23:02:01 +00:00
fn extract(frm: Poll<Option<Frame>, ProtocolError>) -> Frame {
2018-02-26 21:58:23 +00:00
match frm {
Ok(Async::Ready(Some(frame))) => frame,
2018-03-29 22:55:27 +00:00
_ => unreachable!("error"),
2018-02-26 21:58:23 +00:00
}
}
2017-10-23 20:31:22 +00:00
#[test]
fn test_parse() {
2018-04-13 23:02:01 +00:00
let mut buf = PayloadHelper::new(once(Ok(BytesMut::from(
&[0b0000_0001u8, 0b0000_0001u8][..],
).freeze())));
2018-04-05 03:24:09 +00:00
assert!(is_none(&Frame::parse(&mut buf, false, 1024)));
2018-02-26 21:58:23 +00:00
2018-04-05 03:24:09 +00:00
let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);
2017-10-23 20:31:22 +00:00
buf.extend(b"1");
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2018-02-27 18:09:24 +00:00
let frame = extract(Frame::parse(&mut buf, false, 1024));
2017-10-23 20:31:22 +00:00
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload.as_ref(), &b"1"[..]);
2017-10-23 20:31:22 +00:00
}
#[test]
fn test_parse_length0() {
2018-04-05 03:24:09 +00:00
let buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0000u8][..]);
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2018-02-27 18:09:24 +00:00
let frame = extract(Frame::parse(&mut buf, false, 1024));
2017-10-23 20:31:22 +00:00
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert!(frame.payload.is_empty());
}
#[test]
fn test_parse_length2() {
2018-04-05 03:24:09 +00:00
let buf = BytesMut::from(&[0b0000_0001u8, 126u8][..]);
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2018-04-05 03:24:09 +00:00
assert!(is_none(&Frame::parse(&mut buf, false, 1024)));
2018-02-26 21:58:23 +00:00
2018-04-05 03:24:09 +00:00
let mut buf = BytesMut::from(&[0b0000_0001u8, 126u8][..]);
2017-10-23 20:31:22 +00:00
buf.extend(&[0u8, 4u8][..]);
buf.extend(b"1234");
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2017-10-23 20:31:22 +00:00
2018-02-27 18:09:24 +00:00
let frame = extract(Frame::parse(&mut buf, false, 1024));
2017-10-23 20:31:22 +00:00
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload.as_ref(), &b"1234"[..]);
2017-10-23 20:31:22 +00:00
}
#[test]
fn test_parse_length4() {
2018-04-05 03:24:09 +00:00
let buf = BytesMut::from(&[0b0000_0001u8, 127u8][..]);
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2018-04-05 03:24:09 +00:00
assert!(is_none(&Frame::parse(&mut buf, false, 1024)));
2018-02-26 21:58:23 +00:00
2018-04-05 03:24:09 +00:00
let mut buf = BytesMut::from(&[0b0000_0001u8, 127u8][..]);
2017-10-23 20:31:22 +00:00
buf.extend(&[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 4u8][..]);
buf.extend(b"1234");
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2017-10-23 20:31:22 +00:00
2018-02-27 18:09:24 +00:00
let frame = extract(Frame::parse(&mut buf, false, 1024));
2017-10-23 20:31:22 +00:00
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload.as_ref(), &b"1234"[..]);
2017-10-23 20:31:22 +00:00
}
#[test]
fn test_parse_frame_mask() {
2018-04-05 03:24:09 +00:00
let mut buf = BytesMut::from(&[0b0000_0001u8, 0b1000_0001u8][..]);
2017-10-23 20:31:22 +00:00
buf.extend(b"0001");
buf.extend(b"1");
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2017-10-23 20:31:22 +00:00
2018-02-27 18:09:24 +00:00
assert!(Frame::parse(&mut buf, false, 1024).is_err());
2018-02-10 04:43:14 +00:00
2018-02-27 18:09:24 +00:00
let frame = extract(Frame::parse(&mut buf, true, 1024));
2018-02-10 04:43:14 +00:00
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload, vec![1u8].into());
}
#[test]
fn test_parse_frame_no_mask() {
2018-04-05 03:24:09 +00:00
let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);
2018-02-10 04:43:14 +00:00
buf.extend(&[1u8]);
2018-02-26 21:58:23 +00:00
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
2018-02-10 04:43:14 +00:00
2018-02-27 18:09:24 +00:00
assert!(Frame::parse(&mut buf, true, 1024).is_err());
2018-02-10 04:43:14 +00:00
2018-02-27 18:09:24 +00:00
let frame = extract(Frame::parse(&mut buf, false, 1024));
2017-10-23 20:31:22 +00:00
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload, vec![1u8].into());
2017-10-23 20:31:22 +00:00
}
2018-02-27 18:09:24 +00:00
#[test]
fn test_parse_frame_max_size() {
2018-04-05 03:24:09 +00:00
let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0010u8][..]);
2018-02-27 18:09:24 +00:00
buf.extend(&[1u8, 1u8]);
let mut buf = PayloadHelper::new(once(Ok(buf.freeze())));
assert!(Frame::parse(&mut buf, true, 1).is_err());
if let Err(ProtocolError::Overflow) = Frame::parse(&mut buf, false, 0) {
2018-02-27 18:09:24 +00:00
} else {
2018-03-29 22:55:27 +00:00
unreachable!("error");
2018-02-27 18:09:24 +00:00
}
}
2017-10-23 20:31:22 +00:00
#[test]
fn test_ping_frame() {
2018-02-10 08:05:20 +00:00
let frame = Frame::message(Vec::from("data"), OpCode::Ping, true, false);
2017-10-23 20:31:22 +00:00
let mut v = vec![137u8, 4u8];
v.extend(b"data");
2018-02-10 08:05:20 +00:00
assert_eq!(frame, v.into());
2017-10-23 20:31:22 +00:00
}
#[test]
fn test_pong_frame() {
2018-02-10 08:05:20 +00:00
let frame = Frame::message(Vec::from("data"), OpCode::Pong, true, false);
2017-10-23 20:31:22 +00:00
let mut v = vec![138u8, 4u8];
v.extend(b"data");
2018-02-10 08:05:20 +00:00
assert_eq!(frame, v.into());
2017-10-23 20:31:22 +00:00
}
2018-04-22 15:43:47 +00:00
#[test]
fn test_close_frame() {
let reason = (CloseCode::Normal, "data");
let frame = Frame::close(Some(reason.into()), false);
let mut v = vec![136u8, 6u8, 3u8, 232u8];
v.extend(b"data");
assert_eq!(frame, v.into());
}
#[test]
fn test_empty_close_frame() {
let frame = Frame::close(None, false);
assert_eq!(frame, vec![0x88, 0x00].into());
}
2017-10-23 20:31:22 +00:00
}