-
Notifications
You must be signed in to change notification settings - Fork 18k
x/net/http2: misbehaved streams can cause connections to exhaust flow control #28204
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
Comments
I think this might be related to 8f38f28#diff-d863507a61be206d112f6e00e6d812a2 since it appears that the http.Server marks the stream as |
/cc @bradfitz |
Can you capture the logs (from setting |
I can capture some debug logs, I was using then to debug this and had to
add my own logging to capture the flow control values. Would you like me to
keep my log statements in there too?
…On Tue, Oct 16, 2018, 7:58 PM Brad Fitzpatrick ***@***.***> wrote:
Can you capture the logs (from setting GODEBUG=http2debug=2) while this
happens?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#28204 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACu-MlsmdhrhqhK-x99LEqbcpPVLjVLHks5ulpzhgaJpZM4XbrWd>
.
|
We ran into the same issue, and I've put together a small repro using the gRPC client, and a gRPC server hosted via the x/net HTTP/2 server: This example doesn't contain use the reverse proxy, and the issue is exactly as @jared2501 pointed out -- when the server sends When this test is run, the client reports a "deadline exceeded" (since it's waiting to send data, but never gets to it since the window is full):
I have the The client doesn't stop sending frames, and so the server keeps responding with: This seems like a regression introduced in golang/net@039a425 -- before that change, the code would send a window update. I also have the "fix" (update flow-control) on a branch called "fix", where the test mostly passes. However, around 1-2% of the time, it still fails with the same error -- I think there might be other paths where the server sends |
Hey @prashantv - glad to see you can repro my issue! I looked over your example and it's possible you're also being hit by #28634. Although, even with patches for this issue and #28634, I was still seeing some flow control issues when using grpc with a context with a timeout... |
@prashantv You are correct. I did introduce a bug, but there is another one there too. Once we receive a data frame that does not cause a connection error, we must account for the data. The take() logic should be before all the additional state checks regardless of the path taken. I believe there may also be a similar bug on the client side. |
Change https://golang.org/cl/153977 mentions this issue: |
This reverts CL 111676 for golang/go#25023. Reason for revert: The code change no longer issued a WindowUpdate which is required when processing data. The original issue found in golang/go#25023 is not present after the revert. Updates golang/go#28204 Change-Id: Iadbb63d50ca06df1281e699b9ef13181d0593f80 Reviewed-on: https://go-review.googlesource.com/c/153977 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Change https://golang.org/cl/159179 mentions this issue: |
Updates bundled http2 to x/net git rev ed066c81e7 for: http2: Revert a closed stream cannot receive data https://golang.org/cl/153977 Updates #28204 Change-Id: I0a489e4e8a581a107970199f64f0fa9281982efe Reviewed-on: https://go-review.googlesource.com/c/159179 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
You don't need a misbehaved stream to trigger this. If the server doesn't read the request body, flow control gets exhausted. Here's a client/server where the server ignores the body on the first few POST (it just It's broken (server-side) from 1.11.0 through 1.12beta2, and fixed in 1.12rc1 by golang/net@ed066c8 . We see this in production when the client is Chrome, as well as Go, so I don't think it's a misbehaving client. Repro unit test: https://play.golang.org/p/EOUShlw2SNk (replace cert.pem, cert.key, and myhost). Client
Server
If the above doesn't reproduce it for you try increasing To make this easier for others to find, could the issue's title be updated to something like "Not reading request body can cause connections to exhaust flow control"? |
The title should make it clear whether this is a Server or Transport issue. Which is it? I see discussion of both in here. (I haven't looked into this yet.) |
I don't know enough to say if it's Server or Transport, but I guess Server. It's an accounting error in What I think is happening is that when the HTTP handler returns without reading the body (say it returns a 404), any DATA frames in flight are counted towards the connection-level flow control by the client, but not by the server, so they get out of sync. Once the HTTP handler returns the server closes the stream like this:
The in-flight DATA frames are considered invalid:
If I print out the flow control values ( This is the same as @prashantv described above. They added a clarification to the spec for this:
|
In http2 the flow control values are always relative, which makes problems like this very difficult to catch (it survived all of Go 1.11.X). Personally I think the inability to re-sync flow control window sizes between client and server makes the protocol brittle. I started a discussion about that on the IETF HTTP Working Group's mailing list, here https://lists.w3.org/Archives/Public/ietf-http-wg/2019JanMar/0155.html . The emerging consensus seems to be that the protocol is fine but better debugging support would be welcome. |
Heya, I was looking at the latest http2 server code and this looks to be fixed here. Is this issue worth closing? |
The issue reported was fixed. |
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?go1.11 darwin/amd64
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?What did you do? / What did you expect to see? / What did you see instead?
Use httputil.ReverseProxy with an http2.Transport. I'm still trying to find a minimal reproducible example.
Basically, the http2.Transport is closing an HTTP/2 stream and then sending a number of DATA frames on the connection. The http2.Server is detecting that the stream has been half-closed by the remote and is following RFC7540 and responding with an ErrCodeStreamClosed (see http2/server.go).
However, since the http2.Server does not send a window update the remote http2.Transport does not add back the sent DATA frames to its flow control. This causes a re-used http2.Transport to eventually expend all connection flow control and halt sending.
A proposed fix that I've applied to my fork is to make the following patch
Although, one could argue that http2.Transport/httputil.Reverse proxy should be fixed to make sure that data frames aren't sent after a close.
The text was updated successfully, but these errors were encountered: