]> source.dussan.org Git - gitea.git/commitdiff
Decouple the different contexts from each other (#24786)
authorwxiaoguang <wxiaoguang@gmail.com>
Sun, 21 May 2023 01:50:53 +0000 (09:50 +0800)
committerGitHub <noreply@github.com>
Sun, 21 May 2023 01:50:53 +0000 (09:50 +0800)
Replace #16455

Close #21803

Mixing different Gitea contexts together causes some problems:

1. Unable to respond proper content when error occurs, eg: Web should
respond HTML while API should respond JSON
2. Unclear dependency, eg: it's unclear when Context is used in
APIContext, which fields should be initialized, which methods are
necessary.

To make things clear, this PR introduces a Base context, it only
provides basic Req/Resp/Data features.

This PR mainly moves code. There are still many legacy problems and
TODOs in code, leave unrelated changes to future PRs.

57 files changed:
modules/context/api.go
modules/context/base.go [new file with mode: 0644]
modules/context/context.go
modules/context/context_data.go [deleted file]
modules/context/context_form.go [deleted file]
modules/context/context_request.go
modules/context/context_response.go
modules/context/context_serve.go [deleted file]
modules/context/org.go
modules/context/package.go
modules/context/private.go
modules/context/repo.go
modules/context/response.go
modules/context/utils.go
modules/test/context_tests.go
modules/translation/translation.go
modules/web/handler.go
routers/api/actions/artifacts.go
routers/api/packages/api.go
routers/api/v1/api.go
routers/api/v1/misc/markup.go
routers/api/v1/misc/markup_test.go
routers/api/v1/notify/notifications.go
routers/api/v1/repo/file.go
routers/api/v1/repo/hook_test.go
routers/api/v1/repo/issue.go
routers/api/v1/repo/issue_comment.go
routers/api/v1/repo/issue_tracked_time.go
routers/api/v1/repo/migrate.go
routers/api/v1/repo/repo_test.go
routers/api/v1/repo/status.go
routers/api/v1/user/helper.go
routers/api/v1/utils/git.go
routers/common/markup.go
routers/common/middleware.go
routers/common/serve.go
routers/init.go
routers/install/install.go
routers/web/misc/markup.go
routers/web/repo/attachment.go
routers/web/repo/download.go
routers/web/repo/http.go
routers/web/repo/issue.go
routers/web/repo/wiki.go
services/auth/middleware.go
services/context/user.go
services/forms/admin.go
services/forms/auth_form.go
services/forms/org.go
services/forms/package_form.go
services/forms/repo_branch_form.go
services/forms/repo_form.go
services/forms/repo_tag_form.go
services/forms/runner.go
services/forms/user_form.go
services/forms/user_form_auth_openid.go
services/markup/processorhelper_test.go

index e263dcbe8dea131d66db45e01f048971286ffa24..092ad73f311189ee923c4859862ff4bc55b7fc96 100644 (file)
@@ -13,18 +13,32 @@ import (
 
        "code.gitea.io/gitea/models/auth"
        repo_model "code.gitea.io/gitea/models/repo"
-       "code.gitea.io/gitea/modules/cache"
+       "code.gitea.io/gitea/models/unit"
+       user_model "code.gitea.io/gitea/models/user"
+       mc "code.gitea.io/gitea/modules/cache"
        "code.gitea.io/gitea/modules/git"
        "code.gitea.io/gitea/modules/httpcache"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
-       "code.gitea.io/gitea/modules/web/middleware"
+
+       "gitea.com/go-chi/cache"
 )
 
 // APIContext is a specific context for API service
 type APIContext struct {
-       *Context
-       Org *APIOrganization
+       *Base
+
+       Cache cache.Cache
+
+       Doer        *user_model.User // current signed-in user
+       IsSigned    bool
+       IsBasicAuth bool
+
+       ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
+
+       Repo    *Repository
+       Org     *APIOrganization
+       Package *Package
 }
 
 // Currently, we have the following common fields in error response:
@@ -128,11 +142,6 @@ type apiContextKeyType struct{}
 
 var apiContextKey = apiContextKeyType{}
 
-// WithAPIContext set up api context in request
-func WithAPIContext(req *http.Request, ctx *APIContext) *http.Request {
-       return req.WithContext(context.WithValue(req.Context(), apiContextKey, ctx))
-}
-
 // GetAPIContext returns a context for API routes
 func GetAPIContext(req *http.Request) *APIContext {
        return req.Context().Value(apiContextKey).(*APIContext)
@@ -195,21 +204,21 @@ func (ctx *APIContext) CheckForOTP() {
        }
 
        otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
-       twofa, err := auth.GetTwoFactorByUID(ctx.Context.Doer.ID)
+       twofa, err := auth.GetTwoFactorByUID(ctx.Doer.ID)
        if err != nil {
                if auth.IsErrTwoFactorNotEnrolled(err) {
                        return // No 2FA enrollment for this user
                }
-               ctx.Context.Error(http.StatusInternalServerError)
+               ctx.Error(http.StatusInternalServerError, "GetTwoFactorByUID", err)
                return
        }
        ok, err := twofa.ValidateTOTP(otpHeader)
        if err != nil {
-               ctx.Context.Error(http.StatusInternalServerError)
+               ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err)
                return
        }
        if !ok {
-               ctx.Context.Error(http.StatusUnauthorized)
+               ctx.Error(http.StatusUnauthorized, "", nil)
                return
        }
 }
@@ -218,23 +227,17 @@ func (ctx *APIContext) CheckForOTP() {
 func APIContexter() func(http.Handler) http.Handler {
        return func(next http.Handler) http.Handler {
                return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-                       locale := middleware.Locale(w, req)
-                       ctx := APIContext{
-                               Context: &Context{
-                                       Resp:   NewResponse(w),
-                                       Data:   middleware.GetContextData(req.Context()),
-                                       Locale: locale,
-                                       Cache:  cache.GetCache(),
-                                       Repo: &Repository{
-                                               PullRequest: &PullRequest{},
-                                       },
-                                       Org: &Organization{},
-                               },
-                               Org: &APIOrganization{},
+                       base, baseCleanUp := NewBaseContext(w, req)
+                       ctx := &APIContext{
+                               Base:  base,
+                               Cache: mc.GetCache(),
+                               Repo:  &Repository{PullRequest: &PullRequest{}},
+                               Org:   &APIOrganization{},
                        }
-                       defer ctx.Close()
+                       defer baseCleanUp()
 
-                       ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
+                       ctx.Base.AppendContextValue(apiContextKey, ctx)
+                       ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
 
                        // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
                        if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
@@ -247,8 +250,6 @@ func APIContexter() func(http.Handler) http.Handler {
                        httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
                        ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 
-                       ctx.Data["Context"] = &ctx
-
                        next.ServeHTTP(ctx.Resp, ctx.Req)
                })
        }
@@ -301,7 +302,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context
                        return func() {
                                // If it's been set to nil then assume someone else has closed it.
                                if ctx.Repo.GitRepo != nil {
-                                       ctx.Repo.GitRepo.Close()
+                                       _ = ctx.Repo.GitRepo.Close()
                                }
                        }
                }
@@ -337,7 +338,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
                }
 
                var err error
-               refName := getRefName(ctx.Context, RepoRefAny)
+               refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
 
                if ctx.Repo.GitRepo.IsBranchExist(refName) {
                        ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
@@ -368,3 +369,53 @@ func RepoRefForAPI(next http.Handler) http.Handler {
                next.ServeHTTP(w, req)
        })
 }
