Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package envoy.extensions.filters.http.header_mutation.v3;

import "envoy/config/common/mutation_rules/v3/mutation_rules.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/extension.proto";

import "udpa/annotations/status.proto";

Expand All @@ -17,7 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// Mutate HTTP headers and trailers in requests and responses.
// [#extension: envoy.filters.http.header_mutation]

// [#next-free-field: 6]
// [#next-free-field: 7]
message Mutations {
// The request mutations are applied before the request is forwarded to the upstream cluster.
repeated config.common.mutation_rules.v3.HeaderMutation request_mutations = 1;
Expand All @@ -34,6 +35,12 @@ message Mutations {

// The request trailer mutations are applied before the request is sent to the upstream cluster.
repeated config.common.mutation_rules.v3.HeaderMutation request_trailers_mutations = 5;

// Formatter command parsers used for mutation values. Built-in command parsers are available
// without this field; use it to add extension parsers or override a built-in parser's
// configuration.
// [#extension-category: envoy.formatter]
repeated config.core.v3.TypedExtensionConfig formatters = 6;
}

// Per route configuration for the header mutation filter.
Expand Down
5 changes: 4 additions & 1 deletion api/envoy/extensions/formatter/cel/v3/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")
licenses(["notice"]) # Apache 2

api_proto_package(
deps = ["@xds//udpa/annotations:pkg"],
deps = [
"//envoy/config/core/v3:pkg",
"@xds//udpa/annotations:pkg",
],
)
9 changes: 7 additions & 2 deletions api/envoy/extensions/formatter/cel/v3/cel.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ syntax = "proto3";

package envoy.extensions.formatter.cel.v3;

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

import "udpa/annotations/status.proto";

option java_package = "io.envoyproxy.envoy.extensions.formatter.cel.v3";
Expand Down Expand Up @@ -50,7 +52,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// Configuration for the CEL formatter.
//
// .. warning::
// This extension is treated as built-in extension and will be enabled by default now.
// It is unnecessary to configure this extension.
// This extension is treated as a built-in extension and is enabled by default.
// It is unnecessary to configure this extension unless overriding the CEL expression runtime
// via ``cel_config``.
message Cel {
// Configuration for the CEL expression runtime used by this formatter.
config.core.v3.CelExpressionConfig cel_config = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ licenses(["notice"]) # Apache 2
api_proto_package(
deps = [
"//envoy/config/common/mutation_rules/v3:pkg",
"//envoy/config/core/v3:pkg",
"@xds//udpa/annotations:pkg",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ syntax = "proto3";
package envoy.extensions.http.early_header_mutation.header_mutation.v3;

import "envoy/config/common/mutation_rules/v3/mutation_rules.proto";
import "envoy/config/core/v3/extension.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";
Expand All @@ -20,4 +21,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
message HeaderMutation {
repeated config.common.mutation_rules.v3.HeaderMutation mutations = 1
[(validate.rules).repeated = {min_items: 1}];

// Formatter command parsers used for header values. Built-in command parsers are available
// without this field; use it to add extension parsers or override a built-in parser's
// configuration.
// [#extension-category: envoy.formatter]
repeated config.core.v3.TypedExtensionConfig formatters = 2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added :ref:`formatters <envoy_v3_api_field_extensions.http.early_header_mutation.header_mutation.v3.HeaderMutation.formatters>`
to the early header mutation extension, allowing extension formatter command parsers to be
configured for header mutation substitution values.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Exposed :ref:`cel_config <envoy_v3_api_field_extensions.formatter.cel.v3.Cel.cel_config>` on the
``envoy.formatter.cel`` formatter configuration, allowing CEL runtime options such as string
functions to be enabled for configured CEL formatters.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added :ref:`formatters <envoy_v3_api_field_extensions.filters.http.header_mutation.v3.Mutations.formatters>`
to the header mutation filter, allowing extension formatter command parsers to be configured for
header mutation substitution values.
12 changes: 12 additions & 0 deletions source/common/formatter/substitution_format_string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ absl::StatusOr<std::vector<CommandParserPtr>> SubstitutionFormatStringUtils::par
return commands;
}

absl::StatusOr<std::vector<CommandParserPtr>> SubstitutionFormatStringUtils::parseFormatters(
const FormattersConfig& formatters, Server::Configuration::ServerFactoryContext& server_context,
ProtobufMessage::ValidationVisitor& validation_visitor,
std::vector<CommandParserPtr>&& commands_parsers) {
if (formatters.empty()) {
return std::move(commands_parsers);
}

Server::GenericFactoryContextImpl generic_context(server_context, validation_visitor);
return parseFormatters(formatters, generic_context, std::move(commands_parsers));
}

absl::StatusOr<FormatterPtr> SubstitutionFormatStringUtils::fromProtoConfig(
const envoy::config::core::v3::SubstitutionFormatString& config,
Server::Configuration::GenericFactoryContext& context,
Expand Down
5 changes: 5 additions & 0 deletions source/common/formatter/substitution_format_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class SubstitutionFormatStringUtils {
parseFormatters(const FormattersConfig& formatters,
Server::Configuration::GenericFactoryContext& context,
std::vector<CommandParserPtr>&& commands_parsers = {});
static absl::StatusOr<std::vector<CommandParserPtr>>
parseFormatters(const FormattersConfig& formatters,
Server::Configuration::ServerFactoryContext& server_context,
ProtobufMessage::ValidationVisitor& validation_visitor,
std::vector<CommandParserPtr>&& commands_parsers = {});

/**
* Generate a formatter object from config SubstitutionFormatString.
Expand Down
3 changes: 3 additions & 0 deletions source/extensions/filters/http/header_mutation/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ envoy_cc_library(
srcs = ["header_mutation.cc"],
hdrs = ["header_mutation.h"],
deps = [
"//envoy/protobuf:message_validator_interface",
"//envoy/server:filter_config_interface",
"//source/common/config:utility_lib",
"//source/common/formatter:substitution_format_string_lib",
"//source/common/http:header_map_lib",
"//source/common/http:header_mutation_lib",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/http/common:pass_through_filter_lib",
"//source/server:generic_factory_context_lib",
"@envoy_api//envoy/extensions/filters/http/header_mutation/v3:pkg_cc_proto",
],
)
Expand Down
6 changes: 3 additions & 3 deletions source/extensions/filters/http/header_mutation/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ HeaderMutationFactoryConfig::createFilterFactoryFromProtoWithServerContextTyped(
absl::StatusOr<Router::RouteSpecificFilterConfigConstSharedPtr>
HeaderMutationFactoryConfig::createRouteSpecificFilterConfigTyped(
const PerRouteProtoConfig& proto_config, Server::Configuration::ServerFactoryContext& context,
ProtobufMessage::ValidationVisitor&) {
ProtobufMessage::ValidationVisitor& validation_visitor) {
absl::Status creation_status = absl::OkStatus();
auto route_config =
std::make_shared<PerRouteHeaderMutation>(proto_config, context, creation_status);
auto route_config = std::make_shared<PerRouteHeaderMutation>(proto_config, context,
validation_visitor, creation_status);
RETURN_IF_NOT_OK_REF(creation_status);
return route_config;
}
Expand Down
33 changes: 21 additions & 12 deletions source/extensions/filters/http/header_mutation/header_mutation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <cstdint>
#include <memory>

#include "source/common/config/utility.h"
#include "source/common/formatter/substitution_format_string.h"
#include "source/common/http/header_map_impl.h"
#include "source/common/http/utility.h"
#include "source/common/runtime/runtime_features.h"
Expand Down Expand Up @@ -54,22 +54,31 @@ void QueryParameterMutationAppend::mutateQueryParameter(

Mutations::Mutations(const MutationsProto& config,
Server::Configuration::ServerFactoryContext& context,
ProtobufMessage::ValidationVisitor& validation_visitor,
absl::Status& creation_status) {
auto request_mutations_or_error = HeaderMutations::create(config.request_mutations(), context);
auto command_parsers_or_error = Formatter::SubstitutionFormatStringUtils::parseFormatters(
config.formatters(), context, validation_visitor);
SET_AND_RETURN_IF_NOT_OK(command_parsers_or_error.status(), creation_status);
Formatter::CommandParserPtrVector command_parsers =
std::move(command_parsers_or_error.value());

auto request_mutations_or_error =
HeaderMutations::create(config.request_mutations(), context, command_parsers);
SET_AND_RETURN_IF_NOT_OK(request_mutations_or_error.status(), creation_status);
request_mutations_ = std::move(request_mutations_or_error.value());

auto response_mutations_or_error = HeaderMutations::create(config.response_mutations(), context);
auto response_mutations_or_error =
HeaderMutations::create(config.response_mutations(), context, command_parsers);
SET_AND_RETURN_IF_NOT_OK(response_mutations_or_error.status(), creation_status);
response_mutations_ = std::move(response_mutations_or_error.value());

auto response_trailers_mutations_or_error =
HeaderMutations::create(config.response_trailers_mutations(), context);
HeaderMutations::create(config.response_trailers_mutations(), context, command_parsers);
SET_AND_RETURN_IF_NOT_OK(response_trailers_mutations_or_error.status(), creation_status);
response_trailers_mutations_ = std::move(response_trailers_mutations_or_error.value());

auto request_trailers_mutations_or_error =
HeaderMutations::create(config.request_trailers_mutations(), context);
HeaderMutations::create(config.request_trailers_mutations(), context, command_parsers);
SET_AND_RETURN_IF_NOT_OK(request_trailers_mutations_or_error.status(), creation_status);
request_trailers_mutations_ = std::move(request_trailers_mutations_or_error.value());

Expand All @@ -92,8 +101,8 @@ Mutations::Mutations(const MutationsProto& config,
return;
}

auto value_or_error =
Formatter::FormatterImpl::create(mutation.append().record().value().string_value(), true);
auto value_or_error = Formatter::FormatterImpl::create(
mutation.append().record().value().string_value(), true, command_parsers);
SET_AND_RETURN_IF_NOT_OK(value_or_error.status(), creation_status);
query_query_parameter_mutations_.emplace_back(std::make_unique<QueryParameterMutationAppend>(
mutation.append().record().key(), std::move(value_or_error.value()),
Expand Down Expand Up @@ -145,15 +154,15 @@ void Mutations::mutateRequestTrailers(Http::RequestTrailerMap& trailers,
request_trailers_mutations_->evaluateHeaders(trailers, context, stream_info);
}

PerRouteHeaderMutation::PerRouteHeaderMutation(const PerRouteProtoConfig& config,
Server::Configuration::ServerFactoryContext& context,
absl::Status& creation_status)
: mutations_(config.mutations(), context, creation_status) {}
PerRouteHeaderMutation::PerRouteHeaderMutation(
const PerRouteProtoConfig& config, Server::Configuration::ServerFactoryContext& context,
ProtobufMessage::ValidationVisitor& validation_visitor, absl::Status& creation_status)
: mutations_(config.mutations(), context, validation_visitor, creation_status) {}

HeaderMutationConfig::HeaderMutationConfig(const ProtoConfig& config,
Server::Configuration::ServerFactoryContext& context,
absl::Status& creation_status)
: mutations_(config.mutations(), context, creation_status),
: mutations_(config.mutations(), context, context.messageValidationVisitor(), creation_status),
most_specific_header_mutations_wins_(config.most_specific_header_mutations_wins()) {}

void HeaderMutation::maybeInitializeRouteConfigs(Http::StreamFilterCallbacks* callbacks) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "envoy/extensions/filters/http/header_mutation/v3/header_mutation.pb.h"
#include "envoy/http/query_params.h"
#include "envoy/protobuf/message_validator.h"

#include "source/common/common/logger.h"
#include "source/common/formatter/substitution_formatter.h"
Expand Down Expand Up @@ -83,7 +84,7 @@ class Mutations {
using HeaderMutations = Http::HeaderMutations;

Mutations(const MutationsProto& config, Server::Configuration::ServerFactoryContext& context,
absl::Status& creation_status);
ProtobufMessage::ValidationVisitor& validation_visitor, absl::Status& creation_status);

void mutateRequestHeaders(Http::RequestHeaderMap& headers, const Formatter::Context& context,
const StreamInfo::StreamInfo& stream_info) const;
Expand All @@ -107,6 +108,7 @@ class PerRouteHeaderMutation : public Router::RouteSpecificFilterConfig {
public:
PerRouteHeaderMutation(const PerRouteProtoConfig& config,
Server::Configuration::ServerFactoryContext& context,
ProtobufMessage::ValidationVisitor& validation_visitor,
absl::Status& creation_status);

const Mutations& mutations() const { return mutations_; }
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/formatter/cel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ envoy_cc_extension(
],
deps = [
"//envoy/registry",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/common/expr:evaluator_lib",
"//source/extensions/formatter/cel:cel_lib",
"@envoy_api//envoy/extensions/formatter/cel/v3:pkg_cc_proto",
] + select(
Expand Down
21 changes: 18 additions & 3 deletions source/extensions/formatter/cel/cel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ Protobuf::Value CELFormatter::formatValue(const Envoy::Formatter::Context& conte
}
}

CELFormatterCommandParser::CELFormatterCommandParser(
const ::Envoy::LocalInfo::LocalInfo& local_info,
Expr::BuilderInstanceSharedConstPtr expr_builder)
: configured_state_(ConfiguredState{local_info, std::move(expr_builder)}) {
ASSERT(configured_state_->expr_builder != nullptr);
}

::Envoy::Formatter::FormatterProviderPtr
CELFormatterCommandParser::parse(absl::string_view command, absl::string_view subcommand,
absl::optional<size_t> max_length) const {
Expand All @@ -85,10 +92,18 @@ CELFormatterCommandParser::parse(absl::string_view command, absl::string_view su
if (!parse_status.ok()) {
throw EnvoyException("Not able to parse expression: " + parse_status.status().ToString());
}
Server::Configuration::ServerFactoryContext& context =
Server::Configuration::ServerFactoryContextInstance::get();

// Lazily resolve the active server context at CEL-command parse time: built-in command parsers
// are process-global, so they cannot capture server-owned CEL state.
if (!configured_state_.has_value()) {
Server::Configuration::ServerFactoryContext& context =
Server::Configuration::ServerFactoryContextInstance::get();
return std::make_unique<CELFormatter>(context.localInfo(), Expr::getBuilder(context),
parse_status.value().expr(), max_length,
command == "TYPED_CEL");
}
return std::make_unique<CELFormatter>(
context.localInfo(), Extensions::Filters::Common::Expr::getBuilder(context),
configured_state_->local_info.get(), configured_state_->expr_builder,
parse_status.value().expr(), max_length, command == "TYPED_CEL");
}

Expand Down
17 changes: 17 additions & 0 deletions source/extensions/formatter/cel/cel.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <functional>
#include <string>

#include "envoy/config/typed_config.h"
Expand All @@ -8,6 +9,8 @@
#include "source/common/formatter/substitution_formatter.h"
#include "source/extensions/filters/common/expr/evaluator.h"

#include "absl/types/optional.h"

namespace Envoy {
namespace Extensions {
namespace Formatter {
Expand All @@ -33,9 +36,23 @@ class CELFormatter : public ::Envoy::Formatter::FormatterProvider {
class CELFormatterCommandParser : public ::Envoy::Formatter::CommandParser {
public:
CELFormatterCommandParser() = default;
CELFormatterCommandParser(
const ::Envoy::LocalInfo::LocalInfo& local_info,
Extensions::Filters::Common::Expr::BuilderInstanceSharedConstPtr expr_builder);
::Envoy::Formatter::FormatterProviderPtr parse(absl::string_view command,
absl::string_view subcommand,
absl::optional<size_t> max_length) const override;

private:
struct ConfiguredState {
std::reference_wrapper<const ::Envoy::LocalInfo::LocalInfo> local_info;
Extensions::Filters::Common::Expr::BuilderInstanceSharedConstPtr expr_builder;
};

// Present only for parsers created from explicit `envoy.formatter.cel` config. Keeping the
// server-owned values together avoids a half-configured parser; absence means built-in parser
// mode, where `parse()` resolves the active server context for each CEL command.
absl::optional<ConfiguredState> configured_state_;
};

} // namespace Formatter
Expand Down
21 changes: 14 additions & 7 deletions source/extensions/formatter/cel/config.cc
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
#include "source/extensions/formatter/cel/config.h"

#include "envoy/extensions/formatter/cel/v3/cel.pb.h"
#include "envoy/extensions/formatter/cel/v3/cel.pb.validate.h"

#include "source/common/protobuf/utility.h"
#include "source/extensions/formatter/cel/cel.h"

namespace Envoy {
namespace Extensions {
namespace Formatter {

::Envoy::Formatter::CommandParserPtr
CELFormatterFactory::createCommandParserFromProto(const Protobuf::Message&,
Server::Configuration::GenericFactoryContext&) {
::Envoy::Formatter::CommandParserPtr CELFormatterFactory::createCommandParserFromProto(
const Protobuf::Message& proto_config, Server::Configuration::GenericFactoryContext& context) {
#if defined(USE_CEL_PARSER)
ENVOY_LOG_TO_LOGGER(Logger::Registry::getLog(Logger::Id::config), warn,
"'CEL' formatter is treated as a built-in formatter and does not "
"require configuration.");
return std::make_unique<CELFormatterCommandParser>();
const auto& config =
MessageUtil::downcastAndValidate<const envoy::extensions::formatter::cel::v3::Cel&>(
proto_config, context.messageValidationVisitor());
const auto config_ref = config.has_cel_config()
? Envoy::makeOptRef(config.cel_config())
: Envoy::OptRef<const envoy::config::core::v3::CelExpressionConfig>{};
return std::make_unique<CELFormatterCommandParser>(
context.serverFactoryContext().localInfo(),
Filters::Common::Expr::getBuilder(context.serverFactoryContext(), config_ref));
#else
UNREFERENCED_PARAMETER(proto_config);
UNREFERENCED_PARAMETER(context);
throw EnvoyException("CEL is not available for use in this environment.");
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ envoy_cc_library(
hdrs = ["header_mutation.h"],
deps = [
"//envoy/http:early_header_mutation_interface",
"//envoy/protobuf:message_validator_interface",
"//source/common/formatter:substitution_format_string_lib",
"//source/common/http:header_mutation_lib",
"//source/server:generic_factory_context_lib",
"@envoy_api//envoy/config/common/mutation_rules/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/http/early_header_mutation/header_mutation/v3:pkg_cc_proto",
],
Expand Down
Loading