http.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package brotli
  2. import (
  3. "io"
  4. "net/http"
  5. "strings"
  6. "github.com/andybalholm/brotli/flate"
  7. )
  8. // HTTPCompressor chooses a compression method (brotli, gzip, or none) based on
  9. // the Accept-Encoding header, sets the Content-Encoding header, and returns a
  10. // WriteCloser that implements that compression. The Close method must be called
  11. // before the current HTTP handler returns.
  12. func HTTPCompressor(w http.ResponseWriter, r *http.Request) io.WriteCloser {
  13. return HTTPCompressorWithLevel(w, r, 4)
  14. }
  15. func HTTPCompressorWithLevel(w http.ResponseWriter, r *http.Request, level int) io.WriteCloser {
  16. if w.Header().Get("Vary") == "" {
  17. w.Header().Set("Vary", "Accept-Encoding")
  18. }
  19. encoding := negotiateContentEncoding(r, []string{"br", "gzip"})
  20. switch encoding {
  21. case "br":
  22. w.Header().Set("Content-Encoding", "br")
  23. return NewWriterV2(w, level)
  24. case "gzip":
  25. w.Header().Set("Content-Encoding", "gzip")
  26. return flate.NewGZIPWriter(w, level)
  27. }
  28. return nopCloser{w}
  29. }
  30. // negotiateContentEncoding returns the best offered content encoding for the
  31. // request's Accept-Encoding header. If two offers match with equal weight and
  32. // then the offer earlier in the list is preferred. If no offers are
  33. // acceptable, then "" is returned.
  34. func negotiateContentEncoding(r *http.Request, offers []string) string {
  35. bestOffer := "identity"
  36. bestQ := -1.0
  37. specs := parseAccept(r.Header, "Accept-Encoding")
  38. for _, offer := range offers {
  39. for _, spec := range specs {
  40. if spec.Q > bestQ &&
  41. (spec.Value == "*" || spec.Value == offer) {
  42. bestQ = spec.Q
  43. bestOffer = offer
  44. }
  45. }
  46. }
  47. if bestQ == 0 {
  48. bestOffer = ""
  49. }
  50. return bestOffer
  51. }
  52. // acceptSpec describes an Accept* header.
  53. type acceptSpec struct {
  54. Value string
  55. Q float64
  56. }
  57. // parseAccept parses Accept* headers.
  58. func parseAccept(header http.Header, key string) (specs []acceptSpec) {
  59. loop:
  60. for _, s := range header[key] {
  61. for {
  62. var spec acceptSpec
  63. spec.Value, s = expectTokenSlash(s)
  64. if spec.Value == "" {
  65. continue loop
  66. }
  67. spec.Q = 1.0
  68. s = skipSpace(s)
  69. if strings.HasPrefix(s, ";") {
  70. s = skipSpace(s[1:])
  71. if !strings.HasPrefix(s, "q=") {
  72. continue loop
  73. }
  74. spec.Q, s = expectQuality(s[2:])
  75. if spec.Q < 0.0 {
  76. continue loop
  77. }
  78. }
  79. specs = append(specs, spec)
  80. s = skipSpace(s)
  81. if !strings.HasPrefix(s, ",") {
  82. continue loop
  83. }
  84. s = skipSpace(s[1:])
  85. }
  86. }
  87. return
  88. }
  89. func skipSpace(s string) (rest string) {
  90. i := 0
  91. for ; i < len(s); i++ {
  92. if octetTypes[s[i]]&isSpace == 0 {
  93. break
  94. }
  95. }
  96. return s[i:]
  97. }
  98. func expectTokenSlash(s string) (token, rest string) {
  99. i := 0
  100. for ; i < len(s); i++ {
  101. b := s[i]
  102. if (octetTypes[b]&isToken == 0) && b != '/' {
  103. break
  104. }
  105. }
  106. return s[:i], s[i:]
  107. }
  108. func expectQuality(s string) (q float64, rest string) {
  109. switch {
  110. case len(s) == 0:
  111. return -1, ""
  112. case s[0] == '0':
  113. q = 0
  114. case s[0] == '1':
  115. q = 1
  116. default:
  117. return -1, ""
  118. }
  119. s = s[1:]
  120. if !strings.HasPrefix(s, ".") {
  121. return q, s
  122. }
  123. s = s[1:]
  124. i := 0
  125. n := 0
  126. d := 1
  127. for ; i < len(s); i++ {
  128. b := s[i]
  129. if b < '0' || b > '9' {
  130. break
  131. }
  132. n = n*10 + int(b) - '0'
  133. d *= 10
  134. }
  135. return q + float64(n)/float64(d), s[i:]
  136. }
  137. // Octet types from RFC 2616.
  138. var octetTypes [256]octetType
  139. type octetType byte
  140. const (
  141. isToken octetType = 1 << iota
  142. isSpace
  143. )
  144. func init() {
  145. // OCTET = <any 8-bit sequence of data>
  146. // CHAR = <any US-ASCII character (octets 0 - 127)>
  147. // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
  148. // CR = <US-ASCII CR, carriage return (13)>
  149. // LF = <US-ASCII LF, linefeed (10)>
  150. // SP = <US-ASCII SP, space (32)>
  151. // HT = <US-ASCII HT, horizontal-tab (9)>
  152. // <"> = <US-ASCII double-quote mark (34)>
  153. // CRLF = CR LF
  154. // LWS = [CRLF] 1*( SP | HT )
  155. // TEXT = <any OCTET except CTLs, but including LWS>
  156. // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
  157. // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
  158. // token = 1*<any CHAR except CTLs or separators>
  159. // qdtext = <any TEXT except <">>
  160. for c := 0; c < 256; c++ {
  161. var t octetType
  162. isCtl := c <= 31 || c == 127
  163. isChar := 0 <= c && c <= 127
  164. isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c))
  165. if strings.ContainsRune(" \t\r\n", rune(c)) {
  166. t |= isSpace
  167. }
  168. if isChar && !isCtl && !isSeparator {
  169. t |= isToken
  170. }
  171. octetTypes[c] = t
  172. }
  173. }