mirror of
				https://github.com/distribution/distribution.git
				synced 2025-10-31 01:12:48 +00:00 
			
		
		
		
	Fixes issue where an error was returned instead of serving the blob Signed-off-by: Brian Bland <brian.bland@docker.com>
		
			
				
	
	
		
			79 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			79 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package storage
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/docker/distribution"
 | |
| 	"github.com/docker/distribution/context"
 | |
| 	"github.com/docker/distribution/digest"
 | |
| 	"github.com/docker/distribution/registry/storage/driver"
 | |
| )
 | |
| 
 | |
| // TODO(stevvooe): This should configurable in the future.
 | |
| const blobCacheControlMaxAge = 365 * 24 * time.Hour
 | |
| 
 | |
| // blobServer simply serves blobs from a driver instance using a path function
 | |
| // to identify paths and a descriptor service to fill in metadata.
 | |
| type blobServer struct {
 | |
| 	driver   driver.StorageDriver
 | |
| 	statter  distribution.BlobStatter
 | |
| 	pathFn   func(dgst digest.Digest) (string, error)
 | |
| 	redirect bool // allows disabling URLFor redirects
 | |
| }
 | |
| 
 | |
| func (bs *blobServer) ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error {
 | |
| 	desc, err := bs.statter.Stat(ctx, dgst)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	path, err := bs.pathFn(desc.Digest)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if bs.redirect {
 | |
| 		redirectURL, err := bs.driver.URLFor(ctx, path, map[string]interface{}{"method": r.Method})
 | |
| 		switch err.(type) {
 | |
| 		case nil:
 | |
| 			// Redirect to storage URL.
 | |
| 			http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect)
 | |
| 			return err
 | |
| 
 | |
| 		case driver.ErrUnsupportedMethod:
 | |
| 			// Fallback to serving the content directly.
 | |
| 		default:
 | |
| 			// Some unexpected error.
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	br, err := newFileReader(ctx, bs.driver, path, desc.Size)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer br.Close()
 | |
| 
 | |
| 	w.Header().Set("ETag", fmt.Sprintf(`"%s"`, desc.Digest)) // If-None-Match handled by ServeContent
 | |
| 	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%.f", blobCacheControlMaxAge.Seconds()))
 | |
| 
 | |
| 	if w.Header().Get("Docker-Content-Digest") == "" {
 | |
| 		w.Header().Set("Docker-Content-Digest", desc.Digest.String())
 | |
| 	}
 | |
| 
 | |
| 	if w.Header().Get("Content-Type") == "" {
 | |
| 		// Set the content type if not already set.
 | |
| 		w.Header().Set("Content-Type", desc.MediaType)
 | |
| 	}
 | |
| 
 | |
| 	if w.Header().Get("Content-Length") == "" {
 | |
| 		// Set the content length if not already set.
 | |
| 		w.Header().Set("Content-Length", fmt.Sprint(desc.Size))
 | |
| 	}
 | |
| 
 | |
| 	http.ServeContent(w, r, desc.Digest.String(), time.Time{}, br)
 | |
| 	return nil
 | |
| }
 |