fix(client): fix connection leak when Response finishes before Request body
If the Response was received and the body finished while the Request body was still streaming, the connection could get into a state where it was never polled again, thus never re-inserting into the connection pool or being dropped. Closes #1717
This commit is contained in:
@@ -794,7 +794,7 @@ impl State {
|
|||||||
match (&self.reading, &self.writing) {
|
match (&self.reading, &self.writing) {
|
||||||
(&Reading::KeepAlive, &Writing::KeepAlive) => {
|
(&Reading::KeepAlive, &Writing::KeepAlive) => {
|
||||||
if let KA::Busy = self.keep_alive.status() {
|
if let KA::Busy = self.keep_alive.status() {
|
||||||
self.idle();
|
self.idle::<T>();
|
||||||
} else {
|
} else {
|
||||||
trace!("try_keep_alive({}): could keep-alive, but status = {:?}", T::LOG, self.keep_alive);
|
trace!("try_keep_alive({}): could keep-alive, but status = {:?}", T::LOG, self.keep_alive);
|
||||||
self.close();
|
self.close();
|
||||||
@@ -819,12 +819,23 @@ impl State {
|
|||||||
self.keep_alive.busy();
|
self.keep_alive.busy();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn idle(&mut self) {
|
fn idle<T: Http1Transaction>(&mut self) {
|
||||||
|
debug_assert!(!self.is_idle(), "State::idle() called while idle");
|
||||||
|
|
||||||
self.method = None;
|
self.method = None;
|
||||||
self.keep_alive.idle();
|
self.keep_alive.idle();
|
||||||
if self.is_idle() {
|
if self.is_idle() {
|
||||||
self.reading = Reading::Init;
|
self.reading = Reading::Init;
|
||||||
self.writing = Writing::Init;
|
self.writing = Writing::Init;
|
||||||
|
|
||||||
|
// !T::should_read_first() means Client.
|
||||||
|
//
|
||||||
|
// If Client connection has just gone idle, the Dispatcher
|
||||||
|
// should try the poll loop one more time, so as to poll the
|
||||||
|
// pending requests stream.
|
||||||
|
if !T::should_read_first() {
|
||||||
|
self.notify_read = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.close();
|
self.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ where
|
|||||||
// user has dropped sender handle
|
// user has dropped sender handle
|
||||||
Ok(Async::Ready(None))
|
Ok(Async::Ready(None))
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::NotReady) => Ok(Async::NotReady),
|
||||||
Err(never) => match never {},
|
Err(never) => match never {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1215,6 +1215,83 @@ mod dispatch_impl {
|
|||||||
assert_eq!(connects.load(Ordering::Relaxed), 2);
|
assert_eq!(connects.load(Ordering::Relaxed), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn client_keep_alive_when_response_before_request_body_ends() {
|
||||||
|
use futures_timer::Delay;
|
||||||
|
let _ = pretty_env_logger::try_init();
|
||||||
|
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let addr = server.local_addr().unwrap();
|
||||||
|
let mut rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let connector = DebugConnector::new();
|
||||||
|
let connects = connector.connects.clone();
|
||||||
|
|
||||||
|
let client = Client::builder()
|
||||||
|
.build(connector);
|
||||||
|
|
||||||
|
let (tx1, rx1) = oneshot::channel();
|
||||||
|
let (tx2, rx2) = oneshot::channel();
|
||||||
|
let (tx3, rx3) = oneshot::channel();
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut sock = server.accept().unwrap().0;
|
||||||
|
sock.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
|
||||||
|
sock.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
sock.read(&mut buf).expect("read 1");
|
||||||
|
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 1");
|
||||||
|
// after writing the response, THEN stream the body
|
||||||
|
let _ = tx1.send(());
|
||||||
|
|
||||||
|
sock.read(&mut buf).expect("read 2");
|
||||||
|
let _ = tx2.send(());
|
||||||
|
|
||||||
|
let n2 = sock.read(&mut buf).expect("read 3");
|
||||||
|
assert_ne!(n2, 0);
|
||||||
|
let second_get = "GET /b HTTP/1.1\r\n";
|
||||||
|
assert_eq!(s(&buf[..second_get.len()]), second_get);
|
||||||
|
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 2");
|
||||||
|
let _ = tx3.send(());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
assert_eq!(connects.load(Ordering::Relaxed), 0);
|
||||||
|
|
||||||
|
let delayed_body = rx1
|
||||||
|
.map_err(|_| -> hyper::Error { panic!("rx1") })
|
||||||
|
.and_then(|_| Delay::new(Duration::from_millis(200)).map_err(|_| panic!("delay")))
|
||||||
|
.into_stream()
|
||||||
|
.map(|_| "hello a");
|
||||||
|
|
||||||
|
let rx = rx2.expect("thread panicked");
|
||||||
|
let req = Request::builder()
|
||||||
|
.method("POST")
|
||||||
|
.uri(&*format!("http://{}/a", addr))
|
||||||
|
.body(Body::wrap_stream(delayed_body))
|
||||||
|
.unwrap();
|
||||||
|
let client2 = client.clone();
|
||||||
|
|
||||||
|
// req 1
|
||||||
|
let fut = client.request(req)
|
||||||
|
.join(rx)
|
||||||
|
.and_then(|_| Delay::new(Duration::from_millis(200)).expect("delay"))
|
||||||
|
// req 2
|
||||||
|
.and_then(move |()| {
|
||||||
|
let rx = rx3.expect("thread panicked");
|
||||||
|
let req = Request::builder()
|
||||||
|
.uri(&*format!("http://{}/b", addr))
|
||||||
|
.body(Body::empty())
|
||||||
|
.unwrap();
|
||||||
|
client2
|
||||||
|
.request(req)
|
||||||
|
.join(rx)
|
||||||
|
.map(|_| ())
|
||||||
|
});
|
||||||
|
|
||||||
|
rt.block_on(fut).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(connects.load(Ordering::Relaxed), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connect_proxy_sends_absolute_uri() {
|
fn connect_proxy_sends_absolute_uri() {
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|||||||
Reference in New Issue
Block a user