distribution/storage/delegatelayerhandler.go
Brian Bland 17915e1b01 Adds support for content redirects for layer downloads
Includes a delegate implementation which redirects to the URL generated
by the storagedriver, and a cloudfront implementation.
Satisfies proposal #49
2015-01-08 17:01:28 -08:00

84 lines
2.4 KiB
Go

package storage
import (
"net/http"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/storagedriver"
)
// delegateLayerHandler provides a simple implementation of layerHandler that
// simply issues HTTP Temporary Redirects to the URL provided by the
// storagedriver for a given Layer.
type delegateLayerHandler struct {
storageDriver storagedriver.StorageDriver
pathMapper *pathMapper
}
var _ LayerHandler = &delegateLayerHandler{}
func newDelegateLayerHandler(storageDriver storagedriver.StorageDriver, options map[string]interface{}) (LayerHandler, error) {
return &delegateLayerHandler{storageDriver: storageDriver, pathMapper: defaultPathMapper}, nil
}
// Resolve returns an http.Handler which can serve the contents of the given
// Layer, or an error if not supported by the storagedriver.
func (lh *delegateLayerHandler) Resolve(layer Layer) (http.Handler, error) {
layerURL, err := lh.urlFor(layer)
if err != nil {
return nil, err
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, layerURL, http.StatusTemporaryRedirect)
}), nil
}
// urlFor returns a download URL for the given layer, or the empty string if
// unsupported.
func (lh *delegateLayerHandler) urlFor(layer Layer) (string, error) {
blobPath, err := lh.resolveBlobPath(layer.Name(), layer.Digest())
if err != nil {
return "", err
}
layerURL, err := lh.storageDriver.URLFor(blobPath)
if err != nil {
return "", err
}
return layerURL, nil
}
// resolveBlobPath looks up the blob location in the repositories from a
// layer/blob link file, returning blob path or an error on failure.
func (lh *delegateLayerHandler) resolveBlobPath(name string, dgst digest.Digest) (string, error) {
pathSpec := layerLinkPathSpec{name: name, digest: dgst}
layerLinkPath, err := lh.pathMapper.path(pathSpec)
if err != nil {
return "", err
}
layerLinkContent, err := lh.storageDriver.GetContent(layerLinkPath)
if err != nil {
return "", err
}
// NOTE(stevvooe): The content of the layer link should match the digest.
// This layer of indirection is for name-based content protection.
linked, err := digest.ParseDigest(string(layerLinkContent))
if err != nil {
return "", err
}
bp := blobPathSpec{digest: linked}
return lh.pathMapper.path(bp)
}
// init registers the delegate layerHandler backend.
func init() {
RegisterLayerHandler("delegate", LayerHandlerInitFunc(newDelegateLayerHandler))
}