Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/filters/http/grpc_json_transcoder @qiwzhang @lizan
/*/extensions/filters/http/router @alyssawilk @mattklein123 @snowp
/*/extensions/filters/http/ext_authz @gsagula @dio
/*/extensions/filters/http/response_map @esmet @alyssawilk
/*/extensions/filters/http/grpc_web @fengli79 @lizan
/*/extensions/filters/http/grpc_stats @kyessenov @lizan
/*/extensions/filters/http/squash @yuval-k @alyssawilk
Expand Down
2 changes: 2 additions & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ proto_library(
"//envoy/config/rbac/v3:pkg",
"//envoy/config/resource_monitor/fixed_heap/v2alpha:pkg",
"//envoy/config/resource_monitor/injected_resource/v2alpha:pkg",
"//envoy/config/response_map/v3:pkg",
"//envoy/config/retry/omit_canary_hosts/v2:pkg",
"//envoy/config/retry/previous_hosts/v2:pkg",
"//envoy/config/route/v3:pkg",
Expand Down Expand Up @@ -197,6 +198,7 @@ proto_library(
"//envoy/extensions/filters/http/original_src/v3:pkg",
"//envoy/extensions/filters/http/ratelimit/v3:pkg",
"//envoy/extensions/filters/http/rbac/v3:pkg",
"//envoy/extensions/filters/http/response_map/v3:pkg",
"//envoy/extensions/filters/http/router/v3:pkg",
"//envoy/extensions/filters/http/squash/v3:pkg",
"//envoy/extensions/filters/http/tap/v3:pkg",
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/config/core/v3/substitution_format_string.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ syntax = "proto3";

package envoy.config.core.v3;

import "envoy/config/core/v3/base.proto";

import "google/protobuf/struct.proto";

import "udpa/annotations/status.proto";
Expand All @@ -16,6 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;

// Configuration to use multiple :ref:`command operators <config_access_log_command_operators>`
// to generate a new string in either plain text or JSON format.
// [#next-free-field: 7]
message SubstitutionFormatString {
oneof format {
option (validate.required) = true;
Expand Down Expand Up @@ -61,6 +64,12 @@ message SubstitutionFormatString {
// }
//
google.protobuf.Struct json_format = 2 [(validate.rules).message = {required: true}];

// A DataSource variant for text_format.
DataSource text_format_source = 5;

// The empty format string.
bool empty_format = 6 [(validate.rules).bool = {const: true}];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where this is used, shall we just relax the min_len: 1 constraint on text_format if we want an empty format string?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use it here https://github.com/envoyproxy/envoy/pull/14221/files#diff-7a3f06d418ccdf0de0f51a473b6d3450baa5faf9ac8362aa6fe33bc1284b5152R27

We could just relax the constraint, but my initial idea was to avoid making changes to an existing field and figured the explicit "empty" option was clear enough.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would slightly prefer relax the existing one, or we can have a field fixed_text which just do what PlainStringFormatterImpl does.

}

// If set to true, when command operators are evaluated to null,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions api/envoy/config/response_map/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/accesslog/v3:pkg",
"//envoy/config/core/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
89 changes: 89 additions & 0 deletions api/envoy/config/response_map/v3/response_map.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
syntax = "proto3";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not where extension config should live, move to api/envoy/extensions/filters/http/response_map/v3

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention here is for this to be non-extension config (for source/common/response_map/*) and to have separate extension config for the response map filter (source/extensions/http/filters/response_map/...).

Is this idea correct or misguided?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked harder at how the API is organized and I think I understand. My feeling now is that this should live under api/envoy/extensions/common/reponse_map since I intend for it to be shared, eventually, by both the HCM and ResponseMap filter. Does that make sense?


package envoy.config.response_map.v3;

import "envoy/config/accesslog/v3/accesslog.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/substitution_format_string.proto";

import "google/protobuf/struct.proto";
import "google/protobuf/wrappers.proto";

import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.config.response_map.v3";
option java_outer_classname = "ResponseMapProto";
option java_multiple_files = true;
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: ResponseMap]
// Response map filter :ref:`configuration overview <config_response_map>`.

// The configuration to filter and change HTTP responses.
// [#next-free-field: 6]
message ResponseMapper {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't different from ResponseMapper in HCM, why not just use it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to have a common config that could be shared across HCM and any other use case (like the response map http filter). I think we should deprecate the response mapper on the HCM to use this proto instead. I didn't do that here because I wasn't sure of the right/approved migration path. What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, can you give us comment access to the doc?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Filter to determine if this mapper should apply.
accesslog.v3.AccessLogFilter filter = 1 [(validate.rules).message = {required: true}];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lizan can I ask you to do an API pass before I dig into the code?
I think a lot of the components of access logs apply but not all do - not sure how much API refactor we want to do here and how much we can just do via standard metadata matcher.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snowp can you weigh in here re: the new matcher API fits here? I think the access log filter works well for this purpose but just want to double check.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that this could be accomplished with the new matching API, though we would most likely spend some time to reach parity in terms of supporting all the same conditionals that the access log filter supports. The matching API would offer some benefit in terms of better performance when lots of matchers are used, due to being able to structure it more as a tree than as a list of complex matchers.

I think this falls into the category that we'd use the matching API if it was available today but since that work is ongoing it becomes a question of time lines. Maybe @mattklein123 has thoughts on whether we're okay with relying on the access log filters here.

I could imagine the access log filter eventually being subsumed by the new matching work, but that would be a longer term thing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd initially hoped the new matcher code could be used, since using access filters just strikes me as suboptimal and I'd like the perf wins. Looks like it'd also do away with much of the shared code, so probably the way to go (if we opt to have a standalone filter for per-route) is stick with this for now, and move both the HCM and filter matchers over to the new API when it's more feature complete.


// The new response status code if specified.
google.protobuf.UInt32Value status_code = 2 [(validate.rules).uint32 = {lt: 600 gte: 200}];

// The new local reply text if specified. It will be used in the `%LOCAL_REPLY_BODY%`
// command operator in the `body_format`.
core.v3.DataSource body = 3;

// A per mapper `body_format` to use when this mapper is matched.
core.v3.SubstitutionFormatString body_format_override = 4;

// HTTP headers to add to a matched response. This allows the response mapper to append, to add
// or to override headers of any matched response before it is sent to a downstream client.
repeated core.v3.HeaderValueOption headers_to_add = 5
[(validate.rules).repeated = {max_items: 1000}];
}

// The configuration to customize HTTP responses read by Envoy.
message ResponseMap {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is same as LocalReplyConfig in HCM as well?

Copy link
Copy Markdown
Contributor Author

@esmet esmet Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. From the comment above, I intended for there to be a common proto shared across use cases and that we should (eventually) migrate HCM to use this common type. I would appreciate thoughts/guidance on a migration strategy.

// Configuration of list of mappers which allows to filter and change HTTP response.
// The mappers will be checked by the specified order until one is matched.
repeated ResponseMapper mappers = 1;

// The configuration to form response body from the :ref:`command operators <config_access_log_command_operators>`
// and to specify response content type as one of: plain/text or application/json.
//
// Example one: plain/text body_format.
//
// .. code-block::
//
// text_format: %LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=$REQ(:path)%
//
// The following response body in `plain/text` format will be generated for a request with
// local reply body of "upstream connection error", response_code=503 and path=/foo.
//
// .. code-block::
//
// upstream connection error:503:path=/foo
//
// Example two: application/json body_format.
//
// .. code-block::
//
// json_format:
// status: %RESPONSE_CODE%
// message: %LOCAL_REPLY_BODY%
// path: $REQ(:path)%
//
// The following response body in "application/json" format would be generated for a request with
// local reply body of "upstream connection error", response_code=503 and path=/foo.
//
// .. code-block:: json
//
// {
// "status": 503,
// "message": "upstream connection error",
// "path": "/foo"
// }
//
core.v3.SubstitutionFormatString body_format = 2;
}
14 changes: 14 additions & 0 deletions api/envoy/config/response_map/v4alpha/BUILD

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions api/envoy/config/response_map/v4alpha/response_map.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions api/envoy/extensions/filters/http/response_map/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/response_map/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
syntax = "proto3";

package envoy.extensions.filters.http.response_map.v3;

import "envoy/config/response_map/v3/response_map.proto";

import "google/protobuf/struct.proto";
import "google/protobuf/wrappers.proto";

import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.http.response_map.v3";
option java_outer_classname = "ResponseMapProto";
option java_multiple_files = true;
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: ResponseMap]
// Response map filter :ref:`configuration overview <config_http_filters_response_map>`.
// [#extension: envoy.filters.http.response_map]

// The filter configuration for response mapping.
message ResponseMap {
// The response map to use to customize HTTP responses read by Envoy.
config.response_map.v3.ResponseMap response_map = 1 [(validate.rules).message = {required: true}];
}

// Extra settings on a per virtualhost/route/weighted-cluster level.
message ResponseMapPerRoute {
oneof override {
option (validate.required) = true;

// Disable the response map filter for this particular vhost or route.
// If disabled is specified in multiple per-filter-configs, the most specific one will be used.
bool disabled = 1 [(validate.rules).bool = {const: true}];

// Override the global configuration of the response map filter with this new config.
config.response_map.v3.ResponseMap response_map = 2
[(validate.rules).message = {required: true}];
}
}
13 changes: 13 additions & 0 deletions api/envoy/extensions/filters/http/response_map/v4alpha/BUILD

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading