use crate::{FrameParseError, Message};
pub struct Frame {
inner_op_code: u8,
masking_key: Option<u32>,
pub message: Message
}
impl Frame {
pub const FIN_RSV: u8 = 0x80;
pub const OP_CODE_CONTINUATION: u8 = 0x00;
pub const OP_CODE_TEXT: u8 = 0x01;
pub const OP_CODE_BINARY: u8 = 0x02;
pub const OP_CODE_CLOSE: u8 = 0x08;
pub const OP_CODE_PING: u8 = 0x09;
pub const OP_CODE_PONG: u8 = 0x0A;
pub fn op_code(&self) -> u8 {
self.inner_op_code
}
pub fn parse<A: AsRef<[u8]>>(content: A) -> Result<Frame, FrameParseError> {
let candidate = content.as_ref();
if candidate.is_empty() {
return Err(FrameParseError::NullContent);
}
if ((candidate[0] ^ !Frame::FIN_RSV) >> 4) != 0x0f {
return Err(FrameParseError::WrongFinRSV);
}
let min_length = candidate[1] & (!0x80);
let (length, mut offset) = if min_length == 126 {
if candidate.len() < 4 {
return Err(FrameParseError::Malformed)
}
(u16::from_be_bytes([candidate[2], candidate[3]]) as usize, 4usize)
} else if min_length == 127 {
if candidate.len() < 10 {
return Err(FrameParseError::Malformed)
}
(u64::from_be_bytes([candidate[2], candidate[3], candidate[4], candidate[5], candidate[6], candidate[7], candidate[8], candidate[9]]) as usize, 10usize)
} else {
(min_length as usize, 2usize)
};
let masking_key = if 0x80 == (candidate[1] & 0x80) {
offset += 4;
Some([candidate[offset - 4], candidate[offset - 3], candidate[offset - 2], candidate[offset - 1]])
} else {
None
};
let inner_op_code = (candidate[0] << 4) >> 4;
let mut payload = candidate.get(offset..offset+length).ok_or_else(|| FrameParseError::Incomplete{expected: length + offset, obtained: candidate.len()})?.to_vec();
if let Some(masking_key) = &masking_key {
payload = payload.into_iter().enumerate().map(|(idx, v)| {
let j = idx % 4;
v ^ masking_key[j]
}).collect();
}
let message = match inner_op_code {
Frame::OP_CODE_TEXT => Message::Text(String::from_utf8(payload).map_err(|e| FrameParseError::InvalidUtf8(e))?),
Frame::OP_CODE_BINARY => Message::Binary(payload),
Frame::OP_CODE_PING => Message::Ping(payload),
Frame::OP_CODE_PONG => Message::Pong(payload),
Frame::OP_CODE_CLOSE => Message::Close,
_ => return Err(FrameParseError::UnsupportedOpCode)
};
Ok(Frame {
inner_op_code,
masking_key: masking_key.map(u32::from_be_bytes),
message
})
}
pub fn text<A: Into<String>>(text: A) -> Frame {
let payload = text.into();
let message = Message::Text(payload);
Frame {
inner_op_code: Frame::OP_CODE_TEXT,
masking_key: None,
message
}
}
pub fn ping<A: Into<Vec<u8>>>(payload: A) -> Frame {
let payload = payload.into();
let message = Message::Ping(payload);
Frame {
inner_op_code: Frame::OP_CODE_PING,
masking_key: None,
message
}
}
pub fn pong<A: Into<Vec<u8>>>(payload: A) -> Frame {
let payload = payload.into();
let message = Message::Pong(payload);
Frame {
inner_op_code: Frame::OP_CODE_PONG,
masking_key: None,
message
}
}
pub fn binary<A: Into<Vec<u8>>>(binary: A) -> Frame {
let payload = binary.into();
let message = Message::Binary(payload);
Frame {
inner_op_code: Frame::OP_CODE_BINARY,
masking_key: None,
message
}
}
pub fn close() -> Frame {
let masking_key = None; Frame {
inner_op_code: Frame::OP_CODE_CLOSE,
masking_key,
message: Message::Close
}
}
pub fn get_message(&self) -> &Message {
&self.message
}
pub fn is_close(&self) -> bool {
self.inner_op_code == Frame::OP_CODE_CLOSE
}
}
impl From<Frame> for Message {
fn from(source: Frame) -> Message {
source.message
}
}
impl From<Frame> for Vec<u8> {
fn from(source: Frame) -> Vec<u8> {
let mut content = vec![Frame::FIN_RSV ^ source.inner_op_code];
let mut payload: Vec<u8> = source.message.into();
let payload_length = payload.len();
if payload_length < 126 {
content.push(payload_length as u8 | if source.masking_key.is_some() {0x80} else {0x00});
} else if payload_length <= u16::MAX.into() {
content.push(126u8 | if source.masking_key.is_some() {0x80} else {0x00});
content.extend((payload_length as u16).to_be_bytes());
} else {
content.push(127u8 | if source.masking_key.is_some() {0x80} else {0x00});
content.extend((payload_length as u64).to_be_bytes());
}
if let Some(masking_key) = &source.masking_key {
let masking_bytes = masking_key.to_be_bytes();
payload = payload.into_iter().enumerate().map(|(idx, v)| {
let j = idx % 4;
v ^ masking_bytes[j]
}).collect();
content.extend(masking_bytes);
}
if !payload.is_empty() {
content.extend(payload);
}
content
}
}