2024-06-06 14:42:04 +00:00
|
|
|
package compress
|
|
|
|
|
|
|
|
import (
|
|
|
|
"slices"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const acceptEncodingHeader = "Accept-Encoding"
|
|
|
|
|
|
|
|
const (
|
|
|
|
brotliName = "br"
|
|
|
|
gzipName = "gzip"
|
2024-06-12 09:38:04 +00:00
|
|
|
zstdName = "zstd"
|
2024-06-06 14:42:04 +00:00
|
|
|
identityName = "identity"
|
|
|
|
wildcardName = "*"
|
|
|
|
notAcceptable = "not_acceptable"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Encoding struct {
|
|
|
|
Type string
|
|
|
|
Weight *float64
|
|
|
|
}
|
|
|
|
|
2024-08-07 14:20:04 +00:00
|
|
|
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]
|
|
|
|
}
|
2024-06-06 14:42:04 +00:00
|
|
|
}
|
|
|
|
|
2024-08-07 14:20:04 +00:00
|
|
|
encodings, hasWeight := parseAcceptEncoding(acceptEncoding, supportedEncodings)
|
2024-06-06 14:42:04 +00:00
|
|
|
|
|
|
|
if hasWeight {
|
|
|
|
if len(encodings) == 0 {
|
|
|
|
return identityName
|
|
|
|
}
|
|
|
|
|
|
|
|
encoding := encodings[0]
|
|
|
|
|
|
|
|
if encoding.Type == identityName && encoding.Weight != nil && *encoding.Weight == 0 {
|
|
|
|
return notAcceptable
|
|
|
|
}
|
|
|
|
|
|
|
|
if encoding.Type == wildcardName && encoding.Weight != nil && *encoding.Weight == 0 {
|
|
|
|
return notAcceptable
|
|
|
|
}
|
|
|
|
|
|
|
|
if encoding.Type == wildcardName {
|
2024-08-07 14:20:04 +00:00
|
|
|
return defaultEncoding
|
2024-06-06 14:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return encoding.Type
|
|
|
|
}
|
|
|
|
|
2024-08-07 14:20:04 +00:00
|
|
|
for _, dt := range supportedEncodings {
|
2024-06-06 14:42:04 +00:00
|
|
|
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 }) {
|
2024-08-07 14:20:04 +00:00
|
|
|
return defaultEncoding
|
2024-06-06 14:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return identityName
|
|
|
|
}
|
|
|
|
|
2024-08-07 14:20:04 +00:00
|
|
|
func parseAcceptEncoding(acceptEncoding, supportedEncodings []string) ([]Encoding, bool) {
|
2024-06-06 14:42:04 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-08-07 14:20:04 +00:00
|
|
|
if !slices.Contains(supportedEncodings, parsed[0]) &&
|
|
|
|
parsed[0] != identityName &&
|
|
|
|
parsed[0] != wildcardName {
|
2024-06-06 14:42:04 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var weight *float64
|
|
|
|
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, compareEncoding)
|
|
|
|
|
|
|
|
return encodings, hasWeight
|
|
|
|
}
|
|
|
|
|
|
|
|
func compareEncoding(a, b Encoding) int {
|
|
|
|
lhs, rhs := a.Weight, b.Weight
|
|
|
|
|
|
|
|
if lhs == nil && rhs == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if lhs == nil && *rhs == 0 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
if lhs == nil {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if rhs == nil && *lhs == 0 {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if rhs == nil {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
if *lhs < *rhs {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if *lhs > *rhs {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
}
|