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 .