Skip to content

Commit c890454

Browse files
authored
Add direct serving of package content (#25543)
Fixes #24723 Direct serving of content aka HTTP redirect is not mentioned in any of the package registry specs but lots of official registries do that so it should be supported by the usual clients.
1 parent f1cb461 commit c890454

File tree

26 files changed

+195
-235
lines changed

26 files changed

+195
-235
lines changed

modules/packages/content_store.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ package packages
55

66
import (
77
"io"
8+
"net/url"
89
"path"
910
"strings"
1011

12+
"code.gitea.io/gitea/modules/setting"
1113
"code.gitea.io/gitea/modules/storage"
1214
"code.gitea.io/gitea/modules/util"
1315
)
@@ -31,6 +33,14 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
3133
return s.store.Open(KeyToRelativePath(key))
3234
}
3335

36+
func (s *ContentStore) ShouldServeDirect() bool {
37+
return setting.Packages.Storage.MinioConfig.ServeDirect
38+
}
39+
40+
func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string) (*url.URL, error) {
41+
return s.store.URL(KeyToRelativePath(key), filename)
42+
}
43+
3444
// FIXME: Workaround to be removed in v1.20
3545
// https://github.com/go-gitea/gitea/issues/19586
3646
func (s *ContentStore) Has(key BlobHash256Key) error {

routers/api/packages/alpine/alpine.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
6868
return
6969
}
7070

71-
s, pf, err := packages_service.GetFileStreamByPackageVersion(
71+
s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
7272
ctx,
7373
pv,
7474
&packages_service.PackageFileInfo{
@@ -84,12 +84,8 @@ func GetRepositoryFile(ctx *context.Context) {
8484
}
8585
return
8686
}
87-
defer s.Close()
8887

89-
ctx.ServeContent(s, &context.ServeHeaderOptions{
90-
Filename: pf.Name,
91-
LastModified: pf.CreatedUnix.AsLocalTime(),
92-
})
88+
helper.ServePackageFile(ctx, s, u, pf)
9389
}
9490

