use std::io::Write;
use std::borrow::Cow;
use std::iter::{Take, Repeat, repeat};
use result::{WebSocketResult, WebSocketError};
use dataframe::Opcode;
use byteorder::{WriteBytesExt, ReadBytesExt, BigEndian};
use ws::util::bytes_to_string;
use ws;
const FALSE_RESERVED_BITS: &'static [bool; 3] = &[false; 3];
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Type {
Text = 1,
Binary = 2,
Ping = 9,
Pong = 10,
Close = 8,
}
#[derive(PartialEq, Clone, Debug)]
pub struct Message<'a> {
pub opcode: Type,
pub cd_status_code: Option<u16>,
pub payload: Cow<'a, [u8]>,
}
impl<'a> Message<'a> {
fn new(code: Type, status: Option<u16>, payload: Cow<'a, [u8]>) -> Self {
Message {
opcode: code,
cd_status_code: status,
payload: payload,
}
}
pub fn text<S>(data: S) -> Self
where S: Into<Cow<'a, str>> {
Message::new(Type::Text, None, match data.into() {
Cow::Owned(msg) => Cow::Owned(msg.into_bytes()),
Cow::Borrowed(msg) => Cow::Borrowed(msg.as_bytes()),
})
}
pub fn binary<B>(data: B) -> Self
where B: IntoCowBytes<'a> {
Message::new(Type::Binary, None, data.into())
}
pub fn close() -> Self {
Message::new(Type::Close, None, Cow::Borrowed(&[0 as u8; 0]))
}
pub fn close_because<S>(code: u16, reason: S) -> Self
where S: Into<Cow<'a, str>> {
Message::new(Type::Close, Some(code), match reason.into() {
Cow::Owned(msg) => Cow::Owned(msg.into_bytes()),
Cow::Borrowed(msg) => Cow::Borrowed(msg.as_bytes()),
})
}
pub fn ping<P>(data: P) -> Self
where P: IntoCowBytes<'a> {
Message::new(Type::Ping, None, data.into())
}
pub fn pong<P>(data: P) -> Self
where P: IntoCowBytes<'a> {
Message::new(Type::Pong, None, data.into())
}
pub fn into_pong(&mut self) -> Result<(), ()> {
if self.opcode == Type::Ping {
self.opcode = Type::Pong;
Ok(())
} else {
Err(())
}
}
}
impl<'a> ws::dataframe::DataFrame for Message<'a> {
#[inline(always)]
fn is_last(&self) -> bool {
true
}
#[inline(always)]
fn opcode(&self) -> u8 {
self.opcode as u8
}
#[inline(always)]
fn reserved<'b>(&'b self) -> &'b [bool; 3] {
FALSE_RESERVED_BITS
}
fn payload<'b>(&'b self) -> Cow<'b, [u8]> {
let mut buf = Vec::with_capacity(self.size());
self.write_payload(&mut buf).ok();
Cow::Owned(buf)
}
fn size(&self) -> usize {
self.payload.len() + if self.cd_status_code.is_some() {
2
} else {
0
}
}
fn write_payload<W>(&self, socket: &mut W) -> WebSocketResult<()>
where W: Write {
if let Some(reason) = self.cd_status_code {
try!(socket.write_u16::<BigEndian>(reason));
}
try!(socket.write_all(&*self.payload));
Ok(())
}
}
impl<'a, 'b> ws::Message<'b, &'b Message<'a>> for Message<'a> {
type DataFrameIterator = Take<Repeat<&'b Message<'a>>>;
fn dataframes(&'b self) -> Self::DataFrameIterator {
repeat(self).take(1)
}
fn from_dataframes<D>(frames: Vec<D>) -> WebSocketResult<Self>
where D: ws::dataframe::DataFrame {
let opcode = try!(frames.first().ok_or(WebSocketError::ProtocolError(
"No dataframes provided"
)).map(|d| d.opcode()));
let mut data = Vec::new();
for (i, dataframe) in frames.iter().enumerate() {
if i > 0 && dataframe.opcode() != Opcode::Continuation as u8 {
return Err(WebSocketError::ProtocolError(
"Unexpected non-continuation data frame"
));
}
if *dataframe.reserved() != [false; 3] {
return Err(WebSocketError::ProtocolError(
"Unsupported reserved bits received"
));
}
data.extend(dataframe.payload().iter().cloned());
}
Ok(match Opcode::new(opcode) {
Some(Opcode::Text) => Message::text(try!(bytes_to_string(&data[..]))),
Some(Opcode::Binary) => Message::binary(data),
Some(Opcode::Close) => {
if data.len() > 0 {
let status_code = try!((&data[..]).read_u16::<BigEndian>());
let reason = try!(bytes_to_string(&data[2..]));
Message::close_because(status_code, reason)
} else {
Message::close()
}
}
Some(Opcode::Ping) => Message::ping(data),
Some(Opcode::Pong) => Message::pong(data),
_ => return Err(WebSocketError::ProtocolError(
"Unsupported opcode received"
)),
})
}
}
pub trait IntoCowBytes<'a> {
fn into(self) -> Cow<'a, [u8]>;
}
impl<'a> IntoCowBytes<'a> for Vec<u8> {
fn into(self) -> Cow<'a, [u8]> {
Cow::Owned(self)
}
}
impl<'a> IntoCowBytes<'a> for &'a [u8] {
fn into(self) -> Cow<'a, [u8]> {
Cow::Borrowed(self)
}
}
impl<'a> IntoCowBytes<'a> for Cow<'a, [u8]> {
fn into(self) -> Cow<'a, [u8]> {
self
}
}