Skip to content

Commit

Permalink
Use RST_STREAM(NO_ERROR) in case server early respond (hyperium#633) (h…
Browse files Browse the repository at this point in the history
…yperium#634)

Http2 Server are allowed to early respond without fully
  consuming client input stream, but must respond with an
  error code of NO_ERROR when sending RST_STREAM.
  Nginx treat any other error code as fatal if not done so

  Commit change error code from CANCEL to NO_ERROR, when the
  server is early responding to the client

  hyperium#633
  https://trac.nginx.org/nginx/ticket/2376
  • Loading branch information
erebe authored Aug 15, 2022
1 parent 756384f commit b0f54d8
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 2 deletions.
14 changes: 13 additions & 1 deletion src/proto/streams/streams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1461,9 +1461,21 @@ fn drop_stream_ref(inner: &Mutex<Inner>, key: store::Key) {

fn maybe_cancel(stream: &mut store::Ptr, actions: &mut Actions, counts: &mut Counts) {
if stream.is_canceled_interest() {
// Server is allowed to early respond without fully consuming the client input stream
// But per the RFC, must send a RST_STREAM(NO_ERROR) in such cases. https://www.rfc-editor.org/rfc/rfc7540#section-8.1
// Some other http2 implementation may interpret other error code as fatal if not respected (i.e: nginx https://trac.nginx.org/nginx/ticket/2376)
let reason = if counts.peer().is_server()
&& stream.state.is_send_closed()
&& stream.state.is_recv_streaming()
{
Reason::NO_ERROR
} else {
Reason::CANCEL
};

actions
.send
.schedule_implicit_reset(stream, Reason::CANCEL, counts, &mut actions.task);
.schedule_implicit_reset(stream, reason, counts, &mut actions.task);
actions.recv.enqueue_reset_expiration(stream, counts);
}
}
Expand Down
4 changes: 3 additions & 1 deletion tests/h2-tests/tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,9 @@ async fn sends_reset_cancel_when_req_body_is_dropped() {
client
.recv_frame(frames::headers(1).response(200).eos())
.await;
client.recv_frame(frames::reset(1).cancel()).await;
client
.recv_frame(frames::reset(1).reason(Reason::NO_ERROR))
.await;
};

let srv = async move {
Expand Down

0 comments on commit b0f54d8

Please sign in to comment.