use super::{TransportError, TransportResult, MAX_MESSAGE_SIZE};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
pub enum WindowsNamedPipeConnection {
Server(tokio::net::windows::named_pipe::NamedPipeServer),
Client(tokio::net::windows::named_pipe::NamedPipeClient),
}
impl WindowsNamedPipeConnection {
pub fn from_server(pipe: tokio::net::windows::named_pipe::NamedPipeServer) -> Self {
Self::Server(pipe)
}
pub fn from_client(pipe: tokio::net::windows::named_pipe::NamedPipeClient) -> Self {
Self::Client(pipe)
}
pub async fn read_message(&mut self) -> TransportResult<Vec<u8>> {
let mut length_buf = [0u8; 4];
match self {
WindowsNamedPipeConnection::Server(p) => p.read_exact(&mut length_buf).await?,
WindowsNamedPipeConnection::Client(p) => p.read_exact(&mut length_buf).await?,
};
let length = u32::from_be_bytes(length_buf) as usize;
if length == 0 || length > MAX_MESSAGE_SIZE {
return Err(TransportError::MessageTooLarge {
size: length,
max: MAX_MESSAGE_SIZE,
});
}
let mut buffer = vec![0u8; length];
match self {
WindowsNamedPipeConnection::Server(p) => p.read_exact(&mut buffer).await?,
WindowsNamedPipeConnection::Client(p) => p.read_exact(&mut buffer).await?,
};
Ok(buffer)
}
pub async fn write_message(&mut self, data: &[u8]) -> TransportResult<()> {
let length = data.len() as u32;
if length as usize > MAX_MESSAGE_SIZE {
return Err(TransportError::MessageTooLarge {
size: data.len(),
max: MAX_MESSAGE_SIZE,
});
}
match self {
WindowsNamedPipeConnection::Server(p) => p.write_all(&length.to_be_bytes()).await?,
WindowsNamedPipeConnection::Client(p) => p.write_all(&length.to_be_bytes()).await?,
};
match self {
WindowsNamedPipeConnection::Server(p) => p.write_all(data).await?,
WindowsNamedPipeConnection::Client(p) => p.write_all(data).await?,
};
match self {
WindowsNamedPipeConnection::Server(p) => p.flush().await?,
WindowsNamedPipeConnection::Client(p) => p.flush().await?,
};
Ok(())
}
pub fn close(&mut self) -> TransportResult<()> {
match self {
WindowsNamedPipeConnection::Server(p) => {
p.disconnect().map_err(TransportError::Io)?;
}
WindowsNamedPipeConnection::Client(_) => {
}
};
Ok(())
}
pub fn is_open(&self) -> bool {
true
}
}
pub async fn connect_named_pipe(
pipe_name: &str,
timeout_ms: u64,
) -> TransportResult<WindowsNamedPipeConnection> {
use tokio::time::{Duration, Instant};
let deadline = Instant::now() + Duration::from_millis(timeout_ms);
loop {
let client = tokio::net::windows::named_pipe::ClientOptions::new().open(pipe_name);
match client {
Ok(c) => return Ok(WindowsNamedPipeConnection::from_client(c)),
Err(e) => {
if Instant::now() >= deadline {
return Err(TransportError::ConnectionFailed(format!(
"Failed to connect to named pipe {}: {}",
pipe_name, e
)));
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
}
}