-
-
Notifications
You must be signed in to change notification settings - Fork 279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Http2 protocol violation #633
Comments
Yes, I believe we've discussed before that there are cases that this should send The full response has been sent (or scheduled to send), and any option DATA and trailers. Basically, that an end-of-stream frame is going out. I think |
Hello, thanks for the answer ! So I am at the right place 🎆 fn maybe_cancel(stream: &mut store::Ptr, actions: &mut Actions, counts: &mut Counts) {
if stream.is_canceled_interest() {
+ // To match HalfClosedLocal(Streaming) state
+ let reason = if stream.state.is_send_closed() && stream.state.is_recv_streaming() {
+ Reason::NO_ERROR
+ } else {
+ Reason::CANCEL
+ };
actions
.send
.schedule_implicit_reset(stream, reason, counts, &mut actions.task);
actions.recv.enqueue_reset_expiration(stream, counts);
}
} |
Ummmm, hard for me to remember. I imagine if you changed that function without the conditional, some unit test would complain? That might show which unit test needs to be updated, or maybe we dont have this specific case as a test... |
Ok, so I am impressed by the quality of your tests ... Regarding the issue, by adding an diff --git a/src/proto/streams/streams.rs b/src/proto/streams/streams.rs
index f11ee08..bfce840 100644
--- a/src/proto/streams/streams.rs
+++ b/src/proto/streams/streams.rs
@@ -1461,9 +1461,16 @@ 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() {
+ // To match HalfClosedLocal(Streaming) state
+ 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..e61fd3b 100644
--- a/tests/h2-tests/tests/server.rs
+++ b/tests/h2-tests/tests/server.rs
@@ -566,7 +566,7 @@ 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 { If this seems good to you, I can go forward and make a PR next week. |
Nice, looks good to me! |
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
Here we are #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 #633 https://trac.nginx.org/nginx/ticket/2376
Thank you for the kind words and your reactivity ^^ By any chances, do you plan to make a new release soonish ? |
This feels related to hyperium/tonic#992, right? |
Hello @LucioFranco I was planning to open an issue in tonic to ask to bump h2 version, once a new release is made, but seems you are ahead of me |
Thank you @seanmonstar and @LucioFranco , I am closing the issue as it is resolved and released 👍 |
Thanks again for the excellent work! If you're ever interested in helping on other aspects of |
Hello,
I am reporting an issue I have encountered while using tonic grpc server (v0.8.0).
We use nginx as a GRPC reverse proxy and hit the following corner case.
Full details here: https://trac.nginx.org/nginx/ticket/2376 you can also find a pcap capture of the issue in the ticket
With this setup
[client] === grpc stream (with ssl) ===> [nginx] === grpc stream (cleartext) ===> [backend]
and considering the following grpc service description
If the http2 server generated by tonic early respond to the client without consuming fully the input stream
The following packets are going to be emitted by tonic/hyper/h2, in response to the call.
This specific sequence of packet is causing nginx to miss-behave and not forward the DATA & RST_STREAM back to the client.
After discussion with a maintainer of nginx, this is caused by the last RST_STREAM(error: CANCEL) which is invalid in regard of the spec, it should be a RST_STREAM(NO_ERROR)
As per the RFC
I tracked down where is coming this RST_STREAM(CANCEL), at first I thought it was in tonic but it ended-up being in the
impl Drop
of h2h2/src/proto/streams/streams.rs
Line 1466 in 756384f
So it seems a generic way of handling the end of stream for h2.
And now I am stuck in my investigation and require some guidance regarding where to go from here.
We really like tonic and nginx and would appreciate if we can go forward to make both happy to work together
The text was updated successfully, but these errors were encountered: