acktor_ipc/ipc_method/
pipe.rs1use std::io::{Error, ErrorKind};
4use std::sync::atomic::{AtomicU64, Ordering};
5
6use bytes::Bytes;
7use futures_util::{FutureExt, SinkExt, StreamExt};
8use interprocess::local_socket::{
9 GenericNamespaced, ListenerOptions,
10 tokio::{RecvHalf, SendHalf, prelude::*},
11};
12use tokio_util::codec::{FramedRead, FramedWrite, length_delimited::LengthDelimitedCodec};
13use tracing::info;
14
15use super::{IoFuture, IpcConnection, IpcListener};
16
17#[derive(Debug)]
19pub struct PipeListener {
20 name: String,
21 listener: LocalSocketListener,
22 accept_counter: AtomicU64,
23}
24
25impl PipeListener {
26 pub fn new(name: &str) -> Result<Self, Error> {
28 let name_string = name.to_string();
29 let name = name.to_ns_name::<GenericNamespaced>()?;
30
31 let opts = ListenerOptions::new().name(name);
32 let listener = opts.create_tokio()?;
33
34 Ok(Self {
35 name: name_string,
36 listener,
37 accept_counter: AtomicU64::new(0),
38 })
39 }
40}
41
42impl IpcListener for PipeListener {
43 fn local_endpoint(&self) -> &str {
44 self.name.as_str()
45 }
46
47 fn accept(&self) -> IoFuture<'_, Box<dyn IpcConnection>> {
48 async move {
49 let stream = self.listener.accept().await?;
50
51 let id = self.accept_counter.fetch_add(1, Ordering::Relaxed);
52 let name = format!("{}#{}", self.name, id);
53
54 info!("Accepted a new pipe connection: {}", name);
55
56 Ok(Box::new(PipeConnection::new(stream, name)) as Box<dyn IpcConnection>)
57 }
58 .boxed()
59 }
60}
61
62#[derive(Debug)]
64pub struct PipeConnection {
65 name: String,
66 tx: FramedWrite<SendHalf, LengthDelimitedCodec>,
67 rx: FramedRead<RecvHalf, LengthDelimitedCodec>,
68}
69
70impl PipeConnection {
71 fn new(stream: LocalSocketStream, name: String) -> Self {
72 let (rx, tx) = stream.split();
73 let codec = LengthDelimitedCodec::new();
74
75 Self {
76 name,
77 tx: FramedWrite::new(tx, codec.clone()),
78 rx: FramedRead::new(rx, codec),
79 }
80 }
81}
82
83impl IpcConnection for PipeConnection {
84 async fn connect(name: &str) -> Result<Self, Error> {
85 let stream = LocalSocketStream::connect(name.to_ns_name::<GenericNamespaced>()?).await?;
86
87 info!("Connected to pipe {}", name);
88
89 Ok(Self::new(stream, name.to_string()))
90 }
91
92 fn peer_endpoint(&self) -> &str {
93 self.name.as_str()
94 }
95
96 fn close(&mut self) -> IoFuture<'_, ()> {
97 self.tx.close().boxed()
98 }
99
100 fn send(&mut self, buf: Bytes) -> IoFuture<'_, ()> {
101 self.tx.send(buf).boxed()
102 }
103
104 fn recv(&mut self) -> IoFuture<'_, Bytes> {
105 async move {
106 let frame = self
107 .rx
108 .next()
109 .await
110 .ok_or_else(|| Error::from(ErrorKind::ConnectionAborted))??;
111
112 Ok(frame.freeze())
113 }
114 .boxed()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use pretty_assertions::assert_eq;
121
122 use super::*;
123
124 fn unique_pipe_name() -> String {
125 static COUNTER: AtomicU64 = AtomicU64::new(0);
126 format!(
127 "acktor-ipc-pipe-test-{}-{}",
128 std::process::id(),
129 COUNTER.fetch_add(1, Ordering::Relaxed)
130 )
131 }
132
133 #[tokio::test]
134 async fn test_listener() -> anyhow::Result<()> {
135 let name = unique_pipe_name();
137 let listener = PipeListener::new(&name)?;
138 assert_eq!(listener.local_endpoint(), name);
139
140 PipeListener::new(&name).expect_err("second bind to the same name must fail");
142
143 let missing = unique_pipe_name();
145 PipeConnection::connect(&missing)
146 .await
147 .expect_err("connect to non-existent pipe must fail");
148
149 Ok(())
150 }
151
152 #[tokio::test]
153 async fn test_connection() -> anyhow::Result<()> {
154 let name = unique_pipe_name();
155 let listener = PipeListener::new(&name)?;
156 let server_name = name.clone();
157
158 let server = tokio::spawn(async move {
159 let mut c1 = listener.accept().await?;
161 assert_eq!(c1.peer_endpoint(), format!("{}#0", server_name));
162 let msg = c1.recv().await?;
163 c1.send(msg).await?;
164 c1.close().await?;
165
166 let c2 = listener.accept().await?;
169 assert_eq!(c2.peer_endpoint(), format!("{}#1", server_name));
170 drop(c2);
171
172 Ok::<_, anyhow::Error>(())
173 });
174
175 let mut client1 = PipeConnection::connect(&name).await?;
177 assert_eq!(client1.peer_endpoint(), name);
178 let payload = Bytes::from_static(b"hello");
179 client1.send(payload.clone()).await?;
180 assert_eq!(client1.recv().await?, payload);
181 client1.close().await?;
182
183 let mut client2 = PipeConnection::connect(&name).await?;
185 server.await??;
186 let err = client2
187 .recv()
188 .await
189 .expect_err("recv should fail after peer drop");
190 assert_eq!(err.kind(), ErrorKind::ConnectionAborted);
191
192 Ok(())
193 }
194}