Skip to content
Merged
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 @@ -25,6 +25,9 @@ message Decompressor {
// Runtime flag that controls whether the filter is enabled for decompression or not. If set to false, the
// filter will operate as a pass-through filter. If the message is unspecified, the filter will be enabled.
config.core.v3.RuntimeFeatureFlag enabled = 1;

// If set to true, will decompress response even if a *no-transform* cache control header is set.
bool ignore_no_transform_header = 2;
}

// Configuration for filter behavior on the request direction.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ By *default* decompression will be *skipped* when:
- A request/response does NOT contain *content-encoding* header.
- A request/response includes *content-encoding* header, but it does not contain the configured
decompressor's content-encoding.
- A request/response contains a *cache-control* header whose value includes "no-transform".
- A request/response contains a *cache-control* header whose value includes "no-transform",
unless :ref:`ignore_no_transform_header <envoy_v3_api_field_extensions.filters.http.decompressor.v3.Decompressor.CommonDirectionConfig.ignore_no_transform_header>`
is set to *true*.

When decompression is *applied*:

Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ New Features
* bootstrap: added :ref:`typed_dns_resolver_config <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.typed_dns_resolver_config>` in the bootstrap to support DNS resolver as an extension.
* cluster: added :ref:`typed_dns_resolver_config <envoy_v3_api_field_config.cluster.v3.Cluster.typed_dns_resolver_config>` in the cluster to support DNS resolver as an extension.
* config: added :ref:`environment_variable <envoy_v3_api_field_config.core.v3.datasource.environment_variable>` to the :ref:`DataSource <envoy_v3_api_msg_config.core.v3.datasource>`.
* decompressor: added :ref:`ignore_no_transform_header <envoy_v3_api_field_extensions.filters.http.decompressor.v3.Decompressor.CommonDirectionConfig.ignore_no_transform_header>` to run decompression regardless of the value of the *no-transform* cache control header.
* dns: added :ref:`ALL <envoy_v3_api_enum_value_config.cluster.v3.Cluster.DnsLookupFamily.ALL>` option to return both IPv4 and IPv6 addresses.
* dns_cache: added :ref:`typed_dns_resolver_config <envoy_v3_api_field_extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.typed_dns_resolver_config>` in the dns_cache to support DNS resolver as an extension.
* dns_filter: added :ref:`typed_dns_resolver_config <envoy_v3_api_field_extensions.filters.udp.dns_filter.v3.DnsFilterConfig.ClientContextConfig.typed_dns_resolver_config>` in the dns_filter to support DNS resolver as an extension.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ DecompressorFilterConfig::DirectionConfig::DirectionConfig(
proto_config,
const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime)
: stats_(generateStats(stats_prefix, scope)),
decompression_enabled_(proto_config.enabled(), runtime) {}
decompression_enabled_(proto_config.enabled(), runtime),
ignore_no_transform_header_(proto_config.ignore_no_transform_header()) {}

DecompressorFilterConfig::RequestDirectionConfig::RequestDirectionConfig(
const envoy::extensions::filters::http::decompressor::v3::Decompressor::RequestDirectionConfig&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class DecompressorFilterConfig {
virtual const std::string& logString() const PURE;
const DecompressorStats& stats() const { return stats_; }
bool decompressionEnabled() const { return decompression_enabled_.enabled(); }
bool ignoreNoTransformHeader() const { return ignore_no_transform_header_; }

private:
static DecompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) {
Expand All @@ -55,6 +56,7 @@ class DecompressorFilterConfig {

const DecompressorStats stats_;
const Runtime::FeatureFlag decompression_enabled_;
const bool ignore_no_transform_header_;
};

class RequestDirectionConfig : public DirectionConfig {
Expand Down Expand Up @@ -165,8 +167,11 @@ class DecompressorFilter : public Http::PassThroughFilter,
maybeInitDecompress(const DecompressorFilterConfig::DirectionConfig& direction_config,
Compression::Decompressor::DecompressorPtr& decompressor,
Http::StreamFilterCallbacks& callbacks, HeaderType& headers) {
if (direction_config.decompressionEnabled() && !hasCacheControlNoTransform(headers) &&
contentEncodingMatches(headers)) {
const bool should_decompress =
direction_config.decompressionEnabled() &&
(!hasCacheControlNoTransform(headers) || direction_config.ignoreNoTransformHeader()) &&
contentEncodingMatches(headers);
if (should_decompress) {
direction_config.stats().decompressed_.inc();
decompressor = config_->makeDecompressor();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,59 @@ TEST_P(DecompressorFilterTest, ResponseDecompressionDisabled) {
}
}

TEST_P(DecompressorFilterTest, DecompressionDisabledWhenNoTransformIsSet) {
EXPECT_CALL(*decompressor_factory_, createDecompressor(_)).Times(0);
Http::TestRequestHeaderMapImpl headers_before_filter{{"content-encoding", "mock, br , gzip "},
{"content-length", "256"},
{"cache-control", "no-transform"}};
std::unique_ptr<Http::RequestOrResponseHeaderMap> headers_after_filter =
doHeaders(headers_before_filter, false /* end_stream */);

if (isRequestDirection()) {
ASSERT_EQ(headers_after_filter->get(Http::LowerCaseString("accept-encoding"))[0]
->value()
.getStringView(),
"mock");
// The request direction adds Accept-Encoding by default. Other than this header, the rest of
// the headers should be the same before and after the filter.
headers_after_filter->remove(Http::LowerCaseString("accept-encoding"));
}
EXPECT_THAT(headers_after_filter, HeaderMapEqualIgnoreOrder(&headers_before_filter));

expectNoDecompression();
}

TEST_P(DecompressorFilterTest,
DecompressionEnabledWhenNoTransformAndIgnoreNoTransformHeaderAreSet) {
setUpFilter(R"EOF(
decompressor_library:
typed_config:
"@type": "type.googleapis.com/envoy.extensions.compression.gzip.decompressor.v3.Gzip"
response_direction_config:
common_config:
ignore_no_transform_header: true
)EOF");

Http::TestRequestHeaderMapImpl headers_before_filter{
{"content-encoding", "mock"}, {"content-length", "256"}, {"cache-control", "no-transform"}};
if (isRequestDirection()) {
EXPECT_CALL(*decompressor_factory_, createDecompressor(_)).Times(0);
std::unique_ptr<Http::RequestOrResponseHeaderMap> headers_after_filter =
doHeaders(headers_before_filter, false /* end_stream */);

// The request direction adds Accept-Encoding by default. Other than this header, the rest of
// the headers should be the same before and after the filter.
headers_after_filter->remove(Http::LowerCaseString("accept-encoding"));
EXPECT_THAT(headers_after_filter, HeaderMapEqualIgnoreOrder(&headers_before_filter));

expectNoDecompression();
} else {
decompressionActive(headers_before_filter, true /* end_with_data */,
absl::nullopt /* expected_content_encoding*/,
"mock" /* expected_accept_encoding */);
}
}

TEST_P(DecompressorFilterTest, NoDecompressionHeadersOnly) {
EXPECT_CALL(*decompressor_factory_, createDecompressor(_)).Times(0);
Http::TestRequestHeaderMapImpl headers_before_filter;
Expand Down