From 2d3466c276497b838dedcb3738ab0d8dd58dc7e2 Mon Sep 17 00:00:00 2001 From: Yuval Kohavi Date: Mon, 2 Sep 2019 10:11:12 -0400 Subject: [PATCH] Transformation opt (#22) * do more things as onetime init for more performance * transform cleanup 2 * nicer errors and logs --- .../filters/http/transformation/BUILD | 1 + .../transformation/body_header_transformer.h | 1 + .../http/transformation/inja_transformer.cc | 141 +++++++++++------- .../http/transformation/inja_transformer.h | 36 +++-- .../transformation/transformation_filter.cc | 35 ++--- .../transformation/transformation_filter.h | 12 +- .../transformation_filter_config.cc | 18 +-- .../transformation_filter_config.h | 25 ++-- .../filters/http/transformation/transformer.h | 5 + .../transformation/inja_transformer_test.cc | 29 ++-- 10 files changed, 170 insertions(+), 133 deletions(-) diff --git a/source/extensions/filters/http/transformation/BUILD b/source/extensions/filters/http/transformation/BUILD index c345c419d..9f336d6bb 100644 --- a/source/extensions/filters/http/transformation/BUILD +++ b/source/extensions/filters/http/transformation/BUILD @@ -82,6 +82,7 @@ envoy_cc_library( "@envoy//include/envoy/buffer:buffer_interface", "@envoy//include/envoy/http:header_map_interface", "@envoy//source/common/common:macros", + "@envoy//source/common/common:utility_lib", "@envoy//source/common/protobuf", "@inja//:inja-lib", "@json//:json-lib", diff --git a/source/extensions/filters/http/transformation/body_header_transformer.h b/source/extensions/filters/http/transformation/body_header_transformer.h index 5375443df..ef110f824 100644 --- a/source/extensions/filters/http/transformation/body_header_transformer.h +++ b/source/extensions/filters/http/transformation/body_header_transformer.h @@ -12,6 +12,7 @@ namespace Transformation { class BodyHeaderTransformer : public Transformer { public: void transform(Http::HeaderMap &map, Buffer::Instance &body) const override; + bool passthrough_body() const override { return false; }; }; } // namespace Transformation diff --git a/source/extensions/filters/http/transformation/inja_transformer.cc b/source/extensions/filters/http/transformation/inja_transformer.cc index 97cd3318c..ef7b3ef4e 100644 --- a/source/extensions/filters/http/transformation/inja_transformer.cc +++ b/source/extensions/filters/http/transformation/inja_transformer.cc @@ -3,6 +3,7 @@ #include #include "common/common/macros.h" +#include "common/common/utility.h" // For convenience using namespace inja; @@ -34,24 +35,23 @@ const Http::HeaderEntry *getHeader(const Http::HeaderMap &header_map, } // namespace -std::string ExtractorUtil::extract( - const envoy::api::v2::filter::http::Extraction &extractor, - const Http::HeaderMap &header_map) { +Extractor::Extractor(const envoy::api::v2::filter::http::Extraction &extractor) + : headername_(extractor.header()), group_(extractor.subgroup()), + extract_regex_(RegexUtil::parseRegex(extractor.regex())) {} +std::string Extractor::extract(const Http::HeaderMap &header_map) const { // TODO: should we lowercase them in the config? - const std::string &headername = extractor.header(); - const Http::HeaderEntry *header_entry = getHeader(header_map, headername); + const Http::HeaderEntry *header_entry = getHeader(header_map, headername_); if (!header_entry) { return ""; } std::string value(header_entry->value().getStringView()); - unsigned int group = extractor.subgroup(); + // get and regex - std::regex extract_regex(extractor.regex()); std::smatch regex_result; - if (std::regex_match(value, regex_result, extract_regex)) { + if (std::regex_match(value, regex_result, extract_regex_)) { std::smatch::iterator submatch_it = regex_result.begin(); - for (unsigned i = 0; i < group; i++) { + for (unsigned i = 0; i < group_; i++) { std::advance(submatch_it, 1); if (submatch_it == regex_result.end()) { return ""; @@ -65,7 +65,8 @@ std::string ExtractorUtil::extract( TransformerInstance::TransformerInstance( const Http::HeaderMap &header_map, - const std::map &extractions, const json &context) + const std::unordered_map &extractions, + const json &context) : header_map_(header_map), extractions_(extractions), context_(context) { env_.add_callback("header", 1, [this](Arguments args) { return header_callback(args); }); @@ -93,13 +94,59 @@ json TransformerInstance::extracted_callback(Arguments args) { return ""; } -std::string TransformerInstance::render(const std::string &input) { +std::string TransformerInstance::render(const inja::Template &input) { return env_.render(input, context_); } InjaTransformer::InjaTransformer( const envoy::api::v2::filter::http::TransformationTemplate &transformation) - : transformation_(transformation) {} + : advanced_templates_(transformation.advanced_templates()), + passthrough_body_(transformation.has_passthrough()) { + inja::ParserConfig parser_config; + inja::LexerConfig lexer_config; + inja::TemplateStorage template_storage; + if (!advanced_templates_) { + parser_config.notation = inja::ElementNotation::Dot; + } + + inja::Parser parser(parser_config, lexer_config, template_storage); + + const auto &extractors = transformation.extractors(); + for (auto it = extractors.begin(); it != extractors.end(); it++) { + extractors_.emplace_back(std::make_pair(it->first, it->second)); + } + const auto &headers = transformation.headers(); + for (auto it = headers.begin(); it != headers.end(); it++) { + Http::LowerCaseString header_name(it->first); + try { + headers_.emplace_back(std::make_pair(std::move(header_name), + parser.parse(it->second.text()))); + } catch (const std::runtime_error &e) { + throw EnvoyException(fmt::format( + "Failed to parse header template '{}': {}", it->first, e.what())); + } + } + + switch (transformation.body_transformation_case()) { + case envoy::api::v2::filter::http::TransformationTemplate::kBody: { + try { + body_template_.emplace(parser.parse(transformation.body().text())); + } catch (const std::runtime_error &e) { + throw EnvoyException( + fmt::format("Failed to parse body template {}", e.what())); + } + } + case envoy::api::v2::filter::http::TransformationTemplate:: + kMergeExtractorsToBody: { + merged_extractors_to_body_ = true; + } + case envoy::api::v2::filter::http::TransformationTemplate::kPassthrough: + case envoy::api::v2::filter::http::TransformationTemplate:: + BODY_TRANSFORMATION_NOT_SET: { + break; + } + } +} InjaTransformer::~InjaTransformer() {} @@ -113,15 +160,15 @@ void InjaTransformer::transform(Http::HeaderMap &header_map, json_body = json::parse(bodystring); } // get the extractions - std::map extractions; - - const auto &extractors = transformation_.extractors(); + std::unordered_map extractions; + if (advanced_templates_) { + extractions.reserve(extractors_.size()); + } - for (auto it = extractors.begin(); it != extractors.end(); it++) { - const std::string &name = it->first; - const envoy::api::v2::filter::http::Extraction &extractor = it->second; - if (transformation_.advanced_templates()) { - extractions[name] = ExtractorUtil::extract(extractor, header_map); + for (const auto &named_extractor : extractors_) { + const std::string &name = named_extractor.first; + if (advanced_templates_) { + extractions[name] = named_extractor.second.extract(header_map); } else { std::string name_to_split = name; json *current = &json_body; @@ -131,62 +178,40 @@ void InjaTransformer::transform(Http::HeaderMap &header_map, current = &(*current)[field_name]; name_to_split.erase(0, pos + 1); } - (*current)[name_to_split] = ExtractorUtil::extract(extractor, header_map); + (*current)[name_to_split] = named_extractor.second.extract(header_map); } } // start transforming! TransformerInstance instance(header_map, extractions, json_body); - if (!transformation_.advanced_templates()) { - instance.useDotNotation(); - } - - switch (transformation_.body_transformation_case()) { - case envoy::api::v2::filter::http::TransformationTemplate::kBody: { - const std::string &input = transformation_.body().text(); - auto output = instance.render(input); - + // Body transform: + auto replace_body = [&](std::string &output) { // remove content length, as we have new body. header_map.removeContentLength(); // replace body body.drain(body.length()); body.add(output); header_map.insertContentLength().value(body.length()); - break; - } + }; - case envoy::api::v2::filter::http::TransformationTemplate:: - kMergeExtractorsToBody: { + if (body_template_.has_value()) { + std::string output = instance.render(body_template_.value()); + replace_body(output); + } else if (merged_extractors_to_body_) { std::string output = json_body.dump(); - - // remove content length, as we have new body. - header_map.removeContentLength(); - // replace body - body.drain(body.length()); - body.add(output); - header_map.insertContentLength().value(body.length()); - break; - } - case envoy::api::v2::filter::http::TransformationTemplate::kPassthrough: - case envoy::api::v2::filter::http::TransformationTemplate:: - BODY_TRANSFORMATION_NOT_SET: { - break; + replace_body(output); } - } - - // add headers - const auto &headers = transformation_.headers(); - for (auto it = headers.begin(); it != headers.end(); it++) { - std::string name = it->first; - auto lkname = Http::LowerCaseString(std::move(name)); - const envoy::api::v2::filter::http::InjaTemplate &text = it->second; - std::string output = instance.render(text.text()); + // Headers transform: + for (const auto &templated_header : headers_) { + std::string output = instance.render(templated_header.second); // remove existing header - header_map.remove(lkname); + header_map.remove(templated_header.first); // TODO(yuval-k): Do we need to support intentional empty headers? if (!output.empty()) { - header_map.addCopy(lkname, output); + // we can add the key as reference as the headers_ lifetime is as the + // route's + header_map.addReferenceKey(templated_header.first, output); } } } diff --git a/source/extensions/filters/http/transformation/inja_transformer.h b/source/extensions/filters/http/transformation/inja_transformer.h index 8f38948fb..00866e933 100644 --- a/source/extensions/filters/http/transformation/inja_transformer.h +++ b/source/extensions/filters/http/transformation/inja_transformer.h @@ -21,33 +21,34 @@ namespace Transformation { class TransformerInstance { public: - TransformerInstance(const Http::HeaderMap &header_map, - const std::map &extractions, - const nlohmann::json &context); + TransformerInstance( + const Http::HeaderMap &header_map, + const std::unordered_map &extractions, + const nlohmann::json &context); // header_value(name) // extracted_value(name, index) nlohmann::json header_callback(inja::Arguments args); nlohmann::json extracted_callback(inja::Arguments args); - std::string render(const std::string &input); - - void useDotNotation() { - env_.set_element_notation(inja::ElementNotation::Dot); - } + std::string render(const inja::Template &input); private: inja::Environment env_; const Http::HeaderMap &header_map_; - const std::map &extractions_; + const std::unordered_map &extractions_; const nlohmann::json &context_; }; -class ExtractorUtil { +class Extractor { public: - static std::string - extract(const envoy::api::v2::filter::http::Extraction &extractor, - const Http::HeaderMap &header_map); + Extractor(const envoy::api::v2::filter::http::Extraction &extractor); + std::string extract(const Http::HeaderMap &header_map) const; + +private: + const Http::LowerCaseString headername_; + const unsigned int group_; + const std::regex extract_regex_; }; class InjaTransformer : public Transformer { @@ -57,6 +58,7 @@ class InjaTransformer : public Transformer { ~InjaTransformer(); void transform(Http::HeaderMap &map, Buffer::Instance &body) const override; + bool passthrough_body() const override { return passthrough_body_; }; private: /* @@ -69,7 +71,13 @@ class InjaTransformer : public Transformer { std::aligned_storage::type impl_; */ - const envoy::api::v2::filter::http::TransformationTemplate &transformation_; + bool advanced_templates_{}; + bool passthrough_body_{}; + std::vector> extractors_; + std::vector> headers_; + + absl::optional body_template_; + bool merged_extractors_to_body_{}; }; } // namespace Transformation diff --git a/source/extensions/filters/http/transformation/transformation_filter.cc b/source/extensions/filters/http/transformation/transformation_filter.cc index 697557d52..8835b78e3 100644 --- a/source/extensions/filters/http/transformation/transformation_filter.cc +++ b/source/extensions/filters/http/transformation/transformation_filter.cc @@ -41,7 +41,7 @@ TransformationFilter::decodeHeaders(Http::HeaderMap &header_map, request_headers_ = &header_map; - if (end_stream || request_transformation_->body_passthrough()) { + if (end_stream || request_transformation_->passthrough_body()) { transformRequest(); return is_error() ? Http::FilterHeadersStatus::StopIteration @@ -97,7 +97,7 @@ TransformationFilter::encodeHeaders(Http::HeaderMap &header_map, response_headers_ = &header_map; - if (end_stream || response_transformation_->body_passthrough()) { + if (end_stream || response_transformation_->passthrough_body()) { transformResponse(); return Http::FilterHeadersStatus::Continue; } @@ -147,7 +147,7 @@ void TransformationFilter::checkResponseActive() { getTransformFromRoute(TransformationFilter::Direction::Response); } -const Transformation *TransformationFilter::getTransformFromRoute( +TransformerConstSharedPtr TransformationFilter::getTransformFromRoute( TransformationFilter::Direction direction) { if (!route_) { @@ -161,18 +161,11 @@ const Transformation *TransformationFilter::getTransformFromRoute( if (config != nullptr) { switch (direction) { case TransformationFilter::Direction::Request: { - should_clear_cache_ = config->shouldClearCache(); - const absl::optional &maybe_transformation = - config->getRequestTranformation(); - return maybe_transformation.has_value() ? &maybe_transformation.value() - : nullptr; + return config->getRequestTranformation(); } case TransformationFilter::Direction::Response: { - const absl::optional &maybe_transformation = - config->getResponseTranformation(); - return maybe_transformation.has_value() ? &maybe_transformation.value() - : nullptr; + return config->getResponseTranformation(); } default: // TODO(yuval-k): should this be a warning log? @@ -183,7 +176,8 @@ const Transformation *TransformationFilter::getTransformFromRoute( } void TransformationFilter::transformRequest() { - transformSomething(&request_transformation_, *request_headers_, request_body_, + transformSomething(*decoder_callbacks_, request_transformation_, + *request_headers_, request_body_, &TransformationFilter::requestError, &TransformationFilter::addDecoderData); if (should_clear_cache_) { @@ -192,8 +186,9 @@ void TransformationFilter::transformRequest() { } void TransformationFilter::transformResponse() { - transformSomething(&response_transformation_, *response_headers_, - response_body_, &TransformationFilter::responseError, + transformSomething(*encoder_callbacks_, response_transformation_, + *response_headers_, response_body_, + &TransformationFilter::responseError, &TransformationFilter::addEncoderData); } @@ -206,26 +201,28 @@ void TransformationFilter::addEncoderData(Buffer::Instance &data) { } void TransformationFilter::transformSomething( - const Transformation **transformation, Http::HeaderMap &header_map, + Http::StreamFilterCallbacks &callbacks, + TransformerConstSharedPtr &transformation, Http::HeaderMap &header_map, Buffer::Instance &body, void (TransformationFilter::*responeWithError)(), void (TransformationFilter::*addData)(Buffer::Instance &)) { try { - (*transformation)->transformer().transform(header_map, body); + transformation->transform(header_map, body); if (body.length() > 0) { (this->*addData)(body); - } else if (!(*transformation)->body_passthrough()) { + } else if (!transformation->passthrough_body()) { // only remove content type if the request is not passthrough. // This means that the empty body is a result of the transformation. // so the content type should be removed header_map.removeContentType(); } } catch (std::exception &e) { + ENVOY_STREAM_LOG(debug, "failure transforming {}", callbacks, e.what()); error(Error::TemplateParseError, e.what()); } - *transformation = nullptr; + transformation = nullptr; if (is_error()) { (this->*responeWithError)(); } diff --git a/source/extensions/filters/http/transformation/transformation_filter.h b/source/extensions/filters/http/transformation/transformation_filter.h index 59cc3673d..de0b5c060 100644 --- a/source/extensions/filters/http/transformation/transformation_filter.h +++ b/source/extensions/filters/http/transformation/transformation_filter.h @@ -17,7 +17,8 @@ namespace Transformation { * Translation we can be used either as a functional filter, or a non functional * filter. */ -class TransformationFilter : public Http::StreamFilter { +class TransformationFilter : public Http::StreamFilter, + Logger::Loggable { public: TransformationFilter(); ~TransformationFilter(); @@ -79,7 +80,7 @@ class TransformationFilter : public Http::StreamFilter { void error(Error error, std::string msg = ""); bool is_error(); - const Transformation *getTransformFromRoute(Direction direction); + TransformerConstSharedPtr getTransformFromRoute(Direction direction); void transformRequest(); void transformResponse(); @@ -87,7 +88,8 @@ class TransformationFilter : public Http::StreamFilter { void addDecoderData(Buffer::Instance &data); void addEncoderData(Buffer::Instance &data); void - transformSomething(const Transformation **transformation, + transformSomething(Http::StreamFilterCallbacks &callbacks, + TransformerConstSharedPtr &transformation, Http::HeaderMap &header_map, Buffer::Instance &body, void (TransformationFilter::*responeWithError)(), void (TransformationFilter::*addData)(Buffer::Instance &)); @@ -104,8 +106,8 @@ class TransformationFilter : public Http::StreamFilter { Buffer::OwnedImpl request_body_{}; Buffer::OwnedImpl response_body_{}; - const Transformation *request_transformation_{nullptr}; - const Transformation *response_transformation_{nullptr}; + TransformerConstSharedPtr request_transformation_; + TransformerConstSharedPtr response_transformation_; absl::optional error_; Http::Code error_code_; std::string error_messgae_; diff --git a/source/extensions/filters/http/transformation/transformation_filter_config.cc b/source/extensions/filters/http/transformation/transformation_filter_config.cc index 79f320b0f..21e4c1117 100644 --- a/source/extensions/filters/http/transformation/transformation_filter_config.cc +++ b/source/extensions/filters/http/transformation/transformation_filter_config.cc @@ -8,21 +8,17 @@ namespace Extensions { namespace HttpFilters { namespace Transformation { -Transformation::Transformation( - const envoy::api::v2::filter::http::Transformation &transformation) - : transformation_(transformation) { - switch (transformation_.transformation_type_case()) { +TransformerSharedPtr Transformation::getTransformer( + const envoy::api::v2::filter::http::Transformation &transformation) { + switch (transformation.transformation_type_case()) { case envoy::api::v2::filter::http::Transformation::kTransformationTemplate: - passthrough_body_ = - transformation_.transformation_template().has_passthrough(); - transformer_.reset( - new InjaTransformer(transformation_.transformation_template())); - break; + return std::make_unique( + transformation.transformation_template()); case envoy::api::v2::filter::http::Transformation::kHeaderBodyTransform: - transformer_.reset(new BodyHeaderTransformer()); - break; + return std::make_unique(); case envoy::api::v2::filter::http::Transformation:: TRANSFORMATION_TYPE_NOT_SET: + // TODO: return null here? default: throw EnvoyException("non existant transformation"); } diff --git a/source/extensions/filters/http/transformation/transformation_filter_config.h b/source/extensions/filters/http/transformation/transformation_filter_config.h index 6b5ffdcd0..8187758b8 100644 --- a/source/extensions/filters/http/transformation/transformation_filter_config.h +++ b/source/extensions/filters/http/transformation/transformation_filter_config.h @@ -15,17 +15,8 @@ namespace Transformation { class Transformation { public: - Transformation( + static TransformerSharedPtr getTransformer( const envoy::api::v2::filter::http::Transformation &transformation); - - bool body_passthrough() const { return passthrough_body_; } - - const Transformer &transformer() const { return *transformer_; } - -private: - envoy::api::v2::filter::http::Transformation transformation_; - std::unique_ptr transformer_; - bool passthrough_body_{}; }; class RouteTransformationFilterConfig @@ -37,26 +28,28 @@ class RouteTransformationFilterConfig RouteTransformationFilterConfig(ProtoConfig proto_config) : clear_route_cache_(proto_config.clear_route_cache()) { if (proto_config.has_request_transformation()) { - request_transformation_.emplace(proto_config.request_transformation()); + request_transformation_ = + Transformation::getTransformer(proto_config.request_transformation()); } if (proto_config.has_response_transformation()) { - response_transformation_.emplace(proto_config.response_transformation()); + response_transformation_ = Transformation::getTransformer( + proto_config.response_transformation()); } } - const absl::optional &getRequestTranformation() const { + TransformerConstSharedPtr getRequestTranformation() const { return request_transformation_; } bool shouldClearCache() const { return clear_route_cache_; } - const absl::optional &getResponseTranformation() const { + TransformerConstSharedPtr getResponseTranformation() const { return response_transformation_; } private: - absl::optional request_transformation_; - absl::optional response_transformation_; + TransformerConstSharedPtr request_transformation_; + TransformerConstSharedPtr response_transformation_; bool clear_route_cache_{}; }; diff --git a/source/extensions/filters/http/transformation/transformer.h b/source/extensions/filters/http/transformation/transformer.h index 475ea0a9b..89f6e3aca 100644 --- a/source/extensions/filters/http/transformation/transformer.h +++ b/source/extensions/filters/http/transformation/transformer.h @@ -12,10 +12,15 @@ class Transformer { public: virtual ~Transformer() {} + virtual bool passthrough_body() const PURE; + virtual void transform(Http::HeaderMap &map, Buffer::Instance &body) const PURE; }; +typedef std::shared_ptr TransformerSharedPtr; +typedef std::shared_ptr TransformerConstSharedPtr; + } // namespace Transformation } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/transformation/inja_transformer_test.cc b/test/extensions/filters/http/transformation/inja_transformer_test.cc index faa5695e8..0014a1277 100644 --- a/test/extensions/filters/http/transformation/inja_transformer_test.cc +++ b/test/extensions/filters/http/transformation/inja_transformer_test.cc @@ -25,13 +25,22 @@ namespace Extensions { namespace HttpFilters { namespace Transformation { +inja::Template parse(std::string s) { + inja::ParserConfig parser_config; + inja::LexerConfig lexer_config; + inja::TemplateStorage template_storage; + + inja::Parser parser(parser_config, lexer_config, template_storage); + return parser.parse(s); +} + TEST(TransformerInstance, ReplacesValueFromContext) { json originalbody; originalbody["field1"] = "value1"; Http::TestHeaderMapImpl headers; TransformerInstance t(headers, {}, originalbody); - auto res = t.render("{{field1}}"); + auto res = t.render(parse("{{field1}}")); EXPECT_EQ(originalbody["field1"], res); } @@ -46,7 +55,7 @@ TEST(TransformerInstance, ReplacesValueFromInlineHeader) { TransformerInstance t(headers, {}, originalbody); - auto res = t.render("{{header(\":path\")}}"); + auto res = t.render(parse("{{header(\":path\")}}")); EXPECT_EQ(path, res); } @@ -61,32 +70,32 @@ TEST(TransformerInstance, ReplacesValueFromCustomHeader) { {"x-custom-header", header}}; TransformerInstance t(headers, {}, originalbody); - auto res = t.render("{{header(\"x-custom-header\")}}"); + auto res = t.render(parse("{{header(\"x-custom-header\")}}")); EXPECT_EQ(header, res); } TEST(TransformerInstance, ReplaceFromExtracted) { json originalbody; - std::map extractions; + std::unordered_map extractions; std::string field = "res"; extractions["f"] = field; Http::TestHeaderMapImpl headers; TransformerInstance t(headers, extractions, originalbody); - auto res = t.render("{{extraction(\"f\")}}"); + auto res = t.render(parse("{{extraction(\"f\")}}")); EXPECT_EQ(field, res); } TEST(TransformerInstance, ReplaceFromNonExistentExtraction) { json originalbody; - std::map extractions; + std::unordered_map extractions; extractions["foo"] = "bar"; Http::TestHeaderMapImpl headers; TransformerInstance t(headers, extractions, originalbody); - auto res = t.render("{{extraction(\"notsuchfield\")}}"); + auto res = t.render(parse("{{extraction(\"notsuchfield\")}}")); EXPECT_EQ("", res); } @@ -99,7 +108,7 @@ TEST(ExtractorUtil, ExtractIdFromHeader) { extractor.set_header(":path"); extractor.set_regex("/users/(\\d+)"); extractor.set_subgroup(1); - auto res = ExtractorUtil::extract(extractor, headers); + auto res = Extractor(extractor).extract(headers); EXPECT_EQ("123", res); } @@ -277,7 +286,7 @@ TEST(Transformer, transformBodyNotSet) { EXPECT_EQ("456", headers.get_("x-header")); } -TEST(Transformer, transformWithHyphens) { +TEST(InjaTransformer, transformWithHyphens) { Http::TestHeaderMapImpl headers{ {":method", "GET"}, {":path", "/accounts/764b.0f_0f-7319-4b29-bbd0-887a39705a70"}}; @@ -303,7 +312,7 @@ TEST(Transformer, transformWithHyphens) { EXPECT_THAT(res, HasSubstr("\"764b.0f_0f-7319-4b29-bbd0-887a39705a70\"")); } -TEST(ExtractorUtil, RemoveHeadersUsingEmptyTemplate) { +TEST(InjaTransformer, RemoveHeadersUsingEmptyTemplate) { const std::string content_type = "content-type"; Http::TestHeaderMapImpl headers{ {":method", "GET"}, {":path", "/foo"}, {content_type, "x-test"}};