diff --git a/registry.go b/registry.go index 359c80ac..c12a6ad6 100644 --- a/registry.go +++ b/registry.go @@ -108,10 +108,10 @@ type Layer interface { // CreatedAt returns the time this layer was created. CreatedAt() time.Time - // ServeHTTP allows a layer to serve itself, whether by providing - // a redirect directly to the content, or by serving the content - // itself - ServeHTTP(w http.ResponseWriter, r *http.Request) + // Handler returns an HTTP handler which serves the layer content, whether + // by providing a redirect directly to the content, or by serving the + // content itself. + Handler(r *http.Request) (http.Handler, error) } // LayerUpload provides a handle for working with in-progress uploads. diff --git a/registry/handlers/layer.go b/registry/handlers/layer.go index ae73aee0..b8230135 100644 --- a/registry/handlers/layer.go +++ b/registry/handlers/layer.go @@ -63,5 +63,12 @@ func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) { return } - layer.ServeHTTP(w, r) + handler, err := layer.Handler(r) + if err != nil { + ctxu.GetLogger(lh).Debugf("unexpected error getting layer HTTP handler: %s", err) + lh.Errors.Push(v2.ErrorCodeUnknown, err) + return + } + + handler.ServeHTTP(w, r) } diff --git a/registry/storage/driver/storagedriver.go b/registry/storage/driver/storagedriver.go index dd8fb4a0..f0fe7fef 100644 --- a/registry/storage/driver/storagedriver.go +++ b/registry/storage/driver/storagedriver.go @@ -73,7 +73,7 @@ type StorageDriver interface { // URLFor returns a URL which may be used to retrieve the content stored at // the given path, possibly using the given options. - // May return an UnsupportedMethodErr in certain StorageDriver + // May return an ErrUnsupportedMethod in certain StorageDriver // implementations. URLFor(path string, options map[string]interface{}) (string, error) } @@ -85,8 +85,8 @@ type StorageDriver interface { // hyphen. var PathRegexp = regexp.MustCompile(`^(/[a-z0-9._-]+)+$`) -// UnsupportedMethodErr may be returned in the case where a StorageDriver implementation does not support an optional method. -var ErrUnsupportedMethod = errors.New("Unsupported method") +// ErrUnsupportedMethod may be returned in the case where a StorageDriver implementation does not support an optional method. +var ErrUnsupportedMethod = errors.New("unsupported method") // PathNotFoundError is returned when operating on a nonexistent path. type PathNotFoundError struct { diff --git a/registry/storage/layerreader.go b/registry/storage/layerreader.go index 9d6d8c8a..414951d9 100644 --- a/registry/storage/layerreader.go +++ b/registry/storage/layerreader.go @@ -6,6 +6,7 @@ import ( "github.com/docker/distribution" "github.com/docker/distribution/digest" + "github.com/docker/distribution/registry/storage/driver" ) // layerReader implements Layer and provides facilities for reading and @@ -35,11 +36,29 @@ func (lr *layerReader) Close() error { return lr.closeWithErr(distribution.ErrLayerClosed) } -func (lr *layerReader) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Docker-Content-Digest", lr.digest.String()) +func (lr *layerReader) Handler(r *http.Request) (h http.Handler, err error) { + var handlerFunc http.HandlerFunc - if url, err := lr.fileReader.driver.URLFor(lr.path, map[string]interface{}{"method": r.Method}); err == nil { - http.Redirect(w, r, url, http.StatusTemporaryRedirect) + redirectURL, err := lr.fileReader.driver.URLFor(lr.path, map[string]interface{}{"method": r.Method}) + + switch err { + case nil: + handlerFunc = func(w http.ResponseWriter, r *http.Request) { + // Redirect to storage URL. + http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect) + } + case driver.ErrUnsupportedMethod: + handlerFunc = func(w http.ResponseWriter, r *http.Request) { + // Fallback to serving the content directly. + http.ServeContent(w, r, lr.digest.String(), lr.CreatedAt(), lr) + } + default: + // Some unexpected error. + return nil, err } - http.ServeContent(w, r, lr.digest.String(), lr.CreatedAt(), lr) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Docker-Content-Digest", lr.digest.String()) + handlerFunc.ServeHTTP(w, r) + }), nil }