use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::error::{BrainError, Result};
use crate::wire::frame::{Frame, FrameError};
const READ_CHUNK: usize = 16 * 1024;
pub async fn write_frame<W>(stream: &mut W, frame: &Frame) -> Result<()>
where
W: AsyncWrite + Unpin,
{
let bytes = frame.encode()?;
stream.write_all(&bytes).await?;
Ok(())
}
pub async fn read_frame<R>(stream: &mut R, buf: &mut Vec<u8>) -> Result<Frame>
where
R: AsyncRead + Unpin,
{
loop {
match Frame::decode(buf) {
Ok((frame, rest)) => {
let consumed = buf.len() - rest.len();
buf.drain(..consumed);
return Ok(frame);
}
Err(FrameError::Truncated { .. }) => {}
Err(e) => return Err(BrainError::Frame(e)),
}
let start = buf.len();
buf.resize(start + READ_CHUNK, 0);
let n = stream.read(&mut buf[start..]).await?;
buf.truncate(start + n);
if n == 0 {
return Err(BrainError::Closed);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::frame::FLAG_EOS;
#[tokio::test]
async fn round_trips_a_frame_through_a_duplex_pipe() {
let (mut client, mut server) = tokio::io::duplex(1024);
let frame = Frame::new(0x0020, FLAG_EOS, 7, b"payload bytes".to_vec());
write_frame(&mut client, &frame).await.expect("write");
let mut buf = Vec::new();
let got = read_frame(&mut server, &mut buf).await.expect("read");
assert_eq!(got, frame);
assert!(buf.is_empty(), "no bytes should remain after one frame");
}
#[tokio::test]
async fn reads_a_frame_split_across_two_writes() {
let (mut client, mut server) = tokio::io::duplex(1024);
let frame = Frame::new(0x0021, FLAG_EOS, 1, b"hello brain".to_vec());
let bytes = frame.encode().expect("frame fits");
let (head, tail) = bytes.split_at(20);
let reader = tokio::spawn(async move {
let mut buf = Vec::new();
read_frame(&mut server, &mut buf).await.map(|f| (f, buf))
});
client.write_all(head).await.expect("write head");
tokio::task::yield_now().await;
client.write_all(tail).await.expect("write tail");
let (got, leftover) = reader.await.expect("join").expect("read");
assert_eq!(got, frame);
assert_eq!(leftover, &[] as &[u8]);
}
#[tokio::test]
async fn leaves_trailing_bytes_for_the_next_frame() {
let (mut client, mut server) = tokio::io::duplex(4096);
let first = Frame::new(0x0020, FLAG_EOS, 1, b"first".to_vec());
let second = Frame::new(0x0021, FLAG_EOS, 2, b"second".to_vec());
let mut both = first.encode().expect("frame fits");
both.extend_from_slice(&second.encode().expect("frame fits"));
client.write_all(&both).await.expect("write both");
let mut buf = Vec::new();
let a = read_frame(&mut server, &mut buf).await.expect("read first");
let b = read_frame(&mut server, &mut buf)
.await
.expect("read second");
assert_eq!(a, first);
assert_eq!(b, second);
}
#[tokio::test]
async fn closed_stream_maps_to_closed_error() {
let (client, mut server) = tokio::io::duplex(64);
drop(client);
let mut buf = Vec::new();
let err = read_frame(&mut server, &mut buf).await.unwrap_err();
assert!(matches!(err, BrainError::Closed));
}
}