Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,11 @@ impl proto::Peer for Peer {

if let Some(status) = pseudo.status {
b = b.status(status);
} else {
// Every response must include :status (RFC 9113, section 8.3.2).
// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.2
proto_err!(stream: "missing :status; stream={:?}", stream_id);
return Err(Error::library_reset(stream_id, Reason::PROTOCOL_ERROR));
}

let mut response = match b.body(()) {
Expand Down
69 changes: 69 additions & 0 deletions tests/h2-tests/tests/client_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,75 @@ async fn sending_request_on_closed_connection() {
join(srv, h2).await;
}

#[tokio::test]
async fn response_missing_status() {
h2_support::trace_init!();
let (io, mut srv) = mock::new();

let srv = async move {
srv.assert_client_handshake().await;
srv.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.await;

srv.send_frame(frames::headers(1).eos()).await;

srv.recv_frame(
frames::headers(3)
.request("GET", "https://example.com/")
.eos(),
)
.await;

srv.send_frame(frames::headers(3).response(103)).await;
srv.send_frame(frames::headers(3).field("server", "test"))
.await;

srv.recv_frame(frames::reset(3).protocol_error()).await;
srv.recv_frame(
frames::headers(5)
.request("GET", "https://example.com/")
.eos(),
)
.await;

srv.send_frame(frames::headers(5).response(204).eos()).await;
};

let client = async move {
let (mut client, mut conn) = client::handshake(io).await.unwrap();

let request = Request::get("https://example.com/").body(()).unwrap();
let (response, _) = client.send_request(request, true).unwrap();
let err = conn
.drive(response)
.await
.expect_err("stream 1: empty response without :status");
assert!(err.is_reset());
assert_eq!(err.reason(), Some(Reason::PROTOCOL_ERROR));

let request = Request::get("https://example.com/").body(()).unwrap();
let (response, _) = client.send_request(request, true).unwrap();
let err = conn
.drive(response)
.await
.expect_err("stream 3: final response without :status after 103");
assert!(err.is_reset());
assert_eq!(err.reason(), Some(Reason::PROTOCOL_ERROR));

let request = Request::get("https://example.com/").body(()).unwrap();
let (response, _) = client.send_request(request, true).unwrap();
assert_eq!(conn.drive(response).await.unwrap().status(), 204);
};

tokio::time::timeout(std::time::Duration::from_secs(5), join(srv, client))
.await
.expect("response_missing_status timed out");
}

#[tokio::test]
async fn recv_too_big_headers() {
h2_support::trace_init!();
Expand Down
2 changes: 1 addition & 1 deletion tests/h2-tests/tests/stream_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1482,7 +1482,7 @@ async fn srv_window_update_on_lower_stream_id() {
frames::push_promise(7, 2).request("GET", "https://http2.akamai.com/style.css"),
)
.await;
srv.send_frame(frames::headers(7).eos()).await;
srv.send_frame(frames::headers(7).response(200).eos()).await;
srv.recv_frame(frames::reset(2).cancel()).await;
srv.send_frame(frames::window_update(5, 66666)).await;
};
Expand Down