2026-07-12 11:28:39 +07:00
|
|
|
//! Length-prefixed binary framing and JSON (de)serialization helpers for
|
|
|
|
|
//! the IPC wire protocol.
|
|
|
|
|
//!
|
|
|
|
|
//! Flow: `write_frame`/`read_frame` handle the raw byte-level framing
|
|
|
|
|
//! (4-byte big-endian length header + payload) over any `Read`/`Write`;
|
|
|
|
|
//! `serialize_frame`/`deserialize_frame` handle the JSON layer on top.
|
|
|
|
|
//! `Connection` (see `conn.rs`) composes both layers for a full send/receive.
|
|
|
|
|
//!
|
|
|
|
|
//! Why: a fixed-size length prefix lets the reader know exactly how many
|
|
|
|
|
//! bytes to pull before attempting to parse, avoiding partial-JSON reads
|
|
|
|
|
//! over a stream socket.
|
|
|
|
|
|
2026-07-11 13:16:10 +07:00
|
|
|
use std::io::{Read, Write};
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Upper bound on a single frame's byte size (64 MiB), enforced on both
|
|
|
|
|
/// the write and read paths to bound memory use and reject malformed or
|
|
|
|
|
/// malicious oversized length headers.
|
2026-07-11 13:16:10 +07:00
|
|
|
pub(crate) const MAX_FRAME_SIZE: usize = 64 * 1024 * 1024;
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Write `data` as a length-prefixed frame: 4-byte big-endian length
|
|
|
|
|
/// followed by the raw bytes, then flush.
|
|
|
|
|
///
|
|
|
|
|
/// Why: rejects frames over `MAX_FRAME_SIZE` to bound memory use on the
|
|
|
|
|
/// reading side before any bytes are read.
|
2026-07-11 13:16:10 +07:00
|
|
|
pub fn write_frame<W: Write>(writer: &mut W, data: &[u8]) -> Result<()> {
|
|
|
|
|
let len = data.len();
|
|
|
|
|
if len > MAX_FRAME_SIZE {
|
|
|
|
|
anyhow::bail!("frame too large: {} bytes exceeds 64 MiB limit", len);
|
|
|
|
|
}
|
|
|
|
|
let len_bytes = (len as u32).to_be_bytes();
|
|
|
|
|
writer.write_all(&len_bytes)?;
|
|
|
|
|
writer.write_all(data)?;
|
|
|
|
|
writer.flush()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Read one length-prefixed frame written by `write_frame`.
|
|
|
|
|
///
|
|
|
|
|
/// Flow: read 4-byte length header → on clean EOF before any bytes,
|
|
|
|
|
/// return `Ok(None)` (peer closed) → validate against `MAX_FRAME_SIZE`
|
|
|
|
|
/// → read the payload.
|
|
|
|
|
///
|
|
|
|
|
/// Return: `Ok(None)` signals a graceful connection close, distinct
|
|
|
|
|
/// from an `Err` mid-frame I/O failure.
|
2026-07-11 13:16:10 +07:00
|
|
|
pub fn read_frame<R: Read>(reader: &mut R) -> Result<Option<Vec<u8>>> {
|
|
|
|
|
let mut len_buf = [0u8; 4];
|
|
|
|
|
match reader.read_exact(&mut len_buf) {
|
|
|
|
|
Ok(()) => {}
|
|
|
|
|
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
|
|
|
|
|
Err(e) => return Err(e.into()),
|
|
|
|
|
}
|
|
|
|
|
let len = u32::from_be_bytes(len_buf) as usize;
|
|
|
|
|
if len > MAX_FRAME_SIZE {
|
|
|
|
|
anyhow::bail!("frame too large: {} bytes exceeds 64 MiB limit", len);
|
|
|
|
|
}
|
|
|
|
|
let mut buf = vec![0u8; len];
|
|
|
|
|
reader.read_exact(&mut buf)?;
|
|
|
|
|
Ok(Some(buf))
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Serialize `value` to JSON bytes, rejecting output over `MAX_FRAME_SIZE`.
|
2026-07-11 13:16:10 +07:00
|
|
|
pub fn serialize_frame<T: serde::Serialize>(value: &T) -> Result<Vec<u8>> {
|
|
|
|
|
let json = serde_json::to_vec(value)?;
|
|
|
|
|
if json.len() > MAX_FRAME_SIZE {
|
|
|
|
|
anyhow::bail!("serialized frame too large: {} bytes", json.len());
|
|
|
|
|
}
|
|
|
|
|
Ok(json)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Deserialize a frame's raw JSON bytes into `T`.
|
2026-07-11 13:16:10 +07:00
|
|
|
pub fn deserialize_frame<'a, T: serde::Deserialize<'a>>(data: &'a [u8]) -> Result<T> {
|
|
|
|
|
Ok(serde_json::from_slice(data)?)
|
|
|
|
|
}
|