use crate::{
collections::{ArrayVectorCopy, Vector},
misc::{Lease, from_utf8_basic},
web_socket::{
CloseCode, MASK_MASK, MAX_CONTROL_PAYLOAD_LEN, MAX_HEADER_LEN, OpCode, WebSocketError,
misc::{has_masked_frame, header_from_params},
},
};
use core::{hint::unreachable_unchecked, str};
pub type FrameControlArray = Frame<ArrayVectorCopy<u8, MAX_CONTROL_PAYLOAD_LEN>>;
pub type FrameMut<'bytes> = Frame<&'bytes mut [u8]>;
pub type FrameRef<'bytes> = Frame<&'bytes [u8]>;
pub type FrameVector = Frame<Vector<u8>>;
pub type FrameVectorMut<'bytes> = Frame<&'bytes mut Vector<u8>>;
pub type FrameVectorRef<'bytes> = Frame<&'bytes Vector<u8>>;
#[derive(Debug)]
pub struct Frame<P> {
fin: bool,
header: ArrayVectorCopy<u8, MAX_HEADER_LEN>,
op_code: OpCode,
payload: P,
}
impl<P> Frame<P> {
#[inline]
pub const fn fin(&self) -> bool {
self.fin
}
#[inline]
pub fn header_and_payload(&self) -> (&[u8], &P) {
(self.header(), &self.payload)
}
#[inline]
pub const fn op_code(&self) -> OpCode {
self.op_code
}
#[inline]
pub const fn payload(&self) -> &P {
&self.payload
}
#[inline]
pub const fn payload_mut(&mut self) -> &mut P {
&mut self.payload
}
pub(crate) fn header(&self) -> &[u8] {
&self.header
}
pub(crate) fn header_and_payload_mut(&mut self) -> (&mut [u8], &mut P) {
(&mut self.header, &mut self.payload)
}
pub(crate) fn header_first_two_mut(&mut self) -> [&mut u8; 2] {
let [b0, b1, ..] = self.header.as_slice_mut() else {
unsafe { unreachable_unchecked() }
};
[b0, b1]
}
pub(crate) fn set_mask(&mut self, mask: [u8; 4]) {
let [_, b1] = self.header_first_two_mut();
if has_masked_frame(*b1) {
return;
}
*b1 |= MASK_MASK;
let _rslt = self.header.extend_from_copyable_slice(&mask);
}
}
impl<P> Frame<P>
where
P: Lease<[u8]>,
{
#[inline]
pub fn new_fin(op_code: OpCode, payload: P) -> crate::Result<Self> {
let bytes = payload.lease();
check_frame(bytes, op_code)?;
Ok(Self::new(true, op_code, payload, 0))
}
#[inline]
pub unsafe fn new_fin_unchecked(op_code: OpCode, payload: P) -> Self {
Self::new(true, op_code, payload, 0)
}
#[inline]
pub fn new_unfin(op_code: OpCode, payload: P) -> crate::Result<Self> {
let bytes = payload.lease();
check_frame(bytes, op_code)?;
Ok(Self::new(false, op_code, payload, 0))
}
#[inline]
pub unsafe fn new_unfin_unchecked(op_code: OpCode, payload: P) -> Self {
Self::new(false, op_code, payload, 0)
}
#[inline]
pub fn text_payload(&self) -> Option<&str> {
self.op_code.is_text().then(|| {
unsafe { str::from_utf8_unchecked(self.payload.lease()) }
})
}
#[inline]
pub fn to_vector(&self) -> crate::Result<FrameVector> {
Ok(FrameVector {
fin: self.fin,
header: self.header,
op_code: self.op_code,
payload: Vector::from_copyable_slice(self.payload.lease())?,
})
}
pub(crate) fn new(fin: bool, op_code: OpCode, payload: P, rsv1: u8) -> Self {
let payload_len = if op_code.is_control() {
payload.lease().len().min(MAX_CONTROL_PAYLOAD_LEN)
} else {
payload.lease().len()
};
Self { fin, header: header_from_params(fin, op_code, payload_len, rsv1), op_code, payload }
}
}
#[inline]
fn check_frame(bytes: &[u8], op_code: OpCode) -> crate::Result<()> {
if op_code.is_text() {
let _str = from_utf8_basic(bytes)?;
} else if op_code.is_close() {
match bytes {
[] => {}
[_] => return Err(WebSocketError::InvalidCloseFrame.into()),
[b0, b1, rest @ ..] => {
let _close_code = CloseCode::try_from(u16::from_be_bytes([*b0, *b1]))?;
let _str = from_utf8_basic(rest)?;
}
}
} else if op_code.is_control() && bytes.len() > MAX_CONTROL_PAYLOAD_LEN {
return Err(WebSocketError::InvalidControlFrame.into());
}
Ok(())
}