...
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
15 const blobCacheControlMaxAge = 365 * 24 * time.Hour
16
17
18
19 type blobServer struct {
20 driver driver.StorageDriver
21 statter distribution.BlobStatter
22 pathFn func(dgst digest.Digest) (string, error)
23 redirect bool
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
42 http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect)
43 return err
44
45 case driver.ErrUnsupportedMethod:
46
47 default:
48
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))
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
68 w.Header().Set("Content-Type", desc.MediaType)
69 }
70
71 if w.Header().Get("Content-Length") == "" {
72
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