func()

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")
	}
}