diff --git a/api/envoy/extensions/filters/http/response_map/v3/response_map.proto b/api/envoy/extensions/filters/http/response_map/v3/response_map.proto index b60eb5e85d843..4f24250f363b2 100644 --- a/api/envoy/extensions/filters/http/response_map/v3/response_map.proto +++ b/api/envoy/extensions/filters/http/response_map/v3/response_map.proto @@ -81,3 +81,14 @@ message ResponseMap { // config.core.v3.SubstitutionFormatString body_format = 2; } + +// 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}]; + } +} diff --git a/api/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto b/api/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto index 827bf0481be08..4e005f6904ccc 100644 --- a/api/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto +++ b/api/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto @@ -87,3 +87,17 @@ message ResponseMap { // config.core.v4alpha.SubstitutionFormatString body_format = 2; } + +// Extra settings on a per virtualhost/route/weighted-cluster level. +message ResponseMapPerRoute { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.response_map.v3.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}]; + } +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/response_map/v3/response_map.proto b/generated_api_shadow/envoy/extensions/filters/http/response_map/v3/response_map.proto index b60eb5e85d843..4f24250f363b2 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/response_map/v3/response_map.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/response_map/v3/response_map.proto @@ -81,3 +81,14 @@ message ResponseMap { // config.core.v3.SubstitutionFormatString body_format = 2; } + +// 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}]; + } +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto b/generated_api_shadow/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto index 827bf0481be08..4e005f6904ccc 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/response_map/v4alpha/response_map.proto @@ -87,3 +87,17 @@ message ResponseMap { // config.core.v4alpha.SubstitutionFormatString body_format = 2; } + +// Extra settings on a per virtualhost/route/weighted-cluster level. +message ResponseMapPerRoute { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.response_map.v3.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}]; + } +} diff --git a/source/extensions/filters/http/response_map/BUILD b/source/extensions/filters/http/response_map/BUILD index e6d4e8769276b..040c4c1e54060 100644 --- a/source/extensions/filters/http/response_map/BUILD +++ b/source/extensions/filters/http/response_map/BUILD @@ -20,6 +20,7 @@ envoy_cc_library( "//include/envoy/http:codes_interface", "//include/envoy/http:filter_interface", "//source/common/response_map:response_map_lib", + "//source/extensions/filters/http:well_known_names", "//source/common/buffer:buffer_lib", "//source/common/common:assert_lib", "//source/common/common:enum_to_int", diff --git a/source/extensions/filters/http/response_map/config.cc b/source/extensions/filters/http/response_map/config.cc index 9b3cbdabf8996..3133412ea026d 100644 --- a/source/extensions/filters/http/response_map/config.cc +++ b/source/extensions/filters/http/response_map/config.cc @@ -11,7 +11,8 @@ namespace ResponseMapFilter { Http::FilterFactoryCb ResponseMapFilterFactory::createFilterFactoryFromProtoTyped( const envoy::extensions::filters::http::response_map::v3::ResponseMap& proto_config, - const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { + const std::string& stats_prefix, + Server::Configuration::FactoryContext& context) { ResponseMapFilterConfigSharedPtr config = std::make_shared(proto_config, stats_prefix, context); return [config](Http::FilterChainFactoryCallbacks& callbacks) -> void { @@ -19,6 +20,14 @@ Http::FilterFactoryCb ResponseMapFilterFactory::createFilterFactoryFromProtoType }; } +Router::RouteSpecificFilterConfigConstSharedPtr +ResponseMapFilterFactory::createRouteSpecificFilterConfigTyped( + const envoy::extensions::filters::http::response_map::v3::ResponseMapPerRoute& proto_config, + Server::Configuration::ServerFactoryContext&, + ProtobufMessage::ValidationVisitor&) { + return std::make_shared(proto_config); +} + /** * Static registration for the response_map filter. @see RegisterFactory. */ diff --git a/source/extensions/filters/http/response_map/config.h b/source/extensions/filters/http/response_map/config.h index 5f92b8934b3f8..7c38beba643c7 100644 --- a/source/extensions/filters/http/response_map/config.h +++ b/source/extensions/filters/http/response_map/config.h @@ -2,6 +2,7 @@ #include "envoy/extensions/filters/http/response_map/v3/response_map.pb.h" #include "envoy/extensions/filters/http/response_map/v3/response_map.pb.validate.h" +#include "envoy/server/filter_config.h" #include "extensions/filters/http/common/factory_base.h" #include "extensions/filters/http/well_known_names.h" @@ -16,13 +17,20 @@ namespace ResponseMapFilter { */ class ResponseMapFilterFactory : public Common::FactoryBase< - envoy::extensions::filters::http::response_map::v3::ResponseMap> { + envoy::extensions::filters::http::response_map::v3::ResponseMap, + envoy::extensions::filters::http::response_map::v3::ResponseMapPerRoute> { public: ResponseMapFilterFactory() : FactoryBase(HttpFilterNames::get().ResponseMap) {} Http::FilterFactoryCb createFilterFactoryFromProtoTyped( const envoy::extensions::filters::http::response_map::v3::ResponseMap& proto_config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; + +private: + Router::RouteSpecificFilterConfigConstSharedPtr createRouteSpecificFilterConfigTyped( + const envoy::extensions::filters::http::response_map::v3::ResponseMapPerRoute& proto_config, + Server::Configuration::ServerFactoryContext& context, + ProtobufMessage::ValidationVisitor& validator) override; }; } // namespace ResponseMapFilter diff --git a/source/extensions/filters/http/response_map/response_map_filter.cc b/source/extensions/filters/http/response_map/response_map_filter.cc index 3c3fa33fd1eed..a0de46e132a80 100644 --- a/source/extensions/filters/http/response_map/response_map_filter.cc +++ b/source/extensions/filters/http/response_map/response_map_filter.cc @@ -3,11 +3,14 @@ #include "envoy/http/codes.h" #include "envoy/http/header_map.h" +#include "extensions/filters/http/well_known_names.h" + #include "common/common/empty_string.h" #include "common/common/enum_to_int.h" #include "common/common/logger.h" #include "common/http/header_map_impl.h" #include "common/http/headers.h" +#include "common/http/utility.h" namespace Envoy { namespace Extensions { @@ -26,21 +29,44 @@ ResponseMapFilter::ResponseMapFilter(ResponseMapFilterConfigSharedPtr config) Http::FilterHeadersStatus ResponseMapFilter::decodeHeaders(Http::RequestHeaderMap& request_headers, bool end_stream) { ENVOY_LOG(trace, "response map filter: decodeHeaders with end_stream = {}", end_stream); + + // Disable filter per route config if applies + if (decoder_callbacks_->route() != nullptr) { + const auto* per_route_config = + Http::Utility::resolveMostSpecificPerFilterConfig( + Extensions::HttpFilters::HttpFilterNames::get().ResponseMap, + decoder_callbacks_->route()); + ENVOY_LOG(trace, "response map filter: found route. has per_route_config? {}", + per_route_config != nullptr); + if (per_route_config != nullptr && per_route_config->disabled()) { + ENVOY_LOG(trace, "response map filter: disabling due to per_route_config"); + disabled_ = true; + return Http::FilterHeadersStatus::Continue; + } + } + request_headers_ = &request_headers; return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus ResponseMapFilter::encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) { - ENVOY_LOG(trace, "response map filter: encodeHeaders with http status = {} and end_stream = {}", - headers.getStatusValue(), end_stream); + ENVOY_LOG(trace, + "response map filter: encodeHeaders with http status = {}, end_stream = {}, disabled = {}", + headers.getStatusValue(), end_stream, disabled_); + + // If this filter is disabled, continue without doing anything. + if (disabled_) { + return Http::FilterHeadersStatus::Continue; + } // Save a reference to the response headers. If we end up rewriting the response, // we'll need to set the content-length (and possibly other) headers later. response_headers_ = &headers; // Check whether we should rewrite this response based on response headers. - do_rewrite_ = config_->response_map()->match(request_headers_, headers, encoder_callbacks_->streamInfo()); + do_rewrite_ = config_->response_map()->match( + request_headers_, headers, encoder_callbacks_->streamInfo()); ENVOY_LOG(trace, "response map filter: do_rewrite_ {}", do_rewrite_); // If we decided not to rewrite the response, simply pass through to other @@ -61,9 +87,16 @@ Http::FilterHeadersStatus ResponseMapFilter::encodeHeaders(Http::ResponseHeaderM return Http::FilterHeadersStatus::Continue; } -Http::FilterDataStatus ResponseMapFilter::encodeData(Buffer::Instance& data, bool end_stream) { - ENVOY_LOG(trace, "response map filter: encodeData with data length {} and end_stream = {}", - data.length(), end_stream); +Http::FilterDataStatus ResponseMapFilter::encodeData(Buffer::Instance& data, + bool end_stream) { + ENVOY_LOG(trace, + "response map filter: encodeData with data length {}, end_stream = {}, disabled = {}", + data.length(), end_stream, disabled_); + + // If this filter is disabled, continue without doing anything. + if (disabled_) { + return Http::FilterDataStatus::Continue; + } // If we decided not to rewrite the response, simply pass through to other // filters. @@ -94,6 +127,11 @@ void ResponseMapFilter::doRewrite(void) { ENVOY_LOG(trace, "response map filter: doRewrite with {} encoding_buffer", encoding_buffer != nullptr ? "non-null" : "null"); + // If this route is disabled, we should never be doing a rewrite. + // In fact, we never should have even checked if we should do + // a rewrite. + ASSERT(!disabled_); + // We should either see no encoding buffer or an empty encoding buffer. // // We'll see no encoding buffer if the upstream response was never observed diff --git a/source/extensions/filters/http/response_map/response_map_filter.h b/source/extensions/filters/http/response_map/response_map_filter.h index 04a46d57ba6ff..2b05dda88d3a0 100644 --- a/source/extensions/filters/http/response_map/response_map_filter.h +++ b/source/extensions/filters/http/response_map/response_map_filter.h @@ -25,6 +25,18 @@ class ResponseMapFilterConfig { }; using ResponseMapFilterConfigSharedPtr = std::shared_ptr; +class FilterConfigPerRoute : public Router::RouteSpecificFilterConfig { +public: + FilterConfigPerRoute( + const envoy::extensions::filters::http::response_map::v3::ResponseMapPerRoute& + config) + : disabled_(config.disabled()) {} + bool disabled() const { return disabled_; } + +private: + bool disabled_; +}; + class ResponseMapFilter : public Http::StreamFilter, Logger::Loggable { public: ResponseMapFilter(ResponseMapFilterConfigSharedPtr config); @@ -71,6 +83,7 @@ class ResponseMapFilter : public Http::StreamFilter, Logger::Loggable