-
Notifications
You must be signed in to change notification settings - Fork 157
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
streams: Prevent RST_STREAM from being sent multiple times #1267
base: master
Are you sure you want to change the base?
streams: Prevent RST_STREAM from being sent multiple times #1267
Conversation
19e109c
to
ec38cbb
Compare
@MadMockers thanks for sending this fix! |
Hey mate - Yes I had issues with dotnet gRPC, which uses the Kestral HTTP/2 implementation. There's an additional bug in the Kestral implementation that causes a connection error when a second RST_STREAM is received (The correct action for dotnet is to ignore the frame). The bug in Kestral was resolved here (dotnet/aspnetcore@1db20af), however it remains in dotnet 3.1 (an LTS release). I've seen connection errors when using the python grpclib implementation, which uses h2, to talk with the dotnet gRPC server. This issue (coupled with the additional dotnet issue) causes all active gRPC requests to be aborted due to dotnet tearing down the underlying connection. Unfortunately I've found it hard to reproduce this issue with logging as it's racy, however I've manually just updated the file in my venv and am no longer observing it.
I haven't run any of the tests yet, sorry! An additional test would be to have a peer always respond to a I haven't looked at the tests yet, but intend to add one. |
397c071
to
98a0759
Compare
A test for this scenario does already exist, I've added an additional commit which asserts correct behaviour. |
Previously, RST_STREAM would be sent even when the stream closed by state was in SEND_RST_STREAM. This fix instead checks which peer closed the stream initially, and then updates the closed by value from RECV_RST_STREAM to SEND_RST_STREAM after a RST_STREAM frame has been sent on a previously reset stream. Streams that have a closed by value of SEND_RST_STREAM now ignore all frames.
98a0759
to
a1991c7
Compare
Updated with tests and additional coverage (please see CI results here MadMockers#5) I should note that in the process of doing this I did come across a contradiction in the RFC that may be worth discussing. From Section 5.1 (closed state):
The referenced Section 5.4.2:
Essentially we're being told we MUST NOT send any frames, but we MUST treat this as a stream error (which involves sending a frame). I think the common sense interpretation would be that perhaps the RFC should have stated that an endpoint can send both There's other parts however which re-enforce that a Again from Section 5.1 (closed state):
Once a However there is a contradiction, again in Section 5.4.2:
Taken in isolation, the SHOULD NOT portion would imply that the old operation prior to this fix is not invalid (SHOULD NOT != MUST NOT). At a minimum this patch removes behaviour defined as SHOULD NOT. Additionally, as h2 doesn't do round-trip time tracking (that I'm aware of - may have missed this!!!), I think this clause should be taken as a MUST NOT when not doing the time tracking. |
This contradiction has been resolved in the new version of the document. The spec deliberately allows sending multiple RST_STREAM frames to account for the possibility that the peer implementation is buggy: if it is still sending frames on a stream after one RTT from receiving an RST_STREAM frame then the peer implementation is clearly confused and has mishandled the frame. However, the new guidance (that this is a connection error, not a stream error) is probably the best mode of handling this. |
Nice! Just looking through the updated version, it seems the main contradiction is resolved. I'm not sure now when a second
As this portion has now been removed, there doesn't seem to be any state in which a second My reading of the updated
Section 5.4.2. Stream Error Handling still seems to be in contradiction with the updated
This would resolve the issue I'm seeing with the Kestral HTTP2 implementation. That being said, there may be unintended compatibility consequences with other buggy peers. Previously, It may be worth looking at implementing the following suggestion from the updated RFC:
An additional thought which may be deemed out-of-scope is potentially a "strict" mode or a "quirks" mode (understanding that there is legitimate argument against additional code paths to account for these modes). |
Previously, RST_STREAM would be sent even when the stream closed by
state was in SEND_RST_STREAM. This fix instead checks which peer
closed the stream initially, and then updates the closed by value
from RECV_RST_STREAM to SEND_RST_STREAM after a RST_STREAM frame
has been sent on a previously reset stream.
Streams that have a closed by value of SEND_RST_STREAM now ignore
all frames.