hcm: fix sending local reply during encode + grpc request classification#13256
hcm: fix sending local reply during encode + grpc request classification#13256snowp merged 19 commits intoenvoyproxy:masterfrom
Conversation
Fixes two issues that surfaced when adding an integration test for the gRPC reverse bridge: 1) when sending a local reply during the encoding path, local_complete_ is set to true which results in endStream being called twice. We fix this by not calling endStream during the header/body callback when local_complete_ is true 2) the direct reply function was not using the state variable to detect is_grpc_request, which meant that for the reverse bridge is was using the modified headers which no longer have a gRPC content type. Signed-off-by: Snow Pettersen <snowp@lyft.com>
|
Locally this seems to work and cause no issues with integration tests, lmk if you want to proceed with this or revert the other PR @mattklein123 |
|
@zuercher Turns out there was a bug around text/plain as well, because during integration tests we end up taking a different flow and hit code that wasn't checking the state variable but instead checked the request headers that we'd already modified to not be grpc anymore. Lesson to be learnt: integration test it! |
mattklein123
left a comment
There was a problem hiding this comment.
At a high level this makes sense to me. But I would like to see:
- conn_manager_impl unit tests
- non-extension specific integration tests
If it's easier to revert the other PR for now and re-apply with ^ that's fine with me. Thank you!
/wait
source/common/http/filter_manager.cc
Outdated
| filter_manager_callbacks_.encodeHeaders(*filter_manager_callbacks_.responseHeaders(), | ||
| end_stream); | ||
| maybeEndEncode(end_stream); | ||
| maybeEndEncode(end_stream && !state_.local_complete_); |
There was a problem hiding this comment.
Sorry where is local_complete_ set to true? Before the filter chain starts iterating? This is not super intuitive. Can you add more comments?
There was a problem hiding this comment.
but why do we need to sometimes end encode after encode headers / encode data, and sometimes outside of the EncodeFunctions? Can't we just remove the one at the end, or remove these two calls?
There was a problem hiding this comment.
I looked through the code and I think it was there because the call to encode headers could trigger a reset, in which case encodeData wouldn't be called, so in that case endStream wouldn't be called. Calling reset would call local_complete_, and so endStream would be called at the end of the function.
That said, I tried removing it and no integration tests seem to fail. Seems like doEndStream is called as part of reset anyways, so it might not be necessary? Pushed this change, so let's see if CI passes
|
Yeah that's reasonable, let's revert the other PR now to keep HEAD healthy. |
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
| constexpr static char name[] = "local-reply-during-encode"; | ||
|
|
||
| Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override { | ||
| decoder_callbacks_->sendLocalReply(Http::Code::InternalServerError, "", nullptr, absl::nullopt, |
There was a problem hiding this comment.
Would it make sense to add sendLocalReply to the StreamEncoderFilterCallbacks interface?
Signed-off-by: Snow Pettersen <snowp@lyft.com>
mattklein123
left a comment
There was a problem hiding this comment.
Thanks LGTM other than remaining comments.
/wait
| ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); | ||
| } | ||
|
|
||
| TEST_P(ReverseBridgeIntegrationTest, EnabledRouteBadContentType) { |
There was a problem hiding this comment.
FWIW I don't see the harm of leaving this test.
There was a problem hiding this comment.
I was thinking that it wouldn't pass without the other PR but I'm not sure if that's true, I'll try adding it back
There was a problem hiding this comment.
Oh sorry I forgot about that. OK if you want to add it back there that's fine.
There was a problem hiding this comment.
I was able to get it working, seems like it just tested an existing feature so it passes now and should nicely serve as a regression test to make sure that we're not breaking this feature with the change to use sendLocalReply in the future
| constexpr static char name[] = "local-reply-during-encode"; | ||
|
|
||
| Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override { | ||
| decoder_callbacks_->sendLocalReply(Http::Code::InternalServerError, "", nullptr, absl::nullopt, |
This reverts commit 2f4d2f6. Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
|
/retest |
|
Retrying Azure Pipelines, to retry CircleCI checks, use |
|
@alyssawilk do you wanna take a look at this as well? |
alyssawilk
left a comment
There was a problem hiding this comment.
Looks good overall! Really only the one question and the test nits.
source/common/http/filter_manager.cc
Outdated
| filter_manager_callbacks_.encodeHeaders(*filter_manager_callbacks_.responseHeaders(), | ||
| end_stream); | ||
| maybeEndEncode(end_stream); | ||
| maybeEndEncode(end_stream && !state_.local_complete_); |
There was a problem hiding this comment.
but why do we need to sometimes end encode after encode headers / encode data, and sometimes outside of the EncodeFunctions? Can't we just remove the one at the end, or remove these two calls?
| ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); | ||
|
|
||
| // Ensure that we stripped the length prefix and set the appropriate headers. | ||
| EXPECT_EQ("f", upstream_request_->body().toString()); |
There was a problem hiding this comment.
I think a bunch of these are tested in another test.
WDYT of just using sendRequestAndWaitForReponse() and cutting out all the duplicate stuff?
|
|
||
| // Wait for the upstream request and begin sending a response with end_stream = false. | ||
| waitForNextUpstreamRequest(); | ||
| upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, true); |
There was a problem hiding this comment.
again I think you can just sendRequestAndWaitForResponse with default request and response headers, since all you want to do is make sure the status code is set correctly.
Signed-off-by: Snow Pettersen <snowp@lyft.com>
alyssawilk
left a comment
There was a problem hiding this comment.
LGTM modulo ci investigation
|
/retests |
|
/retest |
|
Retrying Azure Pipelines, to retry CircleCI checks, use |
Fixes two issues that surfaced when adding an integration test for the
gRPC reverse bridge:
is set to true which results in endStream being called twice. We fix
this by not calling endStream during the header/body callback when
local_complete_ is true
is_grpc_request, which meant that for the reverse bridge is was using
the modified headers which no longer have a gRPC content type.
Signed-off-by: Snow Pettersen snowp@lyft.com
Risk Level: Medium, HCM changes
Testing: New integration test
Docs Changes: n/a
Release Notes: n/a