http.go 4.2 KB

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