Add Etag header for manifests.
Return 304 (Not Modified) if retrieved with If-None-Match header Signed-off-by: Richard Scothern <richard.scothern@gmail.com>
This commit is contained in:
parent
92e2636de0
commit
6bedf7d1cd
2 changed files with 47 additions and 0 deletions
|
@ -449,6 +449,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK)
|
checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK)
|
||||||
checkHeaders(t, resp, http.Header{
|
checkHeaders(t, resp, http.Header{
|
||||||
"Docker-Content-Digest": []string{dgst.String()},
|
"Docker-Content-Digest": []string{dgst.String()},
|
||||||
|
"ETag": []string{dgst.String()},
|
||||||
})
|
})
|
||||||
|
|
||||||
var fetchedManifest manifest.SignedManifest
|
var fetchedManifest manifest.SignedManifest
|
||||||
|
@ -470,6 +471,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK)
|
checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK)
|
||||||
checkHeaders(t, resp, http.Header{
|
checkHeaders(t, resp, http.Header{
|
||||||
"Docker-Content-Digest": []string{dgst.String()},
|
"Docker-Content-Digest": []string{dgst.String()},
|
||||||
|
"ETag": []string{dgst.String()},
|
||||||
})
|
})
|
||||||
|
|
||||||
var fetchedManifestByDigest manifest.SignedManifest
|
var fetchedManifestByDigest manifest.SignedManifest
|
||||||
|
@ -482,6 +484,33 @@ func TestManifestAPI(t *testing.T) {
|
||||||
t.Fatalf("manifests do not match")
|
t.Fatalf("manifests do not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get by name with etag, gives 304
|
||||||
|
etag := resp.Header.Get("Etag")
|
||||||
|
req, err := http.NewRequest("GET", manifestURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error constructing request: %s", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("If-None-Match", etag)
|
||||||
|
resp, err = http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error constructing request: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkResponse(t, "fetching layer with etag", resp, http.StatusNotModified)
|
||||||
|
|
||||||
|
// Get by digest with etag, gives 304
|
||||||
|
req, err = http.NewRequest("GET", manifestDigestURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error constructing request: %s", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("If-None-Match", etag)
|
||||||
|
resp, err = http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error constructing request: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkResponse(t, "fetching layer with etag", resp, http.StatusNotModified)
|
||||||
|
|
||||||
// Ensure that the tag is listed.
|
// Ensure that the tag is listed.
|
||||||
resp, err = http.Get(tagsURL)
|
resp, err = http.Get(tagsURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -60,6 +60,10 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||||
if imh.Tag != "" {
|
if imh.Tag != "" {
|
||||||
sm, err = manifests.GetByTag(imh.Tag)
|
sm, err = manifests.GetByTag(imh.Tag)
|
||||||
} else {
|
} else {
|
||||||
|
if etagMatch(r, imh.Digest.String()) {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
sm, err = manifests.Get(imh.Digest)
|
sm, err = manifests.Get(imh.Digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +79,10 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeDigestInvalid.WithDetail(err))
|
imh.Errors = append(imh.Errors, v2.ErrorCodeDigestInvalid.WithDetail(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if etagMatch(r, dgst.String()) {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
imh.Digest = dgst
|
imh.Digest = dgst
|
||||||
}
|
}
|
||||||
|
@ -82,9 +90,19 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
w.Header().Set("Content-Length", fmt.Sprint(len(sm.Raw)))
|
w.Header().Set("Content-Length", fmt.Sprint(len(sm.Raw)))
|
||||||
w.Header().Set("Docker-Content-Digest", imh.Digest.String())
|
w.Header().Set("Docker-Content-Digest", imh.Digest.String())
|
||||||
|
w.Header().Set("Etag", imh.Digest.String())
|
||||||
w.Write(sm.Raw)
|
w.Write(sm.Raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func etagMatch(r *http.Request, etag string) bool {
|
||||||
|
for _, headerVal := range r.Header["If-None-Match"] {
|
||||||
|
if headerVal == etag {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// PutImageManifest validates and stores and image in the registry.
|
// PutImageManifest validates and stores and image in the registry.
|
||||||
func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) {
|
func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
ctxu.GetLogger(imh).Debug("PutImageManifest")
|
ctxu.GetLogger(imh).Debug("PutImageManifest")
|
||||||
|
|
Loading…
Reference in a new issue