mirror of
https://github.com/traefik/traefik.git
synced 2025-05-05 15:33:01 +00:00
100 lines
2.6 KiB
Go
100 lines
2.6 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 (c *compress) getCompressionEncoding(acceptEncoding []string) string {
|
|
// RFC says: An Accept-Encoding header field with a field value that is empty implies that the user agent does not want any content coding in response.
|
|
// https://datatracker.ietf.org/doc/html/rfc9110#name-accept-encoding
|
|
if len(acceptEncoding) == 1 && acceptEncoding[0] == "" {
|
|
return identityName
|
|
}
|
|
|
|
acceptableEncodings := parseAcceptableEncodings(acceptEncoding, c.supportedEncodings)
|
|
|
|
// An empty Accept-Encoding header field would have been handled earlier.
|
|
// If empty, it means no encoding is supported, we do not encode.
|
|
if len(acceptableEncodings) == 0 {
|
|
// TODO: return 415 status code instead of deactivating the compression, if the backend was not to compress as well.
|
|
return notAcceptable
|
|
}
|
|
|
|
slices.SortFunc(acceptableEncodings, func(a, b Encoding) int {
|
|
if a.Weight == b.Weight {
|
|
// At same weight, we want to prioritize based on the encoding priority.
|
|
// the lower the index, the higher the priority.
|
|
return cmp.Compare(c.supportedEncodings[a.Type], c.supportedEncodings[b.Type])
|
|
}
|
|
return cmp.Compare(b.Weight, a.Weight)
|
|
})
|
|
|
|
if acceptableEncodings[0].Type == wildcardName {
|
|
if c.defaultEncoding == "" {
|
|
return c.encodings[0]
|
|
}
|
|
|
|
return c.defaultEncoding
|
|
}
|
|
|
|
return acceptableEncodings[0].Type
|
|
}
|
|
|
|
func parseAcceptableEncodings(acceptEncoding []string, supportedEncodings map[string]int) []Encoding {
|
|
var encodings []Encoding
|
|
|
|
for _, line := range acceptEncoding {
|
|
for _, item := range strings.Split(strings.ReplaceAll(line, " ", ""), ",") {
|
|
parsed := strings.SplitN(item, ";", 2)
|
|
if len(parsed) == 0 {
|
|
continue
|
|
}
|
|
|
|
if _, ok := supportedEncodings[parsed[0]]; !ok {
|
|
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)
|
|
|
|
// If the weight is 0, the encoding is not acceptable.
|
|
// We can skip the encoding.
|
|
if w == 0 {
|
|
continue
|
|
}
|
|
|
|
weight = w
|
|
}
|
|
|
|
encodings = append(encodings, Encoding{
|
|
Type: parsed[0],
|
|
Weight: weight,
|
|
})
|
|
}
|
|
}
|
|
|
|
return encodings
|
|
}
|