459 lines
14 KiB
Rust
459 lines
14 KiB
Rust
#![feature(async_await)]
|
|
use futures::future::join;
|
|
use futures::{StreamExt, TryStreamExt};
|
|
use h2_support::prelude::*;
|
|
|
|
#[tokio::test]
|
|
async fn recv_push_works() {
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let mock = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_default_settings!(settings);
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::headers(1).response(404)).await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::data(1, "").eos()).await;
|
|
srv.send_frame(frames::headers(2).response(200)).await;
|
|
srv.send_frame(frames::data(2, "promised_data").eos()).await;
|
|
};
|
|
let h2 = async move {
|
|
let (mut client, mut h2) = client::handshake(io).await.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
let (mut resp, _) = client.send_request(request, true).unwrap();
|
|
let pushed = resp.push_promises();
|
|
let check_resp_status = async move {
|
|
let resp = resp.await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
};
|
|
let check_pushed_response = async move {
|
|
let p = pushed.and_then(|headers| {
|
|
async move {
|
|
let (request, response) = headers.into_parts();
|
|
assert_eq!(request.into_parts().0.method, Method::GET);
|
|
let resp = response.await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let b = resp.into_body().try_concat().await.unwrap();
|
|
assert_eq!(b, "promised_data");
|
|
Ok(())
|
|
}
|
|
});
|
|
let ps: Vec<_> = p.collect().await;
|
|
assert_eq!(1, ps.len())
|
|
};
|
|
|
|
h2.drive(join(check_resp_status, check_pushed_response))
|
|
.await;
|
|
};
|
|
|
|
join(mock, h2).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pushed_streams_arent_dropped_too_early() {
|
|
// tests that by default, received push promises work
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let mock = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_default_settings!(settings);
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::headers(1).response(404)).await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 4).request("GET", "https://http2.akamai.com/style2.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::data(1, "").eos()).await;
|
|
idle_ms(10).await;
|
|
srv.send_frame(frames::headers(2).response(200)).await;
|
|
srv.send_frame(frames::headers(4).response(200).eos()).await;
|
|
srv.send_frame(frames::data(2, "").eos()).await;
|
|
srv.recv_frame(frames::go_away(4)).await;
|
|
};
|
|
|
|
let h2 = async move {
|
|
let (mut client, mut h2) = client::handshake(io).await.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
let (mut resp, _) = client.send_request(request, true).unwrap();
|
|
let mut pushed = resp.push_promises();
|
|
let check_status = async move {
|
|
let resp = resp.await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
};
|
|
|
|
let check_pushed = async move {
|
|
let mut count = 0;
|
|
while let Some(headers) = pushed.next().await {
|
|
let (request, response) = headers.unwrap().into_parts();
|
|
assert_eq!(request.into_parts().0.method, Method::GET);
|
|
let resp = response.await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
count += 1;
|
|
}
|
|
assert_eq!(2, count);
|
|
};
|
|
|
|
drop(client);
|
|
|
|
h2.drive(join(check_pushed, check_status)).await;
|
|
h2.await.expect("client");
|
|
};
|
|
|
|
join(mock, h2).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn recv_push_when_push_disabled_is_conn_error() {
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let mock = async move {
|
|
let _ = srv.assert_client_handshake().await;
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 3).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::headers(1).response(200).eos()).await;
|
|
srv.recv_frame(frames::go_away(0).protocol_error()).await;
|
|
};
|
|
|
|
let h2 = async move {
|
|
let (mut client, h2) = client::Builder::new()
|
|
.enable_push(false)
|
|
.handshake::<_, Bytes>(io)
|
|
.await
|
|
.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
|
|
let req = async move {
|
|
let res = client.send_request(request, true).unwrap().0.await;
|
|
let err = res.unwrap_err();
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"protocol error: unspecific protocol error detected"
|
|
);
|
|
};
|
|
|
|
// client should see a protocol error
|
|
let conn = async move {
|
|
let res = h2.await;
|
|
let err = res.unwrap_err();
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"protocol error: unspecific protocol error detected"
|
|
);
|
|
};
|
|
|
|
join(conn, req).await;
|
|
};
|
|
|
|
join(mock, h2).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pending_push_promises_reset_when_dropped() {
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let srv = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_default_settings!(settings);
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::headers(1).response(200).eos()).await;
|
|
srv.recv_frame(frames::reset(2).cancel()).await;
|
|
};
|
|
|
|
let client = async move {
|
|
let (mut client, mut conn) = client::handshake(io).await.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
let req = async {
|
|
let resp = client
|
|
.send_request(request, true)
|
|
.unwrap()
|
|
.0
|
|
.await
|
|
.expect("response");
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
};
|
|
|
|
let _ = conn.drive(req).await;
|
|
conn.await.expect("client");
|
|
drop(client);
|
|
};
|
|
|
|
join(srv, client).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn recv_push_promise_over_max_header_list_size() {
|
|
let _ = env_logger::try_init();
|
|
let (io, mut srv) = mock::new();
|
|
|
|
let srv = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_frame_eq(settings, frames::settings().max_header_list_size(10));
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.recv_frame(frames::reset(2).refused()).await;
|
|
srv.send_frame(frames::headers(1).response(200).eos()).await;
|
|
idle_ms(10).await;
|
|
};
|
|
|
|
let client = async move {
|
|
let (mut client, mut conn) = client::Builder::new()
|
|
.max_header_list_size(10)
|
|
.handshake::<_, Bytes>(io)
|
|
.await
|
|
.expect("handshake");
|
|
let request = Request::builder()
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
|
|
let req = async move {
|
|
let err = client
|
|
.send_request(request, true)
|
|
.expect("send_request")
|
|
.0
|
|
.await
|
|
.expect_err("response");
|
|
assert_eq!(err.reason(), Some(Reason::REFUSED_STREAM));
|
|
};
|
|
|
|
conn.drive(req).await;
|
|
conn.await.expect("client");
|
|
};
|
|
join(srv, client).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn recv_invalid_push_promise_headers_is_stream_protocol_error() {
|
|
// Unsafe method or content length is stream protocol error
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let mock = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_default_settings!(settings);
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::headers(1).response(404)).await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("POST", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 4)
|
|
.request("GET", "https://http2.akamai.com/style.css")
|
|
.field(http::header::CONTENT_LENGTH, 1),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 6)
|
|
.request("GET", "https://http2.akamai.com/style.css")
|
|
.field(http::header::CONTENT_LENGTH, 0),
|
|
)
|
|
.await;
|
|
srv.send_frame(frames::headers(1).response(404).eos()).await;
|
|
srv.recv_frame(frames::reset(2).protocol_error()).await;
|
|
srv.recv_frame(frames::reset(4).protocol_error()).await;
|
|
srv.send_frame(frames::headers(6).response(200).eos()).await;
|
|
};
|
|
|
|
let h2 = async move {
|
|
let (mut client, mut h2) = client::handshake(io).await.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
let (mut resp, _) = client.send_request(request, true).unwrap();
|
|
let check_pushed_response = async move {
|
|
let pushed = resp.push_promises();
|
|
let p = pushed.and_then(|headers| headers.into_parts().1);
|
|
let ps: Vec<_> = p.collect().await;
|
|
// CONTENT_LENGTH = 0 is ok
|
|
assert_eq!(1, ps.len());
|
|
};
|
|
h2.drive(check_pushed_response).await;
|
|
};
|
|
|
|
join(mock, h2).await;
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn recv_push_promise_with_wrong_authority_is_stream_error() {
|
|
// if server is foo.com, :authority = bar.com is stream error
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn recv_push_promise_skipped_stream_id() {
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let mock = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_default_settings!(settings);
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 4).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.recv_frame(frames::go_away(0).protocol_error()).await;
|
|
};
|
|
|
|
let h2 = async move {
|
|
let (mut client, h2) = client::handshake(io).await.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
|
|
let req = async move {
|
|
let res = client.send_request(request, true).unwrap().0.await;
|
|
assert!(res.is_err());
|
|
};
|
|
|
|
// client should see a protocol error
|
|
let conn = async move {
|
|
let res = h2.await;
|
|
let err = res.unwrap_err();
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"protocol error: unspecific protocol error detected"
|
|
);
|
|
};
|
|
|
|
join(conn, req).await;
|
|
};
|
|
|
|
join(mock, h2).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn recv_push_promise_dup_stream_id() {
|
|
let _ = env_logger::try_init();
|
|
|
|
let (io, mut srv) = mock::new();
|
|
let mock = async move {
|
|
let settings = srv.assert_client_handshake().await;
|
|
assert_default_settings!(settings);
|
|
srv.recv_frame(
|
|
frames::headers(1)
|
|
.request("GET", "https://http2.akamai.com/")
|
|
.eos(),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.send_frame(
|
|
frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"),
|
|
)
|
|
.await;
|
|
srv.recv_frame(frames::go_away(0).protocol_error()).await;
|
|
};
|
|
|
|
let h2 = async move {
|
|
let (mut client, h2) = client::handshake(io).await.unwrap();
|
|
let request = Request::builder()
|
|
.method(Method::GET)
|
|
.uri("https://http2.akamai.com/")
|
|
.body(())
|
|
.unwrap();
|
|
|
|
let req = async move {
|
|
let res = client.send_request(request, true).unwrap().0.await;
|
|
assert!(res.is_err());
|
|
};
|
|
|
|
// client should see a protocol error
|
|
let conn = async move {
|
|
let res = h2.await;
|
|
let err = res.unwrap_err();
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"protocol error: unspecific protocol error detected"
|
|
);
|
|
};
|
|
|
|
join(conn, req).await;
|
|
};
|
|
|
|
join(mock, h2).await;
|
|
}
|