1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-23 02:38:35 +00:00

Refactor request context (#32956)

Introduce RequestContext: is a short-lived context that is used to store
request-specific data.

RequestContext could be used to clean form tmp files, close context git
repo, and do some tracing in the future.

Then a lot of legacy code could be removed or improved. For example:
most `ctx.Repo.GitRepo.Close()` could be removed because the git repo
could be closed when the request is done.
This commit is contained in:
wxiaoguang
2024-12-24 11:43:57 +08:00
committed by GitHub
parent 781c6df40f
commit 6d5aa9218e
34 changed files with 379 additions and 422 deletions

View File

@@ -12,12 +12,12 @@ import (
"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/optional"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/web/middleware"
@@ -25,65 +25,25 @@ import (
"github.com/go-chi/chi/v5"
)
type contextValuePair struct {
key any
valueFn func() any
}
type BaseContextKeyType struct{}
var BaseContextKey BaseContextKeyType
type Base struct {
originCtx context.Context
contextValues []contextValuePair
context.Context
reqctx.RequestDataStore
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
Data reqctx.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")
@@ -295,13 +255,6 @@ func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
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) template.HTML {
return b.Locale.Tr(msg, args...)
}
@@ -310,17 +263,28 @@ func (b *Base) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
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()),
func NewBaseContext(resp http.ResponseWriter, req *http.Request) *Base {
ds := reqctx.GetRequestDataStore(req.Context())
b := &Base{
Context: req.Context(),
RequestDataStore: ds,
Req: req,
Resp: WrapResponseWriter(resp),
Locale: middleware.Locale(resp, req),
Data: ds.GetData(),
}
b.Req = b.Req.WithContext(b)
b.AppendContextValue(BaseContextKey, b)
b.AppendContextValue(translation.ContextKey, b.Locale)
b.AppendContextValue(httplib.RequestContextKey, b.Req)
return b, b.cleanUp
ds.SetContextValue(BaseContextKey, b)
ds.SetContextValue(translation.ContextKey, b.Locale)
ds.SetContextValue(httplib.RequestContextKey, b.Req)
return b
}
func NewBaseContextForTest(resp http.ResponseWriter, req *http.Request) *Base {
if !setting.IsInTesting {
panic("This function is only for testing")
}
ctx := reqctx.NewRequestContextForTest(req.Context())
*req = *req.WithContext(ctx)
return NewBaseContext(resp, req)
}