fix: add support the http HEAD request method for generic packages (#12865)

Closes #6871

### Tests for Go changes

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [x] `make pr-go` before pushing

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/12865): <!--number 12865 --><!--line 0 --><!--description YWRkIHN1cHBvcnQgdGhlIGh0dHAgSEVBRCByZXF1ZXN0IG1ldGhvZCBmb3IgZ2VuZXJpYyBwYWNrYWdlcw==-->add support the http HEAD request method for generic packages<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12865
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Maxim Slipenko 2026-06-18 23:33:50 +02:00 committed by Mathieu Fenniak
commit dc50a79770
3 changed files with 48 additions and 3 deletions

View file

@ -505,6 +505,7 @@ func CommonRoutes() *web.Route {
r.Group("/{packagename}/{packageversion}", func() {
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
r.Group("/{filename}", func() {
r.Head("", generic.CheckPackageFileExistence)
r.Get("", generic.DownloadPackageFile)
r.Group("", func() {
r.Put("", enforcePackagesQuota(), generic.UploadPackage)

View file

@ -29,6 +29,38 @@ func apiError(ctx *context.Context, status int, obj any) {
})
}
func CheckPackageFileExistence(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(
ctx,
ctx.Package.Owner.ID,
packages_model.TypeGeneric,
ctx.Params("packagename"),
ctx.Params("packageversion"),
)
if err != nil {
if err == packages_model.ErrPackageNotExist {
apiError(ctx, http.StatusNotFound, err)
return
}
apiError(ctx, http.StatusInternalServerError, err)
}
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), "")
if err != nil {
if err == packages_model.ErrPackageFileNotExist {
apiError(ctx, http.StatusNotFound, err)
return
}
apiError(ctx, http.StatusInternalServerError, err)
}
ctx.SetServeHeaders(&context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
ctx.Status(http.StatusOK)
}
// DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) {
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(

View file

@ -116,7 +116,10 @@ func TestPackageGeneric(t *testing.T) {
checkDownloadCount(0)
req := NewRequest(t, "GET", url+"/"+filename)
req := NewRequest(t, "HEAD", url+"/"+filename)
MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", url+"/"+filename)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, content, resp.Body.Bytes())
@ -131,7 +134,10 @@ func TestPackageGeneric(t *testing.T) {
t.Run("NotExists", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", url+"/not.found")
req := NewRequest(t, "HEAD", url+"/not.found")
MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "GET", url+"/not.found")
MakeRequest(t, req, http.StatusNotFound)
})
@ -143,6 +149,9 @@ func TestPackageGeneric(t *testing.T) {
setting.Service.RequireSignInView = false
}()
req = NewRequest(t, "HEAD", url+"/dummy.bin")
MakeRequest(t, req, http.StatusUnauthorized)
req = NewRequest(t, "GET", url+"/dummy.bin")
MakeRequest(t, req, http.StatusUnauthorized)
})
@ -164,7 +173,10 @@ func TestPackageGeneric(t *testing.T) {
setting.Packages.Storage.MinioConfig.ServeDirect = true
}
req := NewRequest(t, "GET", url+"/"+filename)
req := NewRequest(t, "HEAD", url+"/"+filename)
MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", url+"/"+filename)
resp := MakeRequest(t, req, http.StatusSeeOther)
checkDownloadCount(3)