+
+// HasAPIError returns true if error occurs in form validation.
+func (ctx *APIContext) HasAPIError() bool {
+       hasErr, ok := ctx.Data["HasError"]
+       if !ok {
+               return false
+       }
+       return hasErr.(bool)
+}
+
+// GetErrMsg returns error message in form validation.
+func (ctx *APIContext) GetErrMsg() string {
+       msg, _ := ctx.Data["ErrorMsg"].(string)
+       if msg == "" {
+               msg = "invalid form data"
+       }
+       return msg
+}
+
+// NotFoundOrServerError use error check function to determine if the error
+// is about not found. It responds with 404 status code for not found error,
+// or error context description for logging purpose of 500 server error.
+func (ctx *APIContext) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
+       if errCheck(logErr) {
+               ctx.JSON(http.StatusNotFound, nil)
+               return
+       }
+       ctx.Error(http.StatusInternalServerError, "NotFoundOrServerError", logMsg)
+}
+
+// IsUserSiteAdmin returns true if current user is a site admin
+func (ctx *APIContext) IsUserSiteAdmin() bool {
+       return ctx.IsSigned && ctx.Doer.IsAdmin
+}
+
+// IsUserRepoAdmin returns true if current user is admin in current repo
+func (ctx *APIContext) IsUserRepoAdmin() bool {
+       return ctx.Repo.IsAdmin()
+}
+
+// IsUserRepoWriter returns true if current user has write privilege in current repo
+func (ctx *APIContext) IsUserRepoWriter(unitTypes []unit.Type) bool {
+       for _, unitType := range unitTypes {
+               if ctx.Repo.CanWrite(unitType) {
+                       return true
+               }
+       }
+
+       return false
+}
diff --git a/modules/context/base.go b/modules/context/base.go
new file mode 100644 (file)
index 0000000..ac9b52d
--- /dev/null
@@ -0,0 +1,300 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+       "context"
+       "fmt"
+       "io"
+       "net/http"
+       "net/url"
+       "strconv"
+       "strings"
+       "time"
+
+       "code.gitea.io/gitea/modules/httplib"
+       "code.gitea.io/gitea/modules/json"
+       "code.gitea.io/gitea/modules/log"
+       "code.gitea.io/gitea/modules/translation"
+       "code.gitea.io/gitea/modules/util"
+       "code.gitea.io/gitea/modules/web/middleware"
+
+       "github.com/go-chi/chi/v5"
+)
+
+type contextValuePair struct {
+       key     any
+       valueFn func() any
+}
+
+type Base struct {
+       originCtx     context.Context
+       contextValues []contextValuePair
+
+       Resp ResponseWriter
+       Req  *http.Request
+
+       // Data is prepared by ContextDataStore middleware, this field only refers to the pre-created/prepared ContextData.
+       // Although it's mainly used for MVC templates, sometimes it's also used to pass data between middlewares/handler
+       Data middleware.ContextData
+
+       // Locale is mainly for Web context, although the API context also uses it in some cases: message response, form validation
+       Locale translation.Locale
+}
+
+func (b *Base) Deadline() (deadline time.Time, ok bool) {
+       return b.originCtx.Deadline()
+}
+
+func (b *Base) Done() <-chan struct{} {
+       return b.originCtx.Done()
+}
+
+func (b *Base) Err() error {
+       return b.originCtx.Err()
+}
+
+func (b *Base) Value(key any) any {
+       for _, pair := range b.contextValues {
+               if pair.key == key {
+                       return pair.valueFn()
+               }
+       }
+       return b.originCtx.Value(key)
+}
+
+func (b *Base) AppendContextValueFunc(key any, valueFn func() any) any {
+       b.contextValues = append(b.contextValues, contextValuePair{key, valueFn})
+       return b
+}
+
+func (b *Base) AppendContextValue(key, value any) any {
+       b.contextValues = append(b.contextValues, contextValuePair{key, func() any { return value }})
+       return b
+}
+
+func (b *Base) GetData() middleware.ContextData {
+       return b.Data
+}
+
+// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
+func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
+       val := b.RespHeader().Get("Access-Control-Expose-Headers")
+       if len(val) != 0 {
+               b.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
+       } else {
+               b.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
+       }
+}
+
+// SetTotalCountHeader set "X-Total-Count" header
+func (b *Base) SetTotalCountHeader(total int64) {
+       b.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
+       b.AppendAccessControlExposeHeaders("X-Total-Count")
+}
+
+// Written returns true if there are something sent to web browser
+func (b *Base) Written() bool {
+       return b.Resp.Status() > 0
+}
+
+// Status writes status code
+func (b *Base) Status(status int) {
+       b.Resp.WriteHeader(status)
+}
+
+// Write writes data to web browser
+func (b *Base) Write(bs []byte) (int, error) {
+       return b.Resp.Write(bs)
+}
+
+// RespHeader returns the response header
+func (b *Base) RespHeader() http.Header {
+       return b.Resp.Header()
+}
+
+// Error returned an error to web browser
+func (b *Base) Error(status int, contents ...string) {
+       v := http.StatusText(status)
+       if len(contents) > 0 {
+               v = contents[0]
+       }
+       http.Error(b.Resp, v, status)
+}
+
+// JSON render content as JSON
+func (b *Base) JSON(status int, content interface{}) {
+       b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
+       b.Resp.WriteHeader(status)
+       if err := json.NewEncoder(b.Resp).Encode(content); err != nil {
+               log.Error("Render JSON failed: %v", err)
+       }
+}
+
+// RemoteAddr returns the client machine ip address
+func (b *Base) RemoteAddr() string {
+       return b.Req.RemoteAddr
+}
+
+// Params returns the param on route
+func (b *Base) Params(p string) string {
+       s, _ := url.PathUnescape(chi.URLParam(b.Req, strings.TrimPrefix(p, ":")))
+       return s
+}
+
+// ParamsInt64 returns the param on route as int64
+func (b *Base) ParamsInt64(p string) int64 {
+       v, _ := strconv.ParseInt(b.Params(p), 10, 64)
+       return v
+}
+
+// SetParams set params into routes
+func (b *Base) SetParams(k, v string) {
+       chiCtx := chi.RouteContext(b)
+       chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
+}
+
+// FormString returns the first value matching the provided key in the form as a string
+func (b *Base) FormString(key string) string {
+       return b.Req.FormValue(key)
+}
+
+// FormStrings returns a string slice for the provided key from the form
+func (b *Base) FormStrings(key string) []string {
+       if b.Req.Form == nil {
+               if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
+                       return nil
+               }
+       }
+       if v, ok := b.Req.Form[key]; ok {
+               return v
+       }
+       return nil
+}
+
+// FormTrim returns the first value for the provided key in the form as a space trimmed string
+func (b *Base) FormTrim(key string) string {
+       return strings.TrimSpace(b.Req.FormValue(key))
+}
+
+// FormInt returns the first value for the provided key in the form as an int
+func (b *Base) FormInt(key string) int {
+       v, _ := strconv.Atoi(b.Req.FormValue(key))
+       return v
+}
+
+// FormInt64 returns the first value for the provided key in the form as an int64
+func (b *Base) FormInt64(key string) int64 {
+       v, _ := strconv.ParseInt(b.Req.FormValue(key), 10, 64)
+       return v
+}
+
+// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
+func (b *Base) FormBool(key string) bool {
+       s := b.Req.FormValue(key)
+       v, _ := strconv.ParseBool(s)
+       v = v || strings.EqualFold(s, "on")
+       return v
+}
+
+// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
+// for the provided key exists in the form else it returns OptionalBoolNone
+func (b *Base) FormOptionalBool(key string) util.OptionalBool {
+       value := b.Req.FormValue(key)
+       if len(value) == 0 {
+               return util.OptionalBoolNone
+       }
+       s := b.Req.FormValue(key)
+       v, _ := strconv.ParseBool(s)
+       v = v || strings.EqualFold(s, "on")
+       return util.OptionalBoolOf(v)
+}
+
+func (b *Base) SetFormString(key, value string) {
+       _ = b.Req.FormValue(key) // force parse form
+       b.Req.Form.Set(key, value)
+}
+
+// PlainTextBytes renders bytes as plain text
+func (b *Base) plainTextInternal(skip, status int, bs []byte) {
+       statusPrefix := status / 100
+       if statusPrefix == 4 || statusPrefix == 5 {
+               log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
+       }
+       b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
+       b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
+       b.Resp.WriteHeader(status)
+       if _, err := b.Resp.Write(bs); err != nil {
+               log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
+       }
+}
+
+// PlainTextBytes renders bytes as plain text
+func (b *Base) PlainTextBytes(status int, bs []byte) {
+       b.plainTextInternal(2, status, bs)
+}
+
+// PlainText renders content as plain text
+func (b *Base) PlainText(status int, text string) {
+       b.plainTextInternal(2, status, []byte(text))
+}
+
+// Redirect redirects the request
+func (b *Base) Redirect(location string, status ...int) {
+       code := http.StatusSeeOther
+       if len(status) == 1 {
+               code = status[0]
+       }
+
+       if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
+               // Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
+               // 1. the first request to "/my-path" contains cookie
+               // 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
+               // 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
+               // 4. then the browser accepts the empty session, then the user is logged out
+               // So in this case, we should remove the session cookie from the response header
+               removeSessionCookieHeader(b.Resp)
+       }
+       http.Redirect(b.Resp, b.Req, location, code)
+}
+
+type ServeHeaderOptions httplib.ServeHeaderOptions
+
+func (b *Base) SetServeHeaders(opt *ServeHeaderOptions) {
+       httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opt))
+}
+
+// ServeContent serves content to http request
+func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
+       httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opts))
+       http.ServeContent(b.Resp, b.Req, opts.Filename, opts.LastModified, r)
+}
+
+// Close frees all resources hold by Context
+func (b *Base) cleanUp() {
+       if b.Req != nil && b.Req.MultipartForm != nil {
+               _ = b.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
+       }
+}
+
+func (b *Base) Tr(msg string, args ...any) string {
+       return b.Locale.Tr(msg, args...)
+}
+
+func (b *Base) TrN(cnt any, key1, keyN string, args ...any) string {
+       return b.Locale.TrN(cnt, key1, keyN, args...)
+}
+
+func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, closeFunc func()) {
+       b = &Base{
+               originCtx: req.Context(),
+               Req:       req,
+               Resp:      WrapResponseWriter(resp),
+               Locale:    middleware.Locale(resp, req),
+               Data:      middleware.GetContextData(req.Context()),
+       }
+       b.AppendContextValue(translation.ContextKey, b.Locale)
+       b.Req = b.Req.WithContext(b)
+       return b, b.cleanUp
+}
index 9ba1985f36558d17d62028ebdecebbe623de29bd..1e15081479ff872e131ee7ce60dc5ef2cba96704 100644 (file)
@@ -5,7 +5,6 @@
 package context
 
 import (
-       "context"
        "html"
        "html/template"
        "io"
@@ -36,38 +35,27 @@ type Render interface {
 
 // Context represents context of a request.
 type Context struct {
-       Resp   ResponseWriter
-       Req    *http.Request
-       Render Render
+       *Base
 
-       Data     middleware.ContextData // data used by MVC templates
-       PageData map[string]any         // data used by JavaScript modules in one page, it's `window.config.pageData`
+       Render   Render
+       PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
 
-       Locale  translation.Locale
        Cache   cache.Cache
        Csrf    CSRFProtector
        Flash   *middleware.Flash
        Session session.Store
 
-       Link        string // current request URL (without query string)
-       Doer        *user_model.User
+       Link string // current request URL (without query string)
+
+       Doer        *user_model.User // current signed-in user
        IsSigned    bool
        IsBasicAuth bool
 
-       ContextUser *user_model.User
-       Repo        *Repository
-       Org         *Organization
-       Package     *Package
-}
+       ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
 
-// Close frees all resources hold by Context
-func (ctx *Context) Close() error {
-       var err error
-       if ctx.Req != nil && ctx.Req.MultipartForm != nil {
-               err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
-       }
-       // TODO: close opened repo, and more
-       return err
+       Repo    *Repository
+       Org     *Organization
+       Package *Package
 }
 
 // TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
@@ -80,55 +68,30 @@ func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
        return ctx.Locale.Tr(msg, trArgs...)
 }
 
-func (ctx *Context) Tr(msg string, args ...any) string {
-       return ctx.Locale.Tr(msg, args...)
-}
-
-func (ctx *Context) TrN(cnt any, key1, keyN string, args ...any) string {
-       return ctx.Locale.TrN(cnt, key1, keyN, args...)
-}
-
-// Deadline is part of the interface for context.Context and we pass this to the request context
-func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
-       return ctx.Req.Context().Deadline()
-}
-
-// Done is part of the interface for context.Context and we pass this to the request context
-func (ctx *Context) Done() <-chan struct{} {
-       return ctx.Req.Context().Done()
-}
-
-// Err is part of the interface for context.Context and we pass this to the request context
-func (ctx *Context) Err() error {
-       return ctx.Req.Context().Err()
-}
-
-// Value is part of the interface for context.Context and we pass this to the request context
-func (ctx *Context) Value(key interface{}) interface{} {
-       if key == git.RepositoryContextKey && ctx.Repo != nil {
-               return ctx.Repo.GitRepo
-       }
-       if key == translation.ContextKey && ctx.Locale != nil {
-               return ctx.Locale
-       }
-       return ctx.Req.Context().Value(key)
-}
-
 type contextKeyType struct{}
 
 var contextKey interface{} = contextKeyType{}
 
-// WithContext set up install context in request
-func WithContext(req *http.Request, ctx *Context) *http.Request {
-       return req.WithContext(context.WithValue(req.Context(), contextKey, ctx))
+func GetContext(req *http.Request) *Context {
+       ctx, _ := req.Context().Value(contextKey).(*Context)
+       return ctx
 }
 
-// GetContext retrieves install context from request
-func GetContext(req *http.Request) *Context {
-       if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
-               return ctx
+// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
+type ValidateContext struct {
+       *Base
+}
+
+// GetValidateContext gets a context for middleware form validation
+func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
+       if ctxAPI, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
+               ctx = &ValidateContext{Base: ctxAPI.Base}
+       } else if ctxWeb, ok := req.Context().Value(contextKey).(*Context); ok {
+               ctx = &ValidateContext{Base: ctxWeb.Base}
+       } else {
+               panic("invalid context, expect either APIContext or Context")
        }
-       return nil
+       return ctx
 }
 
 // Contexter initializes a classic context for a request.
