1#[cfg(unix)]
7use crate::rt::{UnixListener, UnixStream};
8#[cfg(unix)]
9use std::os::unix::fs::FileTypeExt;
10#[cfg(unix)]
11use std::path::Path;
12
13#[cfg(unix)]
14pub async fn connect<P: AsRef<Path>>(path: P) -> std::io::Result<UnixStream> {
27 UnixStream::connect(path).await
28}
29
30#[cfg(unix)]
31pub async fn bind<P: AsRef<Path>>(path: P) -> std::io::Result<UnixListener> {
46 let path_ref = path.as_ref();
47 if path_ref.exists() {
48 let metadata = std::fs::symlink_metadata(path_ref)?;
49 if metadata.file_type().is_socket() {
50 std::fs::remove_file(path_ref)?;
51 } else {
52 return Err(std::io::Error::new(
53 std::io::ErrorKind::AlreadyExists,
54 "IPC bind path exists and is not a Unix socket",
55 ));
56 }
57 }
58
59 UnixListener::bind(path).await
60}
61
62#[cfg(unix)]
63pub async fn accept(listener: &UnixListener) -> std::io::Result<UnixStream> {
65 let (stream, _addr) = listener.accept().await?;
66 Ok(stream)
67}
68
69#[cfg(test)]
70#[cfg(unix)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_ipc_connect_bind() {
76 crate::rt::LocalRuntime::new()
77 .unwrap()
78 .block_on(test_ipc_connect_bind_impl())
79 }
80
81 async fn test_ipc_connect_bind_impl() {
82 let path = "/tmp/monocoque_test_ipc.sock";
83
84 let _ = std::fs::remove_file(path);
86
87 let listener = bind(path).await.unwrap();
88
89 let accept_handle = crate::rt::spawn(async move { accept(&listener).await });
91
92 crate::rt::sleep(std::time::Duration::from_millis(10)).await;
94
95 let client = connect(path).await.unwrap();
97
98 let server = crate::rt::join(accept_handle).await.unwrap();
100
101 assert!(client.peer_addr().is_ok());
102 assert!(server.local_addr().is_ok());
103
104 drop(client);
106 drop(server);
107 let _ = std::fs::remove_file(path);
108 }
109
110 #[test]
111 fn bind_does_not_unlink_existing_regular_file() {
112 crate::rt::LocalRuntime::new()
113 .unwrap()
114 .block_on(bind_does_not_unlink_existing_regular_file_impl())
115 }
116
117 async fn bind_does_not_unlink_existing_regular_file_impl() {
118 let path = std::env::temp_dir().join(format!(
119 "monocoque-ipc-regular-file-{}.sock",
120 std::process::id()
121 ));
122
123 std::fs::write(&path, b"do not delete").unwrap();
124
125 let result = bind(&path).await;
126
127 assert!(
128 result.is_err(),
129 "binding over an existing regular file should fail instead of unlinking it"
130 );
131 assert!(
132 path.exists(),
133 "ipc::bind unlinked an existing regular file before binding"
134 );
135
136 let _ = std::fs::remove_file(path);
137 }
138}