diff --git a/src/stream.rs b/src/stream.rs index 7057a93..a3f91c5 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -46,6 +46,33 @@ impl ::std::fmt::Debug for Stream { } impl Stream { + // Check if the server has closed a stream by performing a one-byte + // non-blocking read. If this returns EOF, the server has closed the + // connection: return true. If this returns WouldBlock (aka EAGAIN), + // that means the connection is still open: return false. Otherwise + // return an error. + fn serverclosed_stream(stream: &std::net::TcpStream) -> IoResult { + let mut buf = [0; 1]; + stream.set_nonblocking(true)?; + + let result = match stream.peek(&mut buf) { + Ok(0) => Ok(true), + Ok(_) => Ok(false), // TODO: Maybe this should produce an "unexpected response" error + Err(e) if e.kind() == ErrorKind::WouldBlock => Ok(false), + Err(e) => Err(e), + }; + stream.set_nonblocking(false)?; + + result + } + // Return true if the server has closed this connection. + pub(crate) fn server_closed(&self) -> IoResult { + match self { + Stream::Http(tcpstream) => Stream::serverclosed_stream(tcpstream), + Stream::Https(rustls_stream) => Stream::serverclosed_stream(&rustls_stream.sock), + _ => Ok(false), + } + } pub fn is_poolable(&self) -> bool { match self { Stream::Http(_) => true, diff --git a/src/unit.rs b/src/unit.rs index b461caa..a3b4205 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -274,8 +274,14 @@ fn connect_socket(unit: &Unit, use_pooled: bool) -> Result<(Stream, bool), Error if use_pooled { let state = &mut unit.agent.lock().unwrap(); if let Some(agent) = state.as_mut() { - if let Some(stream) = agent.pool.try_get_connection(&unit.url) { - return Ok((stream, true)); + // The connection may have been closed by the server + // due to idle timeout while it was sitting in the pool. + // Loop until we find one that is still good or run out of connections. + while let Some(stream) = agent.pool.try_get_connection(&unit.url) { + let server_closed = stream.server_closed()?; + if !server_closed { + return Ok((stream, true)); + } } } }