9591
func UploadPackageFile(ctx *context.Context) {
@@ -200,7 +196,7 @@ func DownloadPackageFile(ctx *context.Context) {
200196
return
201197
}
202198

203-
s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
199+
s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
204200
if err != nil {
205201
if errors.Is(err, util.ErrNotExist) {
206202
apiError(ctx, http.StatusNotFound, err)
@@ -209,12 +205,8 @@ func DownloadPackageFile(ctx *context.Context) {
209205
}
210206
return
211207
}
212-
defer s.Close()
213208

214-
ctx.ServeContent(s, &context.ServeHeaderOptions{
215-
Filename: pf.Name,
216-
LastModified: pf.CreatedUnix.AsLocalTime(),
217-
})
209+
helper.ServePackageFile(ctx, s, u, pf)
218210
}
219211

220212
func DeletePackageFile(ctx *context.Context) {

routers/api/packages/cargo/cargo.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func ListOwners(ctx *context.Context) {
165165

166166
// DownloadPackageFile serves the content of a package
167167
func DownloadPackageFile(ctx *context.Context) {
168-
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
168+
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
169169
ctx,
170170
&packages_service.PackageInfo{
171171
Owner: ctx.Package.Owner,
@@ -185,12 +185,8 @@ func DownloadPackageFile(ctx *context.Context) {
185185
apiError(ctx, http.StatusInternalServerError, err)
186186
return
187187
}
188-
defer s.Close()
189188

190-
ctx.ServeContent(s, &context.ServeHeaderOptions{
191-
Filename: pf.Name,
192-
LastModified: pf.CreatedUnix.AsLocalTime(),
193-
})
189+
helper.ServePackageFile(ctx, s, u, pf)
194190
}
195191

196192
// https://doc.rust-lang.org/cargo/reference/registries.html#publish

routers/api/packages/chef/chef.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -341,17 +341,13 @@ func DownloadPackage(ctx *context.Context) {
341341

342342
pf := pd.Files[0].File
343343

344-
s, _, err := packages_service.GetPackageFileStream(ctx, pf)
344+
s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
345345
if err != nil {
346346
apiError(ctx, http.StatusInternalServerError, err)
347347
return
348348
}
349-
defer s.Close()
350349

351-
ctx.ServeContent(s, &context.ServeHeaderOptions{
352-
Filename: pf.Name,
353-
LastModified: pf.CreatedUnix.AsLocalTime(),
354-
})
350+
helper.ServePackageFile(ctx, s, u, pf)
355351
}
356352

357353
// https://github.com/chef/chef/blob/main/knife/lib/chef/knife/supermarket_unshare.rb

routers/api/packages/composer/composer.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func PackageMetadata(ctx *context.Context) {
162162

163163
// DownloadPackageFile serves the content of a package
164164
func DownloadPackageFile(ctx *context.Context) {
165-
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
165+
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
166166
ctx,
167167
&packages_service.PackageInfo{
168168
Owner: ctx.Package.Owner,
@@ -182,12 +182,8 @@ func DownloadPackageFile(ctx *context.Context) {
182182
apiError(ctx, http.StatusInternalServerError, err)
183183
return
184184
}
185-
defer s.Close()
186185

187-
ctx.ServeContent(s, &context.ServeHeaderOptions{
188-
Filename: pf.Name,
189-
LastModified: pf.CreatedUnix.AsLocalTime(),
190-
})
186+
helper.ServePackageFile(ctx, s, u, pf)
191187
}
192188

193189
// UploadPackage creates a new package

routers/api/packages/conan/conan.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
453453
return
454454
}
455455

456-
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
456+
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
457457
ctx,
458458
&packages_service.PackageInfo{
459459
Owner: ctx.Package.Owner,
@@ -474,12 +474,8 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
474474
apiError(ctx, http.StatusInternalServerError, err)
475475
return
476476
}
477-
defer s.Close()
478477

479-
ctx.ServeContent(s, &context.ServeHeaderOptions{
480-
Filename: pf.Name,
481-
LastModified: pf.CreatedUnix.AsLocalTime(),
482-
})
478+
helper.ServePackageFile(ctx, s, u, pf)
483479
}
484480

485481
// DeleteRecipeV1 deletes the requested recipe(s)

routers/api/packages/conda/conda.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,15 +292,11 @@ func DownloadPackageFile(ctx *context.Context) {
292292

293293
pf := pfs[0]
294294

295-
s, _, err := packages_service.GetPackageFileStream(ctx, pf)
295+
s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
296296
if err != nil {
297297
apiError(ctx, http.StatusInternalServerError, err)
298298
return
299299
}
300-
defer s.Close()
301300

302-
ctx.ServeContent(s, &context.ServeHeaderOptions{
303-
Filename: pf.Name,
304-
LastModified: pf.CreatedUnix.AsLocalTime(),
305-
})
301+
helper.ServePackageFile(ctx, s, u, pf)
306302
}

routers/api/packages/container/container.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -482,22 +482,7 @@ func GetBlob(ctx *context.Context) {
482482
return
483483
}
484484

485-
s, _, err := packages_service.GetPackageFileStream(ctx, blob.File)
486-
if err != nil {
487-
apiError(ctx, http.StatusInternalServerError, err)
488-
return
489-
}
490-
defer s.Close()
491-
492-
setResponseHeaders(ctx.Resp, &containerHeaders{
493-
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
494-
ContentType: blob.Properties.GetByName(container_module.PropertyMediaType),
495-
ContentLength: blob.Blob.Size,
496-
Status: http.StatusOK,
497-
})
498-
if _, err := io.Copy(ctx.Resp, s); err != nil {
499-
log.Error("Error whilst copying content to response: %v", err)
500-
}
485+
serveBlob(ctx, blob)
501486
}
502487

503488
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs
@@ -636,22 +621,7 @@ func GetManifest(ctx *context.Context) {
636621
return
637622
}
638623

639-
s, _, err := packages_service.GetPackageFileStream(ctx, manifest.File)
640-
if err != nil {
641-
apiError(ctx, http.StatusInternalServerError, err)
642-
return
643-
}
644-
defer s.Close()
645-
646-
setResponseHeaders(ctx.Resp, &containerHeaders{
647-
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
648-
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
649-
ContentLength: manifest.Blob.Size,
650-
Status: http.StatusOK,
651-
})
652-
if _, err := io.Copy(ctx.Resp, s); err != nil {
653-
log.Error("Error whilst copying content to response: %v", err)
654-
}
624+
serveBlob(ctx, manifest)
655625
}
656626

657627
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-tags
@@ -686,6 +656,36 @@ func DeleteManifest(ctx *context.Context) {
686656
})
687657
}
688658

659+
func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) {
660+
s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob)
661+
if err != nil {
662+
apiError(ctx, http.StatusInternalServerError, err)
663+
return
664+
}
665+
666+
headers := &containerHeaders{
667+
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
668+
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
669+
ContentLength: pfd.Blob.Size,
670+
Status: http.StatusOK,
671+
}
672+
673+
if u != nil {
674+
headers.Status = http.StatusTemporaryRedirect
675+
headers.Location = u.String()
676+
677+
setResponseHeaders(ctx.Resp, headers)
678+
return
679+
}
680+
681+
defer s.Close()
682+
683+
setResponseHeaders(ctx.Resp, headers)
684+
if _, err := io.Copy(ctx.Resp, s); err != nil {
685+
log.Error("Error whilst copying content to response: %v", err)
686+
}
687+
}
688+
689689
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery
690690
func GetTagList(ctx *context.Context) {
691691
image := ctx.Params("image")

routers/api/packages/cran/cran.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
249249
return
250250
}
251251

