windows_erg/pipes/
client.rs1use std::io;
2use std::time::Duration;
3
4use windows::Win32::Foundation::GetLastError;
5use windows::Win32::Foundation::{GENERIC_READ, GENERIC_WRITE};
6use windows::Win32::Storage::FileSystem::{
7 CreateFileW, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, OPEN_EXISTING, ReadFile, WriteFile,
8};
9use windows::Win32::System::Pipes::WaitNamedPipeW;
10use windows::core::PCWSTR;
11
12use crate::error::InvalidParameterError;
13use crate::utils::to_utf16_nul;
14use crate::{Error, Result};
15
16use super::error_map::map_pipe_windows_error;
17use super::security_attrs::NativePipeSecurityAttributes;
18use super::types::{NamedPipeOpenMode, PipeClientEndpoint, PipeName, PipeSecurityOptions};
19
20#[derive(Debug, Clone)]
22pub struct NamedPipeClientBuilder {
23 pipe_name: Option<PipeName>,
24 open_mode: NamedPipeOpenMode,
25 connect_timeout: Duration,
26 security: PipeSecurityOptions,
27}
28
29impl NamedPipeClientBuilder {
30 pub fn new() -> Self {
32 Self {
33 pipe_name: None,
34 open_mode: NamedPipeOpenMode::Duplex,
35 connect_timeout: Duration::from_secs(5),
36 security: PipeSecurityOptions::default(),
37 }
38 }
39
40 pub fn pipe_name(mut self, pipe_name: PipeName) -> Self {
42 self.pipe_name = Some(pipe_name);
43 self
44 }
45
46 pub fn open_mode(mut self, open_mode: NamedPipeOpenMode) -> Self {
48 self.open_mode = open_mode;
49 self
50 }
51
52 pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self {
54 self.connect_timeout = connect_timeout;
55 self
56 }
57
58 pub fn security(mut self, security: PipeSecurityOptions) -> Self {
60 self.security = security;
61 self
62 }
63
64 pub fn build(self) -> Result<NamedPipeClientConfig> {
66 let pipe_name = self.pipe_name.ok_or_else(|| {
67 Error::InvalidParameter(InvalidParameterError::new(
68 "pipe_name",
69 "Pipe name must be specified",
70 ))
71 })?;
72
73 Ok(NamedPipeClientConfig {
74 pipe_name,
75 open_mode: self.open_mode,
76 connect_timeout: self.connect_timeout,
77 security: self.security,
78 })
79 }
80}
81
82impl Default for NamedPipeClientBuilder {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88#[derive(Debug)]
90pub struct NamedPipeClientConfig {
91 pipe_name: PipeName,
92 open_mode: NamedPipeOpenMode,
93 connect_timeout: Duration,
94 security: PipeSecurityOptions,
95}
96
97impl NamedPipeClientConfig {
98 pub fn builder() -> NamedPipeClientBuilder {
100 NamedPipeClientBuilder::new()
101 }
102
103 pub fn connect(&self) -> Result<NamedPipeClient> {
105 let pipe_name_wide = to_utf16_nul(self.pipe_name.as_str());
106 let timeout_ms = self.connect_timeout.as_millis().min(u32::MAX as u128) as u32;
107
108 let waited = unsafe { WaitNamedPipeW(PCWSTR(pipe_name_wide.as_ptr()), timeout_ms) };
109 if !waited.as_bool() {
110 let code = unsafe { GetLastError().0 as i32 };
111 return Err(map_pipe_windows_error(
112 "connect",
113 Some(&self.pipe_name),
114 code,
115 ));
116 }
117
118 let security_attributes =
119 NativePipeSecurityAttributes::from_options(&self.security, self.pipe_name.as_str())?;
120
121 let desired_access = to_client_access(self.open_mode);
122 let raw_handle = unsafe {
123 CreateFileW(
124 PCWSTR(pipe_name_wide.as_ptr()),
125 desired_access,
126 FILE_SHARE_MODE(0),
127 security_attributes.as_option_ptr(),
128 OPEN_EXISTING,
129 FILE_FLAGS_AND_ATTRIBUTES(0),
130 None,
131 )
132 }
133 .map_err(|e| map_pipe_windows_error("connect", Some(&self.pipe_name), e.code().0))?;
134
135 Ok(NamedPipeClient {
136 endpoint: PipeClientEndpoint::from_raw(
137 raw_handle,
138 true,
139 self.pipe_name.clone(),
140 self.open_mode,
141 ),
142 })
143 }
144
145 pub fn pipe_name(&self) -> &PipeName {
147 &self.pipe_name
148 }
149
150 pub fn open_mode(&self) -> NamedPipeOpenMode {
152 self.open_mode
153 }
154
155 pub fn connect_timeout(&self) -> Duration {
157 self.connect_timeout
158 }
159
160 pub fn security(&self) -> PipeSecurityOptions {
162 self.security.clone()
163 }
164}
165
166#[derive(Debug)]
168pub struct NamedPipeClient {
169 endpoint: PipeClientEndpoint,
170}
171
172impl NamedPipeClient {
173 pub fn endpoint(&self) -> &PipeClientEndpoint {
175 &self.endpoint
176 }
177}
178
179impl io::Read for NamedPipeClient {
180 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
181 let mut read = 0u32;
182 unsafe { ReadFile(self.endpoint.raw_handle(), Some(buf), Some(&mut read), None) }
183 .map_err(|e| io::Error::from_raw_os_error(e.code().0))?;
184 Ok(read as usize)
185 }
186}
187
188impl io::Write for NamedPipeClient {
189 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
190 let mut written = 0u32;
191 unsafe {
192 WriteFile(
193 self.endpoint.raw_handle(),
194 Some(buf),
195 Some(&mut written),
196 None,
197 )
198 }
199 .map_err(|e| io::Error::from_raw_os_error(e.code().0))?;
200 Ok(written as usize)
201 }
202
203 fn flush(&mut self) -> io::Result<()> {
204 Ok(())
205 }
206}
207
208fn to_client_access(open_mode: NamedPipeOpenMode) -> u32 {
209 match open_mode {
210 NamedPipeOpenMode::Inbound => GENERIC_READ.0,
211 NamedPipeOpenMode::Outbound => GENERIC_WRITE.0,
212 NamedPipeOpenMode::Duplex => GENERIC_READ.0 | GENERIC_WRITE.0,
213 }
214}