1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ociclient
16
17 import (
18 "bytes"
19 "context"
20 "fmt"
21 "io"
22 "net/http"
23
24 "cuelabs.dev/go/oci/ociregistry"
25 "cuelabs.dev/go/oci/ociregistry/internal/ocirequest"
26 "github.com/opencontainers/go-digest"
27 )
28
29 func (c *client) GetBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) {
30 return c.read(ctx, &ocirequest.Request{
31 Kind: ocirequest.ReqBlobGet,
32 Repo: repo,
33 Digest: string(digest),
34 })
35 }
36
37 func (c *client) GetBlobRange(ctx context.Context, repo string, digest ociregistry.Digest, o0, o1 int64) (_ ociregistry.BlobReader, _err error) {
38 if o0 == 0 && o1 < 0 {
39 return c.GetBlob(ctx, repo, digest)
40 }
41 rreq := &ocirequest.Request{
42 Kind: ocirequest.ReqBlobGet,
43 Repo: repo,
44 Digest: string(digest),
45 }
46 req, err := newRequest(ctx, rreq, nil)
47 if o1 < 0 {
48 req.Header.Set("Range", fmt.Sprintf("bytes=%d-", o0))
49 } else {
50 req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", o0, o1-1))
51 }
52 resp, err := c.do(req, http.StatusOK, http.StatusPartialContent)
53 if err != nil {
54 return nil, err
55 }
56
57
58
59 defer closeOnError(&_err, resp.Body)
60 desc, err := descriptorFromResponse(resp, ociregistry.Digest(rreq.Digest), true)
61 if err != nil {
62 return nil, fmt.Errorf("invalid descriptor in response: %v", err)
63 }
64 return newBlobReaderUnverified(resp.Body, desc), nil
65 }
66
67 func (c *client) ResolveBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) {
68 return c.resolve(ctx, &ocirequest.Request{
69 Kind: ocirequest.ReqBlobHead,
70 Repo: repo,
71 Digest: string(digest),
72 })
73 }
74
75 func (c *client) ResolveManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) {
76 return c.resolve(ctx, &ocirequest.Request{
77 Kind: ocirequest.ReqManifestHead,
78 Repo: repo,
79 Digest: string(digest),
80 })
81 }
82
83 func (c *client) ResolveTag(ctx context.Context, repo string, tag string) (ociregistry.Descriptor, error) {
84 return c.resolve(ctx, &ocirequest.Request{
85 Kind: ocirequest.ReqManifestHead,
86 Repo: repo,
87 Tag: tag,
88 })
89 }
90
91 func (c *client) resolve(ctx context.Context, rreq *ocirequest.Request) (ociregistry.Descriptor, error) {
92 resp, err := c.doRequest(ctx, rreq)
93 if err != nil {
94 return ociregistry.Descriptor{}, err
95 }
96 resp.Body.Close()
97 desc, err := descriptorFromResponse(resp, "", true)
98 if err != nil {
99 return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor in response: %v", err)
100 }
101 return desc, nil
102 }
103
104 func (c *client) GetManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) {
105 return c.read(ctx, &ocirequest.Request{
106 Kind: ocirequest.ReqManifestGet,
107 Repo: repo,
108 Digest: string(digest),
109 })
110 }
111
112 func (c *client) GetTag(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) {
113 return c.read(ctx, &ocirequest.Request{
114 Kind: ocirequest.ReqManifestGet,
115 Repo: repo,
116 Tag: tagName,
117 })
118 }
119
120
121
122
123
124
125
126
127
128
129
130 const inMemThreshold = 128 * 1024
131
132 func (c *client) read(ctx context.Context, rreq *ocirequest.Request) (_ ociregistry.BlobReader, _err error) {
133 resp, err := c.doRequest(ctx, rreq)
134 if err != nil {
135 return nil, err
136 }
137 defer closeOnError(&_err, resp.Body)
138 desc, err := descriptorFromResponse(resp, ociregistry.Digest(rreq.Digest), true)
139 if err != nil {
140 return nil, fmt.Errorf("invalid descriptor in response: %v", err)
141 }
142 if desc.Digest == "" {
143
144
145
146
147
148
149 if rreq.Kind != ocirequest.ReqManifestGet {
150 return nil, fmt.Errorf("internal error: no digest available for non-tag request")
151 }
152
153
154
155
156
157 if desc.Size <= inMemThreshold {
158 data, err := io.ReadAll(io.LimitReader(resp.Body, desc.Size+1))
159 if err != nil {
160 return nil, fmt.Errorf("failed to read body to determine digest: %v", err)
161 }
162 if int64(len(data)) != desc.Size {
163 return nil, fmt.Errorf("body size mismatch")
164 }
165 desc.Digest = digest.FromBytes(data)
166 resp.Body.Close()
167 resp.Body = io.NopCloser(bytes.NewReader(data))
168 } else {
169 rreq1 := rreq
170 rreq1.Kind = ocirequest.ReqManifestHead
171 resp1, err := c.doRequest(ctx, rreq1)
172 if err != nil {
173 return nil, err
174 }
175 resp1.Body.Close()
176 desc, err = descriptorFromResponse(resp1, "", true)
177 if err != nil {
178 return nil, err
179 }
180 if desc.Digest == "" {
181 return nil, fmt.Errorf("no digest header found in response")
182 }
183 }
184 }
185 return newBlobReader(resp.Body, desc), nil
186 }
187
View as plain text