Skip to content

Commit

Permalink
implement compression
Browse files Browse the repository at this point in the history
Signed-off-by: Huabing Zhao <[email protected]>
  • Loading branch information
zhaohuabing committed Jan 3, 2025
1 parent f71fa99 commit 4e010ee
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 1 deletion.
1 change: 0 additions & 1 deletion api/v1alpha1/backendtrafficpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ type BackendTrafficPolicySpec struct {
// The compression config for the http streams.
//
// +optional
// +notImplementedHide
Compression []*Compression `json:"compression,omitempty"`

// ResponseOverride defines the configuration to override specific responses with a custom one.
Expand Down
3 changes: 3 additions & 0 deletions api/v1alpha1/envoyproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ const (
// EnvoyFilterCustomResponse defines the Envoy HTTP custom response filter.
EnvoyFilterCustomResponse EnvoyFilter = "envoy.filters.http.custom_response"

// EnvoyFilterCompressor defines the Envoy HTTP compressor filter.
EnvoyFilterCompressor EnvoyFilter = "envoy.filters.http.compressor"

// EnvoyFilterRouter defines the Envoy HTTP router filter.
EnvoyFilterRouter EnvoyFilter = "envoy.filters.http.router"
)
Expand Down
13 changes: 13 additions & 0 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
ds *ir.DNS
h2 *ir.HTTP2Settings
ro *ir.ResponseOverride
cp *ir.Compression
err, errs error
)

Expand Down Expand Up @@ -495,6 +496,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
err = perr.WithMessage(err, "ResponseOverride")
errs = errors.Join(errs, err)
}
cp = buildCompression(policy.Spec.Compression)

ds = translateDNS(policy.Spec.ClusterSettings)

Expand Down Expand Up @@ -579,6 +581,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
HTTP2: h2,
DNS: ds,
ResponseOverride: ro,
Compression: cp,
}

// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
Expand Down Expand Up @@ -930,3 +933,13 @@ func defaultResponseOverrideRuleName(policy *egv1a1.BackendTrafficPolicy, index
irConfigName(policy),
strconv.Itoa(index))
}

func buildCompression(compression []*egv1a1.Compression) *ir.Compression {
if len(compression) == 0 {
return nil
}

// Only Gzip is supported for now, so we don't need to do anything special here
return &ir.Compression{
}
}
8 changes: 8 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,12 @@ type HeaderBasedSessionPersistence struct {
Name string `json:"name"`
}

// Compression holds the configuration for HTTP compression.
// Currently, only the default compressor(gzip) is supported.
// +k8s:deepcopy-gen=true
type Compression struct {
}

// TrafficFeatures holds the information associated with the Backend Traffic Policy.
// +k8s:deepcopy-gen=true
type TrafficFeatures struct {
Expand Down Expand Up @@ -762,6 +768,8 @@ type TrafficFeatures struct {
DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"`
// ResponseOverride defines the schema for overriding the response.
ResponseOverride *ResponseOverride `json:"responseOverride,omitempty" yaml:"responseOverride,omitempty"`
// Compression settings for HTTP Response
Compression *Compression
}

func (b *TrafficFeatures) Validate() error {
Expand Down
164 changes: 164 additions & 0 deletions internal/xds/translator/compressor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

package translator

import (
"errors"
"fmt"

corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
gzipv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
compressorv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
"google.golang.org/protobuf/types/known/anypb"

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/ir"
"github.com/envoyproxy/gateway/internal/utils/protocov"
"github.com/envoyproxy/gateway/internal/xds/types"
)

func init() {
registerHTTPFilter(&compressor{})
}

type compressor struct{}

var _ httpFilter = &compressor{}

// patchHCM builds and appends the compressor Filter to the HTTP Connection Manager
// if applicable, and it does not already exist.
func (*compressor) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
if mgr == nil {
return errors.New("hcm is nil")
}
if irListener == nil {
return errors.New("ir listener is nil")
}
if hcmContainsFilter(mgr, egv1a1.EnvoyFilterCompressor.String()) {
return nil
}

var (
irCompression *ir.Compression
filter *hcmv3.HttpFilter
err error
)

for _, route := range irListener.Routes {
if route.Traffic != nil && route.Traffic.Compression != nil {
irCompression = route.Traffic.Compression
}
}
if irCompression == nil {
return nil
}

// The HCM-level filter config doesn't matter since it is overridden at the route level.
if filter, err = buildHCMCompressorFilter(); err != nil {
return err
}
mgr.HttpFilters = append(mgr.HttpFilters, filter)
return err
}

// buildHCMCompressorFilter returns a Compressor HTTP filter from the provided IR HTTPRoute.
func buildHCMCompressorFilter() (*hcmv3.HttpFilter, error) {
var (
compressorProto *compressorv3.Compressor
gzipAny *anypb.Any
compressorAny *anypb.Any
err error
)

if gzipAny, err = protocov.ToAnyWithValidation(&gzipv3.Gzip{}); err != nil {
return nil, err
}

compressorProto = &compressorv3.Compressor{
CompressorLibrary: &corev3.TypedExtensionConfig{
Name: "envoy.compressor.gzip",
TypedConfig:gzipAny,
},
}

if compressorAny, err = protocov.ToAnyWithValidation(compressorProto); err != nil {
return nil, err
}

return &hcmv3.HttpFilter{
Name: egv1a1.EnvoyFilterBasicAuth.String(),
ConfigType: &hcmv3.HttpFilter_TypedConfig{
TypedConfig: compressorAny,
},
Disabled: true,
}, nil
}

func (*compressor) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error {
return nil
}

// patchRoute patches the provided route with the compressor config if applicable.
// Note: this method overwrites the HCM level filter config with the per route filter config.
func (*compressor) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
if route == nil {
return errors.New("xds route is nil")
}
if irRoute == nil {
return errors.New("ir route is nil")
}
if irRoute.Security == nil || irRoute.Security.BasicAuth == nil {
return nil
}

var (
perFilterCfg map[string]*anypb.Any
compressorAny *anypb.Any
err error
)

perFilterCfg = route.GetTypedPerFilterConfig()
if _, ok := perFilterCfg[egv1a1.EnvoyFilterBasicAuth.String()]; ok {
// This should not happen since this is the only place where the filter
// config is added in a route.
return fmt.Errorf("route already contains filter config: %s, %+v",
egv1a1.EnvoyFilterBasicAuth.String(), route)
}

// Overwrite the HCM level filter config with the per route filter config.
compressorProto := compressorPerRouteConfig(irRoute)

if compressorProto == nil {
return nil
}

if compressorAny, err = protocov.ToAnyWithValidation(compressorProto); err != nil {
return err
}

if perFilterCfg == nil {
route.TypedPerFilterConfig = make(map[string]*anypb.Any)
}
route.TypedPerFilterConfig[egv1a1.EnvoyFilterBasicAuth.String()] = compressorAny

return nil
}

func compressorPerRouteConfig(irRoute *ir.HTTPRoute) *compressorv3.CompressorPerRoute {
// Disable compression on this route if no compression is configured.
if irRoute.Traffic==nil || irRoute.Traffic.Compression == nil {
return &compressorv3.CompressorPerRoute{
Override: &compressorv3.CompressorPerRoute_Disabled{
Disabled: true,
},
}

}
// Use the default compressor at the HCM level
return nil
}

0 comments on commit 4e010ee

Please sign in to comment.