Skip to content

Commit

Permalink
Update "Accept" header parsing for list values
Browse files Browse the repository at this point in the history
In Go's header parsing, the same header multiple times results in multiple entries in the `r.Header[...]` slice, but Go does no further parsing beyond that (and in https://golang.org/cl/4528086 it was determined that until/unless the stdlib itself needs it, Go will not do so).

The consequence here for parsing of `Accept:` headers is that we support the way Go outputs headers, but not all language HTTP libraries have a facility to output multiple headers instead of a single list header.

This change ensures that the following (valid) header blocks all parse to the same result for the purposes of what is being tested here:

```
Accept: a/b
Accept: b/c
Accept: d/e
```

```
Accept: a/b; q=0.5, b/c
Accept: d/e
```

```
Accept: a/b; q=0.1, b/c; q=0.2, d/e; q=0.8
```

Signed-off-by: Andrew "Tianon" Page <[email protected]>
  • Loading branch information
tianon committed Jun 10, 2016
1 parent 75882f0 commit 8907f7d
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 4 deletions.
4 changes: 2 additions & 2 deletions registry/handlers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1586,8 +1586,8 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs)
if err != nil {
t.Fatalf("Error constructing request: %s", err)
}
req.Header.Set("Accept", manifestlist.MediaTypeManifestList)
req.Header.Add("Accept", schema1.MediaTypeSignedManifest)
// multiple headers in mixed list format to ensure we parse correctly server-side
req.Header.Set("Accept", fmt.Sprintf(` %s ; q=0.8 , %s ; q=0.5 `, manifestlist.MediaTypeManifestList, schema1.MediaTypeSignedManifest))
req.Header.Add("Accept", schema2.MediaTypeManifest)
resp, err = http.DefaultClient.Do(req)
if err != nil {
Expand Down
20 changes: 18 additions & 2 deletions registry/handlers/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"net/http"
"strings"

"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
Expand Down Expand Up @@ -98,8 +99,23 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http

supportsSchema2 := false
supportsManifestList := false
if acceptHeaders, ok := r.Header["Accept"]; ok {
for _, mediaType := range acceptHeaders {
// this parsing of Accept headers is not quite as full-featured as godoc.org's parser, but we don't care about "q=" values
// https://github.com/golang/gddo/blob/e91d4165076d7474d20abda83f92d15c7ebc3e81/httputil/header/header.go#L165-L202
for _, acceptHeader := range r.Header["Accept"] {
// r.Header[...] is a slice in case the request contains the same header more than once
// if the header isn't set, we'll get the zero value, which "range" will handle gracefully

// we need to split each header value on "," to get the full list of "Accept" values (per RFC 2616)
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
for _, mediaType := range strings.Split(acceptHeader, ",") {
// remove "; q=..." if present
if i := strings.Index(mediaType, ";"); i >= 0 {
mediaType = mediaType[:i]
}

// it's common (but not required) for Accept values to be space separated ("a/b, c/d, e/f")
mediaType = strings.TrimSpace(mediaType)

if mediaType == schema2.MediaTypeManifest {
supportsSchema2 = true
}
Expand Down

0 comments on commit 8907f7d

Please sign in to comment.