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:
Sean McArthur
2018-11-26 16:19:28 -08:00
parent c35bdca8fa
commit 73345be65f
4 changed files with 121 additions and 5 deletions

View File

@@ -991,6 +991,71 @@ fn nonempty_parse_eof_returns_error() {
rt.block_on(fut).expect_err("partial parse eof is error");
}
#[test]
fn socket_half_closed() {
let _ = pretty_env_logger::try_init();
let mut rt = Runtime::new().unwrap();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
thread::spawn(move || {
let mut tcp = connect(&addr);
tcp.write_all(b"GET / HTTP/1.1\r\n\r\n").unwrap();
tcp.shutdown(::std::net::Shutdown::Write).expect("SHDN_WR");
let mut buf = [0; 256];
tcp.read(&mut buf).unwrap();
let expected = "HTTP/1.1 200 OK\r\n";
assert_eq!(s(&buf[..expected.len()]), expected);
});
let fut = listener.incoming()
.into_future()
.map_err(|_| unreachable!())
.and_then(|(item, _incoming)| {
let socket = item.unwrap();
Http::new()
.serve_connection(socket, service_fn(|_| {
Delay::new(Duration::from_millis(500))
.map(|_| {
Response::new(Body::empty())
})
}))
});
rt.block_on(fut).unwrap();
}
#[test]
fn disconnect_after_reading_request_before_responding() {
let _ = pretty_env_logger::try_init();
let mut rt = Runtime::new().unwrap();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
thread::spawn(move || {
let mut tcp = connect(&addr);
tcp.write_all(b"GET / HTTP/1.1\r\n\r\n").unwrap();
});
let fut = listener.incoming()
.into_future()
.map_err(|_| unreachable!())
.and_then(|(item, _incoming)| {
let socket = item.unwrap();
Http::new()
.http1_half_close(false)
.serve_connection(socket, service_fn(|_| {
Delay::new(Duration::from_secs(2))
.map(|_| -> Response<Body> {
panic!("response future should have been dropped");
})
}))
});
rt.block_on(fut).expect_err("socket disconnected");
}
#[test]
fn returning_1xx_response_is_error() {
let mut rt = Runtime::new().unwrap();