//! 每次启动的匿名管道。只有子端进入显式继承列表。运行时拥有 Host 端,必须绑定帧并取消阻塞的 IO。 use crate::workspace::{HostError, Result}; #[cfg(any(feature = "desktop", test))] use std::os::windows::io::FromRawHandle; use std::{ fs::File, os::windows::io::{AsRawHandle, OwnedHandle}, }; use windows_sys::Win32::Foundation::*; #[cfg(any(feature = "desktop", test))] use windows_sys::Win32::System::Pipes::CreatePipe; pub struct HostIo { pub input: File, pub output: File, pub error: File, } pub(crate) struct ChildIo { input: OwnedHandle, output: OwnedHandle, error: OwnedHandle, } #[cfg(any(feature = "desktop", test))] fn pair() -> Result<(OwnedHandle, OwnedHandle)> { let mut read = std::ptr::null_mut(); let mut write = std::ptr::null_mut(); if unsafe { CreatePipe(&mut read, &mut write, std::ptr::null(), 4096) } == 0 { return Err(HostError::new("EXTENSION_PIPE_CREATE_FAILED")); } Ok(unsafe { ( OwnedHandle::from_raw_handle(read), OwnedHandle::from_raw_handle(write), ) }) } impl ChildIo { #[cfg(any(feature = "desktop", test))] pub(crate) fn create() -> Result<(Self, HostIo)> { let (input, host_input) = pair()?; let (host_output, output) = pair()?; let (host_error, error) = pair()?; let child = Self { input, output, error, }; Ok(( child, HostIo { input: host_input.into(), output: host_output.into(), error: host_error.into(), }, )) } /// 同时持有管道端点与启动锁。字段的销毁顺序会先关闭所有可继承端点, /// 再允许其他并发 Host 启动继续执行。 pub(crate) fn inherit(self) -> Result { let lock = crate::process_creation::lock().map_err(HostError::new)?; let guarded = InheritedIo { child: self, _creation: lock, }; for handle in guarded.handles() { if unsafe { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) } == 0 { return Err(HostError::new("EXTENSION_PIPE_CREATE_FAILED")); } } Ok(guarded) } pub(crate) fn handles(&self) -> [HANDLE; 3] { [ self.input.as_raw_handle(), self.output.as_raw_handle(), self.error.as_raw_handle(), ] } } pub(crate) struct InheritedIo { child: ChildIo, _creation: std::sync::MutexGuard<'static, ()>, } impl InheritedIo { pub(crate) fn handles(&self) -> [HANDLE; 3] { self.child.handles() } } /// NDJSON 上限不含行结束符。协议或 IO 错误会使解码器失效;运行时必须终止实例并关闭管道。 /// 此同步解码器需要独立的 IO 取消和期限所有者。 pub const MAX_FRAME_BYTES: usize = 2 * 1024 * 1024; pub struct Frames { reader: R, failed: bool, } impl Frames { pub fn new(reader: R) -> Self { Self { reader, failed: false, } } pub fn read(&mut self) -> Result>> { if self.failed { return Err(HostError::new("EXTENSION_PIPE_CLOSED")); } let result = self.read_inner(); if result.is_err() { self.failed = true; } result } fn read_inner(&mut self) -> Result>> { let mut frame = Vec::new(); loop { let available = self .reader .fill_buf() .map_err(|_| HostError::new("EXTENSION_PIPE_READ_FAILED"))?; if available.is_empty() { return if frame.is_empty() { Ok(None) } else { Err(HostError::new("EXTENSION_PIPE_TRUNCATED_FRAME")) }; } let end = available.iter().position(|b| *b == b'\n'); let count = end.unwrap_or(available.len()); if count > MAX_FRAME_BYTES - frame.len() { return Err(HostError::new("EXTENSION_BROKER_REQUEST_TOO_LARGE")); } frame.extend_from_slice(&available[..count]); self.reader.consume(count + usize::from(end.is_some())); if end.is_some() { if frame.last() == Some(&b'\r') { frame.pop(); } if frame.is_empty() { return Err(HostError::new("EXTENSION_PIPE_EMPTY_FRAME")); } return Ok(Some(frame)); } } } } pub fn write_frame(writer: &mut impl std::io::Write, frame: &[u8]) -> Result<()> { if frame.is_empty() || frame.len() > MAX_FRAME_BYTES || frame.contains(&b'\n') || frame.contains(&b'\r') { return Err(HostError::new("EXTENSION_PIPE_INVALID_FRAME")); } writer .write_all(frame) .and_then(|_| writer.write_all(b"\n")) .and_then(|_| writer.flush()) .map_err(|_| HostError::new("EXTENSION_PIPE_WRITE_FAILED")) } #[cfg(test)] mod tests { use super::*; #[test] fn pipes_are_private_until_the_serialized_creation_window() { let (child, host) = ChildIo::create().unwrap(); let ends = child .handles() .into_iter() .chain([ host.input.as_raw_handle(), host.output.as_raw_handle(), host.error.as_raw_handle(), ]) .collect::>(); for handle in &ends { let mut flags = 0; assert_ne!(unsafe { GetHandleInformation(*handle, &mut flags) }, 0); assert_eq!(flags & HANDLE_FLAG_INHERIT, 0); } let child = child.inherit().unwrap(); for (index, handle) in ends.iter().enumerate() { let mut flags = 0; assert_ne!(unsafe { GetHandleInformation(*handle, &mut flags) }, 0); assert_eq!(flags & HANDLE_FLAG_INHERIT != 0, index < 3); assert!(!ends[..index].contains(handle)); } let (started_tx, started_rx) = std::sync::mpsc::channel(); let (acquired_tx, acquired_rx) = std::sync::mpsc::channel(); let worker = std::thread::spawn(move || { started_tx.send(()).unwrap(); let _creation = crate::process_creation::lock().unwrap(); acquired_tx.send(()).unwrap(); }); started_rx.recv().unwrap(); assert!(acquired_rx .recv_timeout(std::time::Duration::from_millis(30)) .is_err()); drop(child); acquired_rx .recv_timeout(std::time::Duration::from_secs(5)) .unwrap(); worker.join().unwrap(); // 最后一个写端在释放锁前关闭;此所有权测试没有启动子进程,因此 Host 会收到 EOF。 use std::io::Read; let mut output = host.output; assert_eq!(output.read(&mut [0; 1]).unwrap(), 0); } #[test] fn frames_are_bounded_across_fragmentation_and_poison_after_errors() { use std::io::{BufReader, Cursor}; let mut frames = Frames::new(BufReader::with_capacity( 1, Cursor::new(b"{\"id\":1}\n{\"id\":2}\r\n"), )); assert_eq!(frames.read().unwrap().unwrap(), b"{\"id\":1}"); assert_eq!(frames.read().unwrap().unwrap(), b"{\"id\":2}"); assert!(frames.read().unwrap().is_none()); let mut largest = vec![b'x'; MAX_FRAME_BYTES]; largest.push(b'\n'); assert_eq!( Frames::new(BufReader::with_capacity(127, Cursor::new(&largest))) .read() .unwrap() .unwrap() .len(), MAX_FRAME_BYTES ); largest.insert(0, b'x'); let mut overflow = Frames::new(BufReader::with_capacity(127, Cursor::new(largest))); assert_eq!( overflow.read().unwrap_err().code, "EXTENSION_BROKER_REQUEST_TOO_LARGE" ); assert_eq!(overflow.read().unwrap_err().code, "EXTENSION_PIPE_CLOSED"); let mut truncated = Frames::new(Cursor::new(b"{}")); assert_eq!( truncated.read().unwrap_err().code, "EXTENSION_PIPE_TRUNCATED_FRAME" ); assert_eq!(truncated.read().unwrap_err().code, "EXTENSION_PIPE_CLOSED"); assert_eq!( Frames::new(Cursor::new(b"\n")).read().unwrap_err().code, "EXTENSION_PIPE_EMPTY_FRAME" ); let mut output = Vec::new(); for invalid in [&b""[..], &b"{}\n{}"[..], &b"{}\r"[..]] { assert!(write_frame(&mut output, invalid).is_err()); assert!(output.is_empty()); } write_frame(&mut output, b"{}").unwrap(); assert_eq!(output, b"{}\n"); } }