forked from
whitequark.org/git-pages
fork of whitequark.org/git-pages with mods for tangled
1package git_pages
2
3import (
4 "cmp"
5 "regexp"
6 "slices"
7 "strconv"
8 "strings"
9)
10
11var httpAcceptEncodingRegexp = regexp.MustCompile(`` +
12 // token optionally prefixed by whitespace
13 `^[ \t]*([a-zA-Z0-9$!#$%&'*+.^_\x60|~-]+)` +
14 // quality value prefixed by a semicolon optionally surrounded by whitespace
15 `(?:[ \t]*;[ \t]*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?` +
16 // optional whitespace followed by comma or end of line
17 `[ \t]*(?:,|$)`,
18)
19
20type httpEncoding struct {
21 code string
22 qval float64
23}
24
25type httpEncodings struct {
26 encodings []httpEncoding
27}
28
29func parseHTTPEncodings(headerValue string) (result httpEncodings) {
30 for headerValue != "" {
31 matches := httpAcceptEncodingRegexp.FindStringSubmatch(headerValue)
32 if matches == nil {
33 return httpEncodings{}
34 }
35 enc := httpEncoding{strings.ToLower(matches[1]), 1.0}
36 if matches[2] != "" {
37 enc.qval, _ = strconv.ParseFloat(matches[2], 64)
38 }
39 result.encodings = append(result.encodings, enc)
40 headerValue = headerValue[len(matches[0]):]
41 }
42 if len(result.encodings) == 0 {
43 // RFC 9110 says (https://httpwg.org/specs/rfc9110.html#field.accept-encoding):
44 // "If no Accept-Encoding header field is in the request, any content
45 // coding is considered acceptable by the user agent."
46 // In practice, no client expects to receive a compressed response
47 // without having sent Accept-Encoding in the request.
48 }
49 return
50}
51
52// Negotiate returns the most preferred encoding that is acceptable by the
53// client, or an empty string if no encodings are acceptable.
54func (e *httpEncodings) Negotiate(codes ...string) string {
55 prefs := make(map[string]float64, len(codes))
56 for _, code := range codes {
57 prefs[code] = 0
58 }
59 implicitIdentity := true
60 for _, enc := range e.encodings {
61 if enc.code == "*" {
62 for code := range prefs {
63 prefs[code] = enc.qval
64 }
65 implicitIdentity = false
66 } else if _, ok := prefs[enc.code]; ok {
67 prefs[enc.code] = enc.qval
68 }
69 if enc.code == "*" || enc.code == "identity" {
70 implicitIdentity = false
71 }
72 }
73 if _, ok := prefs["identity"]; ok && implicitIdentity {
74 prefs["identity"] = -1 // sort last
75 }
76 encs := make([]httpEncoding, len(codes))
77 for idx, code := range codes {
78 encs[idx] = httpEncoding{code, prefs[code]}
79 }
80 slices.SortStableFunc(encs, func(a, b httpEncoding) int {
81 return -cmp.Compare(a.qval, b.qval)
82 })
83 for _, enc := range encs {
84 if enc.qval != 0 {
85 return enc.code
86 }
87 }
88 return ""
89}