@@ -150,20 +113,17 @@ func Contexter() func(next http.Handler) http.Handler {
        }
        return func(next http.Handler) http.Handler {
                return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
-                       ctx := Context{
-                               Resp:    NewResponse(resp),
+                       base, baseCleanUp := NewBaseContext(resp, req)
+                       ctx := &Context{
+                               Base:    base,
                                Cache:   mc.GetCache(),
-                               Locale:  middleware.Locale(resp, req),
                                Link:    setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"),
                                Render:  rnd,
                                Session: session.GetSession(req),
-                               Repo: &Repository{
-                                       PullRequest: &PullRequest{},
-                               },
-                               Org:  &Organization{},
-                               Data: middleware.GetContextData(req.Context()),
+                               Repo:    &Repository{PullRequest: &PullRequest{}},
+                               Org:     &Organization{},
                        }
-                       defer ctx.Close()
+                       defer baseCleanUp()
 
                        ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
                        ctx.Data["Context"] = &ctx
@@ -175,15 +135,17 @@ func Contexter() func(next http.Handler) http.Handler {
                        ctx.PageData = map[string]any{}
                        ctx.Data["PageData"] = ctx.PageData
 
-                       ctx.Req = WithContext(req, &ctx)
-                       ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
+                       ctx.Base.AppendContextValue(contextKey, ctx)
+                       ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
+
+                       ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
 
                        // Get the last flash message from cookie
                        lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
                        if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
                                // store last Flash message into the template data, to render it
                                ctx.Data["Flash"] = &middleware.Flash{
-                                       DataStore:  &ctx,
+                                       DataStore:  ctx,
                                        Values:     vals,
                                        ErrorMsg:   vals.Get("error"),
                                        SuccessMsg: vals.Get("success"),
@@ -193,7 +155,7 @@ func Contexter() func(next http.Handler) http.Handler {
                        }
 
                        // prepare an empty Flash message for current request
-                       ctx.Flash = &middleware.Flash{DataStore: &ctx, Values: url.Values{}}
+                       ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
                        ctx.Resp.Before(func(resp ResponseWriter) {
                                if val := ctx.Flash.Encode(); val != "" {
                                        middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
@@ -235,3 +197,24 @@ func Contexter() func(next http.Handler) http.Handler {
                })
        }
 }
+
+// HasError returns true if error occurs in form validation.
+// Attention: this function changes ctx.Data and ctx.Flash
+func (ctx *Context) HasError() bool {
+       hasErr, ok := ctx.Data["HasError"]
+       if !ok {
+               return false
+       }
+       ctx.Flash.ErrorMsg = ctx.GetErrMsg()
+       ctx.Data["Flash"] = ctx.Flash
+       return hasErr.(bool)
+}
+
+// GetErrMsg returns error message in form validation.
+func (ctx *Context) GetErrMsg() string {
+       msg, _ := ctx.Data["ErrorMsg"].(string)
+       if msg == "" {
+               msg = "invalid form data"
+       }
+       return msg
+}
diff --git a/modules/context/context_data.go b/modules/context/context_data.go
deleted file mode 100644 (file)
index cdf4ff9..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import "code.gitea.io/gitea/modules/web/middleware"
-
-// GetData returns the data
-func (ctx *Context) GetData() middleware.ContextData {
-       return ctx.Data
-}
-
-// HasAPIError returns true if error occurs in form validation.
-func (ctx *Context) HasAPIError() bool {
-       hasErr, ok := ctx.Data["HasError"]
-       if !ok {
-               return false
-       }
-       return hasErr.(bool)
-}
-
-// GetErrMsg returns error message
-func (ctx *Context) GetErrMsg() string {
-       return ctx.Data["ErrorMsg"].(string)
-}
-
-// HasError returns true if error occurs in form validation.
-// Attention: this function changes ctx.Data and ctx.Flash
-func (ctx *Context) HasError() bool {
-       hasErr, ok := ctx.Data["HasError"]
-       if !ok {
-               return false
-       }
-       ctx.Flash.ErrorMsg = ctx.Data["ErrorMsg"].(string)
-       ctx.Data["Flash"] = ctx.Flash
-       return hasErr.(bool)
-}
-
-// HasValue returns true if value of given name exists.
-func (ctx *Context) HasValue(name string) bool {
-       _, ok := ctx.Data[name]
-       return ok
-}
diff --git a/modules/context/context_form.go b/modules/context/context_form.go
deleted file mode 100644 (file)
index 5c02152..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
-       "strconv"
-       "strings"
-
-       "code.gitea.io/gitea/modules/util"
-)
-
-// FormString returns the first value matching the provided key in the form as a string
-func (ctx *Context) FormString(key string) string {
-       return ctx.Req.FormValue(key)
-}
-
-// FormStrings returns a string slice for the provided key from the form
-func (ctx *Context) FormStrings(key string) []string {
-       if ctx.Req.Form == nil {
-               if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil {
-                       return nil
-               }
-       }
-       if v, ok := ctx.Req.Form[key]; ok {
-               return v
-       }
-       return nil
-}
-
-// FormTrim returns the first value for the provided key in the form as a space trimmed string
-func (ctx *Context) FormTrim(key string) string {
-       return strings.TrimSpace(ctx.Req.FormValue(key))
-}
-
-// FormInt returns the first value for the provided key in the form as an int
-func (ctx *Context) FormInt(key string) int {
-       v, _ := strconv.Atoi(ctx.Req.FormValue(key))
-       return v
-}
-
-// FormInt64 returns the first value for the provided key in the form as an int64
-func (ctx *Context) FormInt64(key string) int64 {
-       v, _ := strconv.ParseInt(ctx.Req.FormValue(key), 10, 64)
-       return v
-}
-
-// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
-func (ctx *Context) FormBool(key string) bool {
-       s := ctx.Req.FormValue(key)
-       v, _ := strconv.ParseBool(s)
-       v = v || strings.EqualFold(s, "on")
-       return v
-}
-
-// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
-// for the provided key exists in the form else it returns OptionalBoolNone
-func (ctx *Context) FormOptionalBool(key string) util.OptionalBool {
-       value := ctx.Req.FormValue(key)
-       if len(value) == 0 {
-               return util.OptionalBoolNone
-       }
-       s := ctx.Req.FormValue(key)
-       v, _ := strconv.ParseBool(s)
-       v = v || strings.EqualFold(s, "on")
-       return util.OptionalBoolOf(v)
-}
-
-func (ctx *Context) SetFormString(key, value string) {
-       _ = ctx.Req.FormValue(key) // force parse form
-       ctx.Req.Form.Set(key, value)
-}
index 0b87552c085ba6ab6cae6d82c2a2ff4e4b49e78e..984b9ac793e8d2ae8d01bf10289cfe7a499f8bc9 100644 (file)
@@ -6,36 +6,9 @@ package context
 import (
        "io"
        "net/http"
-       "net/url"
-       "strconv"
        "strings"
-
-       "github.com/go-chi/chi/v5"
 )
 
-// RemoteAddr returns the client machine ip address
-func (ctx *Context) RemoteAddr() string {
-       return ctx.Req.RemoteAddr
-}
-
-// Params returns the param on route
-func (ctx *Context) Params(p string) string {
-       s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":")))
-       return s
-}
-
-// ParamsInt64 returns the param on route as int64
-func (ctx *Context) ParamsInt64(p string) int64 {
-       v, _ := strconv.ParseInt(ctx.Params(p), 10, 64)
-       return v
-}
-
-// SetParams set params into routes
-func (ctx *Context) SetParams(k, v string) {
-       chiCtx := chi.RouteContext(ctx)
-       chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
-}
-
 // UploadStream returns the request body or the first form file
 // Only form files need to get closed.
 func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {
index 8adff96994bb9d80367b4d9c343f6bd1de5cee53..aeeb51ba377f26c713a547b1a9487de698f6b454 100644 (file)
@@ -16,49 +16,17 @@ import (
 
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/base"
-       "code.gitea.io/gitea/modules/json"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
        "code.gitea.io/gitea/modules/templates"
        "code.gitea.io/gitea/modules/web/middleware"
 )
 
-// SetTotalCountHeader set "X-Total-Count" header
-func (ctx *Context) SetTotalCountHeader(total int64) {
-       ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
-       ctx.AppendAccessControlExposeHeaders("X-Total-Count")
-}
-
-// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
-func (ctx *Context) AppendAccessControlExposeHeaders(names ...string) {
-       val := ctx.RespHeader().Get("Access-Control-Expose-Headers")
-       if len(val) != 0 {
-               ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
-       } else {
-               ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
-       }
-}
-
-// Written returns true if there are something sent to web browser
-func (ctx *Context) Written() bool {
-       return ctx.Resp.Status() > 0
-}
-
-// Status writes status code
-func (ctx *Context) Status(status int) {
-       ctx.Resp.WriteHeader(status)
-}
-
-// Write writes data to web browser
-func (ctx *Context) Write(bs []byte) (int, error) {
-       return ctx.Resp.Write(bs)
-}
-
 // RedirectToUser redirect to a differently-named user
-func RedirectToUser(ctx *Context, userName string, redirectUserID int64) {
+func RedirectToUser(ctx *Base, userName string, redirectUserID int64) {
        user, err := user_model.GetUserByID(ctx, redirectUserID)
        if err != nil {
-               ctx.ServerError("GetUserByID", err)
+               ctx.Error(http.StatusInternalServerError, "unable to get user")
                return
        }
 
@@ -211,69 +179,3 @@ func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bo
        }
        ctx.serverErrorInternal(logMsg, logErr)
 }
-
-// PlainTextBytes renders bytes as plain text
-func (ctx *Context) plainTextInternal(skip, status int, bs []byte) {
-       statusPrefix := status / 100
-       if statusPrefix == 4 || statusPrefix == 5 {
-               log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
-       }
-       ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
-       ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
-       ctx.Resp.WriteHeader(status)
-       if _, err := ctx.Resp.Write(bs); err != nil {
-               log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
-       }
-}
-
-// PlainTextBytes renders bytes as plain text
-func (ctx *Context) PlainTextBytes(status int, bs []byte) {
-       ctx.plainTextInternal(2, status, bs)
-}
-
-// PlainText renders content as plain text
-func (ctx *Context) PlainText(status int, text string) {
-       ctx.plainTextInternal(2, status, []byte(text))
-}
-
-// RespHeader returns the response header
-func (ctx *Context) RespHeader() http.Header {
-       return ctx.Resp.Header()
-}
-
-// Error returned an error to web browser
-func (ctx *Context) Error(status int, contents ...string) {
-       v := http.StatusText(status)
-       if len(contents) > 0 {
-               v = contents[0]
-       }
-       http.Error(ctx.Resp, v, status)
-}
-
-// JSON render content as JSON
-func (ctx *Context) JSON(status int, content interface{}) {
-       ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
-       ctx.Resp.WriteHeader(status)
-       if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
-               ctx.ServerError("Render JSON failed", err)
-       }
-}
-
-// Redirect redirects the request
-func (ctx *Context) Redirect(location string, status ...int) {
-       code := http.StatusSeeOther
-       if len(status) == 1 {
-               code = status[0]
-       }
-
-       if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
-               // Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
-               // 1. the first request to "/my-path" contains cookie
-               // 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
-               // 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
-               // 4. then the browser accepts the empty session, then the user is logged out
-               // So in this case, we should remove the session cookie from the response header
-               removeSessionCookieHeader(ctx.Resp)
-       }
-       http.Redirect(ctx.Resp, ctx.Req, location, code)
-}
diff --git a/modules/context/context_serve.go b/modules/context/context_serve.go
deleted file mode 100644 (file)
index 5569efb..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
-       "io"
-       "net/http"
-
-       "code.gitea.io/gitea/modules/httplib"
-)
-
-type ServeHeaderOptions httplib.ServeHeaderOptions
-
-func (ctx *Context) SetServeHeaders(opt *ServeHeaderOptions) {
-       httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opt))
-}
-
-// ServeContent serves content to http request
-func (ctx *Context) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
-       httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opts))
-       http.ServeContent(ctx.Resp, ctx.Req, opts.Filename, opts.LastModified, r)
-}
index 39a3038f910cbeb790fda8e2991099b76ef51eb4..355ba0ebd01f59fad6f332b07264a94a60775321 100644 (file)
@@ -47,7 +47,7 @@ func GetOrganizationByParams(ctx *Context) {
                if organization.IsErrOrgNotExist(err) {
                        redirectUserID, err := user_model.LookupUserRedirect(orgName)
                        if err == nil {
-                               RedirectToUser(ctx, orgName, redirectUserID)
+                               RedirectToUser(ctx.Base, orgName, redirectUserID)
                        } else if user_model.IsErrUserRedirectNotExist(err) {
                                ctx.NotFound("GetUserByName", err)
                        } else {
index fe5bdac19d67ed6cf69064071b2e3b3f9ecd432a..b1fd7088ddc4046ddfb2266f95c984f95516afc3 100644 (file)
@@ -4,7 +4,6 @@
 package context
 
 import (
-       gocontext "context"
        "fmt"
        "net/http"
 
@@ -16,7 +15,6 @@ import (
        "code.gitea.io/gitea/modules/setting"
        "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/templates"
-       "code.gitea.io/gitea/modules/web/middleware"
 )
 
 // Package contains owner, access mode and optional the package descriptor
@@ -26,10 +24,16 @@ type Package struct {
        Descriptor *packages_model.PackageDescriptor
 }
 
+type packageAssignmentCtx struct {
+       *Base
+       Doer        *user_model.User
+       ContextUser *user_model.User
+}
+
 // PackageAssignment returns a middleware to handle Context.Package assignment
 func PackageAssignment() func(ctx *Context) {
        return func(ctx *Context) {
-               packageAssignment(ctx, func(status int, title string, obj interface{}) {
+               errorFn := func(status int, title string, obj interface{}) {
                        err, ok := obj.(error)
                        if !ok {
                                err = fmt.Errorf("%s", obj)
@@ -39,68 +43,72 @@ func PackageAssignment() func(ctx *Context) {
                        } else {
                                ctx.ServerError(title, err)
                        }
-               })
+               }
+               paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
+               ctx.Package = packageAssignment(paCtx, errorFn)
        }
 }
 
 // PackageAssignmentAPI returns a middleware to handle Context.Package assignment
 func PackageAssignmentAPI() func(ctx *APIContext) {
        return func(ctx *APIContext) {
-               packageAssignment(ctx.Context, ctx.Error)
+               paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
+               ctx.Package = packageAssignment(paCtx, ctx.Error)
        }
 }
 
-func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
-       ctx.Package = &Package{
+func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, interface{})) *Package {
+       pkg := &Package{
                Owner: ctx.ContextUser,
        }
-
        var err error
-       ctx.Package.AccessMode, err = determineAccessMode(ctx)
+       pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer)
        if err != nil {
                errCb(http.StatusInternalServerError, "determineAccessMode", err)
-               return
+               return pkg
        }
 
        packageType := ctx.Params("type")
        name := ctx.Params("name")
        version := ctx.Params("version")
        if packageType != "" && name != "" && version != "" {
-               pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.Type(packageType), name, version)
+               pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
                if err != nil {
                        if err == packages_model.ErrPackageNotExist {
                                errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
                        } else {
                                errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
                        }
-                       return
+                       return pkg
                }
 
-               ctx.Package.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
+               pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
                if err != nil {
                        errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
-                       return
+                       return pkg
                }
        }
+
+       return pkg
 }
 
-func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
-       if setting.Service.RequireSignInView && ctx.Doer == nil {
+func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
+       if setting.Service.RequireSignInView && doer == nil {
                return perm.AccessModeNone, nil
        }
 
-       if ctx.Doer != nil && !ctx.Doer.IsGhost() && (!ctx.Doer.IsActive || ctx.Doer.ProhibitLogin) {
+       if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) {
                return perm.AccessModeNone, nil
        }
 
        // TODO: ActionUser permission check
        accessMode := perm.AccessModeNone
-       if ctx.Package.Owner.IsOrganization() {
-               org := organization.OrgFromUser(ctx.Package.Owner)
+       if pkg.Owner.IsOrganization() {
+               org := organization.OrgFromUser(pkg.Owner)
 
-               if ctx.Doer != nil && !ctx.Doer.IsGhost() {
+               if doer != nil && !doer.IsGhost() {
                        // 1. If user is logged in, check all team packages permissions
-                       teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
+                       teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
                        if err != nil {
                                return accessMode, err
                        }
@@ -110,19 +118,19 @@ func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
                                        accessMode = perm
                                }
                        }
-               } else if organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
+               } else if organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
                        // 2. If user is non-login, check if org is visible to non-login user
                        accessMode = perm.AccessModeRead
                }
        } else {
-               if ctx.Doer != nil && !ctx.Doer.IsGhost() {
+               if doer != nil && !doer.IsGhost() {
                        // 1. Check if user is package owner
-                       if ctx.Doer.ID == ctx.Package.Owner.ID {
+                       if doer.ID == pkg.Owner.ID {
                                accessMode = perm.AccessModeOwner
-                       } else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
+                       } else if pkg.Owner.Visibility == structs.VisibleTypePublic || pkg.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
                                accessMode = perm.AccessModeRead
                        }
-               } else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
+               } else if pkg.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
                        accessMode = perm.AccessModeRead
                }
        }
@@ -131,19 +139,18 @@ func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
 }
 
 // PackageContexter initializes a package context for a request.
-func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handler {
-       rnd := templates.HTMLRenderer()
+func PackageContexter() func(next http.Handler) http.Handler {
+       renderer := templates.HTMLRenderer()
        return func(next http.Handler) http.Handler {
                return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
-                       ctx := Context{
-                               Resp:   NewResponse(resp),
-                               Data:   middleware.GetContextData(req.Context()),
-                               Render: rnd,
+                       base, baseCleanUp := NewBaseContext(resp, req)
+                       ctx := &Context{
+                               Base:   base,
+                               Render: renderer, // it is still needed when rendering 500 page in a package handler
                        }
-                       defer ctx.Close()
-
-                       ctx.Req = WithContext(req, &ctx)
+                       defer baseCleanUp()
 
+                       ctx.Base.AppendContextValue(contextKey, ctx)
                        next.ServeHTTP(ctx.Resp, ctx.Req)
                })
        }
index f621dd68390fe69817918a558796215877a0eda8..41ca8a4709cea67700a25493fda93a13243d7c44 100644 (file)
@@ -11,13 +11,14 @@ import (
 
        "code.gitea.io/gitea/modules/graceful"
        "code.gitea.io/gitea/modules/process"
-       "code.gitea.io/gitea/modules/web/middleware"
 )
 
 // PrivateContext represents a context for private routes
 type PrivateContext struct {
-       *Context
+       *Base
        Override context.Context
+
+       Repo *Repository
 }
 
 // Deadline is part of the interface for context.Context and we pass this to the request context
@@ -25,7 +26,7 @@ func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
        if ctx.Override != nil {
                return ctx.Override.Deadline()
        }
-       return ctx.Req.Context().Deadline()
+       return ctx.Base.Deadline()
 }
 
 // Done is part of the interface for context.Context and we pass this to the request context
@@ -33,7 +34,7 @@ func (ctx *PrivateContext) Done() <-chan struct{} {
        if ctx.Override != nil {
                return ctx.Override.Done()
        }
-       return ctx.Req.Context().Done()
+       return ctx.Base.Done()
 }
 
 // Err is part of the interface for context.Context and we pass this to the request context
@@ -41,16 +42,11 @@ func (ctx *PrivateContext) Err() error {
        if ctx.Override != nil {
                return ctx.Override.Err()
        }
-       return ctx.Req.Context().Err()
+       return ctx.Base.Err()
 }
 
 var privateContextKey interface{} = "default_private_context"
 
