use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum TransportError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("frame too large: {0} bytes (limit {1})")]
FrameTooLarge(usize, usize),
}
pub const DEFAULT_MAX_FRAME: usize = 16 * 1024 * 1024;
pub async fn read_frame<R>(reader: &mut R, max_frame: usize) -> Result<Vec<u8>, TransportError>
where
R: AsyncReadExt + Unpin,
{
let mut len_bytes = [0u8; 4];
reader.read_exact(&mut len_bytes).await?;
let len = u32::from_le_bytes(len_bytes) as usize;
if len > max_frame {
return Err(TransportError::FrameTooLarge(len, max_frame));
}
let mut payload = vec![0u8; len];
reader.read_exact(&mut payload).await?;
Ok(payload)
}
pub async fn write_frame<W>(writer: &mut W, payload: &[u8]) -> Result<(), TransportError>
where
W: AsyncWriteExt + Unpin,
{
let len = u32::try_from(payload.len())
.map_err(|_| TransportError::FrameTooLarge(payload.len(), u32::MAX as usize))?;
writer.write_all(&len.to_le_bytes()).await?;
writer.write_all(payload).await?;
writer.flush().await?;
Ok(())
}