diff --git a/crates/bh_agent_server/src/util/mod.rs b/crates/bh_agent_server/src/util/mod.rs index 0a8af1d..729731f 100644 --- a/crates/bh_agent_server/src/util/mod.rs +++ b/crates/bh_agent_server/src/util/mod.rs @@ -8,6 +8,6 @@ mod unix_functions; pub use read_chars::*; pub use read_lines::read_lines; #[cfg(target_family = "unix")] -pub use set_blocking::set_blocking; +pub use set_blocking::{is_blocking, set_blocking}; #[cfg(target_family = "unix")] pub use unix_functions::{chmod, chown, stat}; diff --git a/crates/bh_agent_server/src/util/read_chars.rs b/crates/bh_agent_server/src/util/read_chars.rs index 4ea2bc9..af5423b 100644 --- a/crates/bh_agent_server/src/util/read_chars.rs +++ b/crates/bh_agent_server/src/util/read_chars.rs @@ -7,17 +7,47 @@ use unicode_reader::Graphemes; use bh_agent_common::FileOpenType; -pub fn read_generic(file: &mut File, n: Option, file_type: FileOpenType) -> Result> { +#[cfg(target_family = "unix")] +use crate::util::is_blocking; + +// This is uhhh... a bit messy. The error handling is the main issue that is +//necessitating this inner function. +pub fn read_generic( + file: &mut File, + mut n: Option, + file_type: FileOpenType, +) -> Result> { trace!("Entering read_generic"); - if let Some(num_bytes) = n { - match file_type { - FileOpenType::Binary => Ok(read_bytes(file, num_bytes as usize)?), - FileOpenType::Text => Ok(read_graphemes(file, num_bytes as usize)?), - } - } else { - // if n is None, we just read the whole file, text parsing happens on the client - Ok(file.bytes().collect::, std::io::Error>>()?) + + #[cfg(target_family = "unix")] + if n.is_none() && !is_blocking(file)? { + n = Some(1024_u32.pow(2)); } + + let inner: Result> = (|| { + trace!("read_generic: entering inner closure..."); + if let Some(num_bytes) = n { + match file_type { + FileOpenType::Binary => Ok(read_bytes(file, num_bytes as usize)?), + FileOpenType::Text => Ok(read_graphemes(file, num_bytes as usize)?), + } + } else { + // if n is None, we just read the whole file, text parsing happens on the client + Ok(file.bytes().collect::, std::io::Error>>()?) + } + })(); + trace!("read_generic: processing inner result..."); + inner.or_else(|e| { + if let Some(std::io::ErrorKind::WouldBlock) = + e.downcast_ref::().map(|e| e.kind()) + { + trace!("read_generic: WouldBlock error, returning empty vec"); + Ok(Vec::new()) + } else { + trace!("read_generic: returning error"); + Err(e) + } + }) } fn read_bytes(file: &mut File, n: usize) -> Result> { diff --git a/crates/bh_agent_server/src/util/set_blocking.rs b/crates/bh_agent_server/src/util/set_blocking.rs index f47d0fc..ea74b66 100644 --- a/crates/bh_agent_server/src/util/set_blocking.rs +++ b/crates/bh_agent_server/src/util/set_blocking.rs @@ -3,7 +3,7 @@ use std::io; use std::os::unix::io::AsRawFd; // TODO: This is using libc directly, but nix has a wrapper for this. We should use that instead. -pub fn set_blocking(fd: &T, blocking: bool) -> io::Result<()> { +pub fn set_blocking(fd: &impl AsRawFd, blocking: bool) -> io::Result<()> { let raw_fd = fd.as_raw_fd(); let flags = unsafe { fcntl(raw_fd, F_GETFL, 0) }; if flags < 0 { @@ -22,3 +22,13 @@ pub fn set_blocking(fd: &T, blocking: bool) -> io::Result<()> { Ok(()) } + +pub fn is_blocking(fd: &impl AsRawFd) -> io::Result { + let raw_fd = fd.as_raw_fd(); + let flags = unsafe { fcntl(raw_fd, F_GETFL, 0) }; + if flags < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(flags & O_NONBLOCK == 0) +}