-// WithPrivateContext set up private context in request
-func WithPrivateContext(req *http.Request, ctx *PrivateContext) *http.Request {
-       return req.WithContext(context.WithValue(req.Context(), privateContextKey, ctx))
-}
-
 // GetPrivateContext returns a context for Private routes
 func GetPrivateContext(req *http.Request) *PrivateContext {
        return req.Context().Value(privateContextKey).(*PrivateContext)
@@ -60,16 +56,11 @@ func GetPrivateContext(req *http.Request) *PrivateContext {
 func PrivateContexter() func(http.Handler) http.Handler {
        return func(next http.Handler) http.Handler {
                return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-                       ctx := &PrivateContext{
-                               Context: &Context{
-                                       Resp: NewResponse(w),
-                                       Data: middleware.GetContextData(req.Context()),
-                               },
-                       }
-                       defer ctx.Close()
+                       base, baseCleanUp := NewBaseContext(w, req)
+                       ctx := &PrivateContext{Base: base}
+                       defer baseCleanUp()
+                       ctx.Base.AppendContextValue(privateContextKey, ctx)
 
-                       ctx.Req = WithPrivateContext(req, ctx)
-                       ctx.Data["Context"] = ctx
                        next.ServeHTTP(ctx.Resp, ctx.Req)
                })
        }
index 5e90e8aec0ec212c28fc7e8dae6bb75cde0ebafa..fd5f20857663e1d391001b99a6a40cc5dbaaa9f8 100644 (file)
@@ -331,13 +331,14 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
 }
 
 // RedirectToRepo redirect to a differently-named repository
