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>master
							parent
							
								
									92e2636de0
								
							
						
					
					
						commit
						6bedf7d1cd
					
				| 
						 | 
					@ -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 New Issue