113 lines
2.5 KiB
Go
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
|
|
}
|