1#[cfg(unix)]
4use std::os::unix::fs::{FileTypeExt, PermissionsExt};
5use std::path::Path;
6
7#[cfg(unix)]
8use hyper_util::rt::TokioIo;
9#[cfg(unix)]
10use tokio::net::{UnixListener, UnixStream};
11#[cfg(unix)]
12use tokio_stream::wrappers::UnixListenerStream;
13use tonic::transport::Channel;
14#[cfg(unix)]
15use tower::service_fn;
16
17use crate::Error;
18
19#[cfg(unix)]
21pub type Incoming = UnixListenerStream;
22
23#[cfg(windows)]
28#[derive(Debug)]
29pub struct Incoming;
30
31#[cfg(unix)]
38pub async fn listen(path: &Path) -> Result<Incoming, Error> {
39 if let Some(parent) = path.parent()
40 && !parent.as_os_str().is_empty()
41 {
42 tokio::fs::create_dir_all(parent).await?;
43 }
44
45 match tokio::fs::symlink_metadata(path).await {
46 Ok(metadata) if metadata.file_type().is_socket() => {
47 if UnixStream::connect(path).await.is_ok() {
48 return Err(
49 std::io::Error::new(
50 std::io::ErrorKind::AddrInUse,
51 "Unix socket is already accepting connections",
52 )
53 .into(),
54 );
55 }
56 tracing::debug!(socket = %path.display(), "removing stale Unix socket");
57 tokio::fs::remove_file(path).await?;
58 },
59 Ok(_) => {},
60 Err(error) if error.kind() == std::io::ErrorKind::NotFound => {},
61 Err(error) => return Err(error.into()),
62 }
63
64 let listener = UnixListener::bind(path)?;
65 tokio::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)).await?;
66 Ok(UnixListenerStream::new(listener))
67}
68
69#[cfg(unix)]
71pub async fn connect(path: &Path) -> Result<Channel, Error> {
72 let path = path.to_owned();
73 let endpoint = tonic::transport::Endpoint::from_static("http://[::]:50051");
74 let channel = endpoint
75 .connect_with_connector(service_fn(move |_| {
76 let path = path.clone();
77 async move { UnixStream::connect(path).await.map(TokioIo::new) }
78 }))
79 .await?;
80 Ok(channel)
81}
82
83#[cfg(windows)]
88pub async fn listen(_path: &Path) -> Result<Incoming, Error> {
89 Err(Error::Unsupported("Unix-domain sockets are unavailable on Windows"))
90}
91
92#[cfg(windows)]
97pub async fn connect(_path: &Path) -> Result<Channel, Error> {
98 Err(Error::Unsupported("Unix-domain sockets are unavailable on Windows"))
99}