252-
s, _, err := packages_service.GetPackageFileStream(ctx, pf)
252+
s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
253253
if err != nil {
254254
if errors.Is(err, util.ErrNotExist) {
255255
apiError(ctx, http.StatusNotFound, err)
@@ -258,10 +258,6 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
258258
}
259259
return
260260
}
261-
defer s.Close()
262261

263-
ctx.ServeContent(s, &context.ServeHeaderOptions{
264-
Filename: pf.Name,
265-
LastModified: pf.CreatedUnix.AsLocalTime(),
266-
})
262+
helper.ServePackageFile(ctx, s, u, pf)
267263
}

routers/api/packages/debian/debian.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func GetRepositoryFile(ctx *context.Context) {
5959
key += "|" + component + "|" + architecture
6060
}
6161

62-
s, pf, err := packages_service.GetFileStreamByPackageVersion(
62+
s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
6363
ctx,
6464
pv,
6565
&packages_service.PackageFileInfo{
@@ -75,12 +75,8 @@ func GetRepositoryFile(ctx *context.Context) {
7575
}
7676
return
7777
}
78-
defer s.Close()
7978

80-
ctx.ServeContent(s, &context.ServeHeaderOptions{
81-
Filename: pf.Name,
82-
LastModified: pf.CreatedUnix.AsLocalTime(),
83-
})
79+
helper.ServePackageFile(ctx, s, u, pf)
8480
}
8581

8682
// https://wiki.debian.org/DebianRepository/Format#indices_acquisition_via_hashsums_.28by-hash.29
@@ -110,7 +106,7 @@ func GetRepositoryFileByHash(ctx *context.Context) {
110106
return
111107
}
112108

113-
s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
109+
s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
114110
if err != nil {
115111
if errors.Is(err, util.ErrNotExist) {
116112
apiError(ctx, http.StatusNotFound, err)
@@ -119,12 +115,8 @@ func GetRepositoryFileByHash(ctx *context.Context) {
119115
}
120116
return
121117
}
122-
defer s.Close()
123118

124-
ctx.ServeContent(s, &context.ServeHeaderOptions{
125-
Filename: pf.Name,
126-
LastModified: pf.CreatedUnix.AsLocalTime(),
127-
})
119+
helper.ServePackageFile(ctx, s, u, pf)
128120
}
129121

130122
func UploadPackageFile(ctx *context.Context) {
@@ -217,7 +209,7 @@ func DownloadPackageFile(ctx *context.Context) {
217209
name := ctx.Params("name")
218210
version := ctx.Params("version")
219211

220-
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
212+
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
221213
ctx,
222214
&packages_service.PackageInfo{
223215
Owner: ctx.Package.Owner,
@@ -238,9 +230,8 @@ func DownloadPackageFile(ctx *context.Context) {
238230
}
239231
return
240232
}
241-
defer s.Close()
242233

243-
ctx.ServeContent(s, &context.ServeHeaderOptions{
234+
helper.ServePackageFile(ctx, s, u, pf, &context.ServeHeaderOptions{
244235
ContentType: "application/vnd.debian.binary-package",
245236
Filename: pf.Name,
246237
LastModified: pf.CreatedUnix.AsLocalTime(),

routers/api/packages/generic/generic.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func apiError(ctx *context.Context, status int, obj interface{}) {
3030

3131
// DownloadPackageFile serves the specific generic package.
3232
func DownloadPackageFile(ctx *context.Context) {
33-
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
33+
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
3434
ctx,
3535
&packages_service.PackageInfo{
3636
Owner: ctx.Package.Owner,
@@ -50,12 +50,8 @@ func DownloadPackageFile(ctx *context.Context) {
5050
apiError(ctx, http.StatusInternalServerError, err)
5151
return
5252
}
53-
defer s.Close()
5453

55-
ctx.ServeContent(s, &context.ServeHeaderOptions{
56-
Filename: pf.Name,
57-
LastModified: pf.CreatedUnix.AsLocalTime(),
58-
})
54+
helper.ServePackageFile(ctx, s, u, pf)
5955
}
6056

6157
// UploadPackage uploads the specific generic package.

routers/api/packages/goproxy/goproxy.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func DownloadPackageFile(ctx *context.Context) {
105105
return
106106
}
107107

108-
s, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
108+
s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
109109
if err != nil {
110110
if errors.Is(err, util.ErrNotExist) {
111111
apiError(ctx, http.StatusNotFound, err)
@@ -114,12 +114,8 @@ func DownloadPackageFile(ctx *context.Context) {
114114
}
115115
return
116116
}
117-
defer s.Close()
118117

119-
ctx.ServeContent(s, &context.ServeHeaderOptions{
120-
Filename: pfs[0].Name,
121-
LastModified: pfs[0].CreatedUnix.AsLocalTime(),
122-
})
118+
helper.ServePackageFile(ctx, s, u, pfs[0])
123119
}
124120

125121
func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (*packages_model.PackageVersion, error) {

0 commit comments

Comments
 (0)