Skip to content

Commit

Permalink
clair: remove goautoneg
Browse files Browse the repository at this point in the history
This commit uses a hand-rolled Accept-Encoding parser instead of
goautoneg. The new code recognizes commonly used values correct, whereas
the goautoneg package is tailored for the Accept header.
  • Loading branch information
hdonnay committed Jan 23, 2020
1 parent 7b6ef7d commit 791610f
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 7 deletions.
85 changes: 81 additions & 4 deletions cmd/clair/httpcompress.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package main
import (
"fmt"
"io"
"mime"
"net/http"
"sort"
"strconv"
"strings"
"sync"

"github.com/klauspost/compress/flate"
"github.com/klauspost/compress/gzip"
"github.com/klauspost/compress/snappy"
"github.com/markusthoemmes/goautoneg"
)

// Compress wraps the provided http.Handler and provides transparent body
Expand Down Expand Up @@ -48,8 +51,59 @@ type header interface {
WriteHeader(int)
}

// ParseAccept parses an "Accept-Encoding" header.
//
// Reports a sorted list of encodings and a map of disallowed encodings.
// Reports nil if no selections were present.
func parseAccept(h string) ([]accept, map[string]struct{}) {
if h == "" {
return nil, nil
}

segs := strings.Split(h, ",")
ret := make([]accept, 0, len(segs))
nok := make(map[string]struct{})
for _, s := range segs {
a := accept{}
t, param, err := mime.ParseMediaType(s)
if err != nil {
continue
}
a.Type = t
if q, ok := param["q"]; ok {
if q == "0" {
nok[t] = struct{}{}
continue
}
qv, err := strconv.ParseFloat(param["q"], 64)
if err != nil {
nok[t] = struct{}{}
continue
}
a.Q = qv
}
ret = append(ret, a)
}

sort.SliceStable(ret, func(i, j int) bool {
return ret[i].Q > ret[j].Q
})
return ret, nok
}

type accept struct {
Type string
Q float64
}

// ServeHTTP implements http.Handler.
func (c *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ae, nok := parseAccept(r.Header.Get("accept-encoding"))
if ae == nil {
// If there was no header, play it cool.
c.next.ServeHTTP(w, r)
return
}
var (
flusher http.Flusher
pusher http.Pusher
Expand All @@ -59,7 +113,9 @@ func (c *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
pusher, _ = w.(http.Pusher)

// Find the first accept-encoding we support.
for _, a := range goautoneg.ParseAccept(r.Header.Get("accept-encoding")) {
// See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 for
// all the sematics.
for _, a := range ae {
switch a.Type {
case "gzip":
w.Header().Set("content-encoding", "gzip")
Expand All @@ -82,6 +138,27 @@ func (c *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "identity":
w.Header().Set("content-encoding", "identity")
case "*":
// If we hit a star, it's technically OK to return any encoding not
// already specified. So, attempt to use gzip and then identity and
// give up.
// Clients that do extremely weird things like
// *;q=1.0, gzip;q=0.1, identity;q=0.1"
// deserve extremely weird replies.
_, gznok := nok["gzip"]
_, idnok := nok["identity"]
switch {
case !gznok:
w.Header().Set("content-encoding", "gzip")
gz := c.gzip.Get().(*gzip.Writer)
gz.Reset(w)
defer c.gzip.Put(gz)
cw = gz
case !idnok:
w.Header().Set("content-encoding", "identity")
default:
w.WriteHeader(http.StatusNotAcceptable)
return
}
default:
continue
}
Expand All @@ -103,7 +180,7 @@ func (c *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// This is a giant truth table to make anonymous types that satisfy as many
// optional interfaces as possible.
//
// We care about 3 interfaces, so there are 2^3 == 8 combinations
// We care about 3 interfaces, so there are 2^3 == 8 combinations.
switch {
case flusher == nil && pusher == nil && cw == nil:
nw = w
Expand Down Expand Up @@ -148,7 +225,7 @@ func (c *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Pusher
}{w, cw, flusher, pusher}
default:
panic(fmt.Sprintf("unexpect type combination: %T/%T/%T", flusher, pusher, cw))
panic(fmt.Sprintf("unexpected type combination: %T/%T/%T", flusher, pusher, cw))
}
c.next.ServeHTTP(nw, r)
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.13

require (
github.com/klauspost/compress v1.9.4
github.com/markusthoemmes/goautoneg v0.0.0-20190713162725-c6008fefa5b1
github.com/mattn/go-sqlite3 v1.11.0 // indirect
github.com/quay/claircore v0.0.13
github.com/rs/zerolog v1.16.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,6 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/markusthoemmes/goautoneg v0.0.0-20190713162725-c6008fefa5b1 h1:Qhv4Ni88zV+8TY65yr2ak8xU4sblgs6aRT9RuGM5SNU=
github.com/markusthoemmes/goautoneg v0.0.0-20190713162725-c6008fefa5b1/go.mod h1:qFhy2RoC9EWZC7fgczcBbUpzGNFfIm5//VO/gde0AbI=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
Expand Down

0 comments on commit 791610f

Please sign in to comment.