split Client into (Client, Connection) (#107)

The Connection type is a `Future` that drives all of the IO of the
client connection.

The Client type is separate, and is used to send requests into the
connection.
This commit is contained in:
Sean McArthur
2017-09-28 16:55:12 -07:00
committed by GitHub
parent 510800ef28
commit f8efb053b9
23 changed files with 489 additions and 262 deletions

View File

@@ -13,7 +13,7 @@ fn handshake() {
.write(SETTINGS_ACK)
.build();
let h2 = Client::handshake(mock).wait().unwrap();
let (_, h2) = Client::handshake(mock).wait().unwrap();
trace!("hands have been shook");
@@ -21,6 +21,43 @@ fn handshake() {
h2.wait().unwrap();
}
#[test]
fn client_other_thread() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://http2.akamai.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200).eos())
.close();
let h2 = Client::handshake(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
::std::thread::spawn(move || {
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let res = client
.send_request(request, true)
.unwrap()
.wait()
.expect("request");
assert_eq!(res.status(), StatusCode::OK);
});
h2.expect("h2")
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn recv_invalid_server_stream_id() {
let _ = ::env_logger::init();
@@ -39,7 +76,7 @@ fn recv_invalid_server_stream_id() {
.write(&[0, 0, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, h2) = Client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
@@ -48,7 +85,7 @@ fn recv_invalid_server_stream_id() {
.unwrap();
info!("sending request");
let stream = h2.send_request(request, true).unwrap();
let stream = client.send_request(request, true).unwrap();
// The connection errors
assert!(h2.wait().is_err());
@@ -67,7 +104,7 @@ fn request_stream_id_overflows() {
.initial_stream_id(::std::u32::MAX >> 1)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
@@ -75,24 +112,26 @@ fn request_stream_id_overflows() {
.unwrap();
// first request is allowed
let req = h2.send_request(request, true).unwrap().unwrap();
let req = client.send_request(request, true).unwrap().unwrap();
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
h2.drive(req).and_then(move |(h2, _)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
// second cannot use the next stream id, it's over
// second cannot use the next stream id, it's over
let poll_err = h2.poll_ready().unwrap_err();
assert_eq!(poll_err.to_string(), "user error: stream ID overflowed");
let poll_err = client.poll_ready().unwrap_err();
assert_eq!(poll_err.to_string(), "user error: stream ID overflowed");
let err = h2.send_request(request, true).unwrap_err();
assert_eq!(err.to_string(), "user error: stream ID overflowed");
let err = client.send_request(request, true).unwrap_err();
assert_eq!(err.to_string(), "user error: stream ID overflowed");
h2.expect("h2").join(req)
h2.expect("h2")
})
});
let srv = srv.assert_client_handshake()
@@ -120,13 +159,27 @@ fn request_over_max_concurrent_streams_errors() {
.max_concurrent_streams(1))
.unwrap()
.recv_settings()
.recv_frame(frames::headers(1).request("POST", "https://example.com/"))
.send_frame(frames::headers(1).response(200))
.recv_frame(
frames::headers(1)
.request("POST", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200).eos())
.recv_frame(frames::headers(3).request("POST", "https://example.com/"))
.send_frame(frames::headers(3).response(200))
.recv_frame(frames::data(3, "hello").eos())
.send_frame(frames::data(3, "").eos())
.recv_frame(frames::headers(5).request("POST", "https://example.com/"))
.send_frame(frames::headers(5).response(200))
.recv_frame(frames::data(5, "hello").eos())
.send_frame(frames::data(5, "").eos())
.close();
let h2 = Client::handshake(io)
.expect("handshake")
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
// we send a simple req here just to drive the connection so we can
// receive the server settings.
let request = Request::builder()
.method(Method::POST)
.uri("https://example.com/")
@@ -134,28 +187,48 @@ fn request_over_max_concurrent_streams_errors() {
.unwrap();
// first request is allowed
let req = h2.send_request(request, false).unwrap().unwrap();
// drive the connection some so we can receive the server settings
h2.drive(req)
let req = client.send_request(request, true).unwrap().unwrap();
h2.drive(req).map(move |(h2, _)| (client, h2))
})
.and_then(|(mut h2, _)| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://example.com/")
.body(())
.unwrap();
// first request is allowed
let mut req = client.send_request(request, false).unwrap();
let request = Request::builder()
.method(Method::POST)
.uri("https://example.com/")
.body(())
.unwrap();
// second request is put into pending_open
let mut req2 = client.send_request(request, false).unwrap();
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
// second stream is over max concurrent
assert!(h2.poll_ready().expect("poll_ready").is_not_ready());
// third stream is over max concurrent
assert!(client.poll_ready().expect("poll_ready").is_not_ready());
let err = h2.send_request(request, true).unwrap_err();
let err = client.send_request(request, true).unwrap_err();
assert_eq!(err.to_string(), "user error: rejected");
h2.expect("h2")
req.send_data("hello".into(), true).expect("req send_data");
h2.drive(req.expect("req")).and_then(move |(h2, _)| {
req2.send_data("hello".into(), true)
.expect("req2 send_data");
h2.expect("h2").join(req2.expect("req2"))
})
});
h2.join(srv).wait().expect("wait");
}

View File

@@ -27,7 +27,7 @@ fn send_data_without_requesting_capacity() {
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
let request = Request::builder()
.method(Method::POST)
@@ -35,7 +35,7 @@ fn send_data_without_requesting_capacity() {
.body(())
.unwrap();
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
// The capacity should be immediately allocated
assert_eq!(stream.capacity(), 0);
@@ -82,14 +82,14 @@ fn release_capacity_sends_window_update() {
// gotta end the connection
.map(drop);
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true).unwrap()
let req = client.send_request(request, true).unwrap()
.unwrap()
// Get the response
.and_then(|resp| {
@@ -145,14 +145,14 @@ fn release_capacity_of_small_amount_does_not_send_window_update() {
// gotta end the connection
.map(drop);
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true).unwrap()
let req = client.send_request(request, true).unwrap()
.unwrap()
// Get the response
.and_then(|resp| {
@@ -212,14 +212,15 @@ fn recv_data_overflows_connection_window() {
.recv_frame(frames::go_away(0).flow_control());
// connection is ended by client
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true)
let req = client
.send_request(request, true)
.unwrap()
.unwrap()
.and_then(|resp| {
@@ -281,14 +282,15 @@ fn recv_data_overflows_stream_window() {
.initial_window_size(16_384)
.handshake::<_, Bytes>(io)
.unwrap()
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true)
let req = client
.send_request(request, true)
.unwrap()
.unwrap()
.and_then(|resp| {
@@ -333,7 +335,7 @@ fn stream_close_by_data_frame_releases_capacity() {
let window_size = frame::DEFAULT_INITIAL_WINDOW_SIZE as usize;
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
@@ -341,7 +343,7 @@ fn stream_close_by_data_frame_releases_capacity() {
.unwrap();
// Send request
let mut s1 = h2.send_request(request, false).unwrap();
let mut s1 = client.send_request(request, false).unwrap();
// This effectively reserves the entire connection window
s1.reserve_capacity(window_size);
@@ -357,7 +359,7 @@ fn stream_close_by_data_frame_releases_capacity() {
.unwrap();
// Create a second stream
let mut s2 = h2.send_request(request, false).unwrap();
let mut s2 = client.send_request(request, false).unwrap();
// Request capacity
s2.reserve_capacity(5);
@@ -401,7 +403,7 @@ fn stream_close_by_trailers_frame_releases_capacity() {
let window_size = frame::DEFAULT_INITIAL_WINDOW_SIZE as usize;
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
@@ -409,7 +411,7 @@ fn stream_close_by_trailers_frame_releases_capacity() {
.unwrap();
// Send request
let mut s1 = h2.send_request(request, false).unwrap();
let mut s1 = client.send_request(request, false).unwrap();
// This effectively reserves the entire connection window
s1.reserve_capacity(window_size);
@@ -425,7 +427,7 @@ fn stream_close_by_trailers_frame_releases_capacity() {
.unwrap();
// Create a second stream
let mut s2 = h2.send_request(request, false).unwrap();
let mut s2 = client.send_request(request, false).unwrap();
// Request capacity
s2.reserve_capacity(5);
@@ -504,14 +506,14 @@ fn recv_window_update_on_stream_closed_by_data_frame() {
let h2 = Client::handshake(io)
.unwrap()
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let stream = h2.send_request(request, false).unwrap();
let stream = client.send_request(request, false).unwrap();
// Wait for the response
h2.drive(GetResponse {
@@ -547,14 +549,14 @@ fn reserved_capacity_assigned_in_multi_window_updates() {
let h2 = Client::handshake(io)
.unwrap()
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
// Consume the capacity
let payload = vec![0; frame::DEFAULT_INITIAL_WINDOW_SIZE as usize];
@@ -674,17 +676,19 @@ fn connection_notified_on_released_capacity() {
let th2 = thread::spawn(move || {
let h2 = Client::handshake(io).wait().unwrap();
let (mut client, h2) = Client::handshake(io).wait().unwrap();
let (mut h2, _) = h2.drive(settings_rx).wait().unwrap();
let (h2, _) = h2.drive(settings_rx).wait().unwrap();
let request = Request::get("https://example.com/a").body(()).unwrap();
tx.send(h2.send_request(request, true).unwrap()).unwrap();
tx.send(client.send_request(request, true).unwrap())
.unwrap();
let request = Request::get("https://example.com/b").body(()).unwrap();
tx.send(h2.send_request(request, true).unwrap()).unwrap();
tx.send(client.send_request(request, true).unwrap())
.unwrap();
// Run the connection to completion
h2.wait().unwrap();

View File

@@ -8,7 +8,9 @@ fn recv_single_ping() {
let (m, mock) = mock::new();
// Create the handshake
let h2 = Client::handshake(m).unwrap().and_then(|conn| conn.unwrap());
let h2 = Client::handshake(m)
.unwrap()
.and_then(|(_, conn)| conn.unwrap());
let mock = mock.assert_client_handshake()
.unwrap()

View File

@@ -25,7 +25,7 @@ fn single_stream_send_large_body() {
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
let request = Request::builder()
.method(Method::POST)
@@ -33,7 +33,7 @@ fn single_stream_send_large_body() {
.body(())
.unwrap();
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
// Reserve capacity to send the payload
stream.reserve_capacity(payload.len());
@@ -79,7 +79,7 @@ fn single_stream_send_extra_large_body_multi_frames_one_buffer() {
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
let request = Request::builder()
.method(Method::POST)
@@ -87,7 +87,7 @@ fn single_stream_send_extra_large_body_multi_frames_one_buffer() {
.body(())
.unwrap();
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
stream.reserve_capacity(payload.len());
@@ -144,7 +144,7 @@ fn single_stream_send_extra_large_body_multi_frames_multi_buffer() {
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
let request = Request::builder()
.method(Method::POST)
@@ -152,7 +152,7 @@ fn single_stream_send_extra_large_body_multi_frames_multi_buffer() {
.body(())
.unwrap();
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
stream.reserve_capacity(payload.len());
@@ -177,7 +177,7 @@ fn send_data_receive_window_update() {
let h2 = Client::handshake(m)
.unwrap()
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
@@ -185,7 +185,7 @@ fn send_data_receive_window_update() {
.unwrap();
// Send request
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
// Send data frame
stream.send_data("hello".into(), false).unwrap();

View File

@@ -20,13 +20,14 @@ fn recv_push_works() {
.send_frame(frames::headers(1).response(200).eos())
.send_frame(frames::headers(2).response(200).eos());
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true)
let req = client
.send_request(request, true)
.unwrap()
.unwrap()
.and_then(|resp| {
@@ -61,13 +62,13 @@ fn recv_push_when_push_disabled_is_conn_error() {
.enable_push(false)
.handshake::<_, Bytes>(io)
.unwrap()
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true).unwrap().then(|res| {
let req = client.send_request(request, true).unwrap().then(|res| {
let err = res.unwrap_err();
assert_eq!(
err.to_string(),

View File

@@ -20,7 +20,7 @@ fn send_recv_headers_only() {
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
@@ -29,7 +29,7 @@ fn send_recv_headers_only() {
.unwrap();
info!("sending request");
let mut stream = h2.send_request(request, true).unwrap();
let mut stream = client.send_request(request, true).unwrap();
let resp = h2.run(poll_fn(|| stream.poll_response())).unwrap();
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
@@ -62,7 +62,7 @@ fn send_recv_data() {
])
.build();
let mut h2 = Client::builder().handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::builder().handshake(mock).wait().unwrap();
let request = Request::builder()
.method(Method::POST)
@@ -71,7 +71,7 @@ fn send_recv_data() {
.unwrap();
info!("sending request");
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
// Reserve send capacity
stream.reserve_capacity(5);
@@ -119,7 +119,7 @@ fn send_headers_recv_data_single_frame() {
])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
@@ -128,7 +128,7 @@ fn send_headers_recv_data_single_frame() {
.unwrap();
info!("sending request");
let mut stream = h2.send_request(request, true).unwrap();
let mut stream = client.send_request(request, true).unwrap();
let resp = h2.run(poll_fn(|| stream.poll_response())).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
@@ -154,32 +154,29 @@ fn closed_streams_are_released() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let h2 = Client::handshake(io)
.unwrap()
.and_then(|mut h2| {
let request = Request::get("https://example.com/").body(()).unwrap();
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::get("https://example.com/").body(()).unwrap();
// Send request
let stream = h2.send_request(request, true).unwrap();
h2.drive(stream)
})
.and_then(|(h2, response)| {
// Send request
let stream = client.send_request(request, true).unwrap();
h2.drive(stream).and_then(move |(_, response)| {
assert_eq!(response.status(), StatusCode::NO_CONTENT);
// There are no active streams
assert_eq!(0, h2.num_active_streams());
assert_eq!(0, client.num_active_streams());
// The response contains a handle for the body. This keeps the
// stream wired.
assert_eq!(1, h2.num_wired_streams());
assert_eq!(1, client.num_wired_streams());
drop(response);
// The stream state is now free
assert_eq!(0, h2.num_wired_streams());
assert_eq!(0, client.num_wired_streams());
Ok(())
});
})
});
let srv = srv.assert_client_handshake()
.unwrap()
@@ -200,14 +197,15 @@ fn errors_if_recv_frame_exceeds_max_frame_size() {
let _ = ::env_logger::init();
let (io, mut srv) = mock::new();
let h2 = Client::handshake(io).unwrap().and_then(|mut h2| {
let h2 = Client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true)
let req = client
.send_request(request, true)
.unwrap()
.unwrap()
.and_then(|resp| {
@@ -258,14 +256,15 @@ fn configure_max_frame_size() {
.max_frame_size(16_384 * 2)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|mut h2| {
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = h2.send_request(request, true)
let req = client
.send_request(request, true)
.unwrap()
.expect("response")
.and_then(|resp| {

View File

@@ -480,7 +480,7 @@ where
Async::Ready((frame, handle)) => (frame, handle),
Async::NotReady => return Ok(Async::NotReady),
};
assert_eq!(frame.unwrap(), self.frame);
assert_eq!(frame.unwrap(), self.frame, "recv_frame");
Ok(Async::Ready(handle))
}
}

View File

@@ -1,5 +1,11 @@
//! Utilities to support tests.
#[cfg(not(feature = "unstable"))]
compile_error!(
"Tests depend on the 'unstable' feature on h2. \
Retry with `cargo test --features unstable`"
);
pub extern crate bytes;
pub extern crate env_logger;
pub extern crate futures;

View File

@@ -60,7 +60,7 @@ pub trait ClientExt {
fn run<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error>;
}
impl<T, B> ClientExt for Client<T, B>
impl<T, B> ClientExt for client::Connection<T, B>
where
T: AsyncRead + AsyncWrite + 'static,
B: IntoBuf + 'static,

View File

@@ -23,7 +23,7 @@ fn recv_trailers_only() {
])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
@@ -32,7 +32,7 @@ fn recv_trailers_only() {
.unwrap();
info!("sending request");
let mut stream = h2.send_request(request, true).unwrap();
let mut stream = client.send_request(request, true).unwrap();
let response = h2.run(poll_fn(|| stream.poll_response())).unwrap();
assert_eq!(response.status(), StatusCode::OK);
@@ -71,7 +71,7 @@ fn send_trailers_immediately() {
])
.build();
let mut h2 = Client::handshake(mock).wait().unwrap();
let (mut client, mut h2) = Client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
@@ -80,7 +80,7 @@ fn send_trailers_immediately() {
.unwrap();
info!("sending request");
let mut stream = h2.send_request(request, false).unwrap();
let mut stream = client.send_request(request, false).unwrap();
let mut trailers = HeaderMap::new();
trailers.insert("zomg", "hello".parse().unwrap());