...

Source file src/github.com/docker/distribution/registry/storage/blobserver.go

Documentation: github.com/docker/distribution/registry/storage

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"time"
     8  
     9  	"github.com/docker/distribution"
    10  	"github.com/docker/distribution/registry/storage/driver"
    11  	"github.com/opencontainers/go-digest"
    12  )
    13  
    14  // TODO(stevvooe): This should configurable in the future.
    15  const blobCacheControlMaxAge = 365 * 24 * time.Hour
    16  
    17  // blobServer simply serves blobs from a driver instance using a path function
    18  // to identify paths and a descriptor service to fill in metadata.
    19  type blobServer struct {
    20  	driver   driver.StorageDriver
    21  	statter  distribution.BlobStatter
    22  	pathFn   func(dgst digest.Digest) (string, error)
    23  	redirect bool // allows disabling URLFor redirects
    24  }
    25  
    26  func (bs *blobServer) ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error {
    27  	desc, err := bs.statter.Stat(ctx, dgst)
    28  	if err != nil {
    29  		return err
    30  	}
    31  
    32  	path, err := bs.pathFn(desc.Digest)
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	if bs.redirect {
    38  		redirectURL, err := bs.driver.URLFor(ctx, path, map[string]interface{}{"method": r.Method})
    39  		switch err.(type) {
    40  		case nil:
    41  			// Redirect to storage URL.
    42  			http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect)
    43  			return err
    44  
    45  		case driver.ErrUnsupportedMethod:
    46  			// Fallback to serving the content directly.
    47  		default:
    48  			// Some unexpected error.
    49  			return err
    50  		}
    51  	}
    52  
    53  	br, err := newFileReader(ctx, bs.driver, path, desc.Size)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	defer br.Close()
    58  
    59  	w.Header().Set("ETag", fmt.Sprintf(`"%s"`, desc.Digest)) // If-None-Match handled by ServeContent
    60  	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%.f", blobCacheControlMaxAge.Seconds()))
    61  
    62  	if w.Header().Get("Docker-Content-Digest") == "" {
    63  		w.Header().Set("Docker-Content-Digest", desc.Digest.String())
    64  	}
    65  
    66  	if w.Header().Get("Content-Type") == "" {
    67  		// Set the content type if not already set.
    68  		w.Header().Set("Content-Type", desc.MediaType)
    69  	}
    70  
    71  	if w.Header().Get("Content-Length") == "" {
    72  		// Set the content length if not already set.
    73  		w.Header().Set("Content-Length", fmt.Sprint(desc.Size))
    74  	}
    75  
    76  	http.ServeContent(w, r, desc.Digest.String(), time.Time{}, br)
    77  	return nil
    78  }
    79  

View as plain text