sentinelpass_protocol/transport/
unix.rs1use super::{TransportError, TransportResult, MAX_MESSAGE_SIZE};
5use std::path::PathBuf;
6use tokio::io::{AsyncReadExt, AsyncWriteExt};
7
8pub struct UnixSocketConnection {
10 stream: tokio::net::UnixStream,
11}
12
13impl UnixSocketConnection {
14 pub fn from_stream(stream: tokio::net::UnixStream) -> Self {
16 Self { stream }
17 }
18
19 pub async fn connect(path: PathBuf) -> TransportResult<Self> {
21 let stream = tokio::net::UnixStream::connect(&path).await.map_err(|e| {
22 TransportError::ConnectionFailed(format!(
23 "Failed to connect to {}: {}",
24 path.display(),
25 e
26 ))
27 })?;
28
29 Ok(Self { stream })
30 }
31
32 pub async fn read_message(&mut self) -> TransportResult<Vec<u8>> {
34 let mut length_buf = [0u8; 4];
36 self.stream.read_exact(&mut length_buf).await?;
37
38 let length = u32::from_be_bytes(length_buf) as usize;
39
40 if length == 0 || length > MAX_MESSAGE_SIZE {
41 return Err(TransportError::MessageTooLarge {
42 size: length,
43 max: MAX_MESSAGE_SIZE,
44 });
45 }
46
47 let mut buffer = vec![0u8; length];
49 self.stream.read_exact(&mut buffer).await?;
50
51 Ok(buffer)
52 }
53
54 pub async fn write_message(&mut self, data: &[u8]) -> TransportResult<()> {
56 let length = data.len() as u32;
57
58 if length as usize > MAX_MESSAGE_SIZE {
60 return Err(TransportError::MessageTooLarge {
61 size: data.len(),
62 max: MAX_MESSAGE_SIZE,
63 });
64 }
65
66 self.stream.write_all(&length.to_be_bytes()).await?;
68
69 self.stream.write_all(data).await?;
71
72 self.stream.flush().await?;
73
74 Ok(())
75 }
76
77 pub async fn close(&mut self) -> TransportResult<()> {
79 self.stream.shutdown().await?;
80 Ok(())
81 }
82
83 pub fn is_open(&self) -> bool {
85 self.stream.peer_addr().is_ok()
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use std::time::Duration;
94
95 #[tokio::test]
96 async fn test_unix_socket_connection_roundtrip() {
97 let temp_dir = std::env::temp_dir();
98 let socket_path = temp_dir.join(format!("test_ipc_{}.sock", uuid_v4()));
99
100 let listener = tokio::net::UnixListener::bind(&socket_path).unwrap();
102 let server_handle = tokio::spawn(async move {
103 let (stream, _) = listener.accept().await.unwrap();
104 let mut conn = UnixSocketConnection::from_stream(stream);
105 let msg = conn.read_message().await.unwrap();
106 conn.write_message(&msg).await.unwrap();
107 conn.close().await.unwrap();
108 });
109
110 tokio::time::sleep(Duration::from_millis(100)).await;
112 let mut client = UnixSocketConnection::connect(socket_path).await.unwrap();
113
114 let test_data = b"Hello, IPC!";
116 client.write_message(test_data).await.unwrap();
117 let received = client.read_message().await.unwrap();
118
119 assert_eq!(received, test_data);
120
121 server_handle.await.unwrap();
122 }
123
124 fn uuid_v4() -> String {
125 let nanos = std::time::SystemTime::now()
127 .duration_since(std::time::UNIX_EPOCH)
128 .unwrap()
129 .as_nanos();
130 format!("proto{n}", n = nanos)
131 }
132}