From b0f54d80f24112d198e630e13767501d47ae7290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Er=C3=A8be=20-=20Romain=20Gerard?= Date: Mon, 15 Aug 2022 23:08:56 +0200 Subject: [PATCH] Use RST_STREAM(NO_ERROR) in case server early respond (#633) (#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 https://github.com/hyperium/h2/issues/633 https://trac.nginx.org/nginx/ticket/2376 --- src/proto/streams/streams.rs | 14 +++++++++++++- tests/h2-tests/tests/server.rs | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/proto/streams/streams.rs b/src/proto/streams/streams.rs index f11ee08..aee64ca 100644 --- a/src/proto/streams/streams.rs +++ b/src/proto/streams/streams.rs @@ -1461,9 +1461,21 @@ fn drop_stream_ref(inner: &Mutex, 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); } } diff --git a/tests/h2-tests/tests/server.rs b/tests/h2-tests/tests/server.rs index b3bf1a2..948ad16 100644 --- a/tests/h2-tests/tests/server.rs +++ b/tests/h2-tests/tests/server.rs @@ -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 {