Skip to content
Permalink
Browse files Browse the repository at this point in the history
move x-http-method-override to beginning of filter chain (#801)
Create a new filter to handle method override. Tested via integration test.
  • Loading branch information
nareddyt committed Mar 28, 2023
1 parent 75d0819 commit e956701
Show file tree
Hide file tree
Showing 25 changed files with 474 additions and 25 deletions.
18 changes: 18 additions & 0 deletions api/envoy/v11/http/header_sanitizer/BUILD
@@ -0,0 +1,18 @@
load("@envoy_api//bazel:api_build_system.bzl", "api_cc_py_proto_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")

package(default_visibility = ["//visibility:public"])

api_cc_py_proto_library(
name = "config_proto",
srcs = [
"config.proto",
],
visibility = ["//visibility:public"],
)

go_proto_library(
name = "config_go_proto",
importpath = "github.com/GoogleCloudPlatform/esp-v2/src/go/proto/api/envoy/v11/http/header_sanitizer",
proto = ":config_proto",
)
22 changes: 22 additions & 0 deletions api/envoy/v11/http/header_sanitizer/config.proto
@@ -0,0 +1,22 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package espv2.api.envoy.v11.http.header_sanitizer;

// header_sanitizer filter doesn't need any config.
// But a config protobuf type is required by FilterFactory.
// Hence defines an empty FilterConfig here.
message FilterConfig {}
4 changes: 4 additions & 0 deletions api/scripts/go_proto_gen.sh
Expand Up @@ -42,3 +42,7 @@ cp -f bazel-bin/api/envoy/v11/http/backend_auth/config_go_proto_/github.com/Goog
bazelisk build //api/envoy/v11/http/grpc_metadata_scrubber:config_go_proto
mkdir -p src/go/proto/api/envoy/v11/http/grpc_metadata_scrubber
cp -f bazel-bin/api/envoy/v11/http/grpc_metadata_scrubber/config_go_proto_/github.com/GoogleCloudPlatform/esp-v2/src/go/proto/api/envoy/v11/http/grpc_metadata_scrubber/* src/go/proto/api/envoy/v11/http/grpc_metadata_scrubber
# HTTP filter header_sanitizer
bazelisk build //api/envoy/v11/http/header_sanitizer:config_go_proto
mkdir -p src/go/proto/api/envoy/v11/http/header_sanitizer
cp -f bazel-bin/api/envoy/v11/http/header_sanitizer/config_go_proto_/github.com/GoogleCloudPlatform/esp-v2/src/go/proto/api/envoy/v11/http/header_sanitizer/* src/go/proto/api/envoy/v11/http/header_sanitizer
6 changes: 6 additions & 0 deletions examples/auth/envoy_config.json
Expand Up @@ -163,6 +163,12 @@
"headersWithUnderscoresAction": "REJECT_REQUEST"
},
"httpFilters": [
{
"name": "com.google.espv2.filters.http.header_sanitizer",
"typedConfig": {
"@type": "type.googleapis.com/espv2.api.envoy.v11.http.header_sanitizer.FilterConfig"
}
},
{
"name": "envoy.filters.http.jwt_authn",
"typedConfig": {
Expand Down
6 changes: 6 additions & 0 deletions examples/dynamic_routing/envoy_config.json
Expand Up @@ -218,6 +218,12 @@
"headersWithUnderscoresAction": "REJECT_REQUEST"
},
"httpFilters": [
{
"name": "com.google.espv2.filters.http.header_sanitizer",
"typedConfig": {
"@type": "type.googleapis.com/espv2.api.envoy.v11.http.header_sanitizer.FilterConfig"
}
},
{
"name": "com.google.espv2.filters.http.backend_auth",
"typedConfig": {
Expand Down
6 changes: 6 additions & 0 deletions examples/grpc_dynamic_routing/envoy_config.json
Expand Up @@ -231,6 +231,12 @@
"headersWithUnderscoresAction": "REJECT_REQUEST"
},
"httpFilters": [
{
"name": "com.google.espv2.filters.http.header_sanitizer",
"typedConfig": {
"@type": "type.googleapis.com/espv2.api.envoy.v11.http.header_sanitizer.FilterConfig"
}
},
{
"name": "envoy.filters.http.compressor",
"typedConfig": {
Expand Down
6 changes: 6 additions & 0 deletions examples/service_control/envoy_config.json
Expand Up @@ -124,6 +124,12 @@
"headersWithUnderscoresAction": "REJECT_REQUEST"
},
"httpFilters": [
{
"name": "com.google.espv2.filters.http.header_sanitizer",
"typedConfig": {
"@type": "type.googleapis.com/espv2.api.envoy.v11.http.header_sanitizer.FilterConfig"
}
},
{
"name": "com.google.espv2.filters.http.service_control",
"typedConfig": {
Expand Down
6 changes: 6 additions & 0 deletions examples/testdata/route_match/envoy_config.json
Expand Up @@ -202,6 +202,12 @@
"headersWithUnderscoresAction": "REJECT_REQUEST"
},
"httpFilters": [
{
"name": "com.google.espv2.filters.http.header_sanitizer",
"typedConfig": {
"@type": "type.googleapis.com/espv2.api.envoy.v11.http.header_sanitizer.FilterConfig"
}
},
{
"name": "com.google.espv2.filters.http.backend_auth",
"typedConfig": {
Expand Down
6 changes: 6 additions & 0 deletions examples/testdata/sidecar_backend/envoy_config.json
Expand Up @@ -124,6 +124,12 @@
"headersWithUnderscoresAction": "REJECT_REQUEST"
},
"httpFilters": [
{
"name": "com.google.espv2.filters.http.header_sanitizer",
"typedConfig": {
"@type": "type.googleapis.com/espv2.api.envoy.v11.http.header_sanitizer.FilterConfig"
}
},
{
"name": "com.google.espv2.filters.http.grpc_metadata_scrubber",
"typedConfig": {
Expand Down
6 changes: 6 additions & 0 deletions src/envoy/BUILD
Expand Up @@ -17,6 +17,11 @@ alias(
actual = "//src/envoy/http/grpc_metadata_scrubber:filter_factory",
)

alias(
name = "header_sanitizer",
actual = "//src/envoy/http/header_sanitizer:filter_factory",
)

alias(
name = "path_rewrite",
actual = "//src/envoy/http/path_rewrite:filter_factory",
Expand All @@ -38,6 +43,7 @@ envoy_cc_binary(
deps = [
":backend_auth",
":grpc_metadata_scrubber",
":header_sanitizer",
":main",
":path_rewrite",
":service_control",
Expand Down
39 changes: 39 additions & 0 deletions src/envoy/http/header_sanitizer/BUILD
@@ -0,0 +1,39 @@
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_cc_test",
)

package(
default_visibility = [
"//src/envoy:__subpackages__",
],
)

envoy_cc_library(
name = "filter_lib",
srcs = [
"filter.cc",
],
hdrs = [
"filter.h",
],
repository = "@envoy",
deps = [
"//src/envoy/utils:http_header_utils_lib",
"//src/envoy/utils:rc_detail_utils_lib",
"@envoy//envoy/stats:stats_interface",
"@envoy//source/extensions/filters/http/common:pass_through_filter_lib",
],
)

envoy_cc_library(
name = "filter_factory",
srcs = ["filter_factory.cc"],
repository = "@envoy",
deps = [
":filter_lib",
"//api/envoy/v11/http/header_sanitizer:config_proto_cc_proto",
"@envoy//source/exe:envoy_common_lib",
],
)
49 changes: 49 additions & 0 deletions src/envoy/http/header_sanitizer/filter.cc
@@ -0,0 +1,49 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "src/envoy/http/header_sanitizer/filter.h"

#include <string>

#include "envoy/http/header_map.h"
#include "source/common/http/headers.h"
#include "source/common/http/utility.h"
#include "src/envoy/utils/http_header_utils.h"
#include "src/envoy/utils/rc_detail_utils.h"

namespace espv2 {
namespace envoy {
namespace http_filters {
namespace header_sanitizer {

using Envoy::Http::FilterDataStatus;
using Envoy::Http::FilterHeadersStatus;
using Envoy::Http::FilterTrailersStatus;
using Envoy::Http::RequestHeaderMap;

FilterHeadersStatus Filter::decodeHeaders(RequestHeaderMap& headers, bool) {
if (utils::handleHttpMethodOverride(headers)) {
// Update later filters that the HTTP method has changed by clearing the
// route cache.
ENVOY_LOG(debug, "HTTP method override occurred, recalculating route");
decoder_callbacks_->downstreamCallbacks()->clearRouteCache();
}

return FilterHeadersStatus::Continue;
}

} // namespace header_sanitizer
} // namespace http_filters
} // namespace envoy
} // namespace espv2
40 changes: 40 additions & 0 deletions src/envoy/http/header_sanitizer/filter.h
@@ -0,0 +1,40 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include "envoy/http/filter.h"
#include "envoy/http/header_map.h"
#include "source/common/common/logger.h"
#include "source/extensions/filters/http/common/pass_through_filter.h"

namespace espv2 {
namespace envoy {
namespace http_filters {
namespace header_sanitizer {

class Filter : public Envoy::Http::PassThroughDecoderFilter,
public Envoy::Logger::Loggable<Envoy::Logger::Id::filter> {
public:
Filter() = default;

// Envoy::Http::StreamDecoderFilter
Envoy::Http::FilterHeadersStatus decodeHeaders(Envoy::Http::RequestHeaderMap&,
bool) override;
};

} // namespace header_sanitizer
} // namespace http_filters
} // namespace envoy
} // namespace espv2
59 changes: 59 additions & 0 deletions src/envoy/http/header_sanitizer/filter_factory.cc
@@ -0,0 +1,59 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "api/envoy/v11/http/header_sanitizer/config.pb.h"
#include "api/envoy/v11/http/header_sanitizer/config.pb.validate.h"
#include "envoy/registry/registry.h"
#include "source/extensions/filters/http/common/factory_base.h"
#include "src/envoy/http/header_sanitizer/filter.h"

namespace espv2 {
namespace envoy {
namespace http_filters {
namespace header_sanitizer {

constexpr const char kFilterName[] =
"com.google.espv2.filters.http.header_sanitizer";

/**
* Config registration for ESPv2 header sanitizer filter.
*/
class FilterFactory
: public Envoy::Extensions::HttpFilters::Common::FactoryBase<
::espv2::api::envoy::v11::http::header_sanitizer::FilterConfig> {
public:
FilterFactory() : FactoryBase(kFilterName) {}

private:
Envoy::Http::FilterFactoryCb createFilterFactoryFromProtoTyped(
const ::espv2::api::envoy::v11::http::header_sanitizer::FilterConfig&,
const std::string&,
Envoy::Server::Configuration::FactoryContext&) override {
return [](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void {
auto filter = std::make_shared<Filter>();
callbacks.addStreamDecoderFilter(filter);
};
}
};
/**
* Static registration for the filter. @see RegisterFactory.
*/
static Envoy::Registry::RegisterFactory<
FilterFactory, Envoy::Server::Configuration::NamedHttpFilterConfigFactory>
register_;

} // namespace header_sanitizer
} // namespace http_filters
} // namespace envoy
} // namespace espv2
7 changes: 0 additions & 7 deletions src/envoy/http/service_control/filter.cc
Expand Up @@ -53,13 +53,6 @@ Envoy::Http::FilterHeadersStatus ServiceControlFilter::decodeHeaders(
return Envoy::Http::FilterHeadersStatus::StopIteration;
}

if (utils::handleHttpMethodOverride(headers)) {
// Update later filters that the HTTP method has changed by clearing the
// route cache.
ENVOY_LOG(debug, "HTTP method override occurred, recalculating route");
decoder_callbacks_->downstreamCallbacks()->clearRouteCache();
}

// Make sure route is calculated
auto route = decoder_callbacks_->route();

Expand Down
1 change: 1 addition & 0 deletions src/go/configgenerator/filter_generator.go
Expand Up @@ -49,6 +49,7 @@ type FilterGenerator interface {
// MakeFilterGenerators provide of a slice of FilterGenerator in sequence.
func MakeFilterGenerators(serviceInfo *ci.ServiceInfo) ([]FilterGenerator, error) {
return []FilterGenerator{
&filtergen.HeaderSanitizerGenerator{},
filtergen.NewCORSGenerator(serviceInfo),

// Health check filter is behind Path Matcher filter, since Service Control
Expand Down

0 comments on commit e956701

Please sign in to comment.