diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-28 05:03:38 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-28 05:03:38 +0300 |
| commit | 986e4a17767a3d4838e340b92cb2db9e6012acc4 (patch) | |
| tree | 663accbb9db2484ee70ae1be599ea4859f0d8315 /src | |
| parent | 37bdff5bbe7f6b50dce2d8920a08bdaafd4fecda (diff) | |
fix: clamp session socket timeouts
Diffstat (limited to 'src')
| -rw-r--r-- | src/session.rs | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/src/session.rs b/src/session.rs index 56330aa8..5e407df8 100644 --- a/src/session.rs +++ b/src/session.rs @@ -10,6 +10,7 @@ pub const DEFAULT_SESSION_NAME: &str = "default"; const MAX_SESSION_NAME_LEN: usize = 64; const STOP_WAIT_TIMEOUT: Duration = Duration::from_secs(2); const STOP_WAIT_POLL: Duration = Duration::from_millis(25); +const MIN_SOCKET_TIMEOUT: Duration = Duration::from_millis(1); static EXPLICIT_SESSION_REQUESTED: AtomicBool = AtomicBool::new(false); @@ -255,10 +256,9 @@ fn send_stop_request( request: &serde_json::Value, deadline: Instant, ) -> Result<Option<serde_json::Value>, String> { - let write_timeout = time_until(deadline); - if write_timeout.is_zero() { + let Some(write_timeout) = socket_timeout_until(deadline) else { return Ok(None); - } + }; stream .set_write_timeout(Some(write_timeout)) .map_err(|err| err.to_string())?; @@ -283,10 +283,9 @@ fn send_stop_request_inner( stream.write_all(b"\n")?; stream.flush()?; - let read_timeout = time_until(deadline); - if read_timeout.is_zero() { + let Some(read_timeout) = socket_timeout_until(deadline) else { return Ok(None); - } + }; stream.set_read_timeout(Some(read_timeout))?; let mut line = String::new(); @@ -327,6 +326,17 @@ fn time_until(deadline: Instant) -> Duration { deadline.saturating_duration_since(Instant::now()) } +fn socket_timeout_until(deadline: Instant) -> Option<Duration> { + socket_timeout_from_remaining(time_until(deadline)) +} + +fn socket_timeout_from_remaining(remaining: Duration) -> Option<Duration> { + if remaining.is_zero() { + return None; + } + Some(remaining.max(MIN_SOCKET_TIMEOUT)) +} + pub fn validate_name(name: &str) -> Result<(), String> { if name.is_empty() { return Err("session name cannot be empty".to_string()); @@ -395,6 +405,19 @@ mod tests { } #[test] + fn socket_timeouts_are_never_zero_duration() { + assert_eq!(socket_timeout_from_remaining(Duration::ZERO), None); + assert_eq!( + socket_timeout_from_remaining(Duration::from_nanos(1)), + Some(MIN_SOCKET_TIMEOUT) + ); + assert_eq!( + socket_timeout_from_remaining(Duration::from_millis(10)), + Some(Duration::from_millis(10)) + ); + } + + #[test] fn stop_request_empty_response_waits_for_socket_state() { let (client, server) = UnixStream::pair().unwrap(); let handle = std::thread::spawn(move || { |
