Files
h2/tests/h2-tests/tests/push_promise.rs
Anthony Ramine 465f0337f8 Refactor errors internals (#556)
h2::Error now knows whether protocol errors happened because the user
sent them, because it was received from the remote peer, or because
the library itself emitted an error because it detected a protocol
violation.

It also keeps track of whether it came from a RST_STREAM or GO_AWAY
frame, and in the case of the latter, it includes the additional
debug data if any.

Fixes #530
2021-09-28 09:04:35 -07:00

468 lines
15 KiB
Rust

use futures::future::join;
use futures::{StreamExt, TryStreamExt};
use h2_support::prelude::*;
#[tokio::test]
async fn recv_push_works() {
h2_support::trace_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 = util::concat(resp.into_body()).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
h2_support::trace_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() {
h2_support::trace_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(),
"connection error detected: 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(),
"connection error detected: unspecific protocol error detected"
);
};
join(conn, req).await;
};
join(mock, h2).await;
}
#[tokio::test]
async fn pending_push_promises_reset_when_dropped() {
h2_support::trace_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() {
h2_support::trace_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
h2_support::trace_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() {
h2_support::trace_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 err = client
.send_request(request, true)
.unwrap()
.0
.await
.unwrap_err();
assert_eq!(
err.to_string(),
"connection error detected: 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(),
"connection error detected: unspecific protocol error detected"
);
};
join(conn, req).await;
};
join(mock, h2).await;
}
#[tokio::test]
async fn recv_push_promise_dup_stream_id() {
h2_support::trace_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;
let err = res.unwrap_err();
assert_eq!(
err.to_string(),
"connection error detected: 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(),
"connection error detected: unspecific protocol error detected"
);
};
join(conn, req).await;
};
join(mock, h2).await;
}