From f8ebb278df95c6b982b1336e585e212f67ddc155 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:07:16 +0000 Subject: [PATCH] Continue flamego migration - fix Context methods and route handlers. Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com> --- internal/route/lfs/route.go | 20 +++++++--- internal/route/repo/http.go | 74 ++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/internal/route/lfs/route.go b/internal/route/lfs/route.go index 4c0a3d940..9b87b467a 100644 --- a/internal/route/lfs/route.go +++ b/internal/route/lfs/route.go @@ -15,6 +15,14 @@ import ( "gogs.io/gogs/internal/lfsutil" ) +// writeError writes an HTTP error response. +func writeError(w http.ResponseWriter, status int, text string) { + w.WriteHeader(status) + if text != "" { + w.Write([]byte(text)) + } +} + // RegisterRoutes registers LFS routes using given router, and inherits all // groups and middleware. func RegisterRoutes(r flamego.Router) { @@ -66,7 +74,7 @@ func authenticate(store Store) flamego.Handler { } if err == nil && store.IsTwoFactorEnabled(c.Request().Context(), user.ID) { - c.Error(http.StatusBadRequest, "Users with 2FA enabled are not allowed to authenticate via username and password.") + writeError(c.ResponseWriter(), http.StatusBadRequest, "Users with 2FA enabled are not allowed to authenticate via username and password.") return } @@ -85,7 +93,7 @@ func authenticate(store Store) flamego.Handler { if database.IsErrAccessTokenNotExist(err) { askCredentials(c.ResponseWriter()) } else { - c.Status(http.StatusInternalServerError) + c.ResponseWriter().WriteHeader(http.StatusInternalServerError) log.Error("Failed to authenticate by access token via password: %v", err) } return @@ -108,7 +116,7 @@ func authorize(store Store, mode database.AccessMode) flamego.Handler { owner, err := store.GetUserByUsername(c.Request().Context(), username) if err != nil { if database.IsErrUserNotExist(err) { - c.Status(http.StatusNotFound) + c.ResponseWriter().WriteHeader(http.StatusNotFound) } else { internalServerError(c.ResponseWriter()) log.Error("Failed to get user [name: %s]: %v", username, err) @@ -119,7 +127,7 @@ func authorize(store Store, mode database.AccessMode) flamego.Handler { repo, err := store.GetRepositoryByName(c.Request().Context(), owner.ID, reponame) if err != nil { if database.IsErrRepoNotExist(err) { - c.Status(http.StatusNotFound) + c.ResponseWriter().WriteHeader(http.StatusNotFound) } else { internalServerError(c.ResponseWriter()) log.Error("Failed to get repository [owner_id: %d, name: %s]: %v", owner.ID, reponame, err) @@ -133,7 +141,7 @@ func authorize(store Store, mode database.AccessMode) flamego.Handler { Private: repo.IsPrivate, }, ) { - c.Status(http.StatusNotFound) + c.ResponseWriter().WriteHeader(http.StatusNotFound) return } @@ -156,7 +164,7 @@ func verifyHeader(key, value string, failCode int) flamego.Handler { } log.Trace("[LFS] HTTP header %q does not contain value %q", key, value) - c.Status(failCode) + c.ResponseWriter().WriteHeader(failCode) } } diff --git a/internal/route/repo/http.go b/internal/route/repo/http.go index 8b5e9f2a6..abf856344 100644 --- a/internal/route/repo/http.go +++ b/internal/route/repo/http.go @@ -34,22 +34,30 @@ type HTTPContext struct { AuthUser *database.User } +// writeError writes an HTTP error response. +func writeError(w http.ResponseWriter, status int, text string) { + w.WriteHeader(status) + if text != "" { + w.Write([]byte(text)) + } +} + // askCredentials responses HTTP header and status which informs client to provide credentials. func askCredentials(c flamego.Context, status int, text string) { - c.Header().Set("WWW-Authenticate", "Basic realm=\".\"") - c.Error(status, text) + c.ResponseWriter().Header().Set("WWW-Authenticate", "Basic realm=\".\"") + writeError(c.ResponseWriter(), status, text) } func HTTPContexter(store Store) flamego.Handler { return func(c flamego.Context) { if len(conf.HTTP.AccessControlAllowOrigin) > 0 { // Set CORS headers for browser-based git clients - c.Header().Set("Access-Control-Allow-Origin", conf.HTTP.AccessControlAllowOrigin) - c.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent") + c.ResponseWriter().Header().Set("Access-Control-Allow-Origin", conf.HTTP.AccessControlAllowOrigin) + c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent") // Handle preflight OPTIONS request - if c.Req.Method == "OPTIONS" { - c.Status(http.StatusOK) + if c.Request().Method == "OPTIONS" { + c.ResponseWriter().WriteHeader(http.StatusOK) return } } @@ -59,26 +67,26 @@ func HTTPContexter(store Store) flamego.Handler { repoName = strings.TrimSuffix(repoName, ".wiki") isPull := c.Query("service") == "git-upload-pack" || - strings.HasSuffix(c.Req.URL.Path, "git-upload-pack") || - c.Req.Method == "GET" + strings.HasSuffix(c.Request().URL.Path, "git-upload-pack") || + c.Request().Method == "GET" - owner, err := store.GetUserByUsername(c.Req.Context(), ownerName) + owner, err := store.GetUserByUsername(c.Request().Context(), ownerName) if err != nil { if database.IsErrUserNotExist(err) { - c.Status(http.StatusNotFound) + c.ResponseWriter().WriteHeader(http.StatusNotFound) } else { - c.Status(http.StatusInternalServerError) + c.ResponseWriter().WriteHeader(http.StatusInternalServerError) log.Error("Failed to get user [name: %s]: %v", ownerName, err) } return } - repo, err := store.GetRepositoryByName(c.Req.Context(), owner.ID, repoName) + repo, err := store.GetRepositoryByName(c.Request().Context(), owner.ID, repoName) if err != nil { if database.IsErrRepoNotExist(err) { - c.Status(http.StatusNotFound) + c.ResponseWriter().WriteHeader(http.StatusNotFound) } else { - c.Status(http.StatusInternalServerError) + c.ResponseWriter().WriteHeader(http.StatusInternalServerError) log.Error("Failed to get repository [owner_id: %d, name: %s]: %v", owner.ID, repoName, err) } return @@ -98,12 +106,12 @@ func HTTPContexter(store Store) flamego.Handler { !strings.Contains(action, "info/") && !strings.Contains(action, "HEAD") && !strings.Contains(action, "objects/") { - c.Error(http.StatusBadRequest, fmt.Sprintf("Unrecognized action %q", action)) + writeError(c.ResponseWriter(), http.StatusBadRequest, fmt.Sprintf("Unrecognized action %q", action)) return } // Handle HTTP Basic Authentication - authHead := c.Req.Header.Get("Authorization") + authHead := c.Request().Header.Get("Authorization") if authHead == "" { askCredentials(c, http.StatusUnauthorized, "") return @@ -120,9 +128,9 @@ func HTTPContexter(store Store) flamego.Handler { return } - authUser, err := store.AuthenticateUser(c.Req.Context(), authUsername, authPassword, -1) + authUser, err := store.AuthenticateUser(c.Request().Context(), authUsername, authPassword, -1) if err != nil && !auth.IsErrBadCredentials(err) { - c.Status(http.StatusInternalServerError) + c.ResponseWriter().WriteHeader(http.StatusInternalServerError) log.Error("Failed to authenticate user [name: %s]: %v", authUsername, err) return } @@ -130,25 +138,25 @@ func HTTPContexter(store Store) flamego.Handler { // If username and password combination failed, try again using either username // or password as the token. if authUser == nil { - authUser, err = context.AuthenticateByToken(store, c.Req.Context(), authUsername) + authUser, err = context.AuthenticateByToken(store, c.Request().Context(), authUsername) if err != nil && !database.IsErrAccessTokenNotExist(err) { - c.Status(http.StatusInternalServerError) + c.ResponseWriter().WriteHeader(http.StatusInternalServerError) log.Error("Failed to authenticate by access token via username: %v", err) return } else if database.IsErrAccessTokenNotExist(err) { // Try again using the password field as the token. - authUser, err = context.AuthenticateByToken(store, c.Req.Context(), authPassword) + authUser, err = context.AuthenticateByToken(store, c.Request().Context(), authPassword) if err != nil { if database.IsErrAccessTokenNotExist(err) { askCredentials(c, http.StatusUnauthorized, "") } else { - c.Status(http.StatusInternalServerError) + c.ResponseWriter().WriteHeader(http.StatusInternalServerError) log.Error("Failed to authenticate by access token via password: %v", err) } return } } - } else if store.IsTwoFactorEnabled(c.Req.Context(), authUser.ID) { + } else if store.IsTwoFactorEnabled(c.Request().Context(), authUser.ID) { askCredentials(c, http.StatusUnauthorized, `User with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password Please create and use personal access token on user settings page`) return @@ -160,7 +168,7 @@ Please create and use personal access token on user settings page`) if isPull { mode = database.AccessModeRead } - if !database.Handle.Permissions().Authorize(c.Req.Context(), authUser.ID, repo.ID, mode, + if !database.Handle.Permissions().Authorize(c.Request().Context(), authUser.ID, repo.ID, mode, database.AccessModeOptions{ OwnerID: repo.OwnerID, Private: repo.IsPrivate, @@ -171,7 +179,7 @@ Please create and use personal access token on user settings page`) } if !isPull && repo.IsMirror { - c.Error(http.StatusForbidden, "Mirror repository is read-only") + writeError(c.ResponseWriter(), http.StatusForbidden, "Mirror repository is read-only") return } @@ -388,7 +396,7 @@ func getGitRepoPath(dir string) (string, error) { func HTTP(c *HTTPContext) { for _, route := range routes { - reqPath := strings.ToLower(c.Req.URL.Path) + reqPath := strings.ToLower(c.Request().URL.Path) m := route.re.FindStringSubmatch(reqPath) if m == nil { continue @@ -398,19 +406,19 @@ func HTTP(c *HTTPContext) { // but we only want to output this message only if user is really trying to access // Git HTTP endpoints. if conf.Repository.DisableHTTPGit { - c.Error(http.StatusForbidden, "Interacting with repositories by HTTP protocol is disabled") + writeError(c.ResponseWriter(), http.StatusForbidden, "Interacting with repositories by HTTP protocol is disabled") return } - if route.method != c.Req.Method { - c.Error(http.StatusNotFound) + if route.method != c.Request().Method { + writeError(c.ResponseWriter(), http.StatusNotFound) return } // 🚨 SECURITY: Prevent path traversal. cleaned := pathutil.Clean(m[1]) if m[1] != "/"+cleaned { - c.Error(http.StatusBadRequest, "Request path contains suspicious characters") + writeError(c.ResponseWriter(), http.StatusBadRequest, "Request path contains suspicious characters") return } @@ -418,13 +426,13 @@ func HTTP(c *HTTPContext) { dir, err := getGitRepoPath(cleaned) if err != nil { log.Warn("HTTP.getGitRepoPath: %v", err) - c.Error(http.StatusNotFound) + writeError(c.ResponseWriter(), http.StatusNotFound) return } route.handler(serviceHandler{ w: c.Resp, - r: c.Req.Request, + r: c.Request().Request, dir: dir, file: file, @@ -437,5 +445,5 @@ func HTTP(c *HTTPContext) { return } - c.Error(http.StatusNotFound) + writeError(c.ResponseWriter(), http.StatusNotFound) }