diff --git a/registry/handlers/api_test.go b/registry/handlers/api_test.go index 076207ed2..93585d45f 100644 --- a/registry/handlers/api_test.go +++ b/registry/handlers/api_test.go @@ -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 { diff --git a/registry/handlers/images.go b/registry/handlers/images.go index dd2ed2c84..df7f869be 100644 --- a/registry/handlers/images.go +++ b/registry/handlers/images.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "net/http" + "strings" "github.com/docker/distribution" ctxu "github.com/docker/distribution/context" @@ -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 }