Skip to content

Commit

Permalink
Merge branch 'alibaba:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
sjtuzbk authored Dec 20, 2023
2 parents 17c4892 + a140f78 commit 243fc05
Show file tree
Hide file tree
Showing 13 changed files with 1,277 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pkg/ingress/kube/annotations/canary.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func ApplyByHeader(canary, route *networking.HTTPRoute, canaryIngress *Ingress)
match.Headers = map[string]*networking.StringMatch{
canaryConfig.Header: {
MatchType: &networking.StringMatch_Regex{
Regex: canaryConfig.HeaderPattern,
Regex: ".*" + canaryConfig.HeaderPattern + ".*",
},
},
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/ingress/kube/configmap/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ type ItemEventHandler = func(name string)

type HigressConfig struct {
Tracing *Tracing `json:"tracing,omitempty"`
Gzip *Gzip `json:"gzip,omitempty"`
}

func NewDefaultHigressConfig() *HigressConfig {
higressConfig := &HigressConfig{
Tracing: NewDefaultTracing(),
Gzip: NewDefaultGzip(),
}
return higressConfig
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/ingress/kube/configmap/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ func NewConfigmapMgr(XDSUpdater model.XDSUpdater, namespace string, higressConfi
configmapMgr.SetHigressConfig(NewDefaultHigressConfig())

tracingController := NewTracingController(namespace)
gzipController := NewGzipController(namespace)
configmapMgr.AddItemControllers(tracingController)
configmapMgr.AddItemControllers(gzipController)
configmapMgr.initEventHandlers()

return configmapMgr
Expand Down
336 changes: 336 additions & 0 deletions pkg/ingress/kube/configmap/gzip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 configmap

import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
"sync/atomic"

"github.com/alibaba/higress/pkg/ingress/kube/util"
. "github.com/alibaba/higress/pkg/ingress/log"
networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pkg/config"
"istio.io/istio/pkg/config/schema/gvk"
)

const (
higressGzipEnvoyFilterName = "higress-config-gzip"
compressionStrategyValues = "DEFAULT_STRATEGY,FILTERED,HUFFMAN_ONLY,RLE,FIXED"
compressionLevelValues = "BEST_COMPRESSION,BEST_SPEED,COMPRESSION_LEVEL_1,COMPRESSION_LEVEL_2,COMPRESSION_LEVEL_3,COMPRESSION_LEVEL_4,COMPRESSION_LEVEL_5,COMPRESSION_LEVEL_6,COMPRESSION_LEVEL_7,COMPRESSION_LEVEL_8,COMPRESSION_LEVEL_9"
)

type Gzip struct {
// Flag to control gzip
Enable bool `json:"enable,omitempty"`
MinContentLength int32 `json:"minContentLength,omitempty"`
ContentType []string `json:"contentType,omitempty"`
DisableOnEtagHeader bool `json:"disableOnEtagHeader,omitempty"`
// Value from 1 to 9 that controls the amount of internal memory used by zlib.
// Higher values use more memory, but are faster and produce better compression results. The default value is 5.
MemoryLevel int32 `json:"memoryLevel,omitempty"`
// Value from 9 to 15 that represents the base two logarithmic of the compressor’s window size.
// Larger window results in better compression at the expense of memory usage.
// The default is 12 which will produce a 4096 bytes window
WindowBits int32 `json:"windowBits,omitempty"`
// Value for Zlib’s next output buffer. If not set, defaults to 4096.
ChunkSize int32 `json:"chunkSize,omitempty"`
// A value used for selecting the zlib compression level.
// From COMPRESSION_LEVEL_1 to COMPRESSION_LEVEL_9
// BEST_COMPRESSION == COMPRESSION_LEVEL_9 , BEST_SPEED == COMPRESSION_LEVEL_1
CompressionLevel string `json:"compressionLevel,omitempty"`
// A value used for selecting the zlib compression strategy which is directly related to the characteristics of the content.
// Most of the time “DEFAULT_STRATEGY”
// Value is one of DEFAULT_STRATEGY, FILTERED, HUFFMAN_ONLY, RLE, FIXED
CompressionStrategy string `json:"compressionStrategy,omitempty"`
}

func validGzip(g *Gzip) error {
if g == nil {
return nil
}

if g.MinContentLength <= 0 {
return errors.New("minContentLength can not be less than zero")
}

if len(g.ContentType) == 0 {
return errors.New("content type can not be empty")
}

if !(g.MemoryLevel >= 1 && g.MemoryLevel <= 9) {
return errors.New("memory level need be between 1 and 9")
}

if !(g.WindowBits >= 9 && g.WindowBits <= 15) {
return errors.New("window bits need be between 9 and 15")
}

if g.ChunkSize <= 0 {
return errors.New("chunk size need be large than zero")
}

compressionLevels := strings.Split(compressionLevelValues, ",")
isFound := false
for _, v := range compressionLevels {
if g.CompressionLevel == v {
isFound = true
break
}
}
if !isFound {
return fmt.Errorf("compressionLevel need be one of %s", compressionLevelValues)
}

isFound = false
compressionStrategies := strings.Split(compressionStrategyValues, ",")
for _, v := range compressionStrategies {
if g.CompressionStrategy == v {
isFound = true
break
}
}
if !isFound {
return fmt.Errorf("compressionStrategy need be one of %s", compressionStrategyValues)
}

return nil
}

func compareGzip(old *Gzip, new *Gzip) (Result, error) {
if old == nil && new == nil {
return ResultNothing, nil
}

if new == nil {
return ResultDelete, nil
}

if !reflect.DeepEqual(old, new) {
return ResultReplace, nil
}

return ResultNothing, nil
}

func deepCopyGzip(gzip *Gzip) (*Gzip, error) {
newGzip := NewDefaultGzip()
bytes, err := json.Marshal(gzip)
if err != nil {
return nil, err
}
err = json.Unmarshal(bytes, newGzip)
return newGzip, err
}

func NewDefaultGzip() *Gzip {
gzip := &Gzip{
Enable: false,
MinContentLength: 1024,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
}
return gzip
}

type GzipController struct {
Namespace string
gzip atomic.Value
Name string
eventHandler ItemEventHandler
}

func NewGzipController(namespace string) *GzipController {
gzipController := &GzipController{
Namespace: namespace,
gzip: atomic.Value{},
Name: "gzip",
}
gzipController.SetGzip(NewDefaultGzip())
return gzipController
}

func (g *GzipController) GetName() string {
return g.Name
}

func (t *GzipController) SetGzip(gzip *Gzip) {
t.gzip.Store(gzip)
}

func (g *GzipController) GetGzip() *Gzip {
value := g.gzip.Load()
if value != nil {
if gzip, ok := value.(*Gzip); ok {
return gzip
}
}
return nil
}

func (g *GzipController) AddOrUpdateHigressConfig(name util.ClusterNamespacedName, old *HigressConfig, new *HigressConfig) error {
if err := validGzip(new.Gzip); err != nil {
IngressLog.Errorf("data:%+v convert to gzip , error: %+v", new.Gzip, err)
return nil
}

result, _ := compareGzip(old.Gzip, new.Gzip)

switch result {
case ResultReplace:
if newGzip, err := deepCopyGzip(new.Gzip); err != nil {
IngressLog.Infof("gzip deepcopy error:%v", err)
} else {
g.SetGzip(newGzip)
IngressLog.Infof("AddOrUpdate Higress config gzip")
g.eventHandler(higressGzipEnvoyFilterName)
IngressLog.Infof("send event with filter name:%s", higressGzipEnvoyFilterName)
}
case ResultDelete:
g.SetGzip(NewDefaultGzip())
IngressLog.Infof("Delete Higress config gzip")
g.eventHandler(higressGzipEnvoyFilterName)
IngressLog.Infof("send event with filter name:%s", higressGzipEnvoyFilterName)
}

return nil
}

func (g *GzipController) ValidHigressConfig(higressConfig *HigressConfig) error {
if higressConfig == nil {
return nil
}
if higressConfig.Gzip == nil {
return nil
}

return validGzip(higressConfig.Gzip)
}

func (g *GzipController) ConstructEnvoyFilters() ([]*config.Config, error) {
configs := make([]*config.Config, 0)
gzip := g.GetGzip()
namespace := g.Namespace

if gzip == nil {
return configs, nil
}

if gzip.Enable == false {
return configs, nil
}

gzipStruct := g.constructGzipStruct(gzip, namespace)
if len(gzipStruct) == 0 {
return configs, nil
}

config := &config.Config{
Meta: config.Meta{
GroupVersionKind: gvk.EnvoyFilter,
Name: higressGzipEnvoyFilterName,
Namespace: namespace,
},
Spec: &networking.EnvoyFilter{
ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{
{
ApplyTo: networking.EnvoyFilter_HTTP_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{
Name: "envoy.filters.http.router",
},
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE,
Value: util.BuildPatchStruct(gzipStruct),
},
},
},
},
}

configs = append(configs, config)
return configs, nil
}

func (g *GzipController) RegisterItemEventHandler(eventHandler ItemEventHandler) {
g.eventHandler = eventHandler
}

func (g *GzipController) constructGzipStruct(gzip *Gzip, namespace string) string {
gzipConfig := ""
contentType := ""
index := 0
for _, v := range gzip.ContentType {
contentType = contentType + fmt.Sprintf("\"%s\"", v)
if index < len(gzip.ContentType)-1 {
contentType = contentType + ","
}
index++
}
structFmt := `{
"name": "envoy.filters.http.compressor",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor",
"response_direction_config": {
"common_config": {
"min_content_length": %d,
"content_type": [%s],
"disable_on_etag_header": %t
}
},
"request_direction_config": {
"common_config": {
"enabled": {
"default_value": false,
"runtime_key": "request_compressor_enabled"
}
}
},
"compressor_library": {
"name": "text_optimized",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip",
"memory_level": %d,
"window_bits": %d,
"check_size": %d,
"compression_level": "%s",
"compression_strategy": "%s"
}
}
}
}`
gzipConfig = fmt.Sprintf(structFmt, gzip.MinContentLength, contentType, gzip.DisableOnEtagHeader,
gzip.MemoryLevel, gzip.WindowBits, gzip.ChunkSize, gzip.CompressionLevel, gzip.CompressionStrategy)
return gzipConfig
}
Loading

0 comments on commit 243fc05

Please sign in to comment.