1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ociserver
16
17 import (
18 "context"
19 "fmt"
20 "io"
21 "net/http"
22
23 "cuelabs.dev/go/oci/ociregistry"
24 "cuelabs.dev/go/oci/ociregistry/internal/ocirequest"
25 )
26
27 func (r *registry) handleBlobHead(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error {
28 desc, err := r.backend.ResolveBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest))
29 if err != nil {
30 return err
31 }
32 resp.Header().Set("Content-Length", fmt.Sprint(desc.Size))
33 resp.Header().Set("Docker-Content-Digest", string(desc.Digest))
34
35 resp.Header().Set("Accept-Ranges", "bytes")
36 resp.WriteHeader(http.StatusOK)
37 return nil
38 }
39
40 func (r *registry) handleBlobGet(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error {
41 if r.opts.LocationsForDescriptor != nil {
42
43
44
45
46 desc, err := r.backend.ResolveBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest))
47 if err != nil {
48
49
50
51
52 return err
53 }
54 locs, err := r.opts.LocationsForDescriptor(false, desc)
55 if err != nil {
56 return err
57 }
58 if len(locs) > 0 {
59
60
61 http.Redirect(resp, req, locs[0], http.StatusTemporaryRedirect)
62 return nil
63 }
64 }
65 ranges, err := parseRange(req.Header.Get("Range"))
66 if err != nil {
67 return withHTTPCode(http.StatusRequestedRangeNotSatisfiable, err)
68 }
69 switch len(ranges) {
70 case 0:
71 blob, err := r.backend.GetBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest))
72 if err != nil {
73 return err
74 }
75 defer blob.Close()
76 desc := blob.Descriptor()
77 resp.Header().Set("Content-Type", desc.MediaType)
78 resp.Header().Set("Content-Length", fmt.Sprint(desc.Size))
79 resp.Header().Set("Docker-Content-Digest", rreq.Digest)
80 resp.WriteHeader(http.StatusOK)
81
82 io.Copy(resp, blob)
83 return nil
84 case 1:
85 rng := ranges[0]
86 blob, err := r.backend.GetBlobRange(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest), rng.start, rng.end)
87 if err != nil {
88
89 return err
90 }
91 defer blob.Close()
92 desc := blob.Descriptor()
93 if rng.end == -1 || rng.end > desc.Size {
94 rng.end = desc.Size
95 }
96 if rng.start > desc.Size {
97 return withHTTPCode(http.StatusRequestedRangeNotSatisfiable, fmt.Errorf("range starts after end of blob"))
98 }
99 if rng.end < rng.start {
100 return withHTTPCode(http.StatusRequestedRangeNotSatisfiable, fmt.Errorf("range end is before start"))
101 }
102 resp.Header().Set("Content-Type", desc.MediaType)
103 resp.Header().Set("Content-Length", fmt.Sprint(rng.end-rng.start))
104 resp.Header().Set("Docker-Content-Digest", rreq.Digest)
105 resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, desc.Size))
106 resp.WriteHeader(http.StatusPartialContent)
107
108 io.Copy(resp, blob)
109 return nil
110
111 default:
112 return withHTTPCode(http.StatusRequestedRangeNotSatisfiable, fmt.Errorf("only a single range is supported"))
113 }
114 }
115
116 func (r *registry) handleManifestGet(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error {
117
118 var mr ociregistry.BlobReader
119 var err error
120 if rreq.Tag != "" {
121 mr, err = r.backend.GetTag(ctx, rreq.Repo, rreq.Tag)
122 } else {
123 mr, err = r.backend.GetManifest(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest))
124 }
125 if err != nil {
126 return err
127 }
128 desc := mr.Descriptor()
129 if !r.opts.OmitDigestFromTagGetResponse {
130 resp.Header().Set("Docker-Content-Digest", string(desc.Digest))
131 }
132 resp.Header().Set("Content-Type", desc.MediaType)
133 resp.Header().Set("Content-Length", fmt.Sprint(desc.Size))
134 resp.WriteHeader(http.StatusOK)
135 io.Copy(resp, mr)
136 return nil
137 }
138
139 func (r *registry) handleManifestHead(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error {
140 var desc ociregistry.Descriptor
141 var err error
142 if rreq.Tag != "" {
143 desc, err = r.backend.ResolveTag(ctx, rreq.Repo, rreq.Tag)
144 } else {
145 desc, err = r.backend.ResolveManifest(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest))
146 }
147 if err != nil {
148 return err
149 }
150 resp.Header().Set("Docker-Content-Digest", string(desc.Digest))
151 resp.Header().Set("Content-Type", desc.MediaType)
152 resp.Header().Set("Content-Length", fmt.Sprint(desc.Size))
153 resp.WriteHeader(http.StatusOK)
154 return nil
155 }
156
View as plain text