Skip to main content

agent_graph_mcp/
transport.rs

1//! Bounded length-prefixed transport shared by the daemon and proxy.
2use std::io::{self, Read, Write};
3use tokio::io::{AsyncWrite, AsyncWriteExt};
4pub const MAX_FRAME: usize = 1024 * 1024;
5#[derive(Debug)]
6pub enum FrameError {
7    Io(io::Error),
8    TooLarge,
9}
10impl From<io::Error> for FrameError {
11    fn from(e: io::Error) -> Self {
12        Self::Io(e)
13    }
14}
15
16impl std::fmt::Display for FrameError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            FrameError::Io(e) => write!(f, "transport io error: {e}"),
20            FrameError::TooLarge => write!(f, "frame exceeds maximum size"),
21        }
22    }
23}
24
25impl std::error::Error for FrameError {}
26pub fn read_frame<R: Read>(r: &mut R) -> Result<Vec<u8>, FrameError> {
27    let mut h = [0; 4];
28    r.read_exact(&mut h)?;
29    let n = u32::from_be_bytes(h) as usize;
30    if n > MAX_FRAME {
31        return Err(FrameError::TooLarge);
32    }
33    let mut b = vec![0; n];
34    r.read_exact(&mut b)?;
35    Ok(b)
36}
37pub fn write_frame<W: Write>(w: &mut W, b: &[u8]) -> Result<(), FrameError> {
38    if b.len() > MAX_FRAME {
39        return Err(FrameError::TooLarge);
40    }
41    w.write_all(&(b.len() as u32).to_be_bytes())?;
42    w.write_all(b)?;
43    w.flush()?;
44    Ok(())
45}
46
47pub async fn write_frame_async<W: AsyncWrite + Unpin>(
48    w: &mut W,
49    b: &[u8],
50) -> Result<(), io::Error> {
51    if b.len() > MAX_FRAME {
52        return Err(io::Error::new(
53            io::ErrorKind::InvalidData,
54            "frame exceeds maximum size",
55        ));
56    }
57    w.write_all(&(b.len() as u32).to_be_bytes()).await?;
58    w.write_all(b).await?;
59    w.flush().await
60}