#[cfg(unix)]
use std::os::unix::fs::{FileTypeExt, PermissionsExt};
use std::path::Path;
#[cfg(unix)]
use hyper_util::rt::TokioIo;
#[cfg(unix)]
use tokio::net::{UnixListener, UnixStream};
#[cfg(unix)]
use tokio_stream::wrappers::UnixListenerStream;
use tonic::transport::Channel;
#[cfg(unix)]
use tower::service_fn;
use crate::Error;
#[cfg(unix)]
pub type Incoming = UnixListenerStream;
#[cfg(windows)]
#[derive(Debug)]
pub struct Incoming;
#[cfg(unix)]
pub async fn listen(path: &Path) -> Result<Incoming, Error> {
if let Some(parent) = path.parent()
&& !parent.as_os_str().is_empty()
{
tokio::fs::create_dir_all(parent).await?;
}
match tokio::fs::symlink_metadata(path).await {
Ok(metadata) if metadata.file_type().is_socket() => {
if UnixStream::connect(path).await.is_ok() {
return Err(
std::io::Error::new(
std::io::ErrorKind::AddrInUse,
"Unix socket is already accepting connections",
)
.into(),
);
}
tracing::debug!(socket = %path.display(), "removing stale Unix socket");
tokio::fs::remove_file(path).await?;
},
Ok(_) => {},
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {},
Err(error) => return Err(error.into()),
}
let listener = UnixListener::bind(path)?;
tokio::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)).await?;
Ok(UnixListenerStream::new(listener))
}
#[cfg(unix)]
pub async fn connect(path: &Path) -> Result<Channel, Error> {
let path = path.to_owned();
let endpoint = tonic::transport::Endpoint::from_static("http://[::]:50051");
let channel = endpoint
.connect_with_connector(service_fn(move |_| {
let path = path.clone();
async move { UnixStream::connect(path).await.map(TokioIo::new) }
}))
.await?;
Ok(channel)
}
#[cfg(windows)]
pub async fn listen(_path: &Path) -> Result<Incoming, Error> {
Err(Error::Unsupported("Unix-domain sockets are unavailable on Windows"))
}
#[cfg(windows)]
pub async fn connect(_path: &Path) -> Result<Channel, Error> {
Err(Error::Unsupported("Unix-domain sockets are unavailable on Windows"))
}