Browse Source

Continue flamego migration - fix Context methods and route handlers.

Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
copilot-swe-agent[bot] 2 weeks ago
parent
commit
f8ebb278df
2 changed files with 55 additions and 39 deletions
  1. 14 6
      internal/route/lfs/route.go
  2. 41 33
      internal/route/repo/http.go

+ 14 - 6
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)
 	}
 }
 

+ 41 - 33
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)
 }