Files
git-pages/src/http.go

90 lines
2.4 KiB
Go

package git_pages
import (
"cmp"
"regexp"
"slices"
"strconv"
"strings"
)
var httpAcceptEncodingRegexp = regexp.MustCompile(`` +
// token optionally prefixed by whitespace
`^[ \t]*([a-zA-Z0-9$!#$%&'*+.^_\x60|~-]+)` +
// quality value prefixed by a semicolon optionally surrounded by whitespace
`(?:[ \t]*;[ \t]*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?` +
// optional whitespace followed by comma or end of line
`[ \t]*(?:,|$)`,
)
type httpEncoding struct {
code string
qval float64
}
type httpEncodings struct {
encodings []httpEncoding
}
func parseHTTPEncodings(headerValue string) (result httpEncodings) {
for headerValue != "" {
matches := httpAcceptEncodingRegexp.FindStringSubmatch(headerValue)
if matches == nil {
return httpEncodings{}
}
enc := httpEncoding{strings.ToLower(matches[1]), 1.0}
if matches[2] != "" {
enc.qval, _ = strconv.ParseFloat(matches[2], 64)
}
result.encodings = append(result.encodings, enc)
headerValue = headerValue[len(matches[0]):]
}
if len(result.encodings) == 0 {
// RFC 9110 says (https://httpwg.org/specs/rfc9110.html#field.accept-encoding):
// "If no Accept-Encoding header field is in the request, any content
// coding is considered acceptable by the user agent."
// In practice, no client expects to receive a compressed response
// without having sent Accept-Encoding in the request.
}
return
}
// Negotiate returns the most preferred encoding that is acceptable by the
// client, or an empty string if no encodings are acceptable.
func (e *httpEncodings) Negotiate(codes ...string) string {
prefs := make(map[string]float64, len(codes))
for _, code := range codes {
prefs[code] = 0
}
implicitIdentity := true
for _, enc := range e.encodings {
if enc.code == "*" {
for code := range prefs {
prefs[code] = enc.qval
}
implicitIdentity = false
} else if _, ok := prefs[enc.code]; ok {
prefs[enc.code] = enc.qval
}
if enc.code == "*" || enc.code == "identity" {
implicitIdentity = false
}
}
if _, ok := prefs["identity"]; ok && implicitIdentity {
prefs["identity"] = -1 // sort last
}
encs := make([]httpEncoding, len(codes))
for idx, code := range codes {
encs[idx] = httpEncoding{code, prefs[code]}
}
slices.SortStableFunc(encs, func(a, b httpEncoding) int {
return -cmp.Compare(a.qval, b.qval)
})
for _, enc := range encs {
if enc.qval != 0 {
return enc.code
}
}
return ""
}