fix(api): support HEAD requests on all API GET endpoints (#38245)

Fixes #38226

## Summary

Add `chi_middleware.GetHead` as the first `BeforeRouting` middleware on
the API router. This makes every API `GET` endpoint automatically handle
`HEAD` requests, as required by RFC 9110 §9.3.2.

Previously, `HEAD` requests to endpoints like `GET
/repos/{owner}/{repo}/git/commits/{sha}` returned `405 Method Not
Allowed`.

The web router already used this same middleware (see
`routers/web/web.go:261`), so this aligns API behaviour with the web
router.

## Changes

- `routers/api/v1/api.go`: add `chi_middleware.GetHead` middleware to
the API router
- `tests/integration/api_repo_git_commits_test.go`: add
`TestAPIReposGitCommitsHEAD` verifying HEAD returns 200 on a valid ref
and 404 (not 405) on a missing ref
This commit is contained in:
bircni
2026-06-28 12:14:39 +00:00
committed by GitHub
parent ce8cf22af9
commit 1c718da16c
2 changed files with 21 additions and 0 deletions
+4
View File
@@ -99,6 +99,7 @@ import (
_ "gitea.dev/routers/api/v1/swagger" // for swagger generation
"gitea.com/go-chi/binding"
chi_middleware "github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
)
@@ -950,6 +951,9 @@ func checkDeprecatedAuthMethods(ctx *context.APIContext) {
func Routes() *web.Router {
m := web.NewRouter()
// redirect HEAD requests to GET if no HEAD handler is defined (RFC 9110 §9.3.2)
m.BeforeRouting(chi_middleware.GetHead)
if setting.CORSConfig.Enabled {
m.BeforeRouting(cors.Handler(cors.Options{
AllowedOrigins: setting.CORSConfig.AllowDomain,
@@ -56,6 +56,23 @@ func TestAPIReposGitCommits(t *testing.T) {
}
}
func TestAPIReposGitCommitsHEAD(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
// HEAD on a valid ref must return 200 (RFC 9110 §9.3.2)
req := NewRequestf(t, "HEAD", "/api/v1/repos/%s/repo1/git/commits/master", user.Name).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
// HEAD on a missing sha must return 404, not 405
req = NewRequestf(t, "HEAD", "/api/v1/repos/%s/repo1/git/commits/12345", user.Name).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
}
func TestAPIReposGitCommitList(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})