-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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/filter: Add a callback for sendLocalReply() and encode gRPC message for local responses when the request is gRPC #3299
http/filter: Add a callback for sendLocalReply() and encode gRPC message for local responses when the request is gRPC #3299
Conversation
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.
Thanks for the contribution, I think this makes a lot of sense.
source/common/router/router.cc
Outdated
{Http::Headers::get().Status, | ||
std::to_string(enumToInt(route_entry_->clusterNotFoundResponseCode()))}}}; | ||
callbacks_->encodeHeaders(std::move(response_headers), true); | ||
sendLocalReply(route_entry_->clusterNotFoundResponseCode(), ""); |
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.
Could we make this a meaningful message?
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.
Tried it, made some tests fail (likely due to body in the response being somehow unexpected), so did not look at it further.
source/common/grpc/common.cc
Outdated
{Http::Headers::get().ContentType, Http::Headers::get().ContentTypeValues.Grpc}, | ||
{Http::Headers::get().GrpcStatus, std::to_string(enumToInt(grpc_status))}}}; | ||
if (!message.empty()) { | ||
response_headers->insertGrpcMessage().value(message); |
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.
We could just punt on this for now and leave it as a TODO if not needed. FWIW, I don't recall percent encoding being mandatory, but maybe this has changed, since the wire format at https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md specifies it, with some confusing language (e.g. UTF-8 followed by percent encoding?!). @lizan for thoughts here.
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.
Yeah I would just TODO it. I don't think it's explicitly required at the h2 level AFAIK. Maybe it is for gRPC.
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.
Yeah I think a TODO here is fine (open an issue to track it) for now. We don't have local messages requires % encoding, also it should just work since h2 doesn't require 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.
Added TODO, which is now in http/utility.cc
test/integration/http_integration.cc
Outdated
@@ -626,6 +626,21 @@ void HttpIntegrationTest::testRetry() { | |||
EXPECT_EQ(512U, response_->body().size()); | |||
} | |||
|
|||
// Change the default route to be restrictive, and send a request to an alternate route. | |||
void HttpIntegrationTest::testGrpcRouterNotFound() { |
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.
Could also consider adding a unit test in router_test
. I don't think it would bring any increased functional or code coverage, but it's the unit test counterpart, would complement existing gRPC tests there and should be simple. Up to you IMHO.
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.
The functionality is now in http/utility.cc
, added a new unit test to test/common/http/utility_test.cc
.
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.
Thanks, this is a longstanding point of confusion for people. One high level comment is IMO we should do this everywhere we send local replies. E.g., https://github.com/envoyproxy/envoy/blob/master/source/extensions/filters/http/buffer/buffer_filter.cc#L116 (there are other examples).
In a perfect world we would push this functionality directly into the send local reply utility which is already used everywhere, but, that has the downside that every filter now has to check if its dealing with gRPC.
In a perfect, perfect world, I think we would make sendLocalReply() a filter API, such that the connection manager would handle this logic as something built-in.
At minimum, I think we should TODO this, and not call the underlying issue fixed yet, but if you want to tackle the better solution feel free. :)
source/common/grpc/common.cc
Outdated
{Http::Headers::get().ContentType, Http::Headers::get().ContentTypeValues.Grpc}, | ||
{Http::Headers::get().GrpcStatus, std::to_string(enumToInt(grpc_status))}}}; | ||
if (!message.empty()) { | ||
response_headers->insertGrpcMessage().value(message); |
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.
Yeah I would just TODO it. I don't think it's explicitly required at the h2 level AFAIK. Maybe it is for gRPC.
Thanks for tackling this - it'll be great to have our response paths be consistent about the application layer :-) I had the same suggestion as Matt here but he beat me to it - we're going to eventually want this to apply to all local replies and I agree that it doing this one step at a time makes sense. One thought - it would be nice if the first step moved us towards a reusable function rather than adding custom code to the router filter. What do we think of moving the wrapper code for "gRPC or normal sendLocalReply" to somewhere generally accessible (http connection manager is fine by me) even if we don't move any other callers of sendLocalReply over to it in this PR? |
WDYT about making this to a grpc-status-converter filter that can applied at the first of the HTTP filter chain? The filter can catch all non-200 status response from all local replies as well as from backend. It basically does 3. in #1934 (comment) The gRPC spec does say clients should expect non-200 status and accept it. When they doesn't, the filter can be used to convert all non-200 status to a gRPC error that doesn't confuse clients. |
+1 to @lizan suggestion, it would simplify Envoy internals here. In general, I wonder how much we can remove from the router and migrate to a filter. |
I like the idea of moving certain functionality to a filter, but it will be a little hard because the connection manager itself sends local replies, so in practice I'm not sure how well it will work in this case... |
Agree that this should be done for all local replies. I first tried to do this at http/utility, but that failed due to circular bazel dependency with grpc/common. One solution to this would be to move the needed status mapping functionality to http/utility. Then, if we passed request headers to If implemented as a filter, then (at least we) would need to configure all http filter chains with the filter as we don't know if/when/what clients have problems with non gRPC responses. |
Yeah, for this particular case I think I'm for inclined to add this to the filter API directly vs. do it as a filter, since IMO gRPC is a core concept that Envoy should just support out of the box. |
OK, so the only actionable feedback is to add TODO/open issue and my above comments? |
I'll take a stab to place the new code to http/utility and maybe use the new functionality from more/all the callers of sendLocalReply() as well. |
@mattklein123 Now looking into adding this to the filter API. Currently local responses sent by filters are passed to the the encodeHeaders() callback in decoder filter callbacks, either directly, or via one of the utility functions. This is implemented so that encodeHeaders() encoder filter member of every encoder filter is called. I would have expected only the encoders downstream of the current decoder filter to see the local response, but apparently all of them, from first to last get the encodeHeaders() call. This may be confusing if later filters that have not seen the request somehow transform the response. My question is: should we keep this behavior for local responses sent via the new API? If the local response is sent by a encoder/decoder filter it should be possible to locate that filter from the encoder chain and start the encoding iteration from there, or from the previous encoder, rather than from the beginning of the encoder chain. This may get complicated if the local response is sent by a decoder that is not a encoder, as it will not be as easy to figure out which encoders should see the local response. At the minimum all the decoders that are also encoders that have seen the request should also see the local response. Thoughts? |
@jrajahalme yeah agreed this is a problem in the current filter iteration system. Great callout! I agree that it would be possible for filters to get confused and only filters downstream should see the response. But I also agree with your assessment that the code really needs to only do this for dual encoder/decoder filters. IMO it's likely fine to skip this for now with some clear comments/TODO somewhere (and even better maybe some type of ASSERT if we are about to do something we might later regret). @alyssawilk thoughts here? |
Yeah, I think logically we should make that change, and definitely have docs explaining it since I suspect folks may get confused either way :-) |
Some gRPC clients get confused when they get non-gRPC responses to their gRPC requests. While such clients should be fixed, we can play nice and format the local responses returned by Envoy router filter as gRPC responses when the request is gRPC. Fixes: envoyproxy#1934 Signed-off-by: Jarno Rajahalme <[email protected]>
Refactor by moving gRPC utility functions that only depend on http to http/utility. This allows making Http::Utility::sendLocalReply() gRPC aware without changing all the callers to be aware of gRPC. Signed-off-by: Jarno Rajahalme <[email protected]>
Add either an 'is_grpc' or 'request_headers' parameter to sendLocalResponse() in order to allow the implementation to issue a gRPC response to gRPC requests. Signed-off-by: Jarno Rajahalme <[email protected]>
Add a StreamDecoderFilter callback sendLocalReply() and convert current users of Utility::sendLocalReply() to use the callback instead. The new callback is implemented using Utility::sendLocalReply(), but making use of locally available state allows simplifying the API, enabling support for gRPC responses without requiring filters to become gRPC aware. Note that the local responses are still implemented via the encodeHeaders() and encodeData() callbacks that call the corresponding member functions of all encoders in the filter chain, not just the ones downstream of the filter issuing the sendLocalReply(). This has the notable effect of the filter issuing the local reply itself receiving a encodeHeaders() call for the local reply it issued. Signed-off-by: Jarno Rajahalme <[email protected]>
a8bbb2e
to
2c264bf
Compare
Sorry for the manual rebase! First commit is same (but rebased) as before, other commits address comments and implement the new callback we discussed above. |
@htuch Addressed your review comments, thanks! |
@mattklein123 IMO it would be best to add a new GH issue for the local responses being traversed through all encoders? |
Include for http/utility.h went missing in a master rebase, add it back. Signed-off-by: Jarno Rajahalme <[email protected]>
@jrajahalme can you check CI errors and then I will review? |
Sure, will take care of the CI. It passed locally, so did not expect it to fail... |
lc_trie_test fails on me locally as well, but the log is not very helpful:
|
@jrajahalme you are hitting #3298. Don't worry about it for now. |
Yes, just verified it fails the same way locally on master as well. |
…Impl. Move sendLocalReply base implementation to ActiveStream, so that it is usable also from ActiveStream. Use sendLocaslReply() instead of encoding headers directly so that we get gRPC responses in all cases when the request was gRPC. Signed-off-by: Jarno Rajahalme <[email protected]>
Signed-off-by: Jarno Rajahalme <[email protected]>
Just about to add a commit that changes all |
@jrajahalme I was about to review, but will hold off if you are still making major changes. Please ping me when I should review. |
@mattklein123 Now, just merged the new commits to the branch :-) |
@mattklein123 CI passed before the last two commits, and those passed all tests for me locally, so IMO it's good to review. |
Signed-off-by: Jarno Rajahalme <[email protected]>
Except for that I messed the format, just pushed a fix for that. |
source/common/http/utility.cc
Outdated
@@ -186,6 +186,88 @@ bool Utility::isWebSocketUpgradeRequest(const HeaderMap& headers) { | |||
Http::Headers::get().UpgradeValues.WebSocket.c_str()))); | |||
} | |||
|
|||
Grpc::Status::GrpcStatus Utility::httpToGrpcStatus(uint64_t http_response_status) { |
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'd also suggest leaving this function and the one below under source/common/grpc
.
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.
As it is, it is not possible due to circular Bazel dependencies, i.e., grpc/common
depends on http:utility
. Would have to make callers of Utility::sendLocalReply()
replicate the gRPC status conversion.
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.
+1 to @htuch, I think you should be able to move it to source/common/grpc:status, then it won't be circular dependencies.
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.
OK, adding source/common/grpc/status_lib.
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.
Thanks, this is great. I have a few comments and I would like @alyssawilk to also take a look on Monday.
include/envoy/http/filter.h
Outdated
@@ -190,6 +190,17 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks { | |||
*/ | |||
virtual void addDecodedData(Buffer::Instance& data, bool streaming_filter) PURE; | |||
|
|||
/** | |||
* Create a locally generated response using the provided lambdas. |
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.
Can you provide some docs/indication that this may be transparently converted to a gRPC response in certain cases? Some of the param text might also need to be altered so that it describes gRPC also.
source/common/http/utility.cc
Outdated
HeaderMapPtr response_headers{ | ||
new HeaderMapImpl{{Headers::get().Status, std::to_string(enumToInt(response_code))}}}; | ||
if (!body_text.empty()) { | ||
response_headers->insertContentLength().value(body_text.size()); | ||
response_headers->insertContentType().value(Headers::get().ContentTypeValues.Text); | ||
} | ||
|
||
// Somehow it's OK to encode headers even if 'is_reset' is true? |
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.
What does this comment mean? The issue here is calling encode_headers() may result in a reset. You should be able to ASSERT(!is_reset); before this call. Can you add that and change the comment to be a bit better?
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 did not understand why the following encode_data()
call is only done if !is_reset
, but this preceding encode_headers()
is not conditional on is_reset
at all. Also, if ASSERT before this is proper, then the conditional on !is_reset
before the following encode_data()
is not needed any more, and the whole purpose of the is_reset
parameter is for us to ASSERT on it?
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.
It's because the stream can get reset recursively as a result of the encodeHeaders() call. I realize this is probably not intuitive, but that's why this whole dance exists. Can you update comments and add relevant asserts? I think it will make it more clear.
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, took a while for me realize that is_reset
can change before the encode_data() call as it is a reference. I'll add a comment to clarify that as well.
@@ -113,8 +113,7 @@ void BufferFilter::onDestroy() { | |||
} | |||
|
|||
void BufferFilter::onRequestTimeout() { | |||
Http::Utility::sendLocalReply(*callbacks_, stream_destroyed_, Http::Code::RequestTimeout, |
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 think the only reason we are tracking stream_destroyed_ is because of this call. Can you remove that member variable? this comment likely applies to other filters also.
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.
OK!
source/common/router/router.cc
Outdated
@@ -198,6 +181,8 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::HeaderMap& headers, bool e | |||
|
|||
downstream_headers_ = &headers; | |||
|
|||
grpc_request_ = Grpc::Common::hasGrpcContentType(headers); |
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 wonder, should we have this be a callback API so the filter doesn't have to recompute/track this? We are making gRPC a fundamental part of request processing at this point which IMO makes sense.
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.
Can you add a TODO here about possibly adding a filter API for this?
@@ -653,7 +651,7 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(ActiveStreamDecoderFilte | |||
for (; entry != decoder_filters_.end(); entry++) { | |||
ASSERT(!(state_.filter_call_state_ & FilterCallState::DecodeHeaders)); | |||
state_.filter_call_state_ |= FilterCallState::DecodeHeaders; | |||
FilterHeadersStatus status = (*entry)->handle_->decodeHeaders( | |||
FilterHeadersStatus status = (*entry)->decodeHeaders( |
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 change is not intuitive. Can you add some comments. Is the idea that you want to recompute gRPC status before each filter? I think that makes sense, but worth some comments here and in the wrapper to forwards to parent.
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.
Right, in case a filter bridges to/from gRPC, for example.
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.
Will add comments.
@@ -257,6 +269,9 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, | |||
void decodeTrailers(ActiveStreamDecoderFilter* filter, HeaderMap& trailers); | |||
void maybeEndDecode(bool end_stream); | |||
void addEncodedData(ActiveStreamEncoderFilter& filter, Buffer::Instance& data, bool streaming); | |||
void sendLocalReply(ActiveStreamEncoderFilter* filter, bool is_grpc_request, Code code, |
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 think the first param here is always set to nullptr? What is the intention to eventually allow proper restart for filters downstream? For now, I would just remove the parameter and add TODOs around allowing proper restart if the filter is also an encoding filter?
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.
Right, but as it is still a TODO, I'll remove it and add the comments to the encodeHeaders()
and encodeData()
calls that take the nullptr
filter pointer as the first parameter in the implementation of sendLocalReply()
.
@@ -821,6 +819,24 @@ void ConnectionManagerImpl::ActiveStream::refreshCachedRoute() { | |||
cached_route_ = std::move(route); | |||
} | |||
|
|||
void ConnectionManagerImpl::ActiveStream::sendLocalReply( | |||
ActiveStreamEncoderFilter* filter, bool is_grpc_request, Code code, const std::string& body, |
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.
Oh, I really like where this is going!
My one question for APIs is id we think sendLocalReply should instead take a reference to the request headers so is_grpc_request can be calculated locally in just one place, and in case we eventually end up with other transformations which might be based on request headers. We can just push this as-is and iterate, but I'd be interested in @mattklein123's take now since I think this is likely to be used in many places here and in downstream filters so it'd be nice to get it right on first pass.
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.
@alyssawilk I think this is internal code and not the main API, right? (So I think the code does what you are asking?)
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.
Yeah, this is what happens when I do reviews before caffeine :-/
I think we could avoid latching is_grpc_request_ in the two places we do, but that's a smaller request.
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.
You mean having is_grpc_request_
in both ConnManagerImpl and AsyncStreamImpl? Right now I don't have a clear picture of the relation between the two, so I don't know if tracking in one of them can be eliminated or not.
Now that sendLocalReply() callback takes care of stream status tracking itself, such tracking is not needed in the filters any more. Suggested-by: Matt Klein <[email protected]> Signed-off-by: Jarno Rajahalme <[email protected]>
Clarify the comments regarding the referenced 'is_reset' parameter to sendLocalReply(). ASSERT that it is initially false. Suggested-by: Matt Klein <[email protected]> Signed-off-by: Jarno Rajahalme <[email protected]>
Remove the first parameter from sendLocalReply() as it currently is always nullptr. Add comments on the endoceHeaders() and decodeHeaders() calls to note about the filter that should start encoding. Signed-off-by: Jarno Rajahalme <[email protected]>
Add comments to sendLocalReply() about gRPC encoding of the sent replies. Signed-off-by: Jarno Rajahalme <[email protected]>
Keep gRPC status code conversion utilities in common/grpc, but put them into a separate bazel lib from common_lib to avoid circular bazel dependency between common/http/utility_lib and common/grpc/common_lib. Signed-off-by: Jarno Rajahalme <[email protected]>
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, just one minor comment.
test/mocks/http/mocks.h
Outdated
@@ -206,6 +221,8 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, | |||
MOCK_METHOD0(continueDecoding, void()); | |||
MOCK_METHOD2(addDecodedData, void(Buffer::Instance& data, bool streaming)); | |||
MOCK_METHOD0(decodingBuffer, const Buffer::Instance*()); | |||
MOCK_METHOD3(sendLocalReply_, void(Code code, const std::string& body, |
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.
How is this used? Looks like sendLocalReply
is overriden above, but doesn't call into the mock method.
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.
My intention was to call the mock method from the real implementation (which is needed to keep existing tests expecting encodeHeaders()
calls working), but forgot to do so. I'll remove the unused mock method for now.
Signed-off-by: Jarno Rajahalme <[email protected]>
{Headers::get().Status, std::to_string(enumToInt(Code::UpgradeRequired))}}; | ||
encodeHeaders(nullptr, headers, true); | ||
// Send "Upgrade Required" if HTTP/1.0 support is not explictly configured on. | ||
sendLocalReply(Grpc::Common::hasGrpcContentType(*request_headers_), Code::UpgradeRequired, "", |
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 don't think you need to check Content-Type in this case, it's HTTP/1.0 so just set to 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.
Changed.
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. Few small comments + @lizan comment. Awesome!
include/envoy/http/filter.h
Outdated
@@ -190,6 +190,21 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks { | |||
*/ | |||
virtual void addDecodedData(Buffer::Instance& data, bool streaming_filter) PURE; | |||
|
|||
/** | |||
* Create a locally generated response using the provided response_code and body_text paramewters. |
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.
typo "paramewters"
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.
Fixes, thanks.
include/envoy/http/filter.h
Outdated
* | ||
* @param response_code supplies the HTTP response code. | ||
* @param body_text supplies the optional body text which is sent using the text/plain content | ||
* type. |
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.
nit: "... or encoded in the grpc-message header."
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.
Added.
Utility::sendLocalReply( | ||
is_grpc_request_, | ||
[this, modify_headers](HeaderMapPtr&& headers, bool end_stream) -> void { | ||
if (headers != nullptr && modify_headers != nullptr) { |
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.
can headers ever be nullptr here?
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.
Correct, removed test for non-null.
std::function<void(HeaderMap& headers)> modify_headers) { | ||
Utility::sendLocalReply(is_grpc_request, | ||
[this, modify_headers](HeaderMapPtr&& headers, bool end_stream) -> void { | ||
if (headers != nullptr && modify_headers != nullptr) { |
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.
same comment can headers be nullptr? I think not?
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.
Right, it can't, removed the test for non-null.
source/common/router/router.cc
Outdated
@@ -198,6 +181,8 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::HeaderMap& headers, bool e | |||
|
|||
downstream_headers_ = &headers; | |||
|
|||
grpc_request_ = Grpc::Common::hasGrpcContentType(headers); |
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.
Can you add a TODO here about possibly adding a filter API for this?
test/mocks/http/mocks.h
Outdated
Utility::sendLocalReply( | ||
is_grpc_request_, | ||
[this, modify_headers](HeaderMapPtr&& headers, bool end_stream) -> void { | ||
if (headers != nullptr && modify_headers != nullptr) { |
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.
headers can't be nullptr?
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.
Ditto. Also added the TODO comment you asked for above (GitHub would not let me comment up there).
Signed-off-by: Jarno Rajahalme <[email protected]>
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.
Thank you!
Thanks for the timely reviews! |
Some gRPC clients get confused when they get non-gRPC responses to
their gRPC requests. While such clients should be fixed, we can play
nice and format the local responses returned by Envoy router filter as
gRPC responses when the request is gRPC.
Risk Level: Medium
Testing: A HTTP2 integration test is added to exercise the new behavior.
None of the existing tests were affected.
Docs Changes: version_history.rst updated.
Fixes: #1934
Signed-off-by: Jarno Rajahalme [email protected]
TODO:
grpc-message
header value should be %-encoded.