traefik/pkg/middlewares/compress/acceptencoding.go
2024-10-09 16:47:13 +02:00

113 lines
2.5 KiB
Go

package compress
import (
"cmp"
"slices"
"strconv"
"strings"
)
const acceptEncodingHeader = "Accept-Encoding"
const (
brotliName = "br"
gzipName = "gzip"
zstdName = "zstd"
identityName = "identity"
wildcardName = "*"
notAcceptable = "not_acceptable"
)
type Encoding struct {
Type string
Weight float64
}
func getCompressionEncoding(acceptEncoding []string, defaultEncoding string, supportedEncodings []string) string {
if defaultEncoding == "" {
if slices.Contains(supportedEncodings, brotliName) {
// Keeps the pre-existing default inside Traefik if brotli is a supported encoding.
defaultEncoding = brotliName
} else if len(supportedEncodings) > 0 {
// Otherwise use the first supported encoding.
defaultEncoding = supportedEncodings[0]
}
}
encodings, hasWeight := parseAcceptEncoding(acceptEncoding, supportedEncodings)
if hasWeight {
if len(encodings) == 0 {
return identityName
}
encoding := encodings[0]
if encoding.Type == identityName && encoding.Weight == 0 {
return notAcceptable
}
if encoding.Type == wildcardName && encoding.Weight == 0 {
return notAcceptable
}
if encoding.Type == wildcardName {
return defaultEncoding
}
return encoding.Type
}
for _, dt := range supportedEncodings {
if slices.ContainsFunc(encodings, func(e Encoding) bool { return e.Type == dt }) {
return dt
}
}
if slices.ContainsFunc(encodings, func(e Encoding) bool { return e.Type == wildcardName }) {
return defaultEncoding
}
return identityName
}
func parseAcceptEncoding(acceptEncoding, supportedEncodings []string) ([]Encoding, bool) {
var encodings []Encoding
var hasWeight bool
for _, line := range acceptEncoding {
for _, item := range strings.Split(strings.ReplaceAll(line, " ", ""), ",") {
parsed := strings.SplitN(item, ";", 2)
if len(parsed) == 0 {
continue
}
if !slices.Contains(supportedEncodings, parsed[0]) &&
parsed[0] != identityName &&
parsed[0] != wildcardName {
continue
}
// If no "q" parameter is present, the default weight is 1.
// https://www.rfc-editor.org/rfc/rfc9110.html#name-quality-values
weight := 1.0
if len(parsed) > 1 && strings.HasPrefix(parsed[1], "q=") {
w, _ := strconv.ParseFloat(strings.TrimPrefix(parsed[1], "q="), 64)
weight = w
hasWeight = true
}
encodings = append(encodings, Encoding{
Type: parsed[0],
Weight: weight,
})
}
}
slices.SortFunc(encodings, func(a, b Encoding) int {
return cmp.Compare(b.Weight, a.Weight)
})
return encodings, hasWeight
}