Files
h2/tests/client_request.rs
Brian Smith b6724f7d7a Upgrade to env_logger 0.5 & log 0.4; reduce related dependencies (#226)
Upgrade to env_logger 0.5 and log 0.4 so that projects that use those
versions don't have to build both those versions and the older ones
that h2 is currently using.

Don't enable the regex support in env_logger. Applications that want
the regex support can enable it themselves; this will happen
automatically when they add their env_logger dependency.

Disable the env_logger dependency in quickcheck.

The result of this is that there are fewer dependencies. For example,
regex and its dependencies are no longer required at all, as can be
seen by observing the changes to the Cargo.lock. That said,
env_logger 0.5 does add more dependencies itself; however it seems
applications are going to use env_logger 0.5 anyway so this is still
a net gain.

Submitted on behalf of Buoyant, Inc.

Signed-off-by: Brian Smith <brian@briansmith.org>
2018-02-23 20:25:42 -08:00

649 lines
19 KiB
Rust

#[macro_use]
extern crate log;
pub mod support;
use support::prelude::*;
#[test]
fn handshake() {
let _ = ::env_logger::try_init();
let mock = mock_io::Builder::new()
.handshake()
.write(SETTINGS_ACK)
.build();
let (_client, h2) = client::handshake(mock).wait().unwrap();
trace!("hands have been shook");
// At this point, the connection should be closed
h2.wait().unwrap();
}
#[test]
fn client_other_thread() {
let _ = ::env_logger::try_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().0
.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::try_init();
let mock = mock_io::Builder::new()
.handshake()
// Write GET /
.write(&[
0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29,
0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84,
])
.write(SETTINGS_ACK)
// Read response
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 2, 137])
// Write GO_AWAY
.write(&[0, 0, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
.build();
let (mut client, h2) = client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
info!("sending request");
let (response, _) = client.send_request(request, true).unwrap();
// The connection errors
assert!(h2.wait().is_err());
// The stream errors
assert!(response.wait().is_err());
}
#[test]
fn request_stream_id_overflows() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let h2 = client::Builder::new()
.initial_stream_id(::std::u32::MAX >> 1)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
// first request is allowed
let (response, _) = client.send_request(request, true).unwrap();
h2.drive(response).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
let poll_err = client.poll_ready().unwrap_err();
assert_eq!(poll_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").map(|ret| {
// Hold on to the `client` handle to avoid sending a GO_AWAY
// frame.
drop(client);
ret
})
})
});
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(::std::u32::MAX >> 1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(
frames::headers(::std::u32::MAX >> 1)
.response(200)
.eos()
)
.idle_ms(10)
.close();
h2.join(srv).wait().expect("wait");
}
#[test]
fn client_builder_max_concurrent_streams() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let mut settings = frame::Settings::default();
settings.set_max_concurrent_streams(Some(1));
let srv = srv
.assert_client_handshake()
.unwrap()
.recv_custom_settings(settings)
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos()
)
.send_frame(frames::headers(1).response(200).eos())
.close();
let mut builder = client::Builder::new();
builder.max_concurrent_streams(1);
let h2 = builder
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let (response, _) = client.send_request(request, true).unwrap();
h2.drive(response).map(move |(h2, _)| (client, h2))
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn request_over_max_concurrent_streams_errors() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake_with_settings(frames::settings()
// super tiny server
.max_concurrent_streams(1))
.unwrap()
.recv_settings()
.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 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/")
.body(())
.unwrap();
// first request is allowed
let (response, _) = client.send_request(request, true).unwrap();
h2.drive(response).map(move |(h2, _)| (client, h2))
})
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::POST)
.uri("https://example.com/")
.body(())
.unwrap();
// first request is allowed
let (resp1, mut stream1) = 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 (resp2, mut stream2) = client.send_request(request, false).unwrap();
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
// third stream is over max concurrent
assert!(client.poll_ready().expect("poll_ready").is_not_ready());
let err = client.send_request(request, true).unwrap_err();
assert_eq!(err.to_string(), "user error: rejected");
stream1.send_data("hello".into(), true).expect("req send_data");
h2.drive(resp1.expect("req")).and_then(move |(h2, _)| {
stream2.send_data("hello".into(), true)
.expect("req2 send_data");
h2.expect("h2").join(resp2.expect("req2"))
})
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn http_11_request_without_scheme_or_authority() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "/")
.scheme("http")
.eos(),
)
.send_frame(frames::headers(1).response(200).eos())
.close();
let h2 = client::handshake(io)
.expect("handshake")
.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::GET)
.uri("/")
.body(())
.unwrap();
// first request is allowed
let (response, _) = client.send_request(request, true).unwrap();
h2.drive(response)
.map(move |(h2, _)| (client, h2))
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn http_2_request_without_scheme_or_authority() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.close();
let h2 = client::handshake(io)
.expect("handshake")
.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()
.version(Version::HTTP_2)
.method(Method::GET)
.uri("/")
.body(())
.unwrap();
// first request is allowed
assert!(client.send_request(request, true).is_err());
h2.expect("h2").map(|ret| {
// Hold on to the `client` handle to avoid sending a GO_AWAY frame.
drop(client);
ret
})
});
h2.join(srv).wait().expect("wait");
}
#[test]
#[ignore]
fn request_with_h1_version() {}
#[test]
fn request_with_connection_headers() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.close();
let headers = vec![
("connection", "foo"),
("keep-alive", "5"),
("proxy-connection", "bar"),
("transfer-encoding", "chunked"),
("upgrade", "HTTP/2.0"),
("te", "boom"),
];
let client = client::handshake(io)
.expect("handshake")
.and_then(move |(mut client, conn)| {
for (name, val) in headers {
let req = Request::builder()
.uri("https://http2.akamai.com/")
.header(name, val)
.body(())
.unwrap();
let err = client.send_request(req, true).expect_err(name);
assert_eq!(err.to_string(), "user error: malformed headers");
}
conn.unwrap()
});
client.join(srv).wait().expect("wait");
}
#[test]
fn connection_close_notifies_response_future() {
let _ = ::env_logger::try_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(),
)
// don't send any response, just close
.close();
let client = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = client
.send_request(request, true)
.expect("send_request1")
.0
.then(|res| {
let err = res.expect_err("response");
assert_eq!(
err.to_string(),
"broken pipe"
);
Ok(())
});
conn.expect("conn").join(req)
});
client.join(srv).wait().expect("wait");
}
#[test]
fn connection_close_notifies_client_poll_ready() {
let _ = ::env_logger::try_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(),
)
.close();
let client = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req = client
.send_request(request, true)
.expect("send_request1")
.0
.then(|res| {
let err = res.expect_err("response");
assert_eq!(
err.to_string(),
"broken pipe"
);
Ok::<_, ()>(())
});
conn.drive(req)
.and_then(move |(_conn, _)| {
let err = client.poll_ready().expect_err("poll_ready");
assert_eq!(
err.to_string(),
"broken pipe"
);
Ok(())
})
});
client.join(srv).wait().expect("wait");
}
#[test]
fn sending_request_on_closed_connection() {
let _ = ::env_logger::try_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())
// a bad frame!
.send_frame(frames::headers(0).response(200).eos())
.close();
let h2 = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
// first request works
let req = client
.send_request(request, true)
.expect("send_request1")
.0
.expect("response1")
.map(|_| ());
// after finish request1, there should be a conn error
let h2 = h2.then(|res| {
res.expect_err("h2 error");
Ok::<(), ()>(())
});
h2.select(req)
.then(|res| match res {
Ok((_, next)) => next,
Err(_) => unreachable!("both selected futures cannot error"),
})
.map(move |_| client)
})
.and_then(|mut client| {
let poll_err = client.poll_ready().unwrap_err();
let msg = "protocol error: unspecific protocol error detected";
assert_eq!(poll_err.to_string(), msg);
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let send_err = client.send_request(request, true).unwrap_err();
assert_eq!(send_err.to_string(), msg);
Ok(())
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn recv_too_big_headers() {
let _ = ::env_logger::try_init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_custom_settings(
frames::settings()
.max_header_list_size(10)
)
.recv_frame(
frames::headers(1)
.request("GET", "https://http2.akamai.com/")
.eos(),
)
.recv_frame(
frames::headers(3)
.request("GET", "https://http2.akamai.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200).eos())
.send_frame(frames::headers(3).response(200))
// no reset for 1, since it's closed anyways
// but reset for 3, since server hasn't closed stream
.recv_frame(frames::reset(3).refused())
.idle_ms(10)
.close();
let client = client::Builder::new()
.max_header_list_size(10)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req1 = client
.send_request(request, true)
.expect("send_request")
.0
.expect_err("response1")
.map(|err| {
assert_eq!(
err.reason(),
Some(Reason::REFUSED_STREAM)
);
});
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let req2 = client
.send_request(request, true)
.expect("send_request")
.0
.expect_err("response2")
.map(|err| {
assert_eq!(
err.reason(),
Some(Reason::REFUSED_STREAM)
);
});
conn.drive(req1.join(req2))
.and_then(|(conn, _)| conn.expect("client"))
.map(|c| (c, client))
});
client.join(srv).wait().expect("wait");
}
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
trait MockH2 {
fn handshake(&mut self) -> &mut Self;
}
impl MockH2 for mock_io::Builder {
fn handshake(&mut self) -> &mut Self {
self.write(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
// Settings frame
.write(SETTINGS)
.read(SETTINGS)
.read(SETTINGS_ACK)
}
}