-
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
headers: Ability to rewrite headers received from upstream cluster #12938
Conversation
// prefix: "/itm/" | ||
// route: | ||
// location_header_rewrite: "/n/error" | ||
string location_header_rewrite = 36 |
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.
Do you think there will ever be a case to rewrite via regex, e.g. using RegexMatchAndSubstitute
, or does it seem that a literal location always works?
I wonder if we want a more general Rewriter
message that can be both string and RegexMatchAndSubstitute
@envoyproxy/api-shepherds
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 was thinking about it too. Regex based rewrite will be more useful. Let me see how I can do that.
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 have a wrapper message that contains an internal oneof with different rewrite types.
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.
location HTTP header rewrites are exactly what I need as well! However, could this be more generalized to support any HTTP header rewrite with regex?
@ccravens We can have an API which is like this:
|
Just went through @ccravens open issue.
This kind of API looks better. Will wait for suggestions. This takes care of add/remove as well as modify on response headers. |
We already have API fields to add/remove response headers, so I would discourage introducing a redundant mechanism unless there is a plan to deprecate the existing mechanisms (and have your new scheme apply hierarchically similar to the existing one). |
@htuch thanks for your response. I don't think we're requesting to deprecate functionality. I'm aware and using the add / remove functionality. I think we're requesting a feature that addresses a need/requirement that the add/remove functions don't provide. @shivanshu21 let me know if I'm mistaken in that assumption from your side, thanks |
@htuch @ccravens If we try to introduce a comprehensive
We would be adding new options to add/remove header too as shown above in Going by what @htuch said, I think it should suffice to have a The core idea is that this should be generic for all headers, not just location. |
/wait |
// In the below example there are two response header rewrites mentioned: one for location header | ||
// and one for x-random-token header. | ||
// | ||
// The location header's value will be matched with the given match pattern as a prefix to the header's |
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 probably want to use envoy.type.v3.StringMatcher
for this, rather than hardcode prefix match semantics.
// If no match string is given, the value is replaced entirely with the given substitution. The | ||
// response status header is also matched before making a substitution. If the response status | ||
// returned from the upstream cluster matches with one of the response statuses in the list, the | ||
// substitution can proceed after matching the match section. If this list is empty, 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 think the response status is really part of the matcher. Also, this only applies to the literal and not when we are doing regex substitute. I think you might want to move this up a level to a header_rewriter
message.
// header_rewrite_literal: | ||
// match: /itm/not-found/ | ||
// substitution: /n/error | ||
// 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.
Status is just one header you could potentially also match one. One thing you could consider doing here for matching is to look at what ResponseMapper
in HCM does, where it uses AccessLogFilter
as a general purpose matcher for responses prior to applying an action.
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 looked at response mapper in HCM. It has a repeated field headers_to_add
. They are trying to add headers, so the default assumption is that every header must be added.
For me the case is different. I am trying to match the headers.
repeated config.core.v3.HeaderValue upstream_headers_matcher = 3
[(validate.rules).repeated = {max_items: 1000}];
Suppose I want to match:
-
Response status should be either 301 OR 302 OR 307
-
Response status is 200 AND content-type is text/html
For case 1, I would want that if ANY header KV pair matches, match the condition and rewrite the requested header.
For case 2, I would want that if ALL header KV pairs match, only then match the condition, and rewrite the requested header.
For this reason, I have introduced a list of acceptable response codes ANY of which could match.
I have also added a list of headers ALL of which must match.
Header rewrite will happen when both the subconditions match.
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 question was more around whether you could use AccessLogFilter
, which is more general purpose than what you currently have.
/wait |
// part is substituted. In case of present match the whole value of the header is overwritten. | ||
// | ||
// The x-random-token header's value will be matched using the regex pattern specified in the pattern | ||
// section, and the matched part will be replaced by the value provided in the substitution section. |
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 comment individual fields rather than a large detached block comment? Please take a look at the generated docs to see how they render, e.g. https://380489-65214191-gh.circle-artifacts.com/0/generated/docs/api-v3/config/route/v3/route_components.proto.html#envoy-v3-api-msg-config-route-v3-routeaction-headerrewriter
// header_rewrite_literal: | ||
// match: /itm/not-found/ | ||
// substitution: /n/error | ||
// 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.
My question was more around whether you could use AccessLogFilter
, which is more general purpose than what you currently have.
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.
Looks good, mostly docs and would like to get agreement on the matcher form.
@htuch I like the idea of |
/wait |
HeaderMatcherFilter request_match_criteria = 3 [(validate.rules).message = {required: true}]; | ||
} | ||
|
||
message HeaderMatcherFilter { |
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. How strongly do @envoyproxy/api-shepherds feel about trying to clean this up further in this PR? I think the ideal would be to migrate both the matching going on in AccessLogfilter
and HeaderMatcher
to envoy.type.matcher.v3
.
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.
Sorry @htuch what are you proposing? cc @snowp @yangminzhu for other efforts in flight to merge matching behavior. I agree at a high level that it pains me to see yet another and/or, etc. in the API 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.
Thanks for working on this and taking the time to get this right.
reserved 12, 18, 19, 16, 22, 21, 10; | ||
|
||
reserved "request_mirror_policy"; | ||
|
||
map<string, HeaderRewriter> response_headers_to_rewrite = 36; |
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.
Please add docs for this field. Also, we haven't typically used a map for things like this. IMO it would be more future proof to have a list of messages, even if internally it is translated to a map? @envoyproxy/api-shepherds?
HeaderMatcherFilter request_match_criteria = 3 [(validate.rules).message = {required: true}]; | ||
} | ||
|
||
message HeaderMatcherFilter { |
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.
Sorry @htuch what are you proposing? cc @snowp @yangminzhu for other efforts in flight to merge matching behavior. I agree at a high level that it pains me to see yet another and/or, etc. in the API here.
* Add UpstreamHeaderRewriter and HeaderRewriteLiteral classes * Call rewriteUpstreamResponseHeaders() from finalizeResponseHeaders() * Only allow exact and prefix match on header value * Fix substitution string logic * Fix format * Adjusting messages and comments Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
…rbage Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
@shivanshu21 I'll give this a review now, but please try avoid force pushes, as they make it hard to track deltas during review. |
HeaderRewriteLiteral value_rewrite_literal = 1; | ||
} | ||
|
||
type.matcher.v3.MatchPredicate response_match_criteria = 2; |
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.
Comments for all fields, thanks (there are some others in this PR that are not commented.
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
// [#next-free-field: 37] | ||
message HeaderRewriteLiteral { | ||
// String match can use prefix match and present match. In case of prefix match, the matched prefix part of | ||
// the header's value is substituted. In case of present match the whole value of the header is overwritten. |
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 are the semantics for the other string match types? Does capture group semantics apply?
I wonder if we want a StringMatchAndSubstitute
that is analogous to RegexMatchAndSubstitute
.
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.
Capture group semantics do not apply but this can be a future enhancement in the message HeaderMatchAndRewrite
which has a oneof
type with an option for this (HeaderRewriteLiteral) message.
Added explanation for other StringMatcher types.
message UpstreamHeaderRewriter { | ||
string header_name = 1; | ||
|
||
HeaderMatchAndRewrite match_and_rewrite = 2; |
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.
Comments.
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
// In the example below, we rewrite the location header received from the upstream cluster | ||
// if the location header is present. | ||
// | ||
// If a header is specified in response_headers_to_rewrite section but is not returned from the |
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: in the *response_headers_to_write* field
.
// "exact_match": "POST" | ||
// } | ||
// } | ||
// [#next-major-version: This message deprecates config.route.v3.HeaderMatcher] |
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.
Please add an explicit comment on this as well for user-facing docs, since users will want to know which one to use.
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
// [#protodoc-title: Logical Matcher API] | ||
|
||
// Match configuration. This is a recursive structure which allows complex nested match | ||
// configurations to be built using various logical operators. |
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 here on user facing docs.
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
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.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.
config_impl_test passes in local
envoy/config/listener/v3/udp_gso_batch_writer_config.proto:5:1: warning: Import google/protobuf/duration.proto but not used.
envoy/config/listener/v3/udp_gso_batch_writer_config.proto:6:1: warning: Import google/protobuf/wrappers.proto but not used.
INFO: From Generating Descriptor Set proto_library @envoy_api//envoy/admin/v3:pkg:
envoy/admin/v3/init_dump.proto:6:1: warning: Import udpa/annotations/versioning.proto but not used.
Target //test/common/router:config_impl_test up-to-date:
bazel-bin/test/common/router/config_impl_test
INFO: Elapsed time: 118.371s, Critical Path: 113.56s
INFO: 14 processes: 14 processwrapper-sandbox.
INFO: Build completed successfully, 15 total actions
//test/common/router:config_impl_test PASSED in 11.4s
Executed 1 out of 1 test: 1 test passes.
INFO: Build completed successfully, 15 total actions
// [#next-free-field: 37] | ||
message HeaderRewriteLiteral { | ||
// String match can use prefix match and present match. In case of prefix match, the matched prefix part of | ||
// the header's value is substituted. In case of present match the whole value of the header is overwritten. |
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.
Capture group semantics do not apply but this can be a future enhancement in the message HeaderMatchAndRewrite
which has a oneof
type with an option for this (HeaderRewriteLiteral) message.
Added explanation for other StringMatcher types.
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.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.
In general this seems on a right direction, though it would be nice to wait a bit to land the new matcher API (at least in proto level), so we would have less tech debt to clean up.
@@ -39,6 +39,7 @@ message TapConfig { | |||
// Exactly one of :ref:`match <envoy_api_field_config.tap.v3.TapConfig.match>` and | |||
// :ref:`match_config <envoy_api_field_config.tap.v3.TapConfig.match_config>` must be set. If both | |||
// are set, the :ref:`match <envoy_api_field_config.tap.v3.TapConfig.match>` will be used. | |||
// [#next-major-version: common.matcher.v3.MatchPredicate should be refactored to use type.matcher.v3.MatchPredicate.] |
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 doc change for tap isn't really related? And we don't have type.matcher.v3.MatchPredicate
(same for elsewhere), and that's not part of the new matcher API either.
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.
type.matcher.v3.MatchPredicate
This is being introduced in this PR. We spoke to @snowp and the Unified matcher is going to take time.
I will handle the tech debt in the next, separate PR while HeaderMatcher references are moved to StringMatcher based type.Matcher.v3.HeaderMatcher
(which is also introduced in this PR).
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.
Sorry, I know that is what we said, but we are actively working on the new matching protos now. I think it's worth intersecting these efforts. Let's circle back next week and see where we are at.
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.
Okay
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Signed-off-by: Shivanshu Goswami <shigoswami@ebay.com>
Just curios why was the closed? Is it still planned to support response header rewriting eventually? |
…response
Signed-off-by: Shivanshu Goswami shigoswami@ebay.com
Fixes: #12591
Commit Message:
Additional Description:
Risk Level: low
Testing: Added unit tests, performed manual testing
Docs Changes: Added
Release Notes:
[Optional Runtime guard:]
[Optional Fixes #Issue]
[Optional Deprecated:]