feat(server): add http1_half_close(bool) option
This option determines whether a read EOF should close the connection automatically. The behavior was to always allow read EOF while waiting to respond, so this option has a default of `true`. Setting this option to `false` will allow Service futures to be canceled as soon as disconnect is noticed. Closes #1716
This commit is contained in:
@@ -38,6 +38,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
Conn {
|
||||
io: Buffered::new(io),
|
||||
state: State {
|
||||
allow_half_close: true,
|
||||
cached_headers: None,
|
||||
error: None,
|
||||
keep_alive: KA::Busy,
|
||||
@@ -75,6 +76,10 @@ where I: AsyncRead + AsyncWrite,
|
||||
self.state.title_case_headers = true;
|
||||
}
|
||||
|
||||
pub(crate) fn set_disable_half_close(&mut self) {
|
||||
self.state.allow_half_close = false;
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> (I, Bytes) {
|
||||
self.io.into_inner()
|
||||
}
|
||||
@@ -228,10 +233,11 @@ where I: AsyncRead + AsyncWrite,
|
||||
|
||||
trace!("read_keep_alive; is_mid_message={}", self.is_mid_message());
|
||||
|
||||
if !self.is_mid_message() {
|
||||
self.require_empty_read().map_err(::Error::new_io)?;
|
||||
if self.is_mid_message() {
|
||||
self.mid_message_detect_eof().map_err(::Error::new_io)
|
||||
} else {
|
||||
self.require_empty_read().map_err(::Error::new_io)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_mid_message(&self) -> bool {
|
||||
@@ -252,7 +258,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
// This should only be called for Clients wanting to enter the idle
|
||||
// state.
|
||||
fn require_empty_read(&mut self) -> io::Result<()> {
|
||||
assert!(!self.can_read_head() && !self.can_read_body());
|
||||
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
||||
|
||||
if !self.io.read_buf().is_empty() {
|
||||
debug!("received an unexpected {} bytes", self.io.read_buf().len());
|
||||
@@ -279,11 +285,21 @@ where I: AsyncRead + AsyncWrite,
|
||||
}
|
||||
}
|
||||
|
||||
fn mid_message_detect_eof(&mut self) -> io::Result<()> {
|
||||
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
||||
|
||||
if self.state.allow_half_close || !self.io.read_buf().is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
self.try_io_read().map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_io_read(&mut self) -> Poll<usize, io::Error> {
|
||||
match self.io.read_from_io() {
|
||||
Ok(Async::Ready(0)) => {
|
||||
trace!("try_io_read; found EOF on connection: {:?}", self.state);
|
||||
let must_error = self.should_error_on_eof();
|
||||
let must_error = !self.state.is_idle();
|
||||
let ret = if must_error {
|
||||
let desc = if self.is_mid_message() {
|
||||
"unexpected EOF waiting for response"
|
||||
@@ -655,6 +671,7 @@ impl<I, B: Buf, T> fmt::Debug for Conn<I, B, T> {
|
||||
}
|
||||
|
||||
struct State {
|
||||
allow_half_close: bool,
|
||||
/// Re-usable HeaderMap to reduce allocating new ones.
|
||||
cached_headers: Option<HeaderMap>,
|
||||
/// If an error occurs when there wasn't a direct way to return it
|
||||
|
||||
Reference in New Issue
Block a user