mirror of
				https://github.com/traefik/traefik.git
				synced 2025-11-04 10:21:15 +01: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
 | 
						|
}
 |