pkg/containerd/handler.go (106 lines of code) (raw):
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package containerd
import (
"fmt"
"net/http"
"strconv"
"time"
pcontext "github.com/azure/peerd/pkg/context"
"github.com/azure/peerd/pkg/oci/distribution"
"github.com/opencontainers/go-digest"
)
// Response headers
const (
maxManifestSize = 4 * 1024 * 1024
dockerContentDigestHeader = "Docker-Content-Digest"
contentLengthHeader = "Content-Length"
contentTypeHeader = "Content-Type"
)
// Registry is a handler that handles requests to this registry.
type Registry struct {
containerdStore Store
}
// Handle handles a request to this registry.
func (r *Registry) Handle(c pcontext.Context) {
dgstStr := c.GetString(pcontext.DigestCtxKey)
ref := c.GetString(pcontext.ReferenceCtxKey)
var d digest.Digest
var err error
l := pcontext.Logger(c).With().Str("handler", "registry").Str("ref", ref).Str("digest", dgstStr).Logger()
l.Debug().Msg("registry handler start")
s := time.Now()
defer func() {
l.Debug().Dur("duration", time.Since(s)).Int("status", c.Writer.Status()).Str("digest", d.String()).Msg("registry handler stop")
}()
// Serve registry endpoints.
if dgstStr == "" {
d, err = r.containerdStore.Resolve(c, ref)
if err != nil {
//nolint
c.AbortWithError(http.StatusNotFound, err)
return
}
} else {
d, err = digest.Parse(dgstStr)
if err != nil {
//nolint
c.AbortWithError(http.StatusBadRequest, err)
return
}
}
refType, ok := c.Get(pcontext.RefTypeCtxKey)
if !ok {
//nolint
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("ref type not found in context"))
return
}
switch refType.(distribution.ReferenceType) {
case distribution.ReferenceTypeManifest:
r.handleManifest(c, d)
return
case distribution.ReferenceTypeBlob:
r.handleBlob(c, d)
return
}
// If nothing matches return 404.
c.Status(http.StatusNotFound)
}
// handleManifest handles a manifest request.
func (r *Registry) handleManifest(c pcontext.Context, dgst digest.Digest) {
size, err := r.containerdStore.Size(c, dgst)
if err != nil {
//nolint
c.AbortWithError(http.StatusNotFound, err)
return
} else if size >= maxManifestSize {
//nolint
c.AbortWithError(http.StatusNotFound, fmt.Errorf("refusing to serve a manifest larger than %v bytes, got: %v", maxManifestSize, size))
return
}
b, mediaType, err := r.containerdStore.Bytes(c, dgst)
if err != nil {
//nolint
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.Header(contentTypeHeader, mediaType)
c.Header(contentLengthHeader, strconv.FormatInt(int64(len(b)), 10))
c.Header(dockerContentDigestHeader, dgst.String())
if c.Request.Method == http.MethodHead {
return
}
_, err = c.Writer.Write(b)
if err != nil {
//nolint
c.AbortWithError(http.StatusServiceUnavailable, err)
return
}
}
// handleBlob handles a blob request.
func (r *Registry) handleBlob(c pcontext.Context, dgst digest.Digest) {
size, err := r.containerdStore.Size(c, dgst)
if err != nil {
//nolint
c.AbortWithError(http.StatusNotFound, err)
return
}
c.Header(contentLengthHeader, strconv.FormatInt(size, 10))
c.Header(dockerContentDigestHeader, dgst.String())
if c.Request.Method == http.MethodHead {
return
}
err = r.containerdStore.Write(c, c.Writer, dgst)
if err != nil {
//nolint
c.AbortWithError(http.StatusInternalServerError, err)
return
}
}
// NewRegistry creates a new registry handler.
func NewRegistry(containerdStore Store) *Registry {
return &Registry{
containerdStore: containerdStore,
}
}