forked from TrueCloudLab/distribution
Include headers when serving blob through proxy
In commit 17952924f3
we updated ServeBlob() to use an io.MultiWriter to
write simultaneously to the local store and the HTTP response.
However, copyContent was using a type assertion to only add headers if
the io.Writer was a http.ResponseWriter. Therefore, this change caused
us to stop sending the expected headers (i.e. Content-Length, Etag,
etc.) on the first request for a blob.
Resolve the issue by explicitly passing in http.Header and setting it
unconditionally.
Signed-off-by: Mikel Rychliski <mikel@mikelr.com>
This commit is contained in:
parent
9b3eac8f08
commit
041824555c
2 changed files with 20 additions and 12 deletions
|
@ -33,22 +33,20 @@ var inflight = make(map[digest.Digest]struct{})
|
||||||
// mu protects inflight
|
// mu protects inflight
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
|
|
||||||
func setResponseHeaders(w http.ResponseWriter, length int64, mediaType string, digest digest.Digest) {
|
func setResponseHeaders(h http.Header, length int64, mediaType string, digest digest.Digest) {
|
||||||
w.Header().Set("Content-Length", strconv.FormatInt(length, 10))
|
h.Set("Content-Length", strconv.FormatInt(length, 10))
|
||||||
w.Header().Set("Content-Type", mediaType)
|
h.Set("Content-Type", mediaType)
|
||||||
w.Header().Set("Docker-Content-Digest", digest.String())
|
h.Set("Docker-Content-Digest", digest.String())
|
||||||
w.Header().Set("Etag", digest.String())
|
h.Set("Etag", digest.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pbs *proxyBlobStore) copyContent(ctx context.Context, dgst digest.Digest, writer io.Writer) (distribution.Descriptor, error) {
|
func (pbs *proxyBlobStore) copyContent(ctx context.Context, dgst digest.Digest, writer io.Writer, h http.Header) (distribution.Descriptor, error) {
|
||||||
desc, err := pbs.remoteStore.Stat(ctx, dgst)
|
desc, err := pbs.remoteStore.Stat(ctx, dgst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return distribution.Descriptor{}, err
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if w, ok := writer.(http.ResponseWriter); ok {
|
setResponseHeaders(h, desc.Size, desc.MediaType, dgst)
|
||||||
setResponseHeaders(w, desc.Size, desc.MediaType, dgst)
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteReader, err := pbs.remoteStore.Open(ctx, dgst)
|
remoteReader, err := pbs.remoteStore.Open(ctx, dgst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -102,7 +100,7 @@ func (pbs *proxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter,
|
||||||
// Will return the blob from the remote store directly.
|
// Will return the blob from the remote store directly.
|
||||||
// TODO Maybe we could reuse the these blobs are serving remotely and caching locally.
|
// TODO Maybe we could reuse the these blobs are serving remotely and caching locally.
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
_, err := pbs.copyContent(ctx, dgst, w)
|
_, err := pbs.copyContent(ctx, dgst, w, w.Header())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
inflight[dgst] = struct{}{}
|
inflight[dgst] = struct{}{}
|
||||||
|
@ -122,7 +120,7 @@ func (pbs *proxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter,
|
||||||
// Serving client and storing locally over same fetching request.
|
// Serving client and storing locally over same fetching request.
|
||||||
// This can prevent a redundant blob fetching.
|
// This can prevent a redundant blob fetching.
|
||||||
multiWriter := io.MultiWriter(w, bw)
|
multiWriter := io.MultiWriter(w, bw)
|
||||||
desc, err := pbs.copyContent(ctx, dgst, multiWriter)
|
desc, err := pbs.copyContent(ctx, dgst, multiWriter, w.Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,12 +448,22 @@ func testProxyStoreServe(t *testing.T, te *testEnv, numClients int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyBytes := w.Body.Bytes()
|
resp := w.Result()
|
||||||
|
bodyBytes, err := io.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
localDigest := digest.FromBytes(bodyBytes)
|
localDigest := digest.FromBytes(bodyBytes)
|
||||||
if localDigest != remoteBlob.Digest {
|
if localDigest != remoteBlob.Digest {
|
||||||
t.Errorf("Mismatching blob fetch from proxy")
|
t.Errorf("Mismatching blob fetch from proxy")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if resp.Header.Get("Docker-Content-Digest") != localDigest.String() {
|
||||||
|
t.Errorf("Mismatching digest in response header")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
desc, err := te.store.localStore.Stat(te.ctx, remoteBlob.Digest)
|
desc, err := te.store.localStore.Stat(te.ctx, remoteBlob.Digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue