1use serde::{Serialize, de::DeserializeOwned};
14use tokio::io::{AsyncReadExt, AsyncWriteExt};
15
16pub const MAX_FRAME_SIZE: u32 = 16 * 1024 * 1024;
18
19pub fn encode_frame<T: Serialize>(value: &T) -> core_types::Result<Vec<u8>> {
30 postcard::to_allocvec(value)
31 .map_err(|e| core_types::Error::Ipc(format!("serialization failed: {e}")))
32}
33
34pub fn decode_frame<T: DeserializeOwned>(payload: &[u8]) -> core_types::Result<T> {
42 postcard::from_bytes(payload)
43 .map_err(|e| core_types::Error::Ipc(format!("deserialization failed: {e}")))
44}
45
46pub async fn read_frame<R: tokio::io::AsyncRead + Unpin>(
52 reader: &mut R,
53) -> core_types::Result<Vec<u8>> {
54 let mut len_buf = [0u8; 4];
55 reader.read_exact(&mut len_buf).await?;
56 let len = u32::from_be_bytes(len_buf);
57 if len > MAX_FRAME_SIZE {
58 return Err(core_types::Error::Ipc(format!(
59 "frame size {len} exceeds maximum {MAX_FRAME_SIZE}"
60 )));
61 }
62 let mut payload = vec![0u8; len as usize];
63 reader.read_exact(&mut payload).await?;
64 Ok(payload)
65}
66
67pub async fn write_frame<W: tokio::io::AsyncWrite + Unpin>(
73 writer: &mut W,
74 payload: &[u8],
75) -> core_types::Result<()> {
76 let len = u32::try_from(payload.len())
77 .map_err(|_| core_types::Error::Ipc("payload exceeds u32 length".into()))?;
78 writer.write_all(&len.to_be_bytes()).await?;
79 writer.write_all(payload).await?;
80 writer.flush().await?;
81 Ok(())
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use core_types::{DaemonId, EventKind, SecurityLevel};
88 use uuid::Uuid;
89
90 #[test]
91 fn encode_decode_roundtrip() {
92 let event = EventKind::DaemonStarted {
93 daemon_id: DaemonId::from_uuid(Uuid::from_u128(1)),
94 version: "0.1.0".into(),
95 capabilities: vec!["test".into()],
96 };
97 let ctx = crate::message::MessageContext::new(DaemonId::from_uuid(Uuid::from_u128(1)));
98 let msg = crate::message::Message::new(
99 &ctx,
100 event,
101 SecurityLevel::Internal,
102 std::time::Instant::now(),
103 );
104 let payload = encode_frame(&msg).unwrap();
105 let decoded: crate::message::Message<EventKind> = decode_frame(&payload).unwrap();
106 assert!(matches!(decoded.payload, EventKind::DaemonStarted { .. }));
107 }
108
109 #[tokio::test]
110 async fn async_read_write_roundtrip() {
111 let payload = b"hello postcard";
112 let mut buf = Vec::new();
113 write_frame(&mut buf, payload).await.unwrap();
114 let mut cursor = &buf[..];
115 let decoded = read_frame(&mut cursor).await.unwrap();
116 assert_eq!(decoded, payload);
117 }
118
119 #[tokio::test]
122 async fn oversized_frame_rejected_on_read() {
123 let oversized_len: u32 = MAX_FRAME_SIZE + 1;
124 let mut buf = Vec::new();
125 buf.extend_from_slice(&oversized_len.to_be_bytes());
126 buf.extend(std::iter::repeat_n(0u8, 64));
128 let mut cursor = &buf[..];
129 let result = read_frame(&mut cursor).await;
130 assert!(
131 result.is_err(),
132 "frame exceeding MAX_FRAME_SIZE must be rejected"
133 );
134 }
135
136 #[tokio::test]
139 async fn zero_length_frame_roundtrips() {
140 let payload: &[u8] = &[];
141 let mut buf = Vec::new();
142 write_frame(&mut buf, payload).await.unwrap();
143 let mut cursor = &buf[..];
144 let decoded = read_frame(&mut cursor).await.unwrap();
145 assert!(decoded.is_empty());
146 }
147}