-
Notifications
You must be signed in to change notification settings - Fork 5.1k
http: Fix ASSERT failure and infinite loop when attempting to unset readDisable state on a closed connection. #9509
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
http: Fix ASSERT failure and infinite loop when attempting to unset readDisable state on a closed connection. #9509
Conversation
…able state on a closed connection. Signed-off-by: Antonio Vicente <avd@google.com>
…d_disabled_close Signed-off-by: Antonio Vicente <avd@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for debugging and fixing this. In general this makes sense to me. Optimally I would like @alyssawilk to review, but if this needs to merge urgently we can do that and she can post-review. Also need to check format.
/wait
I think it would be fine to wait until @alyssawilk is back from the holidays |
…d_disabled_close Signed-off-by: Antonio Vicente <avd@google.com>
…rectly. Signed-off-by: Antonio Vicente <avd@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good find - thanks for tackling it!
test/integration/integration_test.cc
Outdated
@@ -221,6 +221,10 @@ TEST_P(IntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { | |||
testRouterUpstreamDisconnectBeforeResponseComplete(); | |||
} | |||
|
|||
TEST_P(IntegrationTest, ResponseFramedByConnectionCloseWithReadLimits) { | |||
testResponseFramedByConnectionCloseWithReadLimits(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is only used in one place please just put the code inline - it's not totally obvious but I'm trying to push for inline code where things are not reused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/integration/http_integration.cc
Outdated
auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); | ||
waitForNextUpstreamRequest(); | ||
// Disable chunking to trigger framing by connection close. | ||
upstream_request_->encodeHeaders(Http::TestHeaderMapImpl{{":status", "200"}, {":no-chunks", "1"}}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just run for HTTP/1.1 right?
ostensibly the no chunk header is never supposed to be sent on the wire - I think if you don't send a content length and send HTTP/1.0 (see existing example tests) you can force frame by close that way. Mind trying it out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having some trouble finding examples under test/integration that encode an HTTP/1.0 response. Do you have some links handy?
As far as I can tell ":no-chunks" is the way to tell StreamEncoderImpl::encodeHeaders in source/common/http/http1/codec_impl.cc to not chunk a response without a content-length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grep around for config_helper_.addConfigModifier(&setAllowHttp10WithDefaultHost); ?
They all use sendRawHttpAndWaitForResponse but given this is a header only request and you're waiting for connection close I think that should be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fairly sure that the :no-chunk does not end up on the wire. One thing that I wanted to verify in some way is that the Envoy does see an upstream response framed by connection close. I'm not sure how to accomplish that, since I think envoy will add chunk encoding to the response it sends downstream.
Let's talk briefly offline.
… readEnabled is called. Fix typo "chunking" -> "chunk encoding" Signed-off-by: Antonio Vicente <avd@google.com>
Signed-off-by: Antonio Vicente <avd@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for the fixes here. Will defer to @alyssawilk for further review.
upstream_request_->encodeHeaders(Http::TestHeaderMapImpl{{":status", "200"}, {":no-chunks", "1"}}, | ||
false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you wanted to avoid the :no-chunks
business, you could potentially just use a raw TCP fake upstream connection and manually send an HTTP/1.0 response (you could I suppose also build HTTP/1.0 handling into the fake upstream but that sounds like a lot more work), which IIRC Envoy should handle correctly with close framing. I don't feel strongly about it but I know @alyssawilk commented on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I take Antonio's point that :no-chunks doesn't reach the wire - I'm (warily) fine with it.
has alyssawilk seal of approval :-P
/backport |
@lambdai could you create the PR against |
…isable state on a closed connection (#190) This is a port of envoyproxy#9509 Signed-off-by: Dmitri Dolguikh <ddolguik@redhat.com>
…eadDisable state on a closed connection. (envoyproxy#9509) Description: Handle calls to ConnectionImpl::readDisable on Closed connections more gracefully. There are several places under source/common, including ClientConnectionImpl::onMessageComplete, where Connection::readDisable(false) is called on a loop to clear the readDisable state as part of the process of preparing upstream connections for reuse. It is possible to end up in an infinite loop if proxying an HTTP/1 response is framed by connection close, the call to onData triggers readDisable(true), and there is a pending PostIoAction::Close action. The closeSocket call on the upstreamConnection triggers the call to ClientConnectionImpl::onMessageComplete Risk Level: medium Testing: unit and integration tests Docs Changes: n/a Release Notes: n/a Fixes envoyproxy#9508 Signed-off-by: Antonio Vicente <avd@google.com> Signed-off-by: Yuchen Dai <silentdai@gmail.com>
…eadDisable state on a closed connection. (envoyproxy#9509) Description: Handle calls to ConnectionImpl::readDisable on Closed connections more gracefully. There are several places under source/common, including ClientConnectionImpl::onMessageComplete, where Connection::readDisable(false) is called on a loop to clear the readDisable state as part of the process of preparing upstream connections for reuse. It is possible to end up in an infinite loop if proxying an HTTP/1 response is framed by connection close, the call to onData triggers readDisable(true), and there is a pending PostIoAction::Close action. The closeSocket call on the upstreamConnection triggers the call to ClientConnectionImpl::onMessageComplete Risk Level: medium Testing: unit and integration tests Docs Changes: n/a Release Notes: n/a Fixes envoyproxy#9508 Signed-off-by: Antonio Vicente <avd@google.com> Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Description: Handle calls to ConnectionImpl::readDisable on Closed connections more gracefully. There are several places under source/common, including ClientConnectionImpl::onMessageComplete, where Connection::readDisable(false) is called on a loop to clear the readDisable state as part of the process of preparing upstream connections for reuse. It is possible to end up in an infinite loop if proxying an HTTP/1 response is framed by connection close, the call to onData triggers readDisable(true), and there is a pending PostIoAction::Close action. The closeSocket call on the upstreamConnection triggers the call to ClientConnectionImpl::onMessageComplete
Risk Level: Low
Testing: unit and integration tests
Docs Changes: n/a
Release Notes: n/a
Fixes #9508