Skip to content

Commit

Permalink
Add direct serving of package content (#25543)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
KN4CK3R authored Jul 3, 2023
1 parent f1cb461 commit c890454
Show file tree
Hide file tree
Showing 26 changed files with 195 additions and 235 deletions.
10 changes: 10 additions & 0 deletions modules/packages/content_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ package packages

import (
"io"
"net/url"
"path"
"strings"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util"
)
Expand All @@ -31,6 +33,14 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
return s.store.Open(KeyToRelativePath(key))
}

func (s *ContentStore) ShouldServeDirect() bool {
return setting.Packages.Storage.MinioConfig.ServeDirect
}

func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string) (*url.URL, error) {
return s.store.URL(KeyToRelativePath(key), filename)
}

// FIXME: Workaround to be removed in v1.20
// https://github.com/go-gitea/gitea/issues/19586
func (s *ContentStore) Has(key BlobHash256Key) error {
Expand Down
16 changes: 4 additions & 12 deletions routers/api/packages/alpine/alpine.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
return
}

s, pf, err := packages_service.GetFileStreamByPackageVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
Expand All @@ -84,12 +84,8 @@ func GetRepositoryFile(ctx *context.Context) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

func UploadPackageFile(ctx *context.Context) {
Expand Down Expand Up @@ -200,7 +196,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}

s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
Expand All @@ -209,12 +205,8 @@ func DownloadPackageFile(ctx *context.Context) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

func DeletePackageFile(ctx *context.Context) {
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/cargo/cargo.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func ListOwners(ctx *context.Context) {

// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
Expand All @@ -185,12 +185,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

// https://doc.rust-lang.org/cargo/reference/registries.html#publish
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/chef/chef.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,17 +341,13 @@ func DownloadPackage(ctx *context.Context) {

pf := pd.Files[0].File

s, _, err := packages_service.GetPackageFileStream(ctx, pf)
s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

// https://github.com/chef/chef/blob/main/knife/lib/chef/knife/supermarket_unshare.rb
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/composer/composer.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func PackageMetadata(ctx *context.Context) {

// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
Expand All @@ -182,12 +182,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

// UploadPackage creates a new package
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/conan/conan.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
return
}

s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
Expand All @@ -474,12 +474,8 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

// DeleteRecipeV1 deletes the requested recipe(s)
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/conda/conda.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,15 +292,11 @@ func DownloadPackageFile(ctx *context.Context) {

pf := pfs[0]

s, _, err := packages_service.GetPackageFileStream(ctx, pf)
s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}
64 changes: 32 additions & 32 deletions routers/api/packages/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,22 +482,7 @@ func GetBlob(ctx *context.Context) {
return
}

s, _, err := packages_service.GetPackageFileStream(ctx, blob.File)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

setResponseHeaders(ctx.Resp, &containerHeaders{
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
ContentType: blob.Properties.GetByName(container_module.PropertyMediaType),
ContentLength: blob.Blob.Size,
Status: http.StatusOK,
})
if _, err := io.Copy(ctx.Resp, s); err != nil {
log.Error("Error whilst copying content to response: %v", err)
}
serveBlob(ctx, blob)
}

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

s, _, err := packages_service.GetPackageFileStream(ctx, manifest.File)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

setResponseHeaders(ctx.Resp, &containerHeaders{
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
ContentLength: manifest.Blob.Size,
Status: http.StatusOK,
})
if _, err := io.Copy(ctx.Resp, s); err != nil {
log.Error("Error whilst copying content to response: %v", err)
}
serveBlob(ctx, manifest)
}

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

func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) {
s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}

headers := &containerHeaders{
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
ContentLength: pfd.Blob.Size,
Status: http.StatusOK,
}

if u != nil {
headers.Status = http.StatusTemporaryRedirect
headers.Location = u.String()

setResponseHeaders(ctx.Resp, headers)
return
}

defer s.Close()

setResponseHeaders(ctx.Resp, headers)
if _, err := io.Copy(ctx.Resp, s); err != nil {
log.Error("Error whilst copying content to response: %v", err)
}
}

// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery
func GetTagList(ctx *context.Context) {
image := ctx.Params("image")
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/cran/cran.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
return
}

s, _, err := packages_service.GetPackageFileStream(ctx, pf)
s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
Expand All @@ -258,10 +258,6 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}
21 changes: 6 additions & 15 deletions routers/api/packages/debian/debian.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func GetRepositoryFile(ctx *context.Context) {
key += "|" + component + "|" + architecture
}

s, pf, err := packages_service.GetFileStreamByPackageVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
Expand All @@ -75,12 +75,8 @@ func GetRepositoryFile(ctx *context.Context) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

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

s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
Expand All @@ -119,12 +115,8 @@ func GetRepositoryFileByHash(ctx *context.Context) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

func UploadPackageFile(ctx *context.Context) {
Expand Down Expand Up @@ -217,7 +209,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.Params("name")
version := ctx.Params("version")

s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
Expand All @@ -238,9 +230,8 @@ func DownloadPackageFile(ctx *context.Context) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
helper.ServePackageFile(ctx, s, u, pf, &context.ServeHeaderOptions{
ContentType: "application/vnd.debian.binary-package",
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/generic/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func apiError(ctx *context.Context, status int, obj interface{}) {

// DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
Expand All @@ -50,12 +50,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pf)
}

// UploadPackage uploads the specific generic package.
Expand Down
8 changes: 2 additions & 6 deletions routers/api/packages/goproxy/goproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}

s, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
Expand All @@ -114,12 +114,8 @@ func DownloadPackageFile(ctx *context.Context) {
}
return
}
defer s.Close()

ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pfs[0].Name,
LastModified: pfs[0].CreatedUnix.AsLocalTime(),
})
helper.ServePackageFile(ctx, s, u, pfs[0])
}

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

0 comments on commit c890454

Please sign in to comment.