in registry/handlers/manifests.go [112:222]
func (imh *manifestHandler) HandleGetManifest(w http.ResponseWriter, r *http.Request) {
l := log.GetLogger(log.WithContext(imh))
l.Debug("HandleGetImageManifest")
manifestGetter := imh.newManifestGetter(r)
var (
m distribution.Manifest
getErr error
)
if imh.Tag != "" {
m, imh.Digest, getErr = manifestGetter.GetByTag(imh.Context, imh.Tag)
} else {
m, getErr = manifestGetter.GetByDigest(imh.Context, imh.Digest)
}
if getErr != nil {
switch {
case errors.Is(getErr, errETagMatches):
w.WriteHeader(http.StatusNotModified)
case errors.As(getErr, &distribution.ErrManifestUnknownRevision{}),
errors.As(getErr, &distribution.ErrManifestUnknown{}),
errors.Is(getErr, digest.ErrDigestInvalidFormat),
errors.As(getErr, &distribution.ErrTagUnknown{}):
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(getErr))
case errors.Is(getErr, distribution.ErrSchemaV1Unsupported):
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithMessage("Schema 1 manifest not supported"))
default:
imh.Errors = append(imh.Errors, errcode.FromUnknownError(getErr))
}
return
}
// determine the type of the returned manifest
manifestType := manifestSchema1
_, isSchema2 := m.(*schema2.DeserializedManifest)
manifestList, isManifestList := m.(*manifestlist.DeserializedManifestList)
if isSchema2 {
manifestType = manifestSchema2
} else if _, isOCImanifest := m.(*ocischema.DeserializedManifest); isOCImanifest {
manifestType = ociImageManifestSchema
} else if isManifestList {
// nolint: revive // max-control-nesting
if manifestList.MediaType == manifestlist.MediaTypeManifestList {
manifestType = manifestlistSchema
} else if manifestList.MediaType == v1.MediaTypeImageIndex || manifestList.MediaType == "" {
manifestType = ociImageIndexSchema
}
}
if manifestType == manifestSchema1 {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithMessage("Schema 1 manifest not supported"))
return
}
if manifestType == ociImageManifestSchema && !supports(r, ociImageManifestSchema) {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithMessage("OCI manifest found, but accept header does not support OCI manifests"))
return
}
if manifestType == ociImageIndexSchema && !supports(r, ociImageIndexSchema) {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithMessage("OCI index found, but accept header does not support OCI indexes"))
return
}
if isManifestList {
logIfManifestListInvalid(imh, manifestList, http.MethodGet)
}
// Only rewrite manifests lists when they are being fetched by tag. If they
// are being fetched by digest, we can't return something not matching the digest.
if imh.Tag != "" && manifestType == manifestlistSchema && !supports(r, manifestlistSchema) {
var err error
m, err = imh.rewriteManifestList(manifestList)
if err != nil {
switch err := err.(type) {
case distribution.ErrManifestUnknownRevision:
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
case errcode.Error:
imh.Errors = append(imh.Errors, err)
default:
imh.Errors = append(imh.Errors, errcode.FromUnknownError(err))
}
return
}
}
ct, p, err := m.Payload()
if err != nil {
imh.Errors = append(imh.Errors, errcode.FromUnknownError(err))
return
}
if err := imh.queueBridge.ManifestPulled(imh.Repository.Named(), m, distribution.WithTagOption{Tag: imh.Tag}); err != nil {
l.WithError(err).Error("dispatching manifest pull to queue")
}
w.Header().Set("Content-Type", ct)
w.Header().Set("Content-Length", fmt.Sprint(len(p)))
w.Header().Set("Docker-Content-Digest", imh.Digest.String())
w.Header().Set("Etag", fmt.Sprintf(`"%s"`, imh.Digest))
if r.Method == http.MethodGet {
_, _ = w.Write(p)
l.WithFields(log.Fields{
"media_type": manifestType.MediaType(),
"size_bytes": len(p),
"digest": imh.Digest,
"tag_name": imh.Tag,
"reference_count": len(m.References()),
}).Info("manifest downloaded")
}
}