diff --git a/apis/projectcontour/v1alpha1/compression.go b/apis/projectcontour/v1alpha1/compression.go
new file mode 100644
index 00000000000..f3f00b3f7c3
--- /dev/null
+++ b/apis/projectcontour/v1alpha1/compression.go
@@ -0,0 +1,53 @@
+// 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 v1alpha1
+
+import "fmt"
+
+// CompressionAlgorithm defines the type of compression algorithm applied in default HTTP listener filter chain.
+// Allowable values are defined as names of well known compression algorithms (plus "disabled").
+type CompressionAlgorithm string
+
+// EnvoyCompression defines configuration related to compression in the default HTTP Listener filter chain.
+type EnvoyCompression struct {
+ // Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ // Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ // Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ // +kubebuilder:validation:Enum="gzip";"brotli";"zstd";"disabled"
+ // +optional
+ Algorithm CompressionAlgorithm `json:"algorithm,omitempty"`
+}
+
+func (a CompressionAlgorithm) Validate() error {
+ switch a {
+ case BrotliCompression, DisabledCompression, GzipCompression, ZstdCompression, "":
+ return nil
+ default:
+ return fmt.Errorf("invalid compression type: %q", a)
+ }
+}
+
+const (
+ // BrotliCompression specifies brotli as the default HTTP filter chain compression mechanism
+ BrotliCompression CompressionAlgorithm = "brotli"
+
+ // DisabledCompression specifies that there will be no compression in the default HTTP filter chain
+ DisabledCompression CompressionAlgorithm = "disabled"
+
+ // GzipCompression specifies gzip as the default HTTP filter chain compression mechanism
+ GzipCompression CompressionAlgorithm = "gzip"
+
+ // ZstdCompression specifies zstd as the default HTTP filter chain compression mechanism
+ ZstdCompression CompressionAlgorithm = "zstd"
+)
diff --git a/apis/projectcontour/v1alpha1/compression_test.go b/apis/projectcontour/v1alpha1/compression_test.go
new file mode 100644
index 00000000000..a2df6266790
--- /dev/null
+++ b/apis/projectcontour/v1alpha1/compression_test.go
@@ -0,0 +1,32 @@
+// 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 v1alpha1_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ contour_v1alpha1 "github.com/projectcontour/contour/apis/projectcontour/v1alpha1"
+)
+
+func TestValidateEnvoyCompressionAlgorithmType(t *testing.T) {
+ require.Error(t, contour_v1alpha1.CompressionAlgorithm("foo").Validate())
+
+ require.NoError(t, contour_v1alpha1.CompressionAlgorithm("").Validate())
+ require.NoError(t, contour_v1alpha1.BrotliCompression.Validate())
+ require.NoError(t, contour_v1alpha1.DisabledCompression.Validate())
+ require.NoError(t, contour_v1alpha1.GzipCompression.Validate())
+ require.NoError(t, contour_v1alpha1.ZstdCompression.Validate())
+}
diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go
index 0af7a50e08a..fcc6b38638c 100644
--- a/apis/projectcontour/v1alpha1/contourconfig.go
+++ b/apis/projectcontour/v1alpha1/contourconfig.go
@@ -348,6 +348,10 @@ type EnvoyListenerConfig struct {
// +optional
UseProxyProto *bool `json:"useProxyProtocol,omitempty"`
+ // Compression defines configuration related to compression in the default HTTP Listener filters.
+ // +optional
+ Compression *EnvoyCompression `json:"compression,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
diff --git a/changelogs/unreleased/6546-chaosbox-small.md b/changelogs/unreleased/6546-chaosbox-small.md
new file mode 100644
index 00000000000..9cde1d1c957
--- /dev/null
+++ b/changelogs/unreleased/6546-chaosbox-small.md
@@ -0,0 +1 @@
+The HTTP compression algorithm can now be configured using the `compression.algorithm` field in the configuration file or the `spec.envoy.listener.compression.algorithm` field in the `ContourConfiguration` CRD. The available values are `gzip` (default), `brotli`, `zstd`, and `disabled`.
diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go
index 1c48f9fce88..e24d7a403a3 100644
--- a/cmd/contour/serve.go
+++ b/cmd/contour/serve.go
@@ -123,6 +123,7 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext)
return nil
}
+
serve.Flag("accesslog-format", "Format for Envoy access logs.").PlaceHolder("").StringVar((*string)(&ctx.Config.AccessLogFormat))
serve.Flag("config-path", "Path to base configuration.").Short('c').PlaceHolder("/path/to/file").Action(parseConfig).ExistingFileVar(&configFile)
@@ -447,6 +448,7 @@ func (s *Server) doServe() error {
}
listenerConfig := xdscache_v3.ListenerConfig{
+ Compression: contourConfiguration.Envoy.Listener.Compression,
UseProxyProto: *contourConfiguration.Envoy.Listener.UseProxyProto,
HTTPAccessLog: contourConfiguration.Envoy.HTTPListener.AccessLog,
HTTPSAccessLog: contourConfiguration.Envoy.HTTPSListener.AccessLog,
diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go
index 3a1057b6479..6fd2a3643e0 100644
--- a/cmd/contour/servecontext.go
+++ b/cmd/contour/servecontext.go
@@ -333,6 +333,24 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co
accessLogLevel = contour_v1alpha1.LogLevelDisabled
}
+ var compression *contour_v1alpha1.EnvoyCompression
+ if ctx.Config.Compression.Algorithm != "" {
+ var algorithm contour_v1alpha1.CompressionAlgorithm
+ switch ctx.Config.Compression.Algorithm {
+ case config.CompressionBrotli:
+ algorithm = contour_v1alpha1.BrotliCompression
+ case config.CompressionDisabled:
+ algorithm = contour_v1alpha1.DisabledCompression
+ case config.CompressionGzip:
+ algorithm = contour_v1alpha1.GzipCompression
+ case config.CompressionZstd:
+ algorithm = contour_v1alpha1.ZstdCompression
+ }
+ compression = &contour_v1alpha1.EnvoyCompression{
+ Algorithm: algorithm,
+ }
+ }
+
var defaultHTTPVersions []contour_v1alpha1.HTTPVersionType
for _, version := range ctx.Config.DefaultHTTPVersions {
switch version {
@@ -519,6 +537,7 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co
Envoy: &contour_v1alpha1.EnvoyConfig{
Listener: &contour_v1alpha1.EnvoyListenerConfig{
UseProxyProto: &ctx.useProxyProto,
+ Compression: compression,
DisableAllowChunkedLength: &ctx.Config.DisableAllowChunkedLength,
DisableMergeSlashes: &ctx.Config.DisableMergeSlashes,
ServerHeaderTransformation: serverHeaderTransformation,
diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go
index cf205d81a46..ce7665ef4d2 100644
--- a/cmd/contour/servecontext_test.go
+++ b/cmd/contour/servecontext_test.go
@@ -363,150 +363,150 @@ func TestParseHTTPVersions(t *testing.T) {
}
}
-func TestConvertServeContext(t *testing.T) {
- defaultContext := func() *serveContext {
- ctx := newServeContext()
- ctx.ServerConfig = ServerConfig{
- xdsAddr: "127.0.0.1",
- xdsPort: 8001,
- caFile: "/certs/ca.crt",
- contourCert: "/certs/cert.crt",
- contourKey: "/certs/cert.key",
- }
- return ctx
+func defaultContext() *serveContext {
+ ctx := newServeContext()
+ ctx.ServerConfig = ServerConfig{
+ xdsAddr: "127.0.0.1",
+ xdsPort: 8001,
+ caFile: "/certs/ca.crt",
+ contourCert: "/certs/cert.crt",
+ contourKey: "/certs/cert.key",
}
+ return ctx
+}
- defaultContourConfiguration := func() contour_v1alpha1.ContourConfigurationSpec {
- return contour_v1alpha1.ContourConfigurationSpec{
- XDSServer: &contour_v1alpha1.XDSServerConfig{
- Type: contour_v1alpha1.EnvoyServerType,
- Address: "127.0.0.1",
- Port: 8001,
- TLS: &contour_v1alpha1.TLS{
- CAFile: "/certs/ca.crt",
- CertFile: "/certs/cert.crt",
- KeyFile: "/certs/cert.key",
- Insecure: ptr.To(false),
- },
- },
- Ingress: &contour_v1alpha1.IngressConfig{
- ClassNames: nil,
- StatusAddress: "",
- },
- Debug: &contour_v1alpha1.DebugConfig{
- Address: "127.0.0.1",
- Port: 6060,
+func defaultContourConfiguration() contour_v1alpha1.ContourConfigurationSpec {
+ return contour_v1alpha1.ContourConfigurationSpec{
+ XDSServer: &contour_v1alpha1.XDSServerConfig{
+ Type: contour_v1alpha1.EnvoyServerType,
+ Address: "127.0.0.1",
+ Port: 8001,
+ TLS: &contour_v1alpha1.TLS{
+ CAFile: "/certs/ca.crt",
+ CertFile: "/certs/cert.crt",
+ KeyFile: "/certs/cert.key",
+ Insecure: ptr.To(false),
},
- Health: &contour_v1alpha1.HealthConfig{
- Address: "0.0.0.0",
- Port: 8000,
+ },
+ Ingress: &contour_v1alpha1.IngressConfig{
+ ClassNames: nil,
+ StatusAddress: "",
+ },
+ Debug: &contour_v1alpha1.DebugConfig{
+ Address: "127.0.0.1",
+ Port: 6060,
+ },
+ Health: &contour_v1alpha1.HealthConfig{
+ Address: "0.0.0.0",
+ Port: 8000,
+ },
+ Envoy: &contour_v1alpha1.EnvoyConfig{
+ Service: &contour_v1alpha1.NamespacedName{
+ Name: "envoy",
+ Namespace: "projectcontour",
},
- Envoy: &contour_v1alpha1.EnvoyConfig{
- Service: &contour_v1alpha1.NamespacedName{
- Name: "envoy",
- Namespace: "projectcontour",
- },
- Listener: &contour_v1alpha1.EnvoyListenerConfig{
- UseProxyProto: ptr.To(false),
- DisableAllowChunkedLength: ptr.To(false),
- DisableMergeSlashes: ptr.To(false),
- ServerHeaderTransformation: contour_v1alpha1.OverwriteServerHeader,
- TLS: &contour_v1alpha1.EnvoyTLS{
- MinimumProtocolVersion: "",
- MaximumProtocolVersion: "",
- },
- SocketOptions: &contour_v1alpha1.SocketOptions{
- TOS: 0,
- TrafficClass: 0,
- },
- },
- HTTPListener: &contour_v1alpha1.EnvoyListener{
- Address: "0.0.0.0",
- Port: 8080,
- AccessLog: "/dev/stdout",
- },
- HTTPSListener: &contour_v1alpha1.EnvoyListener{
- Address: "0.0.0.0",
- Port: 8443,
- AccessLog: "/dev/stdout",
- },
- Health: &contour_v1alpha1.HealthConfig{
- Address: "0.0.0.0",
- Port: 8002,
- },
- Metrics: &contour_v1alpha1.MetricsConfig{
- Address: "0.0.0.0",
- Port: 8002,
- },
- ClientCertificate: nil,
- Logging: &contour_v1alpha1.EnvoyLogging{
- AccessLogFormat: contour_v1alpha1.EnvoyAccessLog,
- AccessLogFormatString: "",
- AccessLogLevel: contour_v1alpha1.LogLevelInfo,
- AccessLogJSONFields: contour_v1alpha1.AccessLogJSONFields([]string{
- "@timestamp",
- "authority",
- "bytes_received",
- "bytes_sent",
- "downstream_local_address",
- "downstream_remote_address",
- "duration",
- "method",
- "path",
- "protocol",
- "request_id",
- "requested_server_name",
- "response_code",
- "response_flags",
- "uber_trace_id",
- "upstream_cluster",
- "upstream_host",
- "upstream_local_address",
- "upstream_service_time",
- "user_agent",
- "x_forwarded_for",
- "grpc_status",
- "grpc_status_number",
- }),
- },
- DefaultHTTPVersions: nil,
- Timeouts: &contour_v1alpha1.TimeoutParameters{
- ConnectionIdleTimeout: ptr.To("60s"),
- ConnectTimeout: ptr.To("2s"),
- },
- Cluster: &contour_v1alpha1.ClusterParameters{
- DNSLookupFamily: contour_v1alpha1.AutoClusterDNSFamily,
- GlobalCircuitBreakerDefaults: nil,
- UpstreamTLS: &contour_v1alpha1.EnvoyTLS{
- MinimumProtocolVersion: "",
- MaximumProtocolVersion: "",
- },
+ Listener: &contour_v1alpha1.EnvoyListenerConfig{
+ UseProxyProto: ptr.To(false),
+ DisableAllowChunkedLength: ptr.To(false),
+ DisableMergeSlashes: ptr.To(false),
+ ServerHeaderTransformation: contour_v1alpha1.OverwriteServerHeader,
+ TLS: &contour_v1alpha1.EnvoyTLS{
+ MinimumProtocolVersion: "",
+ MaximumProtocolVersion: "",
},
- Network: &contour_v1alpha1.NetworkParameters{
- EnvoyAdminPort: ptr.To(9001),
- XffNumTrustedHops: ptr.To(uint32(0)),
+ SocketOptions: &contour_v1alpha1.SocketOptions{
+ TOS: 0,
+ TrafficClass: 0,
},
},
- Gateway: nil,
- HTTPProxy: &contour_v1alpha1.HTTPProxyConfig{
- DisablePermitInsecure: ptr.To(false),
- FallbackCertificate: nil,
+ HTTPListener: &contour_v1alpha1.EnvoyListener{
+ Address: "0.0.0.0",
+ Port: 8080,
+ AccessLog: "/dev/stdout",
+ },
+ HTTPSListener: &contour_v1alpha1.EnvoyListener{
+ Address: "0.0.0.0",
+ Port: 8443,
+ AccessLog: "/dev/stdout",
},
- EnableExternalNameService: ptr.To(false),
- RateLimitService: nil,
- GlobalExternalAuthorization: nil,
- Policy: &contour_v1alpha1.PolicyConfig{
- RequestHeadersPolicy: &contour_v1alpha1.HeadersPolicy{},
- ResponseHeadersPolicy: &contour_v1alpha1.HeadersPolicy{},
- ApplyToIngress: ptr.To(false),
+ Health: &contour_v1alpha1.HealthConfig{
+ Address: "0.0.0.0",
+ Port: 8002,
},
Metrics: &contour_v1alpha1.MetricsConfig{
Address: "0.0.0.0",
- Port: 8000,
+ Port: 8002,
+ },
+ ClientCertificate: nil,
+ Logging: &contour_v1alpha1.EnvoyLogging{
+ AccessLogFormat: contour_v1alpha1.EnvoyAccessLog,
+ AccessLogFormatString: "",
+ AccessLogLevel: contour_v1alpha1.LogLevelInfo,
+ AccessLogJSONFields: contour_v1alpha1.AccessLogJSONFields([]string{
+ "@timestamp",
+ "authority",
+ "bytes_received",
+ "bytes_sent",
+ "downstream_local_address",
+ "downstream_remote_address",
+ "duration",
+ "method",
+ "path",
+ "protocol",
+ "request_id",
+ "requested_server_name",
+ "response_code",
+ "response_flags",
+ "uber_trace_id",
+ "upstream_cluster",
+ "upstream_host",
+ "upstream_local_address",
+ "upstream_service_time",
+ "user_agent",
+ "x_forwarded_for",
+ "grpc_status",
+ "grpc_status_number",
+ }),
+ },
+ DefaultHTTPVersions: nil,
+ Timeouts: &contour_v1alpha1.TimeoutParameters{
+ ConnectionIdleTimeout: ptr.To("60s"),
+ ConnectTimeout: ptr.To("2s"),
+ },
+ Cluster: &contour_v1alpha1.ClusterParameters{
+ DNSLookupFamily: contour_v1alpha1.AutoClusterDNSFamily,
+ GlobalCircuitBreakerDefaults: nil,
+ UpstreamTLS: &contour_v1alpha1.EnvoyTLS{
+ MinimumProtocolVersion: "",
+ MaximumProtocolVersion: "",
+ },
},
- }
+ Network: &contour_v1alpha1.NetworkParameters{
+ EnvoyAdminPort: ptr.To(9001),
+ XffNumTrustedHops: ptr.To(uint32(0)),
+ },
+ },
+ Gateway: nil,
+ HTTPProxy: &contour_v1alpha1.HTTPProxyConfig{
+ DisablePermitInsecure: ptr.To(false),
+ FallbackCertificate: nil,
+ },
+ EnableExternalNameService: ptr.To(false),
+ RateLimitService: nil,
+ GlobalExternalAuthorization: nil,
+ Policy: &contour_v1alpha1.PolicyConfig{
+ RequestHeadersPolicy: &contour_v1alpha1.HeadersPolicy{},
+ ResponseHeadersPolicy: &contour_v1alpha1.HeadersPolicy{},
+ ApplyToIngress: ptr.To(false),
+ },
+ Metrics: &contour_v1alpha1.MetricsConfig{
+ Address: "0.0.0.0",
+ Port: 8000,
+ },
}
+}
+func TestConvertServeContext(t *testing.T) {
cases := map[string]struct {
getServeContext func(ctx *serveContext) *serveContext
getContourConfiguration func(cfg contour_v1alpha1.ContourConfigurationSpec) contour_v1alpha1.ContourConfigurationSpec
@@ -912,3 +912,29 @@ func TestConvertServeContext(t *testing.T) {
})
}
}
+
+func TestServeContextCompressionOptions(t *testing.T) {
+ cases := map[string]struct {
+ serveCompression config.CompressionAlgorithm
+ configCompression contour_v1alpha1.CompressionAlgorithm
+ }{
+ "Brotli": {config.CompressionBrotli, contour_v1alpha1.BrotliCompression},
+ "Disabled": {config.CompressionDisabled, contour_v1alpha1.DisabledCompression},
+ "Gzip": {config.CompressionGzip, contour_v1alpha1.GzipCompression},
+ "Zstd": {config.CompressionZstd, contour_v1alpha1.ZstdCompression},
+ }
+
+ for name, tc := range cases {
+ t.Run(name, func(t *testing.T) {
+ testServeContext := defaultContext()
+ testServeContext.Config.Compression.Algorithm = tc.serveCompression
+
+ want := defaultContourConfiguration()
+ want.Envoy.Listener.Compression = &contour_v1alpha1.EnvoyCompression{
+ Algorithm: tc.configCompression,
+ }
+
+ assert.Equal(t, want, testServeContext.convertToContourConfigurationSpec())
+ })
+ }
+}
diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml
index 0beece5bc51..48601e397a0 100644
--- a/examples/contour/01-crds.yaml
+++ b/examples/contour/01-crds.yaml
@@ -281,6 +281,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related to
+ compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
@@ -4063,6 +4079,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related
+ to compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml
index 1e085adae27..51aeaa1a389 100644
--- a/examples/render/contour-deployment.yaml
+++ b/examples/render/contour-deployment.yaml
@@ -501,6 +501,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related to
+ compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
@@ -4283,6 +4299,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related
+ to compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml
index b3633a2e0cd..7ea0dc3c500 100644
--- a/examples/render/contour-gateway-provisioner.yaml
+++ b/examples/render/contour-gateway-provisioner.yaml
@@ -292,6 +292,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related to
+ compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
@@ -4074,6 +4090,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related
+ to compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml
index 0db31e989d5..e7981bf7f37 100644
--- a/examples/render/contour-gateway.yaml
+++ b/examples/render/contour-gateway.yaml
@@ -317,6 +317,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related to
+ compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
@@ -4099,6 +4115,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related
+ to compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml
index d78bac68c7b..c1b7b931224 100644
--- a/examples/render/contour.yaml
+++ b/examples/render/contour.yaml
@@ -501,6 +501,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related to
+ compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
@@ -4283,6 +4299,22 @@ spec:
description: Listener hold various configurable Envoy listener
values.
properties:
+ compression:
+ description: Compression defines configuration related
+ to compression in the default HTTP Listener filters.
+ properties:
+ algorithm:
+ description: |-
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+ Values: `gzip` (default), `brotli`, `zstd`, `disabled`.
+ Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response
+ enum:
+ - gzip
+ - brotli
+ - zstd
+ - disabled
+ type: string
+ type: object
connectionBalancer:
description: |-
ConnectionBalancer. If the value is exact, the listener will use the exact connection balancer
diff --git a/internal/contourconfig/contourconfiguration_test.go b/internal/contourconfig/contourconfiguration_test.go
index bb13b8229da..294e6a4609b 100644
--- a/internal/contourconfig/contourconfiguration_test.go
+++ b/internal/contourconfig/contourconfiguration_test.go
@@ -54,11 +54,14 @@ func TestOverlayOnDefaults(t *testing.T) {
},
Envoy: &contour_v1alpha1.EnvoyConfig{
Listener: &contour_v1alpha1.EnvoyListenerConfig{
- UseProxyProto: ptr.To(true),
- DisableAllowChunkedLength: ptr.To(true),
- DisableMergeSlashes: ptr.To(true),
+ UseProxyProto: ptr.To(true),
+ Compression: &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.BrotliCompression,
+ },
MaxRequestsPerConnection: ptr.To(uint32(1)),
HTTP2MaxConcurrentStreams: ptr.To(uint32(10)),
+ DisableAllowChunkedLength: ptr.To(true),
+ DisableMergeSlashes: ptr.To(true),
ServerHeaderTransformation: contour_v1alpha1.PassThroughServerHeader,
ConnectionBalancer: "yesplease",
TLS: &contour_v1alpha1.EnvoyTLS{
diff --git a/internal/envoy/v3/listener.go b/internal/envoy/v3/listener.go
index 5d1f2c233ce..da152e4f225 100644
--- a/internal/envoy/v3/listener.go
+++ b/internal/envoy/v3/listener.go
@@ -23,7 +23,9 @@ import (
envoy_config_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3"
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+ envoy_compression_brotli_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/brotli/compressor/v3"
envoy_compression_gzip_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
+ envoy_compression_zstd_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/zstd/compressor/v3"
envoy_filter_http_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
envoy_filter_http_cors_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3"
envoy_filter_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
@@ -41,6 +43,7 @@ import (
envoy_transport_socket_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
"github.com/envoyproxy/go-control-plane/pkg/wellknown"
+ "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb"
@@ -188,6 +191,7 @@ type httpConnectionManagerBuilder struct {
maxRequestsPerConnection *uint32
http2MaxConcurrentStreams *uint32
enableWebsockets bool
+ compression *contour_v1alpha1.EnvoyCompression
}
func (b *httpConnectionManagerBuilder) EnableWebsockets(enable bool) *httpConnectionManagerBuilder {
@@ -271,6 +275,13 @@ func (b *httpConnectionManagerBuilder) MergeSlashes(enabled bool) *httpConnectio
return b
}
+// SetDefaultFilterCompression configures the builder to set the compression method applied by DefaultFilters() to the
+// given value `compressor`. When chaining builder method calls, this method should be called before DefaultFilters().
+func (b *httpConnectionManagerBuilder) SetDefaultFilterCompression(compressor *contour_v1alpha1.EnvoyCompression) *httpConnectionManagerBuilder {
+ b.compression = compressor
+ return b
+}
+
func (b *httpConnectionManagerBuilder) ServerHeaderTransformation(value contour_v1alpha1.ServerHeaderTransformationType) *httpConnectionManagerBuilder {
switch value {
case contour_v1alpha1.OverwriteServerHeader:
@@ -308,34 +319,56 @@ 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",
+ var compressor proto.Message = &envoy_compression_gzip_compressor_v3.Gzip{}
+ compressorName := string(contour_v1alpha1.GzipCompression)
+ if b.compression != nil {
+ switch b.compression.Algorithm {
+ case contour_v1alpha1.BrotliCompression:
+ compressorName = "brotli"
+ compressor = &envoy_compression_brotli_compressor_v3.Brotli{}
+ case contour_v1alpha1.DisabledCompression:
+ compressor = nil
+ case contour_v1alpha1.ZstdCompression:
+ compressorName = "zstd"
+ compressor = &envoy_compression_zstd_compressor_v3.Zstd{}
+ default:
+ compressorName = "gzip"
+ compressor = &envoy_compression_gzip_compressor_v3.Gzip{}
+ }
+ }
+
+ if compressor != nil {
+ // 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: compressorName,
+ TypedConfig: protobuf.MustMarshalAny(
+ compressor,
+ ),
+ },
+ 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",
+ },
},
},
- },
- }),
- },
- },
+ }),
+ },
+ })
+ }
+ 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{
diff --git a/internal/featuretests/v3/compression_test.go b/internal/featuretests/v3/compression_test.go
new file mode 100644
index 00000000000..9b1cf7cf53e
--- /dev/null
+++ b/internal/featuretests/v3/compression_test.go
@@ -0,0 +1,95 @@
+// 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 TestCompression(t *testing.T) {
+ tests := map[string]struct {
+ algorithm contour_v1alpha1.CompressionAlgorithm
+ want contour_v1alpha1.CompressionAlgorithm
+ }{
+ "default": {algorithm: "", want: contour_v1alpha1.GzipCompression},
+ "disabled": {algorithm: contour_v1alpha1.DisabledCompression, want: contour_v1alpha1.DisabledCompression},
+ "brotli": {algorithm: contour_v1alpha1.BrotliCompression, want: contour_v1alpha1.BrotliCompression},
+ "zstd": {algorithm: contour_v1alpha1.ZstdCompression, want: contour_v1alpha1.ZstdCompression},
+ "gzip": {algorithm: contour_v1alpha1.GzipCompression, want: contour_v1alpha1.GzipCompression},
+ }
+
+ s1 := fixture.NewService("backend").
+ WithPorts(core_v1.ServicePort{Name: "http", Port: 80})
+
+ 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,
+ }},
+ }},
+ },
+ }
+
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ rh, c, done := setup(t, func(conf *xdscache_v3.ListenerConfig) {
+ if tc.algorithm != "" {
+ conf.Compression = &contour_v1alpha1.EnvoyCompression{
+ Algorithm: tc.algorithm,
+ }
+ }
+ })
+ defer done()
+
+ rh.OnAdd(s1)
+ rh.OnAdd(hp1)
+ httpListener := defaultHTTPListener()
+ httpListener.FilterChains = envoy_v3.FilterChains(envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(&contour_v1alpha1.EnvoyCompression{
+ Algorithm: tc.want,
+ }).
+ 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),
+ })
+ })
+ }
+}
diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go
index 6176fdcaad0..9b19e3d5e92 100644
--- a/internal/xdscache/v3/listener.go
+++ b/internal/xdscache/v3/listener.go
@@ -66,6 +66,9 @@ type ListenerConfig struct {
// If not set, defaults to false.
UseProxyProto bool
+ // Compression defines configuration related to compression in the default HTTP Listener filters.
+ Compression *contour_v1alpha1.EnvoyCompression
+
// MinimumTLSVersion defines the minimum TLS protocol version the proxy should accept.
MinimumTLSVersion string
@@ -393,6 +396,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) {
// order for the HTTPS virtualhosts.
if len(listener.VirtualHosts) > 0 {
cm := envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(cfg.Compression).
Codec(envoy_v3.CodecForVersions(cfg.DefaultHTTPVersions...)).
DefaultFilters().
RouteConfigName(httpRouteConfigName(listener)).
@@ -465,6 +469,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) {
// Contour versions since the metrics prefix will be
// coded into monitoring dashboards.
cm := envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(cfg.Compression).
Codec(envoy_v3.CodecForVersions(cfg.DefaultHTTPVersions...)).
AddFilter(envoy_v3.FilterMisdirectedRequests(vh.VirtualHost.Name)).
DefaultFilters().
@@ -549,6 +554,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) {
}
cm := envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(cfg.Compression).
DefaultFilters().
AddFilter(authzFilter).
RouteConfigName(fallbackCertRouteConfigName(listener)).
diff --git a/internal/xdscache/v3/listener_test.go b/internal/xdscache/v3/listener_test.go
index 095f248210a..63c80d54cdb 100644
--- a/internal/xdscache/v3/listener_test.go
+++ b/internal/xdscache/v3/listener_test.go
@@ -3074,6 +3074,236 @@ func TestListenerVisit(t *testing.T) {
SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(),
}),
},
+ "httpproxy with disabled compression set in listener config": {
+ ListenerConfig: ListenerConfig{
+ Compression: &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.DisabledCompression,
+ },
+ },
+ objs: []any{
+ &contour_v1.HTTPProxy{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "simple",
+ Namespace: "default",
+ },
+ Spec: contour_v1.HTTPProxySpec{
+ VirtualHost: &contour_v1.VirtualHost{
+ Fqdn: "www.example.com",
+ },
+ Routes: []contour_v1.Route{{
+ Conditions: []contour_v1.MatchCondition{{
+ Prefix: "/",
+ }},
+ Services: []contour_v1.Service{{
+ Name: "backend",
+ Port: 80,
+ }},
+ }},
+ },
+ },
+ service,
+ },
+ want: listenermap(&envoy_config_listener_v3.Listener{
+ Name: ENVOY_HTTP_LISTENER,
+ Address: envoy_v3.SocketAddress("0.0.0.0", 8080),
+ FilterChains: envoy_v3.FilterChains(
+ envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(&contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.DisabledCompression,
+ }).
+ RouteConfigName(ENVOY_HTTP_LISTENER).
+ MetricsPrefix(ENVOY_HTTP_LISTENER).
+ AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
+ DefaultFilters().
+ Get(),
+ ),
+ SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(),
+ }),
+ },
+ "httpproxy with gzip compression set in listener config": {
+ ListenerConfig: ListenerConfig{
+ Compression: &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.GzipCompression,
+ },
+ },
+ objs: []any{
+ &contour_v1.HTTPProxy{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "simple",
+ Namespace: "default",
+ },
+ Spec: contour_v1.HTTPProxySpec{
+ VirtualHost: &contour_v1.VirtualHost{
+ Fqdn: "www.example.com",
+ },
+ Routes: []contour_v1.Route{{
+ Conditions: []contour_v1.MatchCondition{{
+ Prefix: "/",
+ }},
+ Services: []contour_v1.Service{{
+ Name: "backend",
+ Port: 80,
+ }},
+ }},
+ },
+ },
+ service,
+ },
+ want: listenermap(&envoy_config_listener_v3.Listener{
+ Name: ENVOY_HTTP_LISTENER,
+ Address: envoy_v3.SocketAddress("0.0.0.0", 8080),
+ FilterChains: envoy_v3.FilterChains(
+ envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(&contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.GzipCompression,
+ }).
+ RouteConfigName(ENVOY_HTTP_LISTENER).
+ MetricsPrefix(ENVOY_HTTP_LISTENER).
+ AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
+ DefaultFilters().
+ Get(),
+ ),
+ SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(),
+ }),
+ },
+ "httpproxy with brotli compression set in listener config": {
+ ListenerConfig: ListenerConfig{
+ Compression: &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.BrotliCompression,
+ },
+ },
+ objs: []any{
+ &contour_v1.HTTPProxy{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "simple",
+ Namespace: "default",
+ },
+ Spec: contour_v1.HTTPProxySpec{
+ VirtualHost: &contour_v1.VirtualHost{
+ Fqdn: "www.example.com",
+ },
+ Routes: []contour_v1.Route{{
+ Conditions: []contour_v1.MatchCondition{{
+ Prefix: "/",
+ }},
+ Services: []contour_v1.Service{{
+ Name: "backend",
+ Port: 80,
+ }},
+ }},
+ },
+ },
+ service,
+ },
+ want: listenermap(&envoy_config_listener_v3.Listener{
+ Name: ENVOY_HTTP_LISTENER,
+ Address: envoy_v3.SocketAddress("0.0.0.0", 8080),
+ FilterChains: envoy_v3.FilterChains(
+ envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(&contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.BrotliCompression,
+ }).
+ RouteConfigName(ENVOY_HTTP_LISTENER).
+ MetricsPrefix(ENVOY_HTTP_LISTENER).
+ AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
+ DefaultFilters().
+ Get(),
+ ),
+ SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(),
+ }),
+ },
+ "httpproxy with zstd compression set in listener config": {
+ ListenerConfig: ListenerConfig{
+ Compression: &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.ZstdCompression,
+ },
+ },
+ objs: []any{
+ &contour_v1.HTTPProxy{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "simple",
+ Namespace: "default",
+ },
+ Spec: contour_v1.HTTPProxySpec{
+ VirtualHost: &contour_v1.VirtualHost{
+ Fqdn: "www.example.com",
+ },
+ Routes: []contour_v1.Route{{
+ Conditions: []contour_v1.MatchCondition{{
+ Prefix: "/",
+ }},
+ Services: []contour_v1.Service{{
+ Name: "backend",
+ Port: 80,
+ }},
+ }},
+ },
+ },
+ service,
+ },
+ want: listenermap(&envoy_config_listener_v3.Listener{
+ Name: ENVOY_HTTP_LISTENER,
+ Address: envoy_v3.SocketAddress("0.0.0.0", 8080),
+ FilterChains: envoy_v3.FilterChains(
+ envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(&contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.ZstdCompression,
+ }).
+ RouteConfigName(ENVOY_HTTP_LISTENER).
+ MetricsPrefix(ENVOY_HTTP_LISTENER).
+ AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
+ DefaultFilters().
+ Get(),
+ ),
+ SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(),
+ }),
+ },
+ "httpproxy with invalid compression set in listener config": {
+ ListenerConfig: ListenerConfig{
+ Compression: &contour_v1alpha1.EnvoyCompression{
+ Algorithm: "invalid value",
+ },
+ },
+ objs: []any{
+ &contour_v1.HTTPProxy{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "simple",
+ Namespace: "default",
+ },
+ Spec: contour_v1.HTTPProxySpec{
+ VirtualHost: &contour_v1.VirtualHost{
+ Fqdn: "www.example.com",
+ },
+ Routes: []contour_v1.Route{{
+ Conditions: []contour_v1.MatchCondition{{
+ Prefix: "/",
+ }},
+ Services: []contour_v1.Service{{
+ Name: "backend",
+ Port: 80,
+ }},
+ }},
+ },
+ },
+ service,
+ },
+ want: listenermap(&envoy_config_listener_v3.Listener{
+ Name: ENVOY_HTTP_LISTENER,
+ Address: envoy_v3.SocketAddress("0.0.0.0", 8080),
+ FilterChains: envoy_v3.FilterChains(
+ envoy_v3.HTTPConnectionManagerBuilder().
+ SetDefaultFilterCompression(&contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.GzipCompression,
+ }).
+ RouteConfigName(ENVOY_HTTP_LISTENER).
+ MetricsPrefix(ENVOY_HTTP_LISTENER).
+ AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)).
+ DefaultFilters().
+ Get(),
+ ),
+ SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(),
+ }),
+ },
"httpsproxy with PerConnectionBufferLimitBytes set in listener config": {
ListenerConfig: ListenerConfig{
PerConnectionBufferLimitBytes: ptr.To(uint32(32768)),
diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go
index 04931c8d349..7c9e0360364 100644
--- a/pkg/config/parameters.go
+++ b/pkg/config/parameters.go
@@ -651,6 +651,10 @@ type Parameters struct {
// which strips duplicate slashes from request URL paths.
DisableMergeSlashes bool `yaml:"disableMergeSlashes,omitempty"`
+ // Compression defines configuration relating to compression in the default HTTP filter chain.
+ // +optional
+ Compression CompressionParameters `yaml:"compression,omitempty"`
+
// Defines the action to be applied to the Server header on the response path.
// When configured as overwrite, overwrites any Server header with "envoy".
// When configured as append_if_absent, if a Server header is present, pass it through, otherwise set it to "envoy".
@@ -966,6 +970,33 @@ const (
LogLevelDisabled AccessLogLevel = "disabled"
)
+// CompressionParameters is a type defining configurable compression related values.
+// At present this is just the compression algorithm but this could be extended later with algorithm specific config.
+type CompressionParameters struct {
+ // Algorithm configures which compression algorithm, if any, to use in the default HTTP listener filter chain.
+ // Valid options are 'gzip' (default), 'brotli', 'zstd' and 'disabled'.
+ // +optional
+ Algorithm CompressionAlgorithm `yaml:"algorithm,omitempty"`
+}
+
+func (c CompressionParameters) Validate() error {
+ return c.Algorithm.Validate()
+}
+
+type CompressionAlgorithm string
+
+func (c CompressionAlgorithm) Validate() error {
+ return contour_v1alpha1.CompressionAlgorithm(c).Validate()
+}
+
+const (
+ CompressionGzip CompressionAlgorithm = "gzip"
+ CompressionBrotli CompressionAlgorithm = "brotli"
+ CompressionDisabled CompressionAlgorithm = "disabled"
+ CompressionZstd CompressionAlgorithm = "zstd"
+ CompressionDefault = CompressionGzip
+)
+
// Validate verifies that the parameter values do not have any syntax errors.
func (p *Parameters) Validate() error {
if err := p.Cluster.DNSLookupFamily.Validate(); err != nil {
@@ -996,6 +1027,10 @@ func (p *Parameters) Validate() error {
return err
}
+ if err := p.Compression.Validate(); err != nil {
+ return err
+ }
+
if err := p.TLS.Validate(); err != nil {
return err
}
@@ -1029,6 +1064,9 @@ func (p *Parameters) Validate() error {
return p.Listener.Validate()
}
+// DefaultCompressionAlgorithm is the compression mechanism in the default HTTP filter chain
+const DefaultCompressionAlgorithm = CompressionGzip
+
// Defaults returns the default set of parameters.
func Defaults() Parameters {
contourNamespace := GetenvOr("CONTOUR_NAMESPACE", "projectcontour")
diff --git a/pkg/config/parameters_test.go b/pkg/config/parameters_test.go
index 7f6ad7a7dcd..5b4bc717417 100644
--- a/pkg/config/parameters_test.go
+++ b/pkg/config/parameters_test.go
@@ -325,6 +325,15 @@ func TestTLSParametersValidation(t *testing.T) {
}.Validate())
}
+func TestCompressionValidation(t *testing.T) {
+ require.NoError(t, CompressionParameters{""}.Validate())
+ require.NoError(t, CompressionParameters{CompressionBrotli}.Validate())
+ require.NoError(t, CompressionParameters{CompressionDisabled}.Validate())
+ require.NoError(t, CompressionParameters{CompressionGzip}.Validate())
+ require.NoError(t, CompressionParameters{CompressionZstd}.Validate())
+ require.True(t, strings.Contains(CompressionParameters{"bogus"}.Validate().Error(), "invalid compression type"))
+}
+
func TestConfigFileValidation(t *testing.T) {
check := func(yamlIn string) {
t.Helper()
diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html
index 57bc87795fd..19c8e70e9bd 100644
--- a/site/content/docs/main/config/api-reference.html
+++ b/site/content/docs/main/config/api-reference.html
@@ -5851,6 +5851,37 @@ ClusterParameters
+CompressionAlgorithm
+(string
alias)
+
+(Appears on:
+EnvoyCompression)
+
+
+
CompressionAlgorithm defines the type of compression algorithm applied in default HTTP listener filter chain.
+Allowable values are defined as names of well known compression algorithms (plus “disabled”).
+
+
+
+
+Value |
+Description |
+
+
+"brotli" |
+BrotliCompression specifies brotli as the default HTTP filter chain compression mechanism
+ |
+
"disabled" |
+DisabledCompression specifies that there will be no compression in the default HTTP filter chain
+ |
+
"gzip" |
+GzipCompression specifies gzip as the default HTTP filter chain compression mechanism
+ |
+
"zstd" |
+ZstdCompression specifies zstd as the default HTTP filter chain compression mechanism
+ |
+
+
ContourConfigurationSpec
@@ -6595,6 +6626,42 @@
DeploymentSettings
+EnvoyCompression
+
+
+(Appears on:
+EnvoyListenerConfig)
+
+
+
EnvoyCompression defines configuration related to compression in the default HTTP Listener filter chain.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+algorithm
+
+
+
+CompressionAlgorithm
+
+
+ |
+
+(Optional)
+ Algorithm selects the compression type applied in the compression HTTP filter of the default Listener filters.
+Values: gzip (default), brotli , zstd , disabled .
+Setting this to disabled will make Envoy skip “Accept-Encoding: gzip,deflate” request header and always return uncompressed response
+ |
+
+
+
EnvoyConfig
@@ -6900,6 +6967,21 @@
EnvoyListenerConfig
+compression
+
+
+
+EnvoyCompression
+
+
+ |
+
+(Optional)
+ Compression defines configuration related to compression in the default HTTP Listener filters.
+ |
+
+
+
disableAllowChunkedLength
diff --git a/site/content/docs/main/configuration.md b/site/content/docs/main/configuration.md
index 5277ab79e38..a914ffeba65 100644
--- a/site/content/docs/main/configuration.md
+++ b/site/content/docs/main/configuration.md
@@ -72,37 +72,38 @@ The Contour configuration file is optional.
In its absence, Contour will operate with reasonable defaults.
Where Contour settings can also be specified with command-line flags, the command-line value takes precedence over the configuration file.
-| Field Name | Type | Default | Description |
-|---------------------------| ---------------------- |------------------------------------------------------------------------------------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| accesslog-format | string | `envoy` | This key sets the global [access log format][2] for Envoy. Valid options are `envoy` or `json`. |
-| accesslog-format-string | string | None | If present, this specifies custom access log format for Envoy. See [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage) for more information about the syntax. This field only has effect if `accesslog-format` is `envoy` |
-| accesslog-level | string | `info` | This field specifies the verbosity level of the access log. Valid options are `info` (default, all requests are logged), `error` (all non-success, i.e. 300+ response code, requests are logged), `critical` (all server error, i.e. 500+ response code, requests are logged) and `disabled`. |
-| debug | boolean | `false` | Enables debug logging. |
-| default-http-versions | string array | HTTP/1.1 HTTP/2 | This array specifies the HTTP versions that Contour should program Envoy to serve. HTTP versions are specified as strings of the form "HTTP/x", where "x" represents the version number. |
-| disableAllowChunkedLength | boolean | `false` | If this field is true, Contour will disable 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 default behavior in case of failures.
-| disableMergeSlashes | boolean | `false` | This field disables Envoy's non-standard merge_slashes path transformation behavior that strips duplicate slashes from request URL paths.
-| serverHeaderTransformation | string | `overwrite` | This field defines the action to be applied to the Server header on the response path. Values: `overwrite` (default), `append_if_absent`, `pass_through`
-| disablePermitInsecure | boolean | `false` | If this field is true, Contour will ignore `PermitInsecure` field in HTTPProxy documents. |
-| envoy-service-name | string | `envoy` | This sets the service name that will be inspected for address details to be applied to Ingress objects. |
-| envoy-service-namespace | string | `projectcontour` | This sets the namespace of the service that will be inspected for address details to be applied to Ingress objects. If the `CONTOUR_NAMESPACE` environment variable is present, Contour will populate this field with its value. |
-| ingress-status-address | string | None | If present, this specifies the address that will be copied into the Ingress status for each Ingress that Contour manages. It is exclusive with `envoy-service-name` and `envoy-service-namespace`. |
-| incluster | boolean | `false` | This field specifies that Contour is running in a Kubernetes cluster and should use the in-cluster client access configuration. |
-| json-fields | string array | [fields][5] | This is the list the field names to include in the JSON [access log format][2]. This field only has effect if `accesslog-format` is `json`. |
-| kubeconfig | string | `$HOME/.kube/config` | Path to a Kubernetes [kubeconfig file][3] for when Contour is executed outside a cluster. |
-| kubernetesClientQPS | float32 | | QPS allowed for the Kubernetes client. |
-| kubernetesClientBurst | int | | Burst allowed for the Kubernetes client. |
-| policy | PolicyConfig | | The default [policy configuration](#policy-configuration). |
-| tls | TLS | | The default [TLS configuration](#tls-configuration). |
-| timeouts | TimeoutConfig | | The [timeout configuration](#timeout-configuration). |
-| cluster | ClusterConfig | | The [cluster configuration](#cluster-configuration). |
-| network | NetworkConfig | | The [network configuration](#network-configuration). |
-| listener | ListenerConfig | | The [listener configuration](#listener-configuration). |
-| server | ServerConfig | | The [server configuration](#server-configuration) for `contour serve` command. |
-| gateway | GatewayConfig | | The [gateway-api Gateway configuration](#gateway-configuration). |
-| rateLimitService | RateLimitServiceConfig | | The [rate limit service configuration](#rate-limit-service-configuration). |
-| enableExternalNameService | boolean | `false` | Enable ExternalName Service processing. Enabling this has security implications. Please see the [advisory](https://github.com/projectcontour/contour/security/advisories/GHSA-5ph6-qq5x-7jwc) for more details. |
-| metrics | MetricsParameters | | The [metrics configuration](#metrics-configuration) |
-| featureFlags | string array | `[]` | Defines the toggle to enable new contour features. Available toggles are: 1. `useEndpointSlices` - configures contour to fetch endpoint data from k8s endpoint slices. |
+| Field Name | Type | Default | Description |
+|----------------------------|------------------------|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| accesslog-format | string | `envoy` | This key sets the global [access log format][2] for Envoy. Valid options are `envoy` or `json`. |
+| accesslog-format-string | string | None | If present, this specifies custom access log format for Envoy. See [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage) for more information about the syntax. This field only has effect if `accesslog-format` is `envoy` |
+| accesslog-level | string | `info` | This field specifies the verbosity level of the access log. Valid options are `info` (default, all requests are logged), `error` (all non-success, i.e. 300+ response code, requests are logged), `critical` (all server error, i.e. 500+ response code, requests are logged) and `disabled`. |
+| debug | boolean | `false` | Enables debug logging. |
+| default-http-versions | string array | HTTP/1.1 HTTP/2 | This array specifies the HTTP versions that Contour should program Envoy to serve. HTTP versions are specified as strings of the form "HTTP/x", where "x" represents the version number. |
+| disableAllowChunkedLength | boolean | `false` | If this field is true, Contour will disable 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 default behavior in case of failures. |
+| compression | CompressionParameters | | Sets the compression configuration applied in the compression HTTP filter of the default Listener filters. |
+| disableMergeSlashes | boolean | `false` | This field disables Envoy's non-standard merge_slashes path transformation behavior that strips duplicate slashes from request URL paths. |
+| serverHeaderTransformation | string | `overwrite` | This field defines the action to be applied to the Server header on the response path. Values: `overwrite` (default), `append_if_absent`, `pass_through` |
+| disablePermitInsecure | boolean | `false` | If this field is true, Contour will ignore `PermitInsecure` field in HTTPProxy documents. |
+| envoy-service-name | string | `envoy` | This sets the service name that will be inspected for address details to be applied to Ingress objects. |
+| envoy-service-namespace | string | `projectcontour` | This sets the namespace of the service that will be inspected for address details to be applied to Ingress objects. If the `CONTOUR_NAMESPACE` environment variable is present, Contour will populate this field with its value. |
+| ingress-status-address | string | None | If present, this specifies the address that will be copied into the Ingress status for each Ingress that Contour manages. It is exclusive with `envoy-service-name` and `envoy-service-namespace`. |
+| incluster | boolean | `false` | This field specifies that Contour is running in a Kubernetes cluster and should use the in-cluster client access configuration. |
+| json-fields | string array | [fields][5] | This is the list the field names to include in the JSON [access log format][2]. This field only has effect if `accesslog-format` is `json`. |
+| kubeconfig | string | `$HOME/.kube/config` | Path to a Kubernetes [kubeconfig file][3] for when Contour is executed outside a cluster. |
+| kubernetesClientQPS | float32 | | QPS allowed for the Kubernetes client. |
+| kubernetesClientBurst | int | | Burst allowed for the Kubernetes client. |
+| policy | PolicyConfig | | The default [policy configuration](#policy-configuration). |
+| tls | TLS | | The default [TLS configuration](#tls-configuration). |
+| timeouts | TimeoutConfig | | The [timeout configuration](#timeout-configuration). |
+| cluster | ClusterConfig | | The [cluster configuration](#cluster-configuration). |
+| network | NetworkConfig | | The [network configuration](#network-configuration). |
+| listener | ListenerConfig | | The [listener configuration](#listener-configuration). |
+| server | ServerConfig | | The [server configuration](#server-configuration) for `contour serve` command. |
+| gateway | GatewayConfig | | The [gateway-api Gateway configuration](#gateway-configuration). |
+| rateLimitService | RateLimitServiceConfig | | The [rate limit service configuration](#rate-limit-service-configuration). |
+| enableExternalNameService | boolean | `false` | Enable ExternalName Service processing. Enabling this has security implications. Please see the [advisory](https://github.com/projectcontour/contour/security/advisories/GHSA-5ph6-qq5x-7jwc) for more details. |
+| metrics | MetricsParameters | | The [metrics configuration](#metrics-configuration) |
+| featureFlags | string array | `[]` | Defines the toggle to enable new contour features. Available toggles are: 1. `useEndpointSlices` - configures contour to fetch endpoint data from k8s endpoint slices. |
### TLS Configuration
@@ -301,6 +302,13 @@ Metrics and health endpoints cannot have the same port number when metrics are s
| max-requests | int | 0 | The maximum parallel requests a single Envoy instance allows to the Kubernetes Service; defaults to 1024 |
| max-retries | int | 0 | The maximum number of parallel retries a single Envoy instance allows to the Kubernetes Service; defaults to 3. This setting only makes sense if the cluster is configured to do retries.|
+### Compression Parameters
+
+| Field Name | Type | Default | Description |
+|------------|--------|--------|-------------------------|
+| algorithm | string | "gzip" | Compression algorithm. Setting this to `disabled` will make Envoy skip "Accept-Encoding: gzip,deflate" request header and always return uncompressed response. Values:`gzip` (default), `brotli`, `zstd`, `disabled`. |
+
+
### Configuration Example
The following is an example ConfigMap with configuration file included:
diff --git a/test/e2e/deployment.go b/test/e2e/deployment.go
index 6fc95eec9a5..4128f0d524b 100644
--- a/test/e2e/deployment.go
+++ b/test/e2e/deployment.go
@@ -490,7 +490,7 @@ func (d *Deployment) EnsureResourcesForLocalContour() error {
return err
}
- session.Wait("2s")
+ session.Wait("3s")
bootstrapContents, err := io.ReadAll(bFile)
if err != nil {
return err
diff --git a/test/e2e/httpproxy/envoy_compression_test.go b/test/e2e/httpproxy/envoy_compression_test.go
new file mode 100644
index 00000000000..6f277815fdd
--- /dev/null
+++ b/test/e2e/httpproxy/envoy_compression_test.go
@@ -0,0 +1,84 @@
+// 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.
+
+//go:build e2e
+
+package httpproxy
+
+import (
+ "fmt"
+ "net/http"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ contour_v1 "github.com/projectcontour/contour/apis/projectcontour/v1"
+ "github.com/projectcontour/contour/test/e2e"
+)
+
+func testEnvoyDisableCompression(namespace, acceptEncoding, contentEncoding string, disabled bool) {
+ testSpec := fmt.Sprintf("responses compressed with accept-encoding %s expecting content-encoding %s", acceptEncoding, contentEncoding)
+ if disabled {
+ testSpec = "responses are plaintext when compression disabled"
+ }
+
+ Specify(testSpec, func() {
+ resp := "minimum_text_to_enable_gzipminimum_text_to_enable_gzipminimum_text_to_enable_gzipminimum_text_to_enable_gzipminimum_text_to_enable_gzipminimum_text_to_enable_gzip"
+ p := &contour_v1.HTTPProxy{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "direct-response",
+ Namespace: namespace,
+ },
+ Spec: contour_v1.HTTPProxySpec{
+ VirtualHost: &contour_v1.VirtualHost{
+ Fqdn: fmt.Sprintf("%s-fqdn.projectcontour.io", namespace),
+ }, Routes: []contour_v1.Route{
+ {
+ Conditions: []contour_v1.MatchCondition{{
+ Prefix: "/directresponse",
+ }},
+ DirectResponsePolicy: &contour_v1.HTTPDirectResponsePolicy{
+ StatusCode: 200,
+ Body: resp,
+ },
+ },
+ },
+ },
+ }
+ require.True(f.T(), f.CreateHTTPProxyAndWaitFor(p, e2e.HTTPProxyValid))
+
+ require.EventuallyWithT(f.T(), func(c *assert.CollectT) {
+ res, ok := f.HTTP.RequestUntil(&e2e.HTTPRequestOpts{
+ Path: "/directresponse",
+ Host: p.Spec.VirtualHost.Fqdn,
+ RequestOpts: []func(*http.Request){
+ e2e.OptSetHeaders(map[string]string{
+ "Accept-Encoding": fmt.Sprintf("%s, deflate", acceptEncoding),
+ }),
+ },
+ Condition: e2e.HasStatusCode(200),
+ })
+ assert.NotNil(c, res, "request never succeeded")
+ assert.Truef(c, ok, "expected 200 response code, got %d", res.StatusCode)
+ contentEncodingHeaderValue := res.Headers.Get("Content-Encoding")
+ if disabled {
+ assert.NotEqual(c, contentEncodingHeaderValue, contentEncoding, "expected plain text")
+ return
+ }
+ assert.Equal(c, contentEncoding, contentEncodingHeaderValue, "expected plain text")
+ }, 20*time.Second, f.RetryInterval)
+ })
+}
diff --git a/test/e2e/httpproxy/httpproxy_test.go b/test/e2e/httpproxy/httpproxy_test.go
index ff01a0af897..0a838a2294a 100644
--- a/test/e2e/httpproxy/httpproxy_test.go
+++ b/test/e2e/httpproxy/httpproxy_test.go
@@ -388,6 +388,52 @@ var _ = Describe("HTTPProxy", func() {
})
})
+ f.NamespacedTest("httpproxy-default-compression", func(namespace string) {
+ testEnvoyDisableCompression(namespace, "gzip", "gzip", false)
+ })
+
+ f.NamespacedTest("httpproxy-disable-compression", func(namespace string) {
+ Context("with compression disabled", func() {
+ BeforeEach(func() {
+ contourConfig.Compression = config.CompressionParameters{
+ Algorithm: config.CompressionDisabled,
+ }
+ contourConfiguration.Spec.Envoy.Listener.Compression = &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.DisabledCompression,
+ }
+ })
+ testEnvoyDisableCompression(namespace, "gzip", "gzip", true)
+ })
+ })
+
+ f.NamespacedTest("httpproxy-brotli-compression", func(namespace string) {
+ Context("with brotli compression", func() {
+ BeforeEach(func() {
+ contourConfig.Compression = config.CompressionParameters{
+ Algorithm: config.CompressionBrotli,
+ }
+ contourConfiguration.Spec.Envoy.Listener.Compression = &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.BrotliCompression,
+ }
+ })
+ testEnvoyDisableCompression(namespace, "br", "br", false)
+ })
+ })
+
+ f.NamespacedTest("httpproxy-zstd-compression", func(namespace string) {
+ Context("with zstd compression", func() {
+ BeforeEach(func() {
+ contourConfig.Compression = config.CompressionParameters{
+ Algorithm: config.CompressionZstd,
+ }
+ contourConfiguration.Spec.Envoy.Listener.Compression = &contour_v1alpha1.EnvoyCompression{
+ Algorithm: contour_v1alpha1.ZstdCompression,
+ }
+ })
+ testEnvoyDisableCompression(namespace, "zstd", "zstd", false)
+ })
+ })
+
f.NamespacedTest("httpproxy-external-auth", testExternalAuth)
f.NamespacedTest("httpproxy-http-health-checks", testHTTPHealthChecks)
|