Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

disableCompression: Expose configuration to toggle Envoy GZIP compression on the responses #6546

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b4d5b67
build(deps): bump docker/setup-buildx-action from 3.3.0 to 3.4.0 (#6543)
dependabot[bot] Jul 8, 2024
081a47c
Adds "disableCompression" as a feature to turnon/off Envoy's GZIP res…
Jul 9, 2024
818ccf0
Merge remote-tracking branch 'upstream/main'
Aug 7, 2024
7ddb6f0
This fixes linter nag parameter 'conf' seems to be unused
Aug 22, 2024
8df07e2
This fixes linter nag File is not gci-ed with ...
Aug 22, 2024
100bf40
This fixes linter nag File is not gci-ed with ...
Aug 22, 2024
56e4ee7
This fixes linter nag File is not gofumpt-ed with -extra
Aug 22, 2024
548981e
This fixes linter nag File is not gofumpt-ed with -extra
Aug 22, 2024
934ca48
Fix table borders
Aug 21, 2024
2efb693
Add disableCompression flag to configuration docs.
Aug 21, 2024
b0cfe71
add changelog file
Aug 22, 2024
72e0e9c
Merge remote-tracking branch 'upstream/main'
geomacy Sep 7, 2024
73c4b67
Merge remote-tracking branch 'upstream/main'
Sep 30, 2024
235acc3
Merge remote-tracking branch 'upstream/main'
Oct 8, 2024
7e843b7
rework as --compression flag with options gzip, brotli, zstd, disabled
geomacy Sep 7, 2024
36dbadb
Merge pull request #1 from chaosbox/compression-flag
geomacy Oct 8, 2024
f7b261b
add validation to crd compression field
Oct 19, 2024
b68f0b1
delete unnecessary output log
Oct 19, 2024
5dcdc33
define compression with a struct for API extensibility
Oct 21, 2024
3d83394
lint fixes
Oct 23, 2024
cda9c76
test fix
Oct 24, 2024
ff84c37
fix flag handling
Oct 28, 2024
5725a53
api lint
Oct 28, 2024
630541e
parameters_test
Oct 28, 2024
4610b80
update comment
Oct 28, 2024
45ea946
fix assertion arg order and bump timeout
Oct 28, 2024
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
6 changes: 6 additions & 0 deletions apis/projectcontour/v1alpha1/contourconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ type EnvoyListenerConfig struct {
// +optional
UseProxyProto *bool `json:"useProxyProtocol,omitempty"`

// DisableCompression disables GZIP compression HTTP filter from the default Listener filters
// Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// DisableCompression disables GZIP compression HTTP filter from the default Listener filters
// Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
// DisableCompression disables GZIP compression HTTP filter from the default Listener filters.
// Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response.

// Contour's default is false.
// +optional
DisableCompression bool `json:"disableCompression,omitempty"`

// DisableAllowChunkedLength disables the RFC-compliant Envoy behavior to
// strip the "Content-Length" header if "Transfer-Encoding: chunked" is
// also set. This is an emergency off-switch to revert back to Envoy's
Expand Down
1 change: 1 addition & 0 deletions changelogs/unreleased/6546-chaosbox-small.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add disableCompression boolean flag to disable GZIP compression HTTP filter from the default Listener filters.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog entry could mention where the flag can be found

Suggested change
Add disableCompression boolean flag to disable GZIP compression HTTP filter from the default Listener filters.
Add disableCompression boolean flag in the ContourConfiguration CRD and in configuration file to disable GZIP compression HTTP filter, forcing Envoy to always return uncompressed response.

1 change: 1 addition & 0 deletions cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@
}

listenerConfig := xdscache_v3.ListenerConfig{
DisableCompression: contourConfiguration.Envoy.Listener.DisableCompression,

Check warning on line 450 in cmd/contour/serve.go

View check run for this annotation

Codecov / codecov/patch

cmd/contour/serve.go#L450

Added line #L450 was not covered by tests
UseProxyProto: *contourConfiguration.Envoy.Listener.UseProxyProto,
HTTPAccessLog: contourConfiguration.Envoy.HTTPListener.AccessLog,
HTTPSAccessLog: contourConfiguration.Envoy.HTTPSListener.AccessLog,
Expand Down
1 change: 1 addition & 0 deletions cmd/contour/servecontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co
Envoy: &contour_v1alpha1.EnvoyConfig{
Listener: &contour_v1alpha1.EnvoyListenerConfig{
UseProxyProto: &ctx.useProxyProto,
DisableCompression: ctx.Config.DisableCompression,
DisableAllowChunkedLength: &ctx.Config.DisableAllowChunkedLength,
DisableMergeSlashes: &ctx.Config.DisableMergeSlashes,
ServerHeaderTransformation: serverHeaderTransformation,
Expand Down
2 changes: 2 additions & 0 deletions cmd/contour/servecontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -892,12 +892,14 @@ func TestConvertServeContext(t *testing.T) {
ctx.Config.Listener.MaxRequestsPerIOCycle = ptr.To(uint32(10))
ctx.Config.Listener.HTTP2MaxConcurrentStreams = ptr.To(uint32(30))
ctx.Config.Listener.MaxConnectionsPerListener = ptr.To(uint32(50))
ctx.Config.DisableCompression = true
return ctx
},
getContourConfiguration: func(cfg contour_v1alpha1.ContourConfigurationSpec) contour_v1alpha1.ContourConfigurationSpec {
cfg.Envoy.Listener.MaxRequestsPerIOCycle = ptr.To(uint32(10))
cfg.Envoy.Listener.HTTP2MaxConcurrentStreams = ptr.To(uint32(30))
cfg.Envoy.Listener.MaxConnectionsPerListener = ptr.To(uint32(50))
cfg.Envoy.Listener.DisableCompression = true
return cfg
},
},
Expand Down
12 changes: 12 additions & 0 deletions examples/contour/01-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down Expand Up @@ -4081,6 +4087,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down
12 changes: 12 additions & 0 deletions examples/render/contour-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down Expand Up @@ -4301,6 +4307,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down
12 changes: 12 additions & 0 deletions examples/render/contour-gateway-provisioner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down Expand Up @@ -4092,6 +4098,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down
12 changes: 12 additions & 0 deletions examples/render/contour-gateway.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down Expand Up @@ -4117,6 +4123,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down
12 changes: 12 additions & 0 deletions examples/render/contour.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down Expand Up @@ -4301,6 +4307,12 @@ spec:
See: https://github.com/projectcontour/contour/issues/3221
Contour's default is false.
type: boolean
disableCompression:
description: |-
DisableCompression disables GZIP compression HTTP filter from the default Listener filters
Setting this true will enable Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
Contour's default is false.
type: boolean
disableMergeSlashes:
description: |-
DisableMergeSlashes disables Envoy's non-standard merge_slashes path transformation option
Expand Down
62 changes: 36 additions & 26 deletions internal/envoy/v3/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ type httpConnectionManagerBuilder struct {
maxRequestsPerConnection *uint32
http2MaxConcurrentStreams *uint32
enableWebsockets bool
disableCompression bool
}

func (b *httpConnectionManagerBuilder) EnableWebsockets(enable bool) *httpConnectionManagerBuilder {
Expand Down Expand Up @@ -271,6 +272,11 @@ func (b *httpConnectionManagerBuilder) MergeSlashes(enabled bool) *httpConnectio
return b
}

func (b *httpConnectionManagerBuilder) DisableCompression(disabled bool) *httpConnectionManagerBuilder {
b.disableCompression = disabled
return b
}

func (b *httpConnectionManagerBuilder) ServerHeaderTransformation(value contour_v1alpha1.ServerHeaderTransformationType) *httpConnectionManagerBuilder {
switch value {
case contour_v1alpha1.OverwriteServerHeader:
Expand Down Expand Up @@ -308,34 +314,38 @@ func (b *httpConnectionManagerBuilder) DefaultFilters() *httpConnectionManagerBu
// Add a default set of ordered http filters.
// The names are not required to match anything and are
// identified by the TypeURL of each filter.
b.filters = append(b.filters,
&envoy_filter_network_http_connection_manager_v3.HttpFilter{
Name: CompressorFilterName,
ConfigType: &envoy_filter_network_http_connection_manager_v3.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&envoy_filter_http_compressor_v3.Compressor{
CompressorLibrary: &envoy_config_core_v3.TypedExtensionConfig{
Name: "gzip",
TypedConfig: protobuf.MustMarshalAny(
&envoy_compression_gzip_compressor_v3.Gzip{},
),
},
ResponseDirectionConfig: &envoy_filter_http_compressor_v3.Compressor_ResponseDirectionConfig{
CommonConfig: &envoy_filter_http_compressor_v3.Compressor_CommonDirectionConfig{
ContentType: []string{
// Default content-types https://github.com/envoyproxy/envoy/blob/e74999dbdb12aa4d6b7a5d62d51731ea86bf72be/source/extensions/filters/http/compressor/compressor_filter.cc#L35-L38
"text/html", "text/plain", "text/css", "application/javascript", "application/x-javascript",
"text/javascript", "text/x-javascript", "text/ecmascript", "text/js", "text/jscript",
"text/x-js", "application/ecmascript", "application/x-json", "application/xml",
"application/json", "image/svg+xml", "text/xml", "application/xhtml+xml",
// Additional content-types for grpc-web https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2
"application/grpc-web", "application/grpc-web+proto", "application/grpc-web+json", "application/grpc-web+thrift",
"application/grpc-web-text", "application/grpc-web-text+proto", "application/grpc-web-text+thrift",
if !b.disableCompression {
// If compression is enabled add compressor filter
b.filters = append(b.filters,
&envoy_filter_network_http_connection_manager_v3.HttpFilter{
Name: CompressorFilterName,
ConfigType: &envoy_filter_network_http_connection_manager_v3.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&envoy_filter_http_compressor_v3.Compressor{
CompressorLibrary: &envoy_config_core_v3.TypedExtensionConfig{
Name: "gzip",
TypedConfig: protobuf.MustMarshalAny(
&envoy_compression_gzip_compressor_v3.Gzip{},
),
},
ResponseDirectionConfig: &envoy_filter_http_compressor_v3.Compressor_ResponseDirectionConfig{
CommonConfig: &envoy_filter_http_compressor_v3.Compressor_CommonDirectionConfig{
ContentType: []string{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible alternative approach could be to leave the compressor filter in the chain but disable it by setting default to false in ResponseDirectionConfig.CommonConfig.Enabled

Enabled: &envoy_config_core_v3.RuntimeFeatureFlag{
	// If compression is enabled add compressor filter
	DefaultValue: wrapperspb.Bool(!b.disableCompression),
	RuntimeKey:   "contour.compression.response.enabled",
},

but since we do not have "per route" setting, which would override the default, it make sense to me that the whole filter is excluded from the chain when disabled, like suggested in this PR currently.

Copy link

@geomacy geomacy Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, given

since we do not have "per route" setting

the thought in this PR is to work at the level of the whole filter. The functionality could be extended as described above if needed in future but that would best be left to another PR.

// Default content-types https://github.com/envoyproxy/envoy/blob/e74999dbdb12aa4d6b7a5d62d51731ea86bf72be/source/extensions/filters/http/compressor/compressor_filter.cc#L35-L38
"text/html", "text/plain", "text/css", "application/javascript", "application/x-javascript",
"text/javascript", "text/x-javascript", "text/ecmascript", "text/js", "text/jscript",
"text/x-js", "application/ecmascript", "application/x-json", "application/xml",
"application/json", "image/svg+xml", "text/xml", "application/xhtml+xml",
// Additional content-types for grpc-web https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2
"application/grpc-web", "application/grpc-web+proto", "application/grpc-web+json", "application/grpc-web+thrift",
"application/grpc-web-text", "application/grpc-web-text+proto", "application/grpc-web-text+thrift",
},
},
},
},
}),
},
},
}),
},
})
}
b.filters = append(b.filters,
&envoy_filter_network_http_connection_manager_v3.HttpFilter{
Name: GRPCWebFilterName,
ConfigType: &envoy_filter_network_http_connection_manager_v3.HttpFilter_TypedConfig{
Expand Down
119 changes: 119 additions & 0 deletions internal/featuretests/v3/compression_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright Project Contour Authors
// 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.

package v3

import (
"testing"

envoy_service_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
core_v1 "k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

contour_v1 "github.com/projectcontour/contour/apis/projectcontour/v1"
contour_v1alpha1 "github.com/projectcontour/contour/apis/projectcontour/v1alpha1"
envoy_v3 "github.com/projectcontour/contour/internal/envoy/v3"
"github.com/projectcontour/contour/internal/fixture"
xdscache_v3 "github.com/projectcontour/contour/internal/xdscache/v3"
)

func TestDefaultCompression(t *testing.T) {
rh, c, done := setup(t)
defer done()

s1 := fixture.NewService("backend").
WithPorts(core_v1.ServicePort{Name: "http", Port: 80})
rh.OnAdd(s1)

hp1 := &contour_v1.HTTPProxy{
ObjectMeta: meta_v1.ObjectMeta{
Name: "simple",
Namespace: s1.Namespace,
},
Spec: contour_v1.HTTPProxySpec{
VirtualHost: &contour_v1.VirtualHost{
Fqdn: "example.com",
},
Routes: []contour_v1.Route{{
Conditions: matchconditions(prefixMatchCondition("/")),
Services: []contour_v1.Service{{
Name: s1.Name,
Port: 80,
}},
}},
},
}
rh.OnAdd(hp1)

httpListener := defaultHTTPListener()
httpListener.FilterChains = envoy_v3.FilterChains(envoy_v3.HTTPConnectionManagerBuilder().
RouteConfigName(xdscache_v3.ENVOY_HTTP_LISTENER).
MetricsPrefix(xdscache_v3.ENVOY_HTTP_LISTENER).
AccessLoggers(envoy_v3.FileAccessLogEnvoy(xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
DefaultFilters().
Get(),
)

c.Request(listenerType, xdscache_v3.ENVOY_HTTP_LISTENER).Equals(&envoy_service_discovery_v3.DiscoveryResponse{
TypeUrl: listenerType,
Resources: resources(t, httpListener),
})
}

func TestDisableCompression(t *testing.T) {
withDisableCompression := func(conf *xdscache_v3.ListenerConfig) {
conf.DisableCompression = true
}

rh, c, done := setup(t, withDisableCompression)
defer done()

s1 := fixture.NewService("backend").
WithPorts(core_v1.ServicePort{Name: "http", Port: 80})
rh.OnAdd(s1)

hp1 := &contour_v1.HTTPProxy{
ObjectMeta: meta_v1.ObjectMeta{
Name: "simple",
Namespace: s1.Namespace,
},
Spec: contour_v1.HTTPProxySpec{
VirtualHost: &contour_v1.VirtualHost{
Fqdn: "example.com",
},
Routes: []contour_v1.Route{{
Conditions: matchconditions(prefixMatchCondition("/")),
Services: []contour_v1.Service{{
Name: s1.Name,
Port: 80,
}},
}},
},
}
rh.OnAdd(hp1)

httpListener := defaultHTTPListener()
httpListener.FilterChains = envoy_v3.FilterChains(envoy_v3.HTTPConnectionManagerBuilder().
DisableCompression(true).
RouteConfigName(xdscache_v3.ENVOY_HTTP_LISTENER).
MetricsPrefix(xdscache_v3.ENVOY_HTTP_LISTENER).
AccessLoggers(envoy_v3.FileAccessLogEnvoy(xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
DefaultFilters().
Get(),
)

c.Request(listenerType, xdscache_v3.ENVOY_HTTP_LISTENER).Equals(&envoy_service_discovery_v3.DiscoveryResponse{
TypeUrl: listenerType,
Resources: resources(t, httpListener),
})
}
Loading
Loading