github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/blobserver.go (about) 1 package storage 2 3 import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "github.com/docker/distribution" 9 "github.com/docker/distribution/context" 10 "github.com/docker/distribution/digest" 11 "github.com/docker/distribution/registry/storage/driver" 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 }