use std::io::Cursor;
use anyhow::Result;
use bon::Builder;
use bytes::{Buf as _, BytesMut};
use tokio::{io::AsyncReadExt as _, net::tcp::OwnedReadHalf};
use crate::{Frame, error::Error, frames::encframe::MAX_ENCFRAME_LENGTH};
#[derive(Builder, Debug)]
pub struct ConnectionReader {
reader: OwnedReadHalf,
#[builder(default = BytesMut::with_capacity(4096))]
buffer: BytesMut,
}
fn decode_data_length(len_buf: [u8; 8]) -> Result<usize> {
let length = usize::try_from(u64::from_be_bytes(len_buf))?;
if length > MAX_ENCFRAME_LENGTH {
return Err(Error::FrameTooLarge.into());
}
Ok(length)
}
impl ConnectionReader {
pub async fn read_data(&mut self) -> Result<Option<Vec<u8>>> {
let mut len_buf = [0u8; 8];
match self.reader.read_exact(&mut len_buf).await {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e.into()),
}
let length = decode_data_length(len_buf)?;
let mut buf = vec![0u8; length];
let _ = self
.reader
.read_exact(&mut buf)
.await
.map_err(|_| Error::ConnectionResetByPeer)?;
Ok(Some(buf))
}
pub async fn read_frame(&mut self) -> Result<Option<Frame>> {
loop {
if let Some(frame) = self.parse_frame()? {
return Ok(Some(frame));
}
if 0 == self.reader.read_buf(&mut self.buffer).await? {
if self.buffer.is_empty() {
return Ok(None);
}
return Err(Error::ConnectionResetByPeer.into());
}
}
}
fn parse_frame(&mut self) -> Result<Option<Frame>> {
let mut buf = Cursor::new(&self.buffer[..]);
buf.set_position(0);
match Frame::parse(&mut buf) {
Ok(Some(frame)) => {
let len = usize::try_from(buf.position())?;
self.buffer.advance(len);
Ok(Some(frame))
}
Ok(None) => {
Ok(None)
}
Err(err) => Err(err),
}
}
}
#[cfg(test)]
mod tests {
use tokio::net::{TcpListener, TcpStream};
use super::{ConnectionReader, Frame, decode_data_length};
use crate::ConnectionWriter;
use crate::frames::encframe::MAX_ENCFRAME_LENGTH;
use anyhow::Result;
#[test]
fn decode_data_length_accepts_up_to_cap() {
assert_eq!(decode_data_length(0u64.to_be_bytes()).unwrap(), 0);
let cap = u64::try_from(MAX_ENCFRAME_LENGTH).unwrap();
assert_eq!(
decode_data_length(cap.to_be_bytes()).unwrap(),
MAX_ENCFRAME_LENGTH
);
}
#[test]
fn decode_data_length_rejects_over_cap() {
let over = u64::try_from(MAX_ENCFRAME_LENGTH).unwrap() + 1;
assert!(decode_data_length(over.to_be_bytes()).is_err());
assert!(decode_data_length(u64::MAX.to_be_bytes()).is_err());
}
async fn make_loopback() -> Result<(ConnectionReader, ConnectionWriter)> {
let listener = TcpListener::bind("127.0.0.1:0").await?;
let addr = listener.local_addr()?;
let (server, client) = tokio::join!(
async { listener.accept().await.map(|(s, _)| s) },
TcpStream::connect(addr),
);
let (server_r, _) = server?.into_split();
let (_, client_w) = client?.into_split();
let reader = ConnectionReader::builder().reader(server_r).build();
let writer = ConnectionWriter::builder().writer(client_w).build();
Ok((reader, writer))
}
#[tokio::test]
async fn read_data_round_trips() -> Result<()> {
let (mut reader, mut writer) = make_loopback().await?;
let payload = b"hello data channel";
writer.write_data(payload).await?;
drop(writer);
let received = reader.read_data().await?.expect("expected Some");
assert_eq!(received, payload);
Ok(())
}
#[tokio::test]
async fn read_data_eof_returns_none() -> Result<()> {
let (mut reader, writer) = make_loopback().await?;
drop(writer);
let result = reader.read_data().await?;
assert!(result.is_none());
Ok(())
}
#[tokio::test]
async fn read_frame_round_trip() -> Result<()> {
let (mut reader, mut writer) = make_loopback().await?;
writer.write_frame(&Frame::KexFailure).await?;
drop(writer);
let frame = reader.read_frame().await?;
assert_eq!(frame, Some(Frame::KexFailure));
Ok(())
}
#[tokio::test]
async fn read_frame_eof_returns_none() -> Result<()> {
let (mut reader, writer) = make_loopback().await?;
drop(writer);
let frame = reader.read_frame().await?;
assert_eq!(frame, None);
Ok(())
}
}