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:
@@ -4,7 +4,6 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
goctx "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@@ -51,7 +50,6 @@ func (r *responseWriter) WriteHeader(statusCode int) {
|
||||
var (
|
||||
httpReqType = reflect.TypeOf((*http.Request)(nil))
|
||||
respWriterType = reflect.TypeOf((*http.ResponseWriter)(nil)).Elem()
|
||||
cancelFuncType = reflect.TypeOf((*goctx.CancelFunc)(nil)).Elem()
|
||||
)
|
||||
|
||||
// preCheckHandler checks whether the handler is valid, developers could get first-time feedback, all mistakes could be found at startup
|
||||
@@ -65,11 +63,8 @@ func preCheckHandler(fn reflect.Value, argsIn []reflect.Value) {
|
||||
if !hasStatusProvider {
|
||||
panic(fmt.Sprintf("handler should have at least one ResponseStatusProvider argument, but got %s", fn.Type()))
|
||||
}
|
||||
if fn.Type().NumOut() != 0 && fn.Type().NumIn() != 1 {
|
||||
panic(fmt.Sprintf("handler should have no return value or only one argument, but got %s", fn.Type()))
|
||||
}
|
||||
if fn.Type().NumOut() == 1 && fn.Type().Out(0) != cancelFuncType {
|
||||
panic(fmt.Sprintf("handler should return a cancel function, but got %s", fn.Type()))
|
||||
if fn.Type().NumOut() != 0 {
|
||||
panic(fmt.Sprintf("handler should have no return value other than registered ones, but got %s", fn.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,16 +100,10 @@ func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect
|
||||
return argsIn
|
||||
}
|
||||
|
||||
func handleResponse(fn reflect.Value, ret []reflect.Value) goctx.CancelFunc {
|
||||
if len(ret) == 1 {
|
||||
if cancelFunc, ok := ret[0].Interface().(goctx.CancelFunc); ok {
|
||||
return cancelFunc
|
||||
}
|
||||
panic(fmt.Sprintf("unsupported return type: %s", ret[0].Type()))
|
||||
} else if len(ret) > 1 {
|
||||
func handleResponse(fn reflect.Value, ret []reflect.Value) {
|
||||
if len(ret) != 0 {
|
||||
panic(fmt.Sprintf("unsupported return values: %s", fn.Type()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasResponseBeenWritten(argsIn []reflect.Value) bool {
|
||||
@@ -171,11 +160,8 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||
ret := fn.Call(argsIn)
|
||||
|
||||
// handle the return value, and defer the cancel function if there is one
|
||||
cancelFunc := handleResponse(fn, ret)
|
||||
if cancelFunc != nil {
|
||||
defer cancelFunc()
|
||||
}
|
||||
// handle the return value (no-op at the moment)
|
||||
handleResponse(fn, ret)
|
||||
|
||||
// if the response has not been written, call the next handler
|
||||
if next != nil && !hasResponseBeenWritten(argsIn) {
|
||||
|
@@ -7,46 +7,21 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// ContextDataStore represents a data store
|
||||
type ContextDataStore interface {
|
||||
GetData() ContextData
|
||||
}
|
||||
|
||||
type ContextData map[string]any
|
||||
|
||||
func (ds ContextData) GetData() ContextData {
|
||||
return ds
|
||||
}
|
||||
|
||||
func (ds ContextData) MergeFrom(other ContextData) ContextData {
|
||||
for k, v := range other {
|
||||
ds[k] = v
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
const ContextDataKeySignedUser = "SignedUser"
|
||||
|
||||
type contextDataKeyType struct{}
|
||||
|
||||
var contextDataKey contextDataKeyType
|
||||
|
||||
func WithContextData(c context.Context) context.Context {
|
||||
return context.WithValue(c, contextDataKey, make(ContextData, 10))
|
||||
}
|
||||
|
||||
func GetContextData(c context.Context) ContextData {
|
||||
if ds, ok := c.Value(contextDataKey).(ContextData); ok {
|
||||
return ds
|
||||
func GetContextData(c context.Context) reqctx.ContextData {
|
||||
if rc := reqctx.GetRequestDataStore(c); rc != nil {
|
||||
return rc.GetData()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CommonTemplateContextData() ContextData {
|
||||
return ContextData{
|
||||
func CommonTemplateContextData() reqctx.ContextData {
|
||||
return reqctx.ContextData{
|
||||
"IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
|
||||
|
||||
"ShowRegistrationButton": setting.Service.ShowRegistrationButton,
|
||||
|
@@ -7,11 +7,13 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
)
|
||||
|
||||
// Flash represents a one time data transfer between two requests.
|
||||
type Flash struct {
|
||||
DataStore ContextDataStore
|
||||
DataStore reqctx.RequestDataStore
|
||||
url.Values
|
||||
ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
|
||||
@@ -29,12 +30,12 @@ func Bind[T any](_ T) http.HandlerFunc {
|
||||
}
|
||||
|
||||
// SetForm set the form object
|
||||
func SetForm(dataStore middleware.ContextDataStore, obj any) {
|
||||
func SetForm(dataStore reqctx.ContextDataProvider, obj any) {
|
||||
dataStore.GetData()["__form"] = obj
|
||||
}
|
||||
|
||||
// GetForm returns the validate form information
|
||||
func GetForm(dataStore middleware.ContextDataStore) any {
|
||||
func GetForm(dataStore reqctx.RequestDataStore) any {
|
||||
return dataStore.GetData()["__form"]
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user