-func RedirectToRepo(ctx *Context, redirectRepoID int64) {
+func RedirectToRepo(ctx *Base, redirectRepoID int64) {
        ownerName := ctx.Params(":username")
        previousRepoName := ctx.Params(":reponame")
 
        repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
        if err != nil {
-               ctx.ServerError("GetRepositoryByID", err)
+               log.Error("GetRepositoryByID: %v", err)
+               ctx.Error(http.StatusInternalServerError, "GetRepositoryByID")
                return
        }
 
@@ -456,7 +457,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
                                }
 
                                if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
-                                       RedirectToUser(ctx, userName, redirectUserID)
+                                       RedirectToUser(ctx.Base, userName, redirectUserID)
                                } else if user_model.IsErrUserRedirectNotExist(err) {
                                        ctx.NotFound("GetUserByName", nil)
                                } else {
@@ -498,7 +499,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
                if repo_model.IsErrRepoNotExist(err) {
                        redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
                        if err == nil {
-                               RedirectToRepo(ctx, redirectRepoID)
+                               RedirectToRepo(ctx.Base, redirectRepoID)
                        } else if repo_model.IsErrRedirectNotExist(err) {
                                if ctx.FormString("go-get") == "1" {
                                        EarlyResponseForGoGetMeta(ctx)
@@ -781,46 +782,46 @@ func (rt RepoRefType) RefTypeIncludesTags() bool {
        return false
 }
 
-func getRefNameFromPath(ctx *Context, path string, isExist func(string) bool) string {
+func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string {
        refName := ""
        parts := strings.Split(path, "/")
        for i, part := range parts {
                refName = strings.TrimPrefix(refName+"/"+part, "/")
                if isExist(refName) {
-                       ctx.Repo.TreePath = strings.Join(parts[i+1:], "/")
+                       repo.TreePath = strings.Join(parts[i+1:], "/")
                        return refName
                }
        }
        return ""
 }
 
-func getRefName(ctx *Context, pathType RepoRefType) string {
+func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
        path := ctx.Params("*")
        switch pathType {
        case RepoRefLegacy, RepoRefAny:
-               if refName := getRefName(ctx, RepoRefBranch); len(refName) > 0 {
+               if refName := getRefName(ctx, repo, RepoRefBranch); len(refName) > 0 {
                        return refName
                }
-               if refName := getRefName(ctx, RepoRefTag); len(refName) > 0 {
+               if refName := getRefName(ctx, repo, RepoRefTag); len(refName) > 0 {
                        return refName
                }
                // For legacy and API support only full commit sha
                parts := strings.Split(path, "/")
                if len(parts) > 0 && len(parts[0]) == git.SHAFullLength {
-                       ctx.Repo.TreePath = strings.Join(parts[1:], "/")
+                       repo.TreePath = strings.Join(parts[1:], "/")
                        return parts[0]
                }
-               if refName := getRefName(ctx, RepoRefBlob); len(refName) > 0 {
+               if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
                        return refName
                }
-               ctx.Repo.TreePath = path
-               return ctx.Repo.Repository.DefaultBranch
+               repo.TreePath = path
+               return repo.Repository.DefaultBranch
        case RepoRefBranch:
-               ref := getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist)
+               ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
                if len(ref) == 0 {
                        // maybe it's a renamed branch
-                       return getRefNameFromPath(ctx, path, func(s string) bool {
-                               b, exist, err := git_model.FindRenamedBranch(ctx, ctx.Repo.Repository.ID, s)
+                       return getRefNameFromPath(ctx, repo, path, func(s string) bool {
+                               b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s)
                                if err != nil {
                                        log.Error("FindRenamedBranch", err)
                                        return false
@@ -839,15 +840,15 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
 
                return ref
        case RepoRefTag:
-               return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
+               return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
        case RepoRefCommit:
                parts := strings.Split(path, "/")
                if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength {
-                       ctx.Repo.TreePath = strings.Join(parts[1:], "/")
+                       repo.TreePath = strings.Join(parts[1:], "/")
                        return parts[0]
                }
        case RepoRefBlob:
-               _, err := ctx.Repo.GitRepo.GetBlob(path)
+               _, err := repo.GitRepo.GetBlob(path)
                if err != nil {
                        return ""
                }
@@ -922,7 +923,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
                        }
                        ctx.Repo.IsViewBranch = true
                } else {
-                       refName = getRefName(ctx, refType)
+                       refName = getRefName(ctx.Base, ctx.Repo, refType)
                        ctx.Repo.RefName = refName
                        isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
                        if isRenamedBranch && has {
index 40eb5c0d35d4fbee76398dab3322e08bcbba00f0..ca52ea137d28c00a8e2701d6388c6debaef8359b 100644 (file)
@@ -10,10 +10,9 @@ import (
 // ResponseWriter represents a response writer for HTTP
 type ResponseWriter interface {
        http.ResponseWriter
-       Flush()
+       http.Flusher
        Status() int
        Before(func(ResponseWriter))
-       Size() int
 }
 
 var _ ResponseWriter = &Response{}
@@ -27,11 +26,6 @@ type Response struct {
        beforeExecuted bool
 }
 
-// Size return written size
-func (r *Response) Size() int {
-       return r.written
-}
-
 // Write writes bytes to HTTP endpoint
 func (r *Response) Write(bs []byte) (int, error) {
        if !r.beforeExecuted {
@@ -65,7 +59,7 @@ func (r *Response) WriteHeader(statusCode int) {
        }
 }
 
-// Flush flush cached data
+// Flush flushes cached data
 func (r *Response) Flush() {
        if f, ok := r.ResponseWriter.(http.Flusher); ok {
                f.Flush()
@@ -83,8 +77,7 @@ func (r *Response) Before(f func(ResponseWriter)) {
        r.befores = append(r.befores, f)
 }
 
-// NewResponse creates a response
-func NewResponse(resp http.ResponseWriter) *Response {
+func WrapResponseWriter(resp http.ResponseWriter) *Response {
        if v, ok := resp.(*Response); ok {
                return v
        }
index 1fa99953a23fc0e5c15cd1224268ea720bb15f65..c0f619aa237e78cecd9a4dcb439aef3000367a64 100644 (file)
@@ -10,7 +10,7 @@ import (
 )
 
 // GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
-func GetQueryBeforeSince(ctx *Context) (before, since int64, err error) {
+func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
        qCreatedBefore, err := prepareQueryArg(ctx, "before")
        if err != nil {
                return 0, 0, err
@@ -48,7 +48,7 @@ func parseTime(value string) (int64, error) {
 }
 
 // prepareQueryArg unescape and trim a query arg
-func prepareQueryArg(ctx *Context, name string) (value string, err error) {
+func prepareQueryArg(ctx *Base, name string) (value string, err error) {
        value, err = url.PathUnescape(ctx.FormString(name))
        value = strings.TrimSpace(value)
        return value, err
index 5ba21261261476723b0aba67fa1291907f27e82d..349c7e3e8045f7b64533c3b34b22f8d098b718ca 100644 (file)
@@ -4,7 +4,7 @@
 package test
 
 import (
-       scontext "context"
+       gocontext "context"
        "io"
        "net/http"
        "net/http/httptest"
@@ -28,18 +28,33 @@ import (
 // MockContext mock context for unit tests
 // TODO: move this function to other packages, because it depends on "models" package
 func MockContext(t *testing.T, path string) *context.Context {
-       resp := &mockResponseWriter{}
-       ctx := context.Context{
+       resp := httptest.NewRecorder()
+       requestURL, err := url.Parse(path)
+       assert.NoError(t, err)
+       req := &http.Request{
+               URL:  requestURL,
+               Form: url.Values{},
+       }
+
+       base, baseCleanUp := context.NewBaseContext(resp, req)
+       base.Data = middleware.ContextData{}
+       base.Locale = &translation.MockLocale{}
+       ctx := &context.Context{
+               Base:   base,
                Render: &mockRender{},
-               Data:   make(middleware.ContextData),
-               Flash: &middleware.Flash{
-                       Values: make(url.Values),
-               },
-               Resp:   context.NewResponse(resp),
-               Locale: &translation.MockLocale{},
+               Flash:  &middleware.Flash{Values: url.Values{}},
        }
-       defer ctx.Close()
+       _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
 
+       chiCtx := chi.NewRouteContext()
+       ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+       return ctx
+}
+
+// MockAPIContext mock context for unit tests
+// TODO: move this function to other packages, because it depends on "models" package
+func MockAPIContext(t *testing.T, path string) *context.APIContext {
+       resp := httptest.NewRecorder()
        requestURL, err := url.Parse(path)
        assert.NoError(t, err)
        req := &http.Request{
@@ -47,41 +62,79 @@ func MockContext(t *testing.T, path string) *context.Context {
                Form: url.Values{},
        }
 
+       base, baseCleanUp := context.NewBaseContext(resp, req)
+       base.Data = middleware.ContextData{}
+       base.Locale = &translation.MockLocale{}
+       ctx := &context.APIContext{Base: base}
+       _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
+
        chiCtx := chi.NewRouteContext()
-       req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx))
-       ctx.Req = context.WithContext(req, &ctx)
-       return &ctx
+       ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+       return ctx
 }
 
 // LoadRepo load a repo into a test context.
-func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
-       ctx.Repo = &context.Repository{}
-       ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
+func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
+       var doer *user_model.User
+       repo := &context.Repository{}
+       switch ctx := ctx.(type) {
+       case *context.Context:
+               ctx.Repo = repo
+               doer = ctx.Doer
+       case *context.APIContext:
+               ctx.Repo = repo
+               doer = ctx.Doer
+       default:
+               assert.Fail(t, "context is not *context.Context or *context.APIContext")
+               return
+       }
+
+       repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
        var err error
-       ctx.Repo.Owner, err = user_model.GetUserByID(ctx, ctx.Repo.Repository.OwnerID)
+       repo.Owner, err = user_model.GetUserByID(ctx, repo.Repository.OwnerID)
        assert.NoError(t, err)
-       ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
-       ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, ctx.Doer)
+       repo.RepoLink = repo.Repository.Link()
+       repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo.Repository, doer)
        assert.NoError(t, err)
 }
 
 // LoadRepoCommit loads a repo's commit into a test context.
-func LoadRepoCommit(t *testing.T, ctx *context.Context) {
-       gitRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
+func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
+       var repo *context.Repository
+       switch ctx := ctx.(type) {
+       case *context.Context:
+               repo = ctx.Repo
+       case *context.APIContext:
+               repo = ctx.Repo
+       default:
+               assert.Fail(t, "context is not *context.Context or *context.APIContext")
+               return
+       }
+
+       gitRepo, err := git.OpenRepository(ctx, repo.Repository.RepoPath())
        assert.NoError(t, err)
        defer gitRepo.Close()
        branch, err := gitRepo.GetHEADBranch()
        assert.NoError(t, err)
        assert.NotNil(t, branch)
        if branch != nil {
-               ctx.Repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
+               repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
                assert.NoError(t, err)
        }
 }
 
 // LoadUser load a user into a test context.
-func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
-       ctx.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
+func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
+       doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
+       switch ctx := ctx.(type) {
+       case *context.Context:
+               ctx.Doer = doer
+       case *context.APIContext:
+               ctx.Doer = doer
+       default:
+               assert.Fail(t, "context is not *context.Context or *context.APIContext")
+               return
+       }
 }
 
 // LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
@@ -93,32 +146,6 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) {
        assert.NoError(t, err)
 }
 
-type mockResponseWriter struct {
-       httptest.ResponseRecorder
-       size int
-}
-
-func (rw *mockResponseWriter) Write(b []byte) (int, error) {
-       rw.size += len(b)
-       return rw.ResponseRecorder.Write(b)
-}
-
-func (rw *mockResponseWriter) Status() int {
-       return rw.ResponseRecorder.Code
-}
-
-func (rw *mockResponseWriter) Written() bool {
-       return rw.ResponseRecorder.Code > 0
-}
-
-func (rw *mockResponseWriter) Size() int {
-       return rw.size
-}
-
-func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error {
-       return nil
-}
-
 type mockRender struct{}
 
 func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) {
index 49dfa84d1b0efafdfb15af7a369156e42841e6c0..dba4de6607da08e8873796e1ca46ee7b510c2c7e 100644 (file)
@@ -38,10 +38,12 @@ type LangType struct {
 }
 
 var (
-       lock          *sync.RWMutex
+       lock *sync.RWMutex
+
+       allLangs   []*LangType
+       allLangMap map[string]*LangType
+
        matcher       language.Matcher
-       allLangs      []*LangType
-       allLangMap    map[string]*LangType
        supportedTags []language.Tag
 )
 
@@ -251,3 +253,9 @@ func (l *locale) PrettyNumber(v any) string {
        }
        return l.msgPrinter.Sprintf("%v", number.Decimal(v))
 }
+
+func init() {
+       // prepare a default matcher, especially for tests
+       supportedTags = []language.Tag{language.English}
+       matcher = language.NewMatcher(supportedTags)
+}
index bfb83820c88545c50e4bfe67c68ca3ddcedb7911..5013bac93f64db840aa3fdc6f406018f3d8d5f8f 100644 (file)
@@ -10,6 +10,7 @@ import (
        "reflect"
 
        "code.gitea.io/gitea/modules/context"
+       "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/web/routing"
 )
 
@@ -25,6 +26,10 @@ var argTypeProvider = map[reflect.Type]func(req *http.Request) ResponseStatusPro
        reflect.TypeOf(&context.PrivateContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetPrivateContext(req) },
 }
 
+func RegisterHandleTypeProvider[T any](fn func(req *http.Request) ResponseStatusProvider) {
+       argTypeProvider[reflect.TypeOf((*T)(nil)).Elem()] = fn
+}
+
 // responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written
 type responseWriter struct {
        respWriter http.ResponseWriter
@@ -78,7 +83,13 @@ func preCheckHandler(fn reflect.Value, argsIn []reflect.Value) {
        }
 }
 
-func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value) []reflect.Value {
+func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value, fnInfo *routing.FuncInfo) []reflect.Value {
+       defer func() {
+               if err := recover(); err != nil {
+                       log.Error("unable to prepare handler arguments for %s: %v", fnInfo.String(), err)
+                       panic(err)
+               }
+       }()
        isPreCheck := req == nil
 
        argsIn := make([]reflect.Value, fn.Type().NumIn())
@@ -155,7 +166,7 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
                        }
 
                        // prepare the arguments for the handler and do pre-check
-                       argsIn := prepareHandleArgsIn(resp, req, fn)
+                       argsIn := prepareHandleArgsIn(resp, req, fn, funcInfo)
                        if req == nil {
                                preCheckHandler(fn, argsIn)
                                return // it's doing pre-check, just return
index 61d432c8621e07d0676c8c3de8f138d49fab8f0f..4b10cd7ad110865c373d87483408d6d8d194ff4c 100644 (file)
@@ -3,7 +3,7 @@
 
 package actions
 
-// Github Actions Artifacts API Simple Description
+// GitHub Actions Artifacts API Simple Description
 //
 // 1. Upload artifact
 // 1.1. Post upload url
@@ -63,7 +63,6 @@ package actions
 
 import (
        "compress/gzip"
-       gocontext "context"
        "crypto/md5"
        "encoding/base64"
        "errors"
@@ -92,9 +91,25 @@ const (
 
 const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"
 
-func ArtifactsRoutes(goctx gocontext.Context, prefix string) *web.Route {
+type artifactContextKeyType struct{}
+
+var artifactContextKey = artifactContextKeyType{}
+
+type ArtifactContext struct {
+       *context.Base
+
+       ActionTask *actions.ActionTask
+}
+
+func init() {
+       web.RegisterHandleTypeProvider[*ArtifactContext](func(req *http.Request) web.ResponseStatusProvider {
+               return req.Context().Value(artifactContextKey).(*ArtifactContext)
+       })
+}
+
+func ArtifactsRoutes(prefix string) *web.Route {
        m := web.NewRoute()
-       m.Use(withContexter(goctx))
+       m.Use(ArtifactContexter())
 
        r := artifactRoutes{
                prefix: prefix,
@@ -115,15 +130,14 @@ func ArtifactsRoutes(goctx gocontext.Context, prefix string) *web.Route {
        return m
 }
 
-// withContexter initializes a package context for a request.
-func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler {
+func ArtifactContexter() func(next http.Handler) http.Handler {
        return func(next http.Handler) http.Handler {
                return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
-                       ctx := context.Context{
-                               Resp: context.NewResponse(resp),
-                               Data: map[string]interface{}{},
-                       }
-                       defer ctx.Close()
+                       base, baseCleanUp := context.NewBaseContext(resp, req)
+                       defer baseCleanUp()
+
+                       ctx := &ArtifactContext{Base: base}
+                       ctx.AppendContextValue(artifactContextKey, ctx)
 
                        // action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
                        // we should verify the ACTIONS_RUNTIME_TOKEN
@@ -132,6 +146,7 @@ func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler
                                ctx.Error(http.StatusUnauthorized, "Bad authorization header")
                                return
                        }
+
                        authToken := strings.TrimPrefix(authHeader, "Bearer ")
                        task, err := actions.GetRunningTaskByToken(req.Context(), authToken)
                        if err != nil {
@@ -139,16 +154,14 @@ func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler
                                ctx.Error(http.StatusInternalServerError, "Error runner api getting task")
                                return
                        }
-                       ctx.Data["task"] = task
 
-                       if err := task.LoadJob(goctx); err != nil {
+                       if err := task.LoadJob(req.Context()); err != nil {
                                log.Error("Error runner api getting job: %v", err)
                                ctx.Error(http.StatusInternalServerError, "Error runner api getting job")
                                return
                        }
 
-                       ctx.Req = context.WithContext(req, &ctx)
-
+                       ctx.ActionTask = task
                        next.ServeHTTP(ctx.Resp, ctx.Req)
                })
        }
@@ -175,13 +188,8 @@ type getUploadArtifactResponse struct {
        FileContainerResourceURL string `json:"fileContainerResourceUrl"`
 }
 
-func (ar artifactRoutes) validateRunID(ctx *context.Context) (*actions.ActionTask, int64, bool) {
-       task, ok := ctx.Data["task"].(*actions.ActionTask)
-       if !ok {
-               log.Error("Error getting task in context")
-               ctx.Error(http.StatusInternalServerError, "Error getting task in context")
-               return nil, 0, false
-       }
+func (ar artifactRoutes) validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
+       task := ctx.ActionTask
        runID := ctx.ParamsInt64("run_id")
        if task.Job.RunID != runID {
                log.Error("Error runID not match")
@@ -192,7 +200,7 @@ func (ar artifactRoutes) validateRunID(ctx *context.Context) (*actions.ActionTas
 }
 
 // getUploadArtifactURL generates a URL for uploading an artifact
-func (ar artifactRoutes) getUploadArtifactURL(ctx *context.Context) {
+func (ar artifactRoutes) getUploadArtifactURL(ctx *ArtifactContext) {
        task, runID, ok := ar.validateRunID(ctx)
        if !ok {
                return
@@ -220,7 +228,7 @@ func (ar artifactRoutes) getUploadArtifactURL(ctx *context.Context) {
 
 // getUploadFileSize returns the size of the file to be uploaded.
 // The raw size is the size of the file as reported by the header X-TFS-FileLength.
-func (ar artifactRoutes) getUploadFileSize(ctx *context.Context) (int64, int64, error) {
+func (ar artifactRoutes) getUploadFileSize(ctx *ArtifactContext) (int64, int64, error) {
        contentLength := ctx.Req.ContentLength
        xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64)
        if xTfsLength > 0 {
@@ -229,7 +237,7 @@ func (ar artifactRoutes) getUploadFileSize(ctx *context.Context) (int64, int64,
        return contentLength, contentLength, nil
 }
 
-func (ar artifactRoutes) saveUploadChunk(ctx *context.Context,
+func (ar artifactRoutes) saveUploadChunk(ctx *ArtifactContext,
        artifact *actions.ActionArtifact,
        contentSize, runID int64,
 ) (int64, error) {
@@ -273,7 +281,7 @@ func (ar artifactRoutes) saveUploadChunk(ctx *context.Context,
 // The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
 var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "")
 
-func (ar artifactRoutes) uploadArtifact(ctx *context.Context) {
+func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
        _, runID, ok := ar.validateRunID(ctx)
        if !ok {
                return
@@ -341,7 +349,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *context.Context) {
 
 // comfirmUploadArtifact comfirm upload artifact.
 // if all chunks are uploaded, merge them to one file.
-func (ar artifactRoutes) comfirmUploadArtifact(ctx *context.Context) {
+func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
        _, runID, ok := ar.validateRunID(ctx)
        if !ok {
                return
@@ -364,7 +372,7 @@ type chunkItem struct {
        Path       string
 }
 
-func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64) error {
+func (ar artifactRoutes) mergeArtifactChunks(ctx *ArtifactContext, runID int64) error {
        storageDir := fmt.Sprintf("tmp%d", runID)
        var chunks []*chunkItem
        if err := ar.fs.IterateObjects(storageDir, func(path string, obj storage.Object) error {
@@ -415,14 +423,20 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
 
                // use multiReader
                readers := make([]io.Reader, 0, len(allChunks))
-               readerClosers := make([]io.Closer, 0, len(allChunks))
+               closeReaders := func() {
+                       for _, r := range readers {
+                               _ = r.(io.Closer).Close() // it guarantees to be io.Closer by the following loop's Open function
+                       }
+                       readers = nil
+               }
+               defer closeReaders()
+
                for _, c := range allChunks {
-                       reader, err := ar.fs.Open(c.Path)
-                       if err != nil {
+                       var readCloser io.ReadCloser
+                       if readCloser, err = ar.fs.Open(c.Path); err != nil {
                                return fmt.Errorf("open chunk error: %v, %s", err, c.Path)
                        }
-                       readers = append(readers, reader)
-                       readerClosers = append(readerClosers, reader)
+                       readers = append(readers, readCloser)
                }
                mergedReader := io.MultiReader(readers...)
 
@@ -445,11 +459,6 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
                        return fmt.Errorf("merged file size is not equal to chunk length")
                }
 
-               // close readers
-               for _, r := range readerClosers {
-                       r.Close()
-               }
-
                // save storage path to artifact
                log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
                artifact.StoragePath = storagePath
@@ -458,6 +467,8 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
                        return fmt.Errorf("update artifact error: %v", err)
                }
 
+               closeReaders() // close before delete
+
                // drop chunks
                for _, c := range cs {
                        if err := ar.fs.Delete(c.Path); err != nil {
@@ -479,21 +490,21 @@ type (
        }
 )
 
-func (ar artifactRoutes) listArtifacts(ctx *context.Context) {
+func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) {
        _, runID, ok := ar.validateRunID(ctx)
        if !ok {
                return
        }
 
-       artficats, err := actions.ListArtifactsByRunID(ctx, runID)
+       artifacts, err := actions.ListArtifactsByRunID(ctx, runID)
        if err != nil {
                log.Error("Error getting artifacts: %v", err)
                ctx.Error(http.StatusInternalServerError, err.Error())
                return
        }
 
-       artficatsData := make([]listArtifactsResponseItem, 0, len(artficats))
-       for _, a := range artficats {
+       artficatsData := make([]listArtifactsResponseItem, 0, len(artifacts))
+       for _, a := range artifacts {
                artficatsData = append(artficatsData, listArtifactsResponseItem{
                        Name:                     a.ArtifactName,
                        FileContainerResourceURL: ar.buildArtifactURL(runID, a.ID, "path"),
@@ -517,7 +528,7 @@ type (
        }
 )
 
-func (ar artifactRoutes) getDownloadArtifactURL(ctx *context.Context) {
+func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
        _, runID, ok := ar.validateRunID(ctx)
        if !ok {
                return
@@ -546,7 +557,7 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *context.Context) {
        ctx.JSON(http.StatusOK, respData)
 }
 
-func (ar artifactRoutes) downloadArtifact(ctx *context.Context) {
+func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) {
        _, runID, ok := ar.validateRunID(ctx)
        if !ok {
                return
index aaceb8a92b30e399e3147d453f36425f11da2690..e715997e82951a2a9395d8a8132f63b53b3a70e7 100644 (file)
@@ -98,7 +98,7 @@ func verifyAuth(r *web.Route, authMethods []auth.Method) {
 func CommonRoutes(ctx gocontext.Context) *web.Route {
        r := web.NewRoute()
 
-       r.Use(context.PackageContexter(ctx))
+       r.Use(context.PackageContexter())
 
        verifyAuth(r, []auth.Method{
                &auth.OAuth2{},
@@ -574,7 +574,7 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
 func ContainerRoutes(ctx gocontext.Context) *web.Route {
        r := web.NewRoute()
 
-       r.Use(context.PackageContexter(ctx))
+       r.Use(context.PackageContexter())
 
        verifyAuth(r, []auth.Method{
                &auth.Basic{},
index a67a5420ac66ef589c921f21252e766843f6c6cc..f1e1cf946a01ba250e08b699fae6d43389f282ee 100644 (file)
@@ -149,7 +149,7 @@ func repoAssignment() func(ctx *context.APIContext) {
                        if err != nil {
                                if user_model.IsErrUserNotExist(err) {
                                        if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
-                                               context.RedirectToUser(ctx.Context, userName, redirectUserID)
+                                               context.RedirectToUser(ctx.Base, userName, redirectUserID)
                                        } else if user_model.IsErrUserRedirectNotExist(err) {
                                                ctx.NotFound("GetUserByName", err)
                                        } else {
@@ -170,7 +170,7 @@ func repoAssignment() func(ctx *context.APIContext) {
                        if repo_model.IsErrRepoNotExist(err) {
                                redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
                                if err == nil {
-                                       context.RedirectToRepo(ctx.Context, redirectRepoID)
+                                       context.RedirectToRepo(ctx.Base, redirectRepoID)
                                } else if repo_model.IsErrRedirectNotExist(err) {
                                        ctx.NotFound()
                                } else {
@@ -274,7 +274,7 @@ func reqToken(requiredScope auth_model.AccessTokenScope) func(ctx *context.APICo
                        ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope)
                        return
                }
-               if ctx.Context.IsBasicAuth {
+               if ctx.IsBasicAuth {
                        ctx.CheckForOTP()
                        return
                }
@@ -295,7 +295,7 @@ func reqExploreSignIn() func(ctx *context.APIContext) {
 
 func reqBasicAuth() func(ctx *context.APIContext) {
        return func(ctx *context.APIContext) {
-               if !ctx.Context.IsBasicAuth {
+               if !ctx.IsBasicAuth {
                        ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required")
                        return
                }
@@ -375,7 +375,7 @@ func reqAnyRepoReader() func(ctx *context.APIContext) {
 // reqOrgOwnership user should be an organization owner, or a site admin
 func reqOrgOwnership() func(ctx *context.APIContext) {
        return func(ctx *context.APIContext) {
-               if ctx.Context.IsUserSiteAdmin() {
+               if ctx.IsUserSiteAdmin() {
                        return
                }
 
@@ -407,7 +407,7 @@ func reqOrgOwnership() func(ctx *context.APIContext) {
 // reqTeamMembership user should be an team member, or a site admin
 func reqTeamMembership() func(ctx *context.APIContext) {
        return func(ctx *context.APIContext) {
-               if ctx.Context.IsUserSiteAdmin() {
+               if ctx.IsUserSiteAdmin() {
                        return
                }
                if ctx.Org.Team == nil {
@@ -444,7 +444,7 @@ func reqTeamMembership() func(ctx *context.APIContext) {
 // reqOrgMembership user should be an organization member, or a site admin
 func reqOrgMembership() func(ctx *context.APIContext) {
        return func(ctx *context.APIContext) {
-               if ctx.Context.IsUserSiteAdmin() {
+               if ctx.IsUserSiteAdmin() {
                        return
                }
 
@@ -512,7 +512,7 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
                                if organization.IsErrOrgNotExist(err) {
                                        redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org"))
                                        if err == nil {
-                                               context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID)
+                                               context.RedirectToUser(ctx.Base, ctx.Params(":org"), redirectUserID)
                                        } else if user_model.IsErrUserRedirectNotExist(err) {
                                                ctx.NotFound("GetOrgByName", err)
                                        } else {
index 93d57544444409674c66590d9cc9d05c12cac632..7b24b353b63d0abcc1f0cd1e2e14cca68d568070 100644 (file)
@@ -41,7 +41,7 @@ func Markup(ctx *context.APIContext) {
                return
        }
 
-       common.RenderMarkup(ctx.Context, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
+       common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
 }
 
 // Markdown render markdown document to HTML
@@ -76,7 +76,7 @@ func Markdown(ctx *context.APIContext) {
                mode = form.Mode
        }
 
-       common.RenderMarkup(ctx.Context, mode, form.Text, form.Context, "", form.Wiki)
+       common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "", form.Wiki)
 }
 
 // MarkdownRaw render raw markdown HTML
index 68776613b272a16b4fed2df89580bb5e26f35bb4..fdf540fd65b76a4a50a9eed83bc5825389f7b84f 100644 (file)
@@ -16,7 +16,6 @@ import (
        "code.gitea.io/gitea/modules/markup"
        "code.gitea.io/gitea/modules/setting"
        api "code.gitea.io/gitea/modules/structs"
-       "code.gitea.io/gitea/modules/templates"
        "code.gitea.io/gitea/modules/util"
        "code.gitea.io/gitea/modules/web"
        "code.gitea.io/gitea/modules/web/middleware"
@@ -30,26 +29,16 @@ const (
        AppSubURL = AppURL + Repo + "/"
 )
 
-func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) {
-       rnd := templates.HTMLRenderer()
+func createAPIContext(req *http.Request) (*context.APIContext, *httptest.ResponseRecorder) {
        resp := httptest.NewRecorder()
-       c := &context.Context{
-               Req:    req,
-               Resp:   context.NewResponse(resp),
-               Render: rnd,
-               Data:   make(middleware.ContextData),
-       }
-       defer c.Close()
+       base, baseCleanUp := context.NewBaseContext(resp, req)
+       base.Data = middleware.ContextData{}
+       c := &context.APIContext{Base: base}
+       _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
 
        return c, resp
 }
 
-func wrap(ctx *context.Context) *context.APIContext {
-       return &context.APIContext{
-               Context: ctx,
-       }
-}
-
 func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) {
        setting.AppURL = AppURL
 
@@ -65,8 +54,7 @@ func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, r
                Method: "POST",
                URL:    requrl,
        }
-       m, resp := createContext(req)
-       ctx := wrap(m)
+       ctx, resp := createAPIContext(req)
 
        options.Text = text
        web.SetForm(ctx, &options)
@@ -90,8 +78,7 @@ func testRenderMarkdown(t *testing.T, mode, text, responseBody string, responseC
                Method: "POST",
                URL:    requrl,
        }
-       m, resp := createContext(req)
-       ctx := wrap(m)
+       ctx, resp := createAPIContext(req)
 
        options.Text = text
        web.SetForm(ctx, &options)
@@ -211,8 +198,7 @@ func TestAPI_RenderSimple(t *testing.T) {
                Method: "POST",
                URL:    requrl,
        }
-       m, resp := createContext(req)
-       ctx := wrap(m)
+       ctx, resp := createAPIContext(req)
 
        for i := 0; i < len(simpleCases); i += 2 {
                options.Text = simpleCases[i]
@@ -231,8 +217,7 @@ func TestAPI_RenderRaw(t *testing.T) {
                Method: "POST",
                URL:    requrl,
        }
-       m, resp := createContext(req)
-       ctx := wrap(m)
+       ctx, resp := createAPIContext(req)
 
        for i := 0; i < len(simpleCases); i += 2 {
                ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i]))
index 3b6a9bfdc297f4bda6999d690ecec876cb0e54c0..b22ea8a7715793ae30c7ecac11e05d9560974b89 100644 (file)
@@ -25,7 +25,7 @@ func NewAvailable(ctx *context.APIContext) {
 }
 
 func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
-       before, since, err := context.GetQueryBeforeSince(ctx.Context)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return nil
index eb63dda5902477cecc806d5ac337fbbd1f18658c..786407827c9d8c09ace1f6047b03f33c82e8615a 100644 (file)
@@ -80,7 +80,7 @@ func GetRawFile(ctx *context.APIContext) {
 
        ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry)))
 
-       if err := common.ServeBlob(ctx.Context, blob, lastModified); err != nil {
+       if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
                ctx.Error(http.StatusInternalServerError, "ServeBlob", err)
        }
 }
@@ -137,7 +137,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
                }
 
                // OK not cached - serve!
-               if err := common.ServeBlob(ctx.Context, blob, lastModified); err != nil {
+               if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
                        ctx.ServerError("ServeBlob", err)
                }
                return
@@ -159,7 +159,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
        }
 
        if err := dataRc.Close(); err != nil {
-               log.Error("Error whilst closing blob %s reader in %-v. Error: %v", blob.ID, ctx.Context.Repo.Repository, err)
+               log.Error("Error whilst closing blob %s reader in %-v. Error: %v", blob.ID, ctx.Repo.Repository, err)
        }
 
        // Check if the blob represents a pointer
@@ -173,7 +173,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
                }
 
                // OK not cached - serve!
-               common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
+               common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
                return
        }
 
@@ -187,7 +187,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
                        return
                }
 
-               common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
+               common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
                return
        } else if err != nil {
                ctx.ServerError("GetLFSMetaObjectByOid", err)
@@ -215,7 +215,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
        }
        defer lfsDataRc.Close()
 
-       common.ServeContentByReadSeeker(ctx.Context, ctx.Repo.TreePath, lastModified, lfsDataRc)
+       common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
 }
 
 func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) {
index 34dc990c3da49febb31d37ffed8df82d4ac19077..56658b45d59b88e6809eb0d14a3805fe00c2613b 100644 (file)
@@ -9,7 +9,6 @@ import (
 
        "code.gitea.io/gitea/models/unittest"
        "code.gitea.io/gitea/models/webhook"
-       "code.gitea.io/gitea/modules/context"
        "code.gitea.io/gitea/modules/test"
 
        "github.com/stretchr/testify/assert"
@@ -18,12 +17,12 @@ import (
 func TestTestHook(t *testing.T) {
        unittest.PrepareTestEnv(t)
 
-       ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
+       ctx := test.MockAPIContext(t, "user2/repo1/wiki/_pages")
        ctx.SetParams(":id", "1")
        test.LoadRepo(t, ctx, 1)
        test.LoadRepoCommit(t, ctx)
        test.LoadUser(t, ctx, 2)
-       TestHook(&context.APIContext{Context: ctx, Org: nil})
+       TestHook(ctx)
        assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
 
        unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
index 95528d664d7b9426514f2cec271e50ba7b1bc852..49252f7a4b49af43ba7da4a00001cf2628e80ccb 100644 (file)
@@ -116,7 +116,7 @@ func SearchIssues(ctx *context.APIContext) {
        //   "200":
        //     "$ref": "#/responses/IssueList"
 
-       before, since, err := context.GetQueryBeforeSince(ctx.Context)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
@@ -368,7 +368,7 @@ func ListIssues(ctx *context.APIContext) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/IssueList"
-       before, since, err := context.GetQueryBeforeSince(ctx.Context)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
index 6ae60633035314569bcfd3650610028822e26218..7c8f30f1161343e982f15b8d27ce33837354ea46 100644 (file)
@@ -59,7 +59,7 @@ func ListIssueComments(ctx *context.APIContext) {
        //   "200":
        //     "$ref": "#/responses/CommentList"
 
-       before, since, err := context.GetQueryBeforeSince(ctx.Context)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
@@ -156,7 +156,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
        //   "200":
        //     "$ref": "#/responses/TimelineList"
 
-       before, since, err := context.GetQueryBeforeSince(ctx.Context)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
@@ -259,7 +259,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
        //   "200":
        //     "$ref": "#/responses/CommentList"
 
-       before, since, err := context.GetQueryBeforeSince(ctx.Context)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
index 16bb8cb73d6c37a3b43b52cb228a89447524a6ee..1ff934950c041426f9c8139fd2e5e212a5a39533 100644 (file)
@@ -103,7 +103,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
                opts.UserID = user.ID
        }
 
-       if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
+       if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
        }
@@ -522,7 +522,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
        }
 
        var err error
-       if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
+       if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
        }
@@ -596,7 +596,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
        }
 
        var err error
-       if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
+       if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
                ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
                return
        }
index efce39e5203443de844600a9831d20c25b5eb333..b458cd122b8a3ad482c3679564d8df2f3f5dc3f0 100644 (file)
@@ -79,7 +79,7 @@ func Migrate(ctx *context.APIContext) {
                return
        }
 
-       if ctx.HasError() {
+       if ctx.HasAPIError() {
                ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
                return
        }
index 59c3bde81930ee5530c1a62b7ed155dab45d9b08..e1bdea5c82bd8406a915de655b7c37c91bb9c101 100644 (file)
@@ -9,7 +9,6 @@ import (
 
        repo_model "code.gitea.io/gitea/models/repo"
        "code.gitea.io/gitea/models/unittest"
-       "code.gitea.io/gitea/modules/context"
        api "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/test"
        "code.gitea.io/gitea/modules/web"
@@ -20,7 +19,7 @@ import (
 func TestRepoEdit(t *testing.T) {
        unittest.PrepareTestEnv(t)
 
-       ctx := test.MockContext(t, "user2/repo1")
+       ctx := test.MockAPIContext(t, "user2/repo1")
        test.LoadRepo(t, ctx, 1)
        test.LoadUser(t, ctx, 2)
        ctx.Repo.Owner = ctx.Doer
@@ -54,9 +53,8 @@ func TestRepoEdit(t *testing.T) {
                Archived:                  &archived,
        }
 
-       apiCtx := &context.APIContext{Context: ctx, Org: nil}
-       web.SetForm(apiCtx, &opts)
-       Edit(apiCtx)
+       web.SetForm(ctx, &opts)
+       Edit(ctx)
 
        assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
        unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
@@ -67,7 +65,7 @@ func TestRepoEdit(t *testing.T) {
 func TestRepoEditNameChange(t *testing.T) {
        unittest.PrepareTestEnv(t)
 
-       ctx := test.MockContext(t, "user2/repo1")
+       ctx := test.MockAPIContext(t, "user2/repo1")
        test.LoadRepo(t, ctx, 1)
        test.LoadUser(t, ctx, 2)
        ctx.Repo.Owner = ctx.Doer
@@ -76,9 +74,8 @@ func TestRepoEditNameChange(t *testing.T) {
                Name: &name,
        }
 
-       apiCtx := &context.APIContext{Context: ctx, Org: nil}
-       web.SetForm(apiCtx, &opts)
-       Edit(apiCtx)
+       web.SetForm(ctx, &opts)
+       Edit(ctx)
        assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 
        unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
index 5158f38e144e5014b742e155d06fee80d87398fa..c1110ebce553c363087ac544b26c037c13f316ad 100644 (file)
@@ -183,7 +183,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
                ctx.Error(http.StatusBadRequest, "ref/sha not given", nil)
                return
        }
-       sha = utils.MustConvertToSHA1(ctx.Context, sha)
+       sha = utils.MustConvertToSHA1(ctx.Base, ctx.Repo, sha)
        repo := ctx.Repo.Repository
 
        listOptions := utils.GetListOptions(ctx)
index 28f600ad92a23233bea289c28d4ac5971c4235b3..4b642910b19c6a9b4b1709e5ebc112cfe70835d2 100644 (file)
@@ -17,7 +17,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *user_model.User
        if err != nil {
                if user_model.IsErrUserNotExist(err) {
                        if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil {
-                               context.RedirectToUser(ctx.Context, username, redirectUserID)
+                               context.RedirectToUser(ctx.Base, username, redirectUserID)
                        } else {
                                ctx.NotFound("GetUserByName", err)
                        }
index eaf0f5fd37fce08a9257ce0ceb3b3969b21bc7ef..32f5c85319d46e19e3cf59341fe53decf91b6c7c 100644 (file)
@@ -4,6 +4,7 @@
 package utils
 
 import (
+       gocontext "context"
        "fmt"
        "net/http"
 
@@ -33,7 +34,7 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
                }
        }
 
-       sha = MustConvertToSHA1(ctx.Context, sha)
+       sha = MustConvertToSHA1(ctx, ctx.Repo, sha)
 
        if ctx.Repo.GitRepo != nil {
                err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha)
@@ -69,7 +70,7 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str
 }
 
 // ConvertToSHA1 returns a full-length SHA1 from a potential ID string
-func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
+func ConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) (git.SHA1, error) {
        if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
                sha1, err := git.NewIDFromString(commitID)
                if err == nil {
@@ -77,7 +78,7 @@ func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
                }
        }
 
-       gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, ctx.Repo.Repository.RepoPath())
+       gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath())
        if err != nil {
                return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
        }
@@ -87,8 +88,8 @@ func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
 }
 
 // MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
-func MustConvertToSHA1(ctx *context.Context, commitID string) string {
-       sha, err := ConvertToSHA1(ctx, commitID)
+func MustConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) string {
+       sha, err := ConvertToSHA1(ctx, repo, commitID)
        if err != nil {
                return commitID
        }
index 3acd12721e8549fa456527150718d8a71215a07a..5f412014d7e7644f93399011ea1fbd474c681a7c 100644 (file)
@@ -19,7 +19,7 @@ import (
 )
 
 // RenderMarkup renders markup text for the /markup and /markdown endpoints
-func RenderMarkup(ctx *context.Context, mode, text, urlPrefix, filePath string, wiki bool) {
+func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPrefix, filePath string, wiki bool) {
        var markupType string
        relativePath := ""
 
@@ -63,11 +63,11 @@ func RenderMarkup(ctx *context.Context, mode, text, urlPrefix, filePath string,
        }
 
        meta := map[string]string{}
-       if ctx.Repo != nil && ctx.Repo.Repository != nil {
+       if repo != nil && repo.Repository != nil {
                if mode == "comment" {
-                       meta = ctx.Repo.Repository.ComposeMetas()
+                       meta = repo.Repository.ComposeMetas()
                } else {
-                       meta = ctx.Repo.Repository.ComposeDocumentMetas()
+                       meta = repo.Repository.ComposeDocumentMetas()
                }
        }
        if mode != "comment" {
index c1ee9dd765af9604c2c355601e017772b855e2bf..a25ff1ee00a5ed902f204d2e2a129e8804a3b90a 100644 (file)
@@ -42,7 +42,7 @@ func ProtocolMiddlewares() (handlers []any) {
                return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
                        ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
                        defer finished()
-                       next.ServeHTTP(context.NewResponse(resp), req.WithContext(cache.WithCacheContext(ctx)))
+                       next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx)))
                })
        })
 
index 59b993328e47ec5e0090cdcaff572575dec451db..3094ee6a6ec9b7505af9fc28a20061e004e39fa4 100644 (file)
@@ -15,7 +15,7 @@ import (
 )
 
 // ServeBlob download a git.Blob
-func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) error {
+func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified time.Time) error {
        if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
                return nil
        }
@@ -30,14 +30,14 @@ func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) err
                }
        }()
 
-       httplib.ServeContentByReader(ctx.Req, ctx.Resp, ctx.Repo.TreePath, blob.Size(), dataRc)
+       httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, blob.Size(), dataRc)
        return nil
 }
 
-func ServeContentByReader(ctx *context.Context, filePath string, size int64, reader io.Reader) {
+func ServeContentByReader(ctx *context.Base, filePath string, size int64, reader io.Reader) {
        httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader)
 }
 
-func ServeContentByReadSeeker(ctx *context.Context, filePath string, modTime time.Time, reader io.ReadSeeker) {
+func ServeContentByReadSeeker(ctx *context.Base, filePath string, modTime time.Time, reader io.ReadSeeker) {
        httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader)
 }
index 087d8c2915bb9b8afb58f489f8db9402e0814c8f..5737ef3dc06a4662d27e4899531ad756be36c1df 100644 (file)
@@ -198,7 +198,7 @@ func NormalRoutes(ctx context.Context) *web.Route {
                // In Github, it uses ACTIONS_RUNTIME_URL=https://pipelines.actions.githubusercontent.com/fLgcSHkPGySXeIFrg8W8OBSfeg3b5Fls1A1CwX566g8PayEGlg/
                // TODO: this prefix should be generated with a token string with runner ?
                prefix = "/api/actions_pipeline"
-               r.Mount(prefix, actions_router.ArtifactsRoutes(ctx, prefix))
+               r.Mount(prefix, actions_router.ArtifactsRoutes(prefix))
        }
 
        return r
index 714ddd554845464af987b8f2493ca79a0819f621..89b91a5a483a5d1b0815ef81e46f377c7dd4e2a2 100644 (file)
@@ -58,15 +58,14 @@ func Contexter() func(next http.Handler) http.Handler {
        dbTypeNames := getSupportedDbTypeNames()
        return func(next http.Handler) http.Handler {
                return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+                       base, baseCleanUp := context.NewBaseContext(resp, req)
                        ctx := context.Context{
-                               Resp:    context.NewResponse(resp),
+                               Base:    base,
                                Flash:   &middleware.Flash{},
-                               Locale:  middleware.Locale(resp, req),
                                Render:  rnd,
-                               Data:    middleware.GetContextData(req.Context()),
                                Session: session.GetSession(req),
                        }
-                       defer ctx.Close()
+                       defer baseCleanUp()
 
                        ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
                        ctx.Data.MergeFrom(middleware.ContextData{
@@ -78,7 +77,6 @@ func Contexter() func(next http.Handler) http.Handler {
 
                                "PasswordHashAlgorithms": hash.RecommendedHashAlgorithms,
                        })
-                       ctx.Req = context.WithContext(req, &ctx)
                        next.ServeHTTP(resp, ctx.Req)
                })
        }
@@ -249,15 +247,8 @@ func SubmitInstall(ctx *context.Context) {
        ctx.Data["CurDbType"] = form.DbType
 
        if ctx.HasError() {
-               if ctx.HasValue("Err_SMTPUser") {
-                       ctx.Data["Err_SMTP"] = true
-               }
-               if ctx.HasValue("Err_AdminName") ||
-                       ctx.HasValue("Err_AdminPasswd") ||
-                       ctx.HasValue("Err_AdminEmail") {
-                       ctx.Data["Err_Admin"] = true
-               }
-
+               ctx.Data["Err_SMTP"] = ctx.Data["Err_SMTPUser"] != nil
+               ctx.Data["Err_Admin"] = ctx.Data["Err_AdminName"] != nil || ctx.Data["Err_AdminPasswd"] != nil || ctx.Data["Err_AdminEmail"] != nil
                ctx.HTML(http.StatusOK, tplInstall)
                return
        }
index 169037894530ce39354b71ab18fa5662d795c5aa..c91da9a7f1028233ec6a3d670863282284d7ebc0 100644 (file)
@@ -5,8 +5,6 @@
 package misc
 
 import (
-       "net/http"
-
        "code.gitea.io/gitea/modules/context"
        api "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/web"
@@ -16,11 +14,5 @@ import (
 // Markup render markup document to HTML
 func Markup(ctx *context.Context) {
        form := web.GetForm(ctx).(*api.MarkupOption)
-
-       if ctx.HasAPIError() {
-               ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
-               return
-       }
-
-       common.RenderMarkup(ctx, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
+       common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
 }
index c46ec29841a73d5f670600c395b1a2735eef3a59..fb95e63ecf403aafecb05a24373fae338185e013 100644 (file)
@@ -153,7 +153,7 @@ func ServeAttachment(ctx *context.Context, uuid string) {
        }
        defer fr.Close()
 
-       common.ServeContentByReadSeeker(ctx, attach.Name, attach.CreatedUnix.AsTime(), fr)
+       common.ServeContentByReadSeeker(ctx.Base, attach.Name, attach.CreatedUnix.AsTime(), fr)
 }
 
 // GetAttachment serve attachments
index 1c87f9bed73cd0a2ef2dd1008c3d15e04b10b434..a498180f35ab3130acfff21d72c86ac27f11f567 100644 (file)
@@ -47,7 +47,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
                                log.Error("ServeBlobOrLFS: Close: %v", err)
                        }
                        closed = true
-                       return common.ServeBlob(ctx, blob, lastModified)
+                       return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
                }
                if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
                        return nil
@@ -71,7 +71,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
                                log.Error("ServeBlobOrLFS: Close: %v", err)
                        }
                }()
-               common.ServeContentByReadSeeker(ctx, ctx.Repo.TreePath, lastModified, lfsDataRc)
+               common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
                return nil
        }
        if err = dataRc.Close(); err != nil {
@@ -79,7 +79,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
        }
        closed = true
 
-       return common.ServeBlob(ctx, blob, lastModified)
+       return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
 }
 
 func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) {
@@ -120,7 +120,7 @@ func SingleDownload(ctx *context.Context) {
                return
        }
 
-       if err := common.ServeBlob(ctx, blob, lastModified); err != nil {
+       if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
                ctx.ServerError("ServeBlob", err)
        }
 }
@@ -148,7 +148,7 @@ func DownloadByID(ctx *context.Context) {
                }
                return
        }
-       if err = common.ServeBlob(ctx, blob, time.Time{}); err != nil {
+       if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, time.Time{}); err != nil {
                ctx.ServerError("ServeBlob", err)
        }
 }
index 4e45a9b6e21df95f2dc5e8de4abcfeffa3df73e0..b6ebd259156260a7b3e8cdf33564c0811c058bc9 100644 (file)
@@ -109,7 +109,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
        if err != nil {
                if repo_model.IsErrRepoNotExist(err) {
                        if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil {
-                               context.RedirectToRepo(ctx, redirectRepoID)
+                               context.RedirectToRepo(ctx.Base, redirectRepoID)
                                return
                        }
                        repoExist = false
index 88d2a97a7ad9c914e29a560cdc12ef92a252e801..7a0dc9940beb04c645720b28a13f8513b6a2ba38 100644 (file)
@@ -2344,7 +2344,7 @@ func UpdatePullReviewRequest(ctx *context.Context) {
 
 // SearchIssues searches for issues across the repositories that the user has access to
 func SearchIssues(ctx *context.Context) {
-       before, since, err := context.GetQueryBeforeSince(ctx)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, err.Error())
                return
@@ -2545,7 +2545,7 @@ func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
 
 // ListIssues list the issues of a repository
 func ListIssues(ctx *context.Context) {
-       before, since, err := context.GetQueryBeforeSince(ctx)
+       before, since, err := context.GetQueryBeforeSince(ctx.Base)
        if err != nil {
                ctx.Error(http.StatusUnprocessableEntity, err.Error())
                return
index a335c114be6953c2f993dc43b1c1f8466075220f..115418887d30a0459545ce5ef44b72d9b887c0ae 100644 (file)
@@ -671,7 +671,7 @@ func WikiRaw(ctx *context.Context) {
        }
 
        if entry != nil {
-               if err = common.ServeBlob(ctx, entry.Blob(), time.Time{}); err != nil {
+               if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, entry.Blob(), time.Time{}); err != nil {
                        ctx.ServerError("ServeBlob", err)
                }
                return
index 3b2f883d009c174057624b56975648194bf2a96b..d1955a4c900108aa1b111331cd0e36cb6960895d 100644 (file)
@@ -8,6 +8,7 @@ import (
        "strings"
 
        "code.gitea.io/gitea/models/auth"
+       user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/context"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
@@ -17,11 +18,15 @@ import (
 // Auth is a middleware to authenticate a web user
 func Auth(authMethod Method) func(*context.Context) {
        return func(ctx *context.Context) {
-               if err := authShared(ctx, authMethod); err != nil {
+               ar, err := authShared(ctx.Base, ctx.Session, authMethod)
+               if err != nil {
                        log.Error("Failed to verify user: %v", err)
                        ctx.Error(http.StatusUnauthorized, "Verify")
                        return
                }
+               ctx.Doer = ar.Doer
+               ctx.IsSigned = ar.Doer != nil
+               ctx.IsBasicAuth = ar.IsBasicAuth
                if ctx.Doer == nil {
                        // ensure the session uid is deleted
                        _ = ctx.Session.Delete("uid")
@@ -32,32 +37,41 @@ func Auth(authMethod Method) func(*context.Context) {
 // APIAuth is a middleware to authenticate an api user
 func APIAuth(authMethod Method) func(*context.APIContext) {
        return func(ctx *context.APIContext) {
-               if err := authShared(ctx.Context, authMethod); err != nil {
+               ar, err := authShared(ctx.Base, nil, authMethod)
+               if err != nil {
                        ctx.Error(http.StatusUnauthorized, "APIAuth", err)
+                       return
                }
+               ctx.Doer = ar.Doer
+               ctx.IsSigned = ar.Doer != nil
+               ctx.IsBasicAuth = ar.IsBasicAuth
        }
 }
 
-func authShared(ctx *context.Context, authMethod Method) error {
-       var err error
-       ctx.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
+type authResult struct {
+       Doer        *user_model.User
+       IsBasicAuth bool
+}
+
+func authShared(ctx *context.Base, sessionStore SessionStore, authMethod Method) (ar authResult, err error) {
+       ar.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, sessionStore)
        if err != nil {
-               return err
+               return ar, err
        }
-       if ctx.Doer != nil {
-               if ctx.Locale.Language() != ctx.Doer.Language {
+       if ar.Doer != nil {
+               if ctx.Locale.Language() != ar.Doer.Language {
                        ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
                }
-               ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == BasicMethodName
-               ctx.IsSigned = true
-               ctx.Data["IsSigned"] = ctx.IsSigned
-               ctx.Data[middleware.ContextDataKeySignedUser] = ctx.Doer
-               ctx.Data["SignedUserID"] = ctx.Doer.ID
-               ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin
+               ar.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == BasicMethodName
+
+               ctx.Data["IsSigned"] = true
+               ctx.Data[middleware.ContextDataKeySignedUser] = ar.Doer
+               ctx.Data["SignedUserID"] = ar.Doer.ID
+               ctx.Data["IsAdmin"] = ar.Doer.IsAdmin
        } else {
                ctx.Data["SignedUserID"] = int64(0)
        }
-       return nil
+       return ar, nil
 }
 
 // VerifyOptions contains required or check options
@@ -68,7 +82,7 @@ type VerifyOptions struct {
        DisableCSRF     bool
 }
 
-// Checks authentication according to options
+// VerifyAuthWithOptions checks authentication according to options
 func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) {
        return func(ctx *context.Context) {
                // Check prohibit login users.
@@ -153,7 +167,7 @@ func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) {
        }
 }
 
-// Checks authentication according to options
+// VerifyAuthWithOptionsAPI checks authentication according to options
 func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIContext) {
        return func(ctx *context.APIContext) {
                // Check prohibit login users.
@@ -197,7 +211,9 @@ func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIConte
                                return
                        } else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
                                ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
-                               ctx.HTML(http.StatusOK, "user/auth/activate")
+                               ctx.JSON(http.StatusForbidden, map[string]string{
+                                       "message": "This account is not activated.",
+                               })
                                return
                        }
                        if ctx.IsSigned && ctx.IsBasicAuth {
index c713667bca75aa5abb9973c6ec5cfa8e2325fdeb..4e74aa50bd1e290a113b8464c56900a517b63acd 100644 (file)
@@ -15,7 +15,7 @@ import (
 // UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
 func UserAssignmentWeb() func(ctx *context.Context) {
        return func(ctx *context.Context) {
-               userAssignment(ctx, func(status int, title string, obj interface{}) {
+               errorFn := func(status int, title string, obj interface{}) {
                        err, ok := obj.(error)
                        if !ok {
                                err = fmt.Errorf("%s", obj)
@@ -25,7 +25,8 @@ func UserAssignmentWeb() func(ctx *context.Context) {
                        } else {
                                ctx.ServerError(title, err)
                        }
-               })
+               }
+               ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, errorFn)
        }
 }
 
@@ -53,18 +54,18 @@ func UserIDAssignmentAPI() func(ctx *context.APIContext) {
 // UserAssignmentAPI returns a middleware to handle context-user assignment for api routes
 func UserAssignmentAPI() func(ctx *context.APIContext) {
        return func(ctx *context.APIContext) {
-               userAssignment(ctx.Context, ctx.Error)
+               ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.Error)
        }
 }
 
-func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) {
+func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, interface{})) (contextUser *user_model.User) {
        username := ctx.Params(":username")
 
-       if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(username) {
-               ctx.ContextUser = ctx.Doer
+       if doer != nil && doer.LowerName == strings.ToLower(username) {
+               contextUser = doer
        } else {
                var err error
-               ctx.ContextUser, err = user_model.GetUserByName(ctx, username)
+               contextUser, err = user_model.GetUserByName(ctx, username)
                if err != nil {
                        if user_model.IsErrUserNotExist(err) {
                                if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil {
@@ -79,4 +80,5 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{}))
                        }
                }
        }
+       return contextUser
 }
index a749f863f3dc8a44c892d049b727b57a4d34e36b..4b3cacc606f7db4350ec4d2f5d2dc643438709ed 100644 (file)
@@ -27,7 +27,7 @@ type AdminCreateUserForm struct {
 
 // Validate validates form fields
 func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -55,7 +55,7 @@ type AdminEditUserForm struct {
 
 // Validate validates form fields
 func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -67,6 +67,6 @@ type AdminDashboardForm struct {
 
 // Validate validates form fields
 func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index 5625aa1e2ed4855cd39f14e239641933bac9f4ab..25acbbb99e877fa6676d0b902d0c0c4a3c1cc312 100644 (file)
@@ -86,6 +86,6 @@ type AuthenticationForm struct {
 
 // Validate validates fields
 func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index d7535313713e34006450d5f171e8f577e8dfaf3f..c333bead316dd7b01c1b86d45d83ec05a0b5c2e7 100644 (file)
@@ -30,7 +30,7 @@ type CreateOrgForm struct {
 
 // Validate validates the fields
 func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -48,7 +48,7 @@ type UpdateOrgSettingForm struct {
 
 // Validate validates the fields
 func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -70,6 +70,6 @@ type CreateTeamForm struct {
 
 // Validate validates the fields
 func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index dfec98fff455bbb809e8cf4d7fa6b54c8d14ddfc..cf8abfb8fbc934bd97d8565e9971933a3cfb4d76 100644 (file)
@@ -25,6 +25,6 @@ type PackageCleanupRuleForm struct {
 }
 
 func (f *PackageCleanupRuleForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index bf1183fc43411b92b66b33500e6bcca803b3f993..5deb0ae463a033c5abaaafcaa8db628d65f19988 100644 (file)
@@ -21,7 +21,7 @@ type NewBranchForm struct {
 
 // Validate validates the fields
 func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -33,6 +33,6 @@ type RenameBranchForm struct {
 
 // Validate validates the fields
 func (f *RenameBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index d705ecad3f7db24965ce62d55235284f53e6932c..cacfb64b172fe0391f5df4cce34654477b37c263 100644 (file)
@@ -54,7 +54,7 @@ type CreateRepoForm struct {
 
 // Validate validates the fields
 func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -87,7 +87,7 @@ type MigrateRepoForm struct {
 
 // Validate validates the fields
 func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -176,7 +176,7 @@ type RepoSettingForm struct {
 
 // Validate validates the fields
 func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -215,7 +215,7 @@ type ProtectBranchForm struct {
 
 // Validate validates the fields
 func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -280,7 +280,7 @@ type NewWebhookForm struct {
 
 // Validate validates the fields
 func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -294,7 +294,7 @@ type NewGogshookForm struct {
 
 // Validate validates the fields
 func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -310,7 +310,7 @@ type NewSlackHookForm struct {
 
 // Validate validates the fields
 func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
                errs = append(errs, binding.Error{
                        FieldNames:     []string{"Channel"},
@@ -331,7 +331,7 @@ type NewDiscordHookForm struct {
 
 // Validate validates the fields
 func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -343,7 +343,7 @@ type NewDingtalkHookForm struct {
 
 // Validate validates the fields
 func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -356,7 +356,7 @@ type NewTelegramHookForm struct {
 
 // Validate validates the fields
 func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -370,7 +370,7 @@ type NewMatrixHookForm struct {
 
 // Validate validates the fields
 func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -382,7 +382,7 @@ type NewMSTeamsHookForm struct {
 
 // Validate validates the fields
 func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -394,7 +394,7 @@ type NewFeishuHookForm struct {
 
 // Validate validates the fields
 func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -406,7 +406,7 @@ type NewWechatWorkHookForm struct {
 
 // Validate validates the fields
 func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -420,7 +420,7 @@ type NewPackagistHookForm struct {
 
 // Validate validates the fields
 func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -447,7 +447,7 @@ type CreateIssueForm struct {
 
 // Validate validates the fields
 func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -460,7 +460,7 @@ type CreateCommentForm struct {
 
 // Validate validates the fields
 func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -471,7 +471,7 @@ type ReactionForm struct {
 
 // Validate validates the fields
 func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -482,7 +482,7 @@ type IssueLockForm struct {
 
 // Validate validates the fields
 func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, i, ctx.Locale)
 }
 
@@ -550,7 +550,7 @@ type CreateMilestoneForm struct {
 
 // Validate validates the fields
 func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -572,7 +572,7 @@ type CreateLabelForm struct {
 
 // Validate validates the fields
 func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -583,7 +583,7 @@ type InitializeLabelsForm struct {
 
 // Validate validates the fields
 func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -611,7 +611,7 @@ type MergePullRequestForm struct {
 
 // Validate validates the fields
 func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -629,7 +629,7 @@ type CodeCommentForm struct {
 
 // Validate validates the fields
 func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -643,7 +643,7 @@ type SubmitReviewForm struct {
 
 // Validate validates the fields
 func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -704,7 +704,7 @@ type NewReleaseForm struct {
 
 // Validate validates the fields
 func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -719,7 +719,7 @@ type EditReleaseForm struct {
 
 // Validate validates the fields
 func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -740,7 +740,7 @@ type NewWikiForm struct {
 // Validate validates the fields
 // FIXME: use code generation to generate this method.
 func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -765,7 +765,7 @@ type EditRepoFileForm struct {
 
 // Validate validates the fields
 func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -776,7 +776,7 @@ type EditPreviewDiffForm struct {
 
 // Validate validates the fields
 func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -800,7 +800,7 @@ type CherryPickForm struct {
 
 // Validate validates the fields
 func (f *CherryPickForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -825,7 +825,7 @@ type UploadRepoFileForm struct {
 
 // Validate validates the fields
 func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -836,7 +836,7 @@ type RemoveUploadFileForm struct {
 
 // Validate validates the fields
 func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -859,7 +859,7 @@ type DeleteRepoFileForm struct {
 
 // Validate validates the fields
 func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -878,7 +878,7 @@ type AddTimeManuallyForm struct {
 
 // Validate validates the fields
 func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -894,6 +894,6 @@ type DeadlineForm struct {
 
 // Validate validates the fields
 func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index 1209d2346f4669866265792e4e44aa098852a842..4dd99f9e32feeee022a80f8593179ea5a39f411a 100644 (file)
@@ -21,6 +21,6 @@ type ProtectTagForm struct {
 
 // Validate validates the fields
 func (f *ProtectTagForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index 906306034628f270f2675a023d31e500382743d0..22dea49e3129cee672eb65df31002b4ec91d8d0a 100644 (file)
@@ -20,6 +20,6 @@ type EditRunnerForm struct {
 
 // Validate validates form fields
 func (f *EditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index 285bc398b26c51dce0951a1253da5d8914fa3394..fa8129bf85d02e1df6cfee35103514ba344d9244 100644 (file)
@@ -78,7 +78,7 @@ type InstallForm struct {
 
 // Validate validates the fields
 func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -99,7 +99,7 @@ type RegisterForm struct {
 
 // Validate validates the fields
 func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -148,7 +148,7 @@ type MustChangePasswordForm struct {
 
 // Validate validates the fields
 func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -162,7 +162,7 @@ type SignInForm struct {
 
 // Validate validates the fields
 func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -182,7 +182,7 @@ type AuthorizationForm struct {
 
 // Validate validates the fields
 func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -197,7 +197,7 @@ type GrantApplicationForm struct {
 
 // Validate validates the fields
 func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -216,7 +216,7 @@ type AccessTokenForm struct {
 
 // Validate validates the fields
 func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -227,7 +227,7 @@ type IntrospectTokenForm struct {
 
 // Validate validates the fields
 func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -252,7 +252,7 @@ type UpdateProfileForm struct {
 
 // Validate validates the fields
 func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -263,7 +263,7 @@ type UpdateLanguageForm struct {
 
 // Validate validates the fields
 func (f *UpdateLanguageForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -283,7 +283,7 @@ type AvatarForm struct {
 
 // Validate validates the fields
 func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -294,7 +294,7 @@ type AddEmailForm struct {
 
 // Validate validates the fields
 func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -305,7 +305,7 @@ type UpdateThemeForm struct {
 
 // Validate validates the field
 func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -332,7 +332,7 @@ type ChangePasswordForm struct {
 
 // Validate validates the fields
 func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -343,7 +343,7 @@ type AddOpenIDForm struct {
 
 // Validate validates the fields
 func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -360,7 +360,7 @@ type AddKeyForm struct {
 
 // Validate validates the fields
 func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -372,7 +372,7 @@ type AddSecretForm struct {
 
 // Validate validates the fields
 func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -384,7 +384,7 @@ type NewAccessTokenForm struct {
 
 // Validate validates the fields
 func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -403,7 +403,7 @@ type EditOAuth2ApplicationForm struct {
 
 // Validate validates the fields
 func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -414,7 +414,7 @@ type TwoFactorAuthForm struct {
 
 // Validate validates the fields
 func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -425,7 +425,7 @@ type TwoFactorScratchAuthForm struct {
 
 // Validate validates the fields
 func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -436,7 +436,7 @@ type WebauthnRegistrationForm struct {
 
 // Validate validates the fields
 func (f *WebauthnRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -447,7 +447,7 @@ type WebauthnDeleteForm struct {
 
 // Validate validates the fields
 func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -459,6 +459,6 @@ type PackageSettingForm struct {
 
 // Validate validates the fields
 func (f *PackageSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index f95eb98405a5c26f247eca21369f30fd5c33a724..d8137a8d134fe257483957737d374524591b7305 100644 (file)
@@ -20,7 +20,7 @@ type SignInOpenIDForm struct {
 
 // Validate validates the fields
 func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -32,7 +32,7 @@ type SignUpOpenIDForm struct {
 
 // Validate validates the fields
 func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
@@ -44,6 +44,6 @@ type ConnectOpenIDForm struct {
 
 // Validate validates the fields
 func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
-       ctx := context.GetContext(req)
+       ctx := context.GetValidateContext(req)
        return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
index 6c9c1c27e72fb073bd1433e28f9669da5972eab9..2f48e03b22bf0d6cdc58b3c3040c6d834e493fe3 100644 (file)
@@ -6,6 +6,7 @@ package markup
 import (
        "context"
        "net/http"
+       "net/http/httptest"
        "testing"
 
        "code.gitea.io/gitea/models/db"
@@ -36,12 +37,12 @@ func TestProcessorHelper(t *testing.T) {
        assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch))
 
        // when using web context, use user.IsUserVisibleToViewer to check
-       var err error
-       giteaCtx := &gitea_context.Context{}
-       giteaCtx.Req, err = http.NewRequest("GET", "/", nil)
+       req, err := http.NewRequest("GET", "/", nil)
        assert.NoError(t, err)
+       base, baseCleanUp := gitea_context.NewBaseContext(httptest.NewRecorder(), req)
+       defer baseCleanUp()
+       giteaCtx := &gitea_context.Context{Base: base}
 
-       giteaCtx.Doer = nil
        assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic))
        assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate))