use std::time::Duration;
use interprocess::local_socket::traits::Stream as _;
use interprocess::local_socket::Stream;
const DEFAULT_PROBE_TIMEOUT: Duration = Duration::from_millis(250);
pub fn probe_local_socket(endpoint: &str) -> std::io::Result<()> {
if endpoint.is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"probe_local_socket: empty endpoint",
));
}
probe_local_socket_with_deadline(endpoint, DEFAULT_PROBE_TIMEOUT)
}
pub fn probe_local_socket_with_deadline(endpoint: &str, deadline: Duration) -> std::io::Result<()> {
let endpoint = endpoint.to_owned();
call_with_io_deadline("probe_local_socket", deadline, move || {
#[cfg(windows)]
let name = {
use interprocess::local_socket::{GenericNamespaced, ToNsName};
ToNsName::to_ns_name::<GenericNamespaced>(endpoint.as_str())?
};
#[cfg(unix)]
let name = {
use interprocess::local_socket::{GenericFilePath, ToFsName};
ToFsName::to_fs_name::<GenericFilePath>(endpoint.as_str())?
};
let stream = Stream::connect(name)?;
drop(stream);
Ok(())
})
}
fn call_with_io_deadline<T, F>(label: &'static str, deadline: Duration, f: F) -> std::io::Result<T>
where
T: Send + 'static,
F: FnOnce() -> std::io::Result<T> + Send + 'static,
{
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let _ = tx.send(f());
});
match rx.recv_timeout(deadline) {
Ok(result) => result,
Err(_) => Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
format!("{label}: exceeded deadline of {deadline:?}"),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probe_local_socket_no_listener_returns_err() {
#[cfg(windows)]
let endpoint = r"\\.\pipe\zccache-probe-no-listener-1001";
#[cfg(unix)]
let endpoint = "/tmp/zccache-probe-no-listener-1001.sock";
let err = probe_local_socket(endpoint).expect_err("no listener => Err");
assert!(matches!(
err.kind(),
std::io::ErrorKind::NotFound
| std::io::ErrorKind::ConnectionRefused
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::Other
));
}
#[test]
fn probe_local_socket_empty_endpoint_is_invalid_input() {
let err = probe_local_socket("").expect_err("empty => Err");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
}