package brotli
import (
"compress/gzip"
"io"
"net/http"
"strings"
)
func HTTPCompressor (w http .ResponseWriter , r *http .Request ) io .WriteCloser {
if w .Header ().Get ("Vary" ) == "" {
w .Header ().Set ("Vary" , "Accept-Encoding" )
}
encoding := negotiateContentEncoding (r , []string {"br" , "gzip" })
switch encoding {
case "br" :
w .Header ().Set ("Content-Encoding" , "br" )
return NewWriter (w )
case "gzip" :
w .Header ().Set ("Content-Encoding" , "gzip" )
return gzip .NewWriter (w )
}
return nopCloser {w }
}
func negotiateContentEncoding(r *http .Request , offers []string ) string {
bestOffer := "identity"
bestQ := -1.0
specs := parseAccept (r .Header , "Accept-Encoding" )
for _ , offer := range offers {
for _ , spec := range specs {
if spec .Q > bestQ &&
(spec .Value == "*" || spec .Value == offer ) {
bestQ = spec .Q
bestOffer = offer
}
}
}
if bestQ == 0 {
bestOffer = ""
}
return bestOffer
}
type acceptSpec struct {
Value string
Q float64
}
func parseAccept(header http .Header , key string ) (specs []acceptSpec ) {
loop :
for _ , s := range header [key ] {
for {
var spec acceptSpec
spec .Value , s = expectTokenSlash (s )
if spec .Value == "" {
continue loop
}
spec .Q = 1.0
s = skipSpace (s )
if strings .HasPrefix (s , ";" ) {
s = skipSpace (s [1 :])
if !strings .HasPrefix (s , "q=" ) {
continue loop
}
spec .Q , s = expectQuality (s [2 :])
if spec .Q < 0.0 {
continue loop
}
}
specs = append (specs , spec )
s = skipSpace (s )
if !strings .HasPrefix (s , "," ) {
continue loop
}
s = skipSpace (s [1 :])
}
}
return
}
func skipSpace(s string ) (rest string ) {
i := 0
for ; i < len (s ); i ++ {
if octetTypes [s [i ]]&isSpace == 0 {
break
}
}
return s [i :]
}
func expectTokenSlash(s string ) (token , rest string ) {
i := 0
for ; i < len (s ); i ++ {
b := s [i ]
if (octetTypes [b ]&isToken == 0 ) && b != '/' {
break
}
}
return s [:i ], s [i :]
}
func expectQuality(s string ) (q float64 , rest string ) {
switch {
case len (s ) == 0 :
return -1 , ""
case s [0 ] == '0' :
q = 0
case s [0 ] == '1' :
q = 1
default :
return -1 , ""
}
s = s [1 :]
if !strings .HasPrefix (s , "." ) {
return q , s
}
s = s [1 :]
i := 0
n := 0
d := 1
for ; i < len (s ); i ++ {
b := s [i ]
if b < '0' || b > '9' {
break
}
n = n *10 + int (b ) - '0'
d *= 10
}
return q + float64 (n )/float64 (d ), s [i :]
}
var octetTypes [256 ]octetType
type octetType byte
const (
isToken octetType = 1 << iota
isSpace
)
func init() {
for c := 0 ; c < 256 ; c ++ {
var t octetType
isCtl := c <= 31 || c == 127
isChar := 0 <= c && c <= 127
isSeparator := strings .ContainsRune (" \t\"(),/:;<=>?@[]\\{}" , rune (c ))
if strings .ContainsRune (" \t\r\n" , rune (c )) {
t |= isSpace
}
if isChar && !isCtl && !isSeparator {
t |= isToken
}
octetTypes [c ] = t
}
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .