encoding.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package response
  4. import (
  5. "slices"
  6. "strconv"
  7. "strings"
  8. )
  9. type acceptEncodingParser struct {
  10. // accepted contains all encoding that particular parser instance advertises.
  11. accepted []string
  12. }
  13. // AcceptEncoding creates parser instance for "Accept-Encoding" header values.
  14. // It accepts list of encodings recognized by user of this parser instance.
  15. func AcceptEncoding(accepted ...string) *acceptEncodingParser {
  16. return &acceptEncodingParser{accepted: accepted}
  17. }
  18. // Parse the input string according to [HTTP Semantics] and return our
  19. // most-preferred encoding that the client accepts. Candidates are matched in
  20. // the order of the accepted list passed to [AcceptEncoding], so the server's
  21. // preference wins over the order (and weights) the client advertises.
  22. //
  23. // Currently this function ignores set weights other than q=0.
  24. // Encodings with q=0 will not be considered.
  25. //
  26. // Returns "identity" if the input is empty or no accepted encoding matches.
  27. //
  28. // [HTTP Semantics]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding.
  29. func (p *acceptEncodingParser) Parse(acceptEncoding string) string {
  30. // bestIdx tracks the lowest index into p.accepted (i.e. the server's most
  31. // preferred encoding) seen so far. Tracking it inline avoids collecting the
  32. // client's encodings into a slice, keeping this method allocation free.
  33. bestIdx := -1
  34. for enc := range strings.SplitSeq(acceptEncoding, ",") {
  35. enc = strings.TrimSpace(enc)
  36. if qi := strings.IndexByte(enc, ';'); qi > -1 {
  37. qstr := strings.TrimPrefix(enc[qi:], ";")
  38. qstr = strings.TrimSpace(qstr)
  39. qstr = strings.TrimPrefix(qstr, "q=")
  40. q, err := strconv.ParseFloat(qstr, 64)
  41. if err != nil || q == 0 {
  42. continue // Ignore weird float values.
  43. }
  44. enc = strings.TrimSpace(enc[:qi])
  45. }
  46. if i := slices.Index(p.accepted, enc); i > -1 && (bestIdx == -1 || i < bestIdx) {
  47. bestIdx = i
  48. if bestIdx == 0 {
  49. break // Nothing can beat the top server preference.
  50. }
  51. }
  52. }
  53. if bestIdx == -1 {
  54. return "identity"
  55. }
  56. return p.accepted[bestIdx]
  57. }