edge-ws 0.8.0

Async + `no_std` + no-alloc implementation of the Websockets protocol
Documentation
use core::cmp::min;

use embedded_io_async::{self, Read, ReadExactError, Write};

use super::*;

pub type Error<E> = super::Error<E>;

impl<E> Error<E>
where
    E: embedded_io_async::Error,
{
    pub fn erase(&self) -> Error<embedded_io_async::ErrorKind> {
        match self {
            Self::Incomplete(size) => Error::Incomplete(*size),
            Self::Invalid => Error::Invalid,
            Self::BufferOverflow => Error::BufferOverflow,
            Self::InvalidLen => Error::InvalidLen,
            Self::Io(e) => Error::Io(e.kind()),
        }
    }
}

impl<E> From<ReadExactError<E>> for Error<E> {
    fn from(e: ReadExactError<E>) -> Self {
        match e {
            ReadExactError::UnexpectedEof => Error::Invalid,
            ReadExactError::Other(e) => Error::Io(e),
        }
    }
}

impl FrameHeader {
    pub async fn recv<R>(mut read: R) -> Result<Self, Error<R::Error>>
    where
        R: Read,
    {
        let mut header_buf = [0; FrameHeader::MAX_LEN];
        let mut read_offset = 0;
        let mut read_end = FrameHeader::MIN_LEN;

        loop {
            read.read_exact(&mut header_buf[read_offset..read_end])
                .await
                .map_err(Error::from)?;

            match FrameHeader::deserialize(&header_buf[..read_end]) {
                Ok((header, _)) => return Ok(header),
                Err(Error::Incomplete(more)) => {
                    read_offset = read_end;
                    read_end += more;
                }
                Err(e) => return Err(e.recast()),
            }
        }
    }

    pub async fn send<W>(&self, mut write: W) -> Result<(), Error<W::Error>>
    where
        W: Write,
    {
        let mut header_buf = [0; FrameHeader::MAX_LEN];
        let header_len = unwrap!(self.serialize(&mut header_buf));

        write
            .write_all(&header_buf[..header_len])
            .await
            .map_err(Error::Io)
    }

    pub async fn recv_payload<'a, R>(
        &self,
        mut read: R,
        payload_buf: &'a mut [u8],
    ) -> Result<&'a [u8], Error<R::Error>>
    where
        R: Read,
    {
        if (payload_buf.len() as u64) < self.payload_len {
            Err(Error::BufferOverflow)
        } else if self.payload_len == 0 {
            Ok(&[])
        } else {
            let payload = &mut payload_buf[..self.payload_len as _];

            read.read_exact(payload).await.map_err(Error::from)?;

            self.mask(payload, 0);

            Ok(payload)
        }
    }

    pub async fn send_payload<'a, W>(
        &'a self,
        mut write: W,
        payload: &'a [u8],
    ) -> Result<(), Error<W::Error>>
    where
        W: Write,
    {
        let payload_buf_len = payload.len() as u64;

        if payload_buf_len != self.payload_len {
            Err(Error::InvalidLen)
        } else if payload.is_empty() {
            Ok(())
        } else if self.mask_key.is_none() {
            write.write_all(payload).await.map_err(Error::Io)
        } else {
            let mut buf = [0_u8; 32];

            let mut offset = 0;

            while offset < payload.len() {
                let len = min(buf.len(), payload.len() - offset);

                let buf = &mut buf[..len];

                buf.copy_from_slice(&payload[offset..offset + len]);

                self.mask(buf, offset);

                write.write_all(buf).await.map_err(Error::Io)?;

                offset += len;
            }

            Ok(())
        }
    }
}

pub async fn recv<R>(
    mut read: R,
    frame_data_buf: &mut [u8],
) -> Result<(FrameType, usize), Error<R::Error>>
where
    R: Read,
{
    let header = FrameHeader::recv(&mut read).await?;
    header.recv_payload(read, frame_data_buf).await?;

    Ok((header.frame_type, header.payload_len as _))
}

pub async fn send<W>(
    mut write: W,
    frame_type: FrameType,
    mask_key: Option<u32>,
    frame_data_buf: &[u8],
) -> Result<(), Error<W::Error>>
where
    W: Write,
{
    let header = FrameHeader {
        frame_type,
        payload_len: frame_data_buf.len() as _,
        mask_key,
    };

    header.send(&mut write).await?;
    header.send_payload(write, frame_data_buf).await
}