/ src / http.go
http.go
 1  package git_pages
 2  
 3  import (
 4  	"cmp"
 5  	"regexp"
 6  	"slices"
 7  	"strconv"
 8  	"strings"
 9  )
10  
11  var 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  
20  type httpEncoding struct {
21  	code string
22  	qval float64
23  }
24  
25  type httpEncodings struct {
26  	encodings []httpEncoding
27  }
28  
29  func 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.
54  func (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  }