diff --git a/api/v1alpha1/compression_types.go b/api/v1alpha1/compression_types.go index 9ceb0efedd..805e367f86 100644 --- a/api/v1alpha1/compression_types.go +++ b/api/v1alpha1/compression_types.go @@ -68,4 +68,13 @@ type Compression struct { // +kubebuilder:validation:XIntOrString // +kubebuilder:validation:Pattern="^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$" MinContentLength *resource.Quantity `json:"minContentLength,omitempty"` + + // ContentTypes defines the set of response Content-Types that will trigger compression. + // When set, only responses whose Content-Type matches one of these values are compressed. + // If not specified, Envoy's default content types are used: + // application/javascript, application/json, application/xhtml+xml, image/svg+xml, + // text/css, text/html, text/plain, text/xml. + // + // +optional + ContentTypes []string `json:"contentTypes,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 4207e23a77..b6e6c2210b 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1683,6 +1683,11 @@ func (in *Compression) DeepCopyInto(out *Compression) { x := (*in).DeepCopy() *out = &x } + if in.ContentTypes != nil { + in, out := &in.ContentTypes, &out.ContentTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Compression. diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 575c1136c5..1e180ef4a8 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -369,6 +369,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -414,6 +424,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml index d8a048a90b..3b511b0b3d 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -14692,6 +14692,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 7572080368..362ca86c6e 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -368,6 +368,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -413,6 +423,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 5736db43cd..b0e0ae66a5 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -14691,6 +14691,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 933ba491fb..b6defc4ecd 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -2092,17 +2092,7 @@ func buildCompression(compression, compressor []*egv1a1.Compression) []*ir.Compr if (c.Type == egv1a1.GzipCompressorType && c.Gzip != nil) || (c.Type == egv1a1.BrotliCompressorType && c.Brotli != nil) || (c.Type == egv1a1.ZstdCompressorType && c.Zstd != nil) { - irCompression := ir.Compression{ - Type: c.Type, - ChooseFirst: i == 0, // only the first compressor is marked as ChooseFirst - } - if c.MinContentLength != nil { - minContentLength, ok := c.MinContentLength.AsInt64() - if ok { - irCompression.MinContentLength = new(uint32(minContentLength)) - } - } - result = append(result, &irCompression) + result = append(result, buildIRCompression(c, i == 0)) } } return result @@ -2114,22 +2104,29 @@ func buildCompression(compression, compressor []*egv1a1.Compression) []*ir.Compr } result := make([]*ir.Compression, 0, len(compression)) for i, c := range compression { - irCompression := ir.Compression{ - Type: c.Type, - ChooseFirst: i == 0, // only the first compressor is marked as ChooseFirst - } - if c.MinContentLength != nil { - minContentLength, ok := c.MinContentLength.AsInt64() - if ok { - irCompression.MinContentLength = new(uint32(minContentLength)) - } - } - result = append(result, &irCompression) + result = append(result, buildIRCompression(c, i == 0)) } return result } +func buildIRCompression(c *egv1a1.Compression, chooseFirst bool) *ir.Compression { + irCompression := &ir.Compression{ + Type: c.Type, + ChooseFirst: chooseFirst, // only the first compressor is marked as ChooseFirst + } + if c.MinContentLength != nil { + minContentLength, ok := c.MinContentLength.AsInt64() + if ok { + irCompression.MinContentLength = new(uint32(minContentLength)) + } + } + if len(c.ContentTypes) > 0 { + irCompression.ContentTypes = append([]string{}, c.ContentTypes...) + } + return irCompression +} + func buildHTTPProtocolUpgradeConfig(cfgs []*egv1a1.ProtocolUpgradeConfig) []ir.HTTPUpgradeConfig { if len(cfgs) == 0 { return nil diff --git a/internal/gatewayapi/backendtrafficpolicy_test.go b/internal/gatewayapi/backendtrafficpolicy_test.go index cb4c8e6cac..8a2069d444 100644 --- a/internal/gatewayapi/backendtrafficpolicy_test.go +++ b/internal/gatewayapi/backendtrafficpolicy_test.go @@ -396,6 +396,23 @@ func TestBuildCompression(t *testing.T) { }, }, }, + { + name: "compressor with contentTypes", + compressor: []*egv1a1.Compression{ + { + Type: egv1a1.GzipCompressorType, + Gzip: &egv1a1.GzipCompressor{}, + ContentTypes: []string{"application/json", "text/html"}, + }, + }, + expected: []*ir.Compression{ + { + Type: egv1a1.GzipCompressorType, + ChooseFirst: true, + ContentTypes: []string{"application/json", "text/html"}, + }, + }, + }, { name: "multiple compressors with different minContentLength", compressor: []*egv1a1.Compression{ @@ -424,14 +441,26 @@ func TestBuildCompression(t *testing.T) { }, }, { - name: "compressor takes priority over compression", - compression: []*egv1a1.Compression{ + name: "compressor with minContentLength and contentTypes", + compressor: []*egv1a1.Compression{ { - Type: egv1a1.GzipCompressorType, - Gzip: &egv1a1.GzipCompressor{}, - MinContentLength: new(resource.MustParse("100")), + Type: egv1a1.BrotliCompressorType, + Brotli: &egv1a1.BrotliCompressor{}, + MinContentLength: new(resource.MustParse("200")), + ContentTypes: []string{"application/json"}, }, }, + expected: []*ir.Compression{ + { + Type: egv1a1.BrotliCompressorType, + ChooseFirst: true, + MinContentLength: new(uint32(200)), + ContentTypes: []string{"application/json"}, + }, + }, + }, + { + name: "compressor takes priority over compression", compressor: []*egv1a1.Compression{ { Type: egv1a1.BrotliCompressorType, @@ -439,6 +468,13 @@ func TestBuildCompression(t *testing.T) { MinContentLength: new(resource.MustParse("200")), }, }, + compression: []*egv1a1.Compression{ + { + Type: egv1a1.GzipCompressorType, + Gzip: &egv1a1.GzipCompressor{}, + MinContentLength: new(resource.MustParse("100")), + }, + }, expected: []*ir.Compression{ { Type: egv1a1.BrotliCompressorType, diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-compressor-basic.content-types.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-compressor-basic.content-types.in.yaml new file mode 100644 index 0000000000..3f7aeb75ae --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-compressor-basic.content-types.in.yaml @@ -0,0 +1,57 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + compressor: + - type: Brotli + brotli: {} + minContentLength: 100 + contentTypes: + - application/json + - text/html + - type: Gzip + gzip: {} + contentTypes: + - application/json diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-compressor-basic.content-types.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-compressor-basic.content-types.out.yaml new file mode 100644 index 0000000000..e2c902785c --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-compressor-basic.content-types.out.yaml @@ -0,0 +1,231 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + name: policy-for-route + namespace: default + spec: + compressor: + - brotli: {} + contentTypes: + - application/json + - text/html + minContentLength: "100" + type: Brotli + - contentTypes: + - application/json + gzip: {} + type: Gzip + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: spec.targetRef is deprecated, use spec.targetRefs instead + reason: DeprecatedField + status: "True" + type: Warning + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + policies: + - kind: BackendTrafficPolicy + name: policy-for-route + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + compression: + - chooseFirst: true + contentTypes: + - application/json + - text/html + minContentLength: 100 + type: Brotli + - contentTypes: + - application/json + type: Gzip + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/ir/xds.go b/internal/ir/xds.go index dfb5ef6986..8f8ddc142b 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -1038,6 +1038,8 @@ type Compression struct { ChooseFirst bool `json:"chooseFirst,omitempty" yaml:"chooseFirst,omitempty"` // MinContentLength defines the minimum response size in bytes to apply compression. MinContentLength *uint32 `json:"minContentLength,omitempty" yaml:"minContentLength,omitempty"` + // ContentTypes defines the set of response Content-Types that will trigger compression. + ContentTypes []string `json:"contentTypes,omitempty" yaml:"contentTypes,omitempty"` } // TrafficFeatures holds the information associated with the Backend Traffic Policy. diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index a334741cd0..6b214a841a 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -922,6 +922,11 @@ func (in *Compression) DeepCopyInto(out *Compression) { *out = new(uint32) **out = **in } + if in.ContentTypes != nil { + in, out := &in.ContentTypes, &out.ContentTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Compression. diff --git a/internal/xds/translator/compressor.go b/internal/xds/translator/compressor.go index ddc35d6d6a..d2b3963bd7 100644 --- a/internal/xds/translator/compressor.go +++ b/internal/xds/translator/compressor.go @@ -110,11 +110,16 @@ func buildCompressorFilter(compression *ir.Compression) (*hcmv3.HttpFilter, erro compressorProto.ChooseFirst = true } - if compression.MinContentLength != nil { + if compression.MinContentLength != nil || len(compression.ContentTypes) > 0 { + commonConfig := &compressorv3.Compressor_CommonDirectionConfig{} + if compression.MinContentLength != nil { + commonConfig.MinContentLength = wrapperspb.UInt32(*compression.MinContentLength) + } + if len(compression.ContentTypes) > 0 { + commonConfig.ContentType = append([]string{}, compression.ContentTypes...) + } compressorProto.ResponseDirectionConfig = &compressorv3.Compressor_ResponseDirectionConfig{ - CommonConfig: &compressorv3.Compressor_CommonDirectionConfig{ - MinContentLength: wrapperspb.UInt32(*compression.MinContentLength), - }, + CommonConfig: commonConfig, } } diff --git a/internal/xds/translator/compressor_test.go b/internal/xds/translator/compressor_test.go index f391fd9ab3..c39bb66050 100644 --- a/internal/xds/translator/compressor_test.go +++ b/internal/xds/translator/compressor_test.go @@ -92,12 +92,28 @@ func TestBuildCompressorFilter(t *testing.T) { assert.Equal(t, uint32(1024), c.ResponseDirectionConfig.CommonConfig.MinContentLength.Value) }, }, + { + name: "with content types", + compression: &ir.Compression{ + Type: egv1a1.GzipCompressorType, + ContentTypes: []string{"application/json", "text/html"}, + }, + expectedName: "envoy.filters.http.compressor.gzip", + expectedExtName: "envoy.compression.gzip.compressor", + validateProto: func(t *testing.T, c *compressorv3.Compressor) { + require.NotNil(t, c.ResponseDirectionConfig) + require.NotNil(t, c.ResponseDirectionConfig.CommonConfig) + assert.Equal(t, []string{"application/json", "text/html"}, c.ResponseDirectionConfig.CommonConfig.ContentType) + assert.Nil(t, c.ResponseDirectionConfig.CommonConfig.MinContentLength) + }, + }, { name: "with all options", compression: &ir.Compression{ Type: egv1a1.BrotliCompressorType, ChooseFirst: true, MinContentLength: new(uint32(2048)), + ContentTypes: []string{"application/json"}, }, expectedName: "envoy.filters.http.compressor.brotli", expectedExtName: "envoy.compression.brotli.compressor", @@ -105,6 +121,7 @@ func TestBuildCompressorFilter(t *testing.T) { assert.True(t, c.ChooseFirst) require.NotNil(t, c.ResponseDirectionConfig) assert.Equal(t, uint32(2048), c.ResponseDirectionConfig.CommonConfig.MinContentLength.Value) + assert.Equal(t, []string{"application/json"}, c.ResponseDirectionConfig.CommonConfig.ContentType) }, }, } diff --git a/internal/xds/translator/testdata/in/xds-ir/compressor.yaml b/internal/xds/translator/testdata/in/xds-ir/compressor.yaml index 7e665addcd..f41975dcbd 100644 --- a/internal/xds/translator/testdata/in/xds-ir/compressor.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/compressor.yaml @@ -38,7 +38,14 @@ http: compression: - type: Brotli minContentLength: 100 + contentTypes: + - application/json + - text/html - type: Gzip minContentLength: 200 + contentTypes: + - application/json - type: Zstd minContentLength: 300 + contentTypes: + - text/plain diff --git a/internal/xds/translator/testdata/out/xds-ir/compressor.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/compressor.listeners.yaml index 36bf05d23a..60adda4586 100644 --- a/internal/xds/translator/testdata/out/xds-ir/compressor.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/compressor.listeners.yaml @@ -24,6 +24,9 @@ '@type': type.googleapis.com/envoy.extensions.compression.brotli.compressor.v3.Brotli responseDirectionConfig: commonConfig: + contentType: + - application/json + - text/html minContentLength: 100 - disabled: true name: envoy.filters.http.compressor.gzip @@ -35,6 +38,8 @@ '@type': type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip responseDirectionConfig: commonConfig: + contentType: + - application/json minContentLength: 200 - disabled: true name: envoy.filters.http.compressor.zstd @@ -46,6 +51,8 @@ '@type': type.googleapis.com/envoy.extensions.compression.zstd.compressor.v3.Zstd responseDirectionConfig: commonConfig: + contentType: + - text/plain minContentLength: 300 - name: envoy.filters.http.router typedConfig: diff --git a/release-notes/current/new_features/9511-compression-content-types.md b/release-notes/current/new_features/9511-compression-content-types.md new file mode 100644 index 0000000000..ca0d70ee48 --- /dev/null +++ b/release-notes/current/new_features/9511-compression-content-types.md @@ -0,0 +1 @@ +Added support for configuring compression `contentTypes` in `BackendTrafficPolicy`, allowing users to control which response Content-Types are eligible for compression without an EnvoyPatchPolicy. diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index d59fec9715..4e0d26212e 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1118,6 +1118,7 @@ _Appears in:_ | `gzip` | _[GzipCompressor](#gzipcompressor)_ | false | | The configuration for GZIP compressor. | | `zstd` | _[ZstdCompressor](#zstdcompressor)_ | false | | The configuration for Zstd compressor. | | `minContentLength` | _[Quantity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#quantity-resource-api)_ | false | | MinContentLength defines the minimum response size in bytes to apply compression.
Responses smaller than this threshold will not be compressed.
Must be at least 30 bytes as enforced by Envoy Proxy.
Note that when the suffix is not provided, the value is interpreted as bytes.
Default: 30 bytes | +| `contentTypes` | _string array_ | false | | ContentTypes defines the set of response Content-Types that will trigger compression.
When set, only responses whose Content-Type matches one of these values are compressed.
If not specified, Envoy's default content types are used:
application/javascript, application/json, application/xhtml+xml, image/svg+xml,
text/css, text/html, text/plain, text/xml. | #### CompressorType diff --git a/site/content/en/latest/tasks/traffic/response-compression.md b/site/content/en/latest/tasks/traffic/response-compression.md index 008783f03f..52038ce22d 100644 --- a/site/content/en/latest/tasks/traffic/response-compression.md +++ b/site/content/en/latest/tasks/traffic/response-compression.md @@ -98,6 +98,30 @@ spec: minContentLength: 1024 ``` +### Configuring Content Types + +You can configure which response `Content-Type` values are eligible for compression using the `contentTypes` field. When set, only responses whose `Content-Type` matches one of the listed values are compressed. + +If not specified, Envoy's default content types are used: `application/javascript`, `application/json`, `application/xhtml+xml`, `image/svg+xml`, `text/css`, `text/html`, `text/plain`, and `text/xml`. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: response-compression +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: backend + compressor: + - type: Gzip + gzip: {} + contentTypes: + - application/json + - application/xml +``` + ### Deprecated Configuration The following configuration uses the deprecated `compression` field. While still supported, it's recommended to migrate to the `compressor` field: diff --git a/test/e2e/testdata/compression.yaml b/test/e2e/testdata/compression.yaml index fff24baf3e..08e60097e1 100644 --- a/test/e2e/testdata/compression.yaml +++ b/test/e2e/testdata/compression.yaml @@ -54,9 +54,15 @@ spec: - type: Brotli brotli: {} minContentLength: 50 + contentTypes: + - application/json - type: Gzip gzip: {} minContentLength: 50 + contentTypes: + - application/json - type: Zstd zstd: {} minContentLength: 50 + contentTypes: + - application/json diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 39fc1c10c4..99ce8bbf94 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -24986,6 +24986,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -25031,6 +25041,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -48434,6 +48454,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index 357137c6ee..53387c1b3f 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -924,6 +924,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -969,6 +979,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -24372,6 +24392,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 44ab69394c..c41390e9b3 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -924,6 +924,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -969,6 +979,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object @@ -24372,6 +24392,16 @@ spec: brotli: description: The configuration for Brotli compressor. type: object + contentTypes: + description: |- + ContentTypes defines the set of response Content-Types that will trigger compression. + When set, only responses whose Content-Type matches one of these values are compressed. + If not specified, Envoy's default content types are used: + application/javascript, application/json, application/xhtml+xml, image/svg+xml, + text/css, text/html, text/plain, text/xml. + items: + type: string + type: array gzip: description: The configuration for GZIP compressor. type: object