feat(client): implement connection pooling for Client
Closes #830 Closes #848
This commit is contained in:
@@ -363,3 +363,28 @@ fn client_read_timeout() {
|
||||
other => panic!("expected timeout, actual: {:?}", other)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_keep_alive() {
|
||||
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = server.local_addr().unwrap();
|
||||
let client = client();
|
||||
let res = client.request(format!("http://{}/a", addr), opts());
|
||||
|
||||
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");
|
||||
|
||||
while let Ok(_) = res.recv() {}
|
||||
|
||||
let res = client.request(format!("http://{}/b", addr), opts());
|
||||
sock.read(&mut buf).expect("read 2");
|
||||
let second_get = b"GET /b HTTP/1.1\r\n";
|
||||
assert_eq!(&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");
|
||||
|
||||
while let Ok(_) = res.recv() {}
|
||||
}
|
||||
|
||||
100
tests/server.rs
100
tests/server.rs
@@ -72,7 +72,7 @@ impl Drop for Serve {
|
||||
|
||||
struct TestHandler {
|
||||
tx: mpsc::Sender<Msg>,
|
||||
rx: mpsc::Receiver<Reply>,
|
||||
reply: Vec<Reply>,
|
||||
peeked: Option<Vec<u8>>,
|
||||
timeout: Option<Duration>,
|
||||
}
|
||||
@@ -123,28 +123,26 @@ impl Handler<HttpStream> for TestHandler {
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut Response) -> Next {
|
||||
loop {
|
||||
match self.rx.try_recv() {
|
||||
Ok(Reply::Status(s)) => {
|
||||
for reply in self.reply.drain(..) {
|
||||
match reply {
|
||||
Reply::Status(s) => {
|
||||
res.set_status(s);
|
||||
},
|
||||
Ok(Reply::Headers(headers)) => {
|
||||
Reply::Headers(headers) => {
|
||||
use std::iter::Extend;
|
||||
res.headers_mut().extend(headers.iter());
|
||||
},
|
||||
Ok(Reply::Body(body)) => {
|
||||
Reply::Body(body) => {
|
||||
self.peeked = Some(body);
|
||||
},
|
||||
Err(..) => {
|
||||
return if self.peeked.is_some() {
|
||||
self.next(Next::write())
|
||||
} else {
|
||||
self.next(Next::end())
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if self.peeked.is_some() {
|
||||
self.next(Next::write())
|
||||
} else {
|
||||
self.next(Next::end())
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
@@ -167,13 +165,18 @@ fn serve_with_timeout(dur: Option<Duration>) -> Serve {
|
||||
|
||||
let (msg_tx, msg_rx) = mpsc::channel();
|
||||
let (reply_tx, reply_rx) = mpsc::channel();
|
||||
let mut reply_rx = Some(reply_rx);
|
||||
let (listening, server) = Server::http(&"127.0.0.1:0".parse().unwrap()).unwrap()
|
||||
.handle(move |_| TestHandler {
|
||||
tx: msg_tx.clone(),
|
||||
timeout: dur,
|
||||
rx: reply_rx.take().unwrap(),
|
||||
peeked: None,
|
||||
.handle(move |_| {
|
||||
let mut replies = Vec::new();
|
||||
while let Ok(reply) = reply_rx.try_recv() {
|
||||
replies.push(reply);
|
||||
}
|
||||
TestHandler {
|
||||
tx: msg_tx.clone(),
|
||||
timeout: dur,
|
||||
reply: replies,
|
||||
peeked: None,
|
||||
}
|
||||
}).unwrap();
|
||||
|
||||
|
||||
@@ -377,3 +380,62 @@ fn server_empty_response_chunked_without_calling_write() {
|
||||
assert_eq!(lines.next(), Some(""));
|
||||
assert_eq!(lines.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_keep_alive() {
|
||||
extern crate env_logger;
|
||||
env_logger::init().unwrap();
|
||||
|
||||
let foo_bar = b"foo bar baz";
|
||||
let server = serve();
|
||||
server.reply()
|
||||
.status(hyper::Ok)
|
||||
.header(hyper::header::ContentLength(foo_bar.len() as u64))
|
||||
.body(foo_bar);
|
||||
let mut req = TcpStream::connect(server.addr()).unwrap();
|
||||
req.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
|
||||
req.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
|
||||
req.write_all(b"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: example.domain\r\n\
|
||||
Connection: keep-alive\r\n\
|
||||
\r\n\
|
||||
").expect("writing 1");
|
||||
|
||||
let mut buf = [0; 1024 * 8];
|
||||
loop {
|
||||
let n = req.read(&mut buf[..]).expect("reading 1");
|
||||
if n < buf.len() {
|
||||
if &buf[n - foo_bar.len()..n] == foo_bar {
|
||||
break;
|
||||
} else {
|
||||
println!("{:?}", ::std::str::from_utf8(&buf[..n]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try again!
|
||||
|
||||
let quux = b"zar quux";
|
||||
server.reply()
|
||||
.status(hyper::Ok)
|
||||
.header(hyper::header::ContentLength(quux.len() as u64))
|
||||
.body(quux);
|
||||
req.write_all(b"\
|
||||
GET /quux HTTP/1.1\r\n\
|
||||
Host: example.domain\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
").expect("writing 2");
|
||||
|
||||
let mut buf = [0; 1024 * 8];
|
||||
loop {
|
||||
let n = req.read(&mut buf[..]).expect("reading 2");
|
||||
assert!(n > 0, "n = {}", n);
|
||||
if n < buf.len() {
|
||||
if &buf[n - quux.len()..n] == quux {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user