Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
wxiaoguang committed Jun 16, 2023
1 parent 6db66d8 commit 4ba6d51
Show file tree
Hide file tree
Showing 25 changed files with 128 additions and 158 deletions.
2 changes: 1 addition & 1 deletion cmd/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func runWeb(ctx *cli.Context) error {
}

// Set up Chi routes
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
c := routers.NormalRoutes()
err := listen(c, true)
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
Expand Down
8 changes: 8 additions & 0 deletions modules/context/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
web_types "code.gitea.io/gitea/modules/web/types"

"gitea.com/go-chi/cache"
)
Expand All @@ -41,6 +43,12 @@ type APIContext struct {
Package *Package
}

func init() {
web.RegisterResponseStatusProvider[*APIContext](func(req *http.Request) web_types.ResponseStatusProvider {
return req.Context().Value(apiContextKey).(*APIContext)
})
}

// Currently, we have the following common fields in error response:
// * message: the message for end users (it shouldn't be used for error type detection)
// if we need to indicate some errors, we should introduce some new fields like ErrorCode or ErrorType
Expand Down
6 changes: 5 additions & 1 deletion modules/context/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ func (b *Base) SetTotalCountHeader(total int64) {

// Written returns true if there are something sent to web browser
func (b *Base) Written() bool {
return b.Resp.Status() > 0
return b.Resp.WrittenStatus() != 0
}

func (b *Base) WrittenStatus() int {
return b.Resp.WrittenStatus()
}

// Status writes status code
Expand Down
8 changes: 8 additions & 0 deletions modules/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
web_types "code.gitea.io/gitea/modules/web/types"

"gitea.com/go-chi/cache"
"gitea.com/go-chi/session"
Expand Down Expand Up @@ -58,6 +60,12 @@ type Context struct {
Package *Package
}

func init() {
web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider {
return req.Context().Value(WebContextKey).(*Context)
})
}

// TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
// This is useful if the locale message is intended to only produce HTML content.
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
Expand Down
8 changes: 8 additions & 0 deletions modules/context/private.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (

"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/web"
web_types "code.gitea.io/gitea/modules/web/types"
)

// PrivateContext represents a context for private routes
Expand All @@ -21,6 +23,12 @@ type PrivateContext struct {
Repo *Repository
}

func init() {
web.RegisterResponseStatusProvider[*PrivateContext](func(req *http.Request) web_types.ResponseStatusProvider {
return req.Context().Value(privateContextKey).(*PrivateContext)
})
}

// Deadline is part of the interface for context.Context and we pass this to the request context
func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
if ctx.Override != nil {
Expand Down
17 changes: 13 additions & 4 deletions modules/context/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ package context

import (
"net/http"

web_types "code.gitea.io/gitea/modules/web/types"
)

// ResponseWriter represents a response writer for HTTP
type ResponseWriter interface {
http.ResponseWriter
http.Flusher
Status() int
web_types.ResponseStatusProvider

Before(func(ResponseWriter))
Size() int // used by access logger template

Status() int // used by access logger template
Size() int // used by access logger template
}

var _ ResponseWriter = &Response{}
Expand Down Expand Up @@ -46,6 +51,10 @@ func (r *Response) Write(bs []byte) (int, error) {
return size, nil
}

func (r *Response) Status() int {
return r.status
}

func (r *Response) Size() int {
return r.written
}
Expand All @@ -71,8 +80,8 @@ func (r *Response) Flush() {
}
}

// Status returned status code written
func (r *Response) Status() int {
// WrittenStatus returned status code written
func (r *Response) WrittenStatus() int {
return r.status
}

Expand Down
8 changes: 4 additions & 4 deletions modules/test/context_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ func MockContext(t *testing.T, path string) *context.Context {
URL: requestURL,
Form: url.Values{},
}

req = req.WithContext(middleware.WithContextData(req.Context()))
base, baseCleanUp := context.NewBaseContext(resp, req)
base.Data = middleware.ContextData{}
base.Data = middleware.GetContextData(req.Context())
base.Locale = &translation.MockLocale{}
ctx := &context.Context{
Base: base,
Expand All @@ -61,9 +61,9 @@ func MockAPIContext(t *testing.T, path string) *context.APIContext {
URL: requestURL,
Form: url.Values{},
}

req = req.WithContext(middleware.WithContextData(req.Context()))
base, baseCleanUp := context.NewBaseContext(resp, req)
base.Data = middleware.ContextData{}
base.Data = middleware.GetContextData(req.Context())
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
Expand Down
34 changes: 12 additions & 22 deletions modules/web/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,15 @@ import (
"net/http"
"reflect"

"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web/routing"
"code.gitea.io/gitea/modules/web/types"
)

// ResponseStatusProvider is an interface to check whether the response has been written by the handler
type ResponseStatusProvider interface {
Written() bool
}

// TODO: decouple this from the context package, let the context package register these providers
var argTypeProvider = map[reflect.Type]func(req *http.Request) ResponseStatusProvider{
reflect.TypeOf(&context.APIContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetAPIContext(req) },
reflect.TypeOf(&context.Context{}): func(req *http.Request) ResponseStatusProvider { return context.GetWebContext(req) },
reflect.TypeOf(&context.PrivateContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetPrivateContext(req) },
}
var responseStatusProviders = map[reflect.Type]func(req *http.Request) types.ResponseStatusProvider{}

func RegisterHandleTypeProvider[T any](fn func(req *http.Request) ResponseStatusProvider) {
argTypeProvider[reflect.TypeOf((*T)(nil)).Elem()] = fn
func RegisterResponseStatusProvider[T any](fn func(req *http.Request) types.ResponseStatusProvider) {
responseStatusProviders[reflect.TypeOf((*T)(nil)).Elem()] = fn
}

// responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written
Expand All @@ -36,10 +26,10 @@ type responseWriter struct {
status int
}

var _ ResponseStatusProvider = (*responseWriter)(nil)
var _ types.ResponseStatusProvider = (*responseWriter)(nil)

func (r *responseWriter) Written() bool {
return r.status > 0
func (r *responseWriter) WrittenStatus() int {
return r.status
}

func (r *responseWriter) Header() http.Header {
Expand Down Expand Up @@ -68,7 +58,7 @@ var (
func preCheckHandler(fn reflect.Value, argsIn []reflect.Value) {
hasStatusProvider := false
for _, argIn := range argsIn {
if _, hasStatusProvider = argIn.Interface().(ResponseStatusProvider); hasStatusProvider {
if _, hasStatusProvider = argIn.Interface().(types.ResponseStatusProvider); hasStatusProvider {
break
}
}
Expand Down Expand Up @@ -101,7 +91,7 @@ func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect
case httpReqType:
argsIn[i] = reflect.ValueOf(req)
default:
if argFn, ok := argTypeProvider[argTyp]; ok {
if argFn, ok := responseStatusProviders[argTyp]; ok {
if isPreCheck {
argsIn[i] = reflect.ValueOf(&responseWriter{})
} else {
Expand Down Expand Up @@ -129,8 +119,8 @@ func handleResponse(fn reflect.Value, ret []reflect.Value) goctx.CancelFunc {

func hasResponseBeenWritten(argsIn []reflect.Value) bool {
for _, argIn := range argsIn {
if statusProvider, ok := argIn.Interface().(ResponseStatusProvider); ok {
if statusProvider.Written() {
if statusProvider, ok := argIn.Interface().(types.ResponseStatusProvider); ok {
if statusProvider.WrittenStatus() != 0 {
return true
}
}
Expand Down Expand Up @@ -161,7 +151,7 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
// wrap the response writer to check whether the response has been written
resp := respOrig
if _, ok := resp.(ResponseStatusProvider); !ok {
if _, ok := resp.(types.ResponseStatusProvider); !ok {
resp = &responseWriter{respWriter: resp}
}

Expand Down
26 changes: 14 additions & 12 deletions modules/web/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,36 @@
package web

import (
"context"
"net/http"
"strings"

"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"

"gitea.com/go-chi/binding"
chi "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5"
)

// Bind binding an obj to a handler
func Bind[T any](_ T) any {
return func(ctx *context.Context) {
// Bind binding an obj to a handler's context data
func Bind[T any](_ T) http.HandlerFunc {
return func(resp http.ResponseWriter, req *http.Request) {
theObj := new(T) // create a new form obj for every request but not use obj directly
binding.Bind(ctx.Req, theObj)
SetForm(ctx, theObj)
middleware.AssignForm(theObj, ctx.Data)
binding.Bind(req, theObj)
SetForm(req.Context(), theObj)
middleware.AssignForm(theObj, middleware.GetContextData(req.Context()))
}
}

// SetForm set the form object
func SetForm(data middleware.ContextDataStore, obj interface{}) {
data.GetData()["__form"] = obj
func SetForm(ctx context.Context, obj interface{}) {
ctxData := middleware.GetContextData(ctx)
ctxData["__form"] = obj
}

// GetForm returns the validate form information
func GetForm(data middleware.ContextDataStore) interface{} {
return data.GetData()["__form"]
func GetForm(ctx context.Context) interface{} {
ctxData := middleware.GetContextData(ctx)
return ctxData["__form"]
}

// Route defines a route based on chi's router
Expand Down
6 changes: 3 additions & 3 deletions modules/web/routing/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web/types"
)

// NewLoggerHandler is a handler that will log routing to the router log taking account of
Expand Down Expand Up @@ -86,8 +86,8 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
}

var status int
if v, ok := record.responseWriter.(context.ResponseWriter); ok {
status = v.Status()
if v, ok := record.responseWriter.(types.ResponseStatusProvider); ok {
status = v.WrittenStatus()
}
logf := log.Info
if strings.HasPrefix(req.RequestURI, "/assets/") {
Expand Down
10 changes: 10 additions & 0 deletions modules/web/types/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package types

// ResponseStatusProvider is an interface to get the written status in the response
// Many packages need this interface, so put it in the separate package to avoid import cycle
type ResponseStatusProvider interface {
WrittenStatus() int
}
3 changes: 1 addition & 2 deletions routers/api/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
package actions

import (
"context"
"net/http"

"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/actions/ping"
"code.gitea.io/gitea/routers/api/actions/runner"
)

func Routes(_ context.Context, prefix string) *web.Route {
func Routes(prefix string) *web.Route {
m := web.NewRoute()

path, handler := ping.NewPingServiceHandler()
Expand Down
3 changes: 2 additions & 1 deletion routers/api/actions/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import (
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
web_types "code.gitea.io/gitea/modules/web/types"
)

const (
Expand All @@ -102,7 +103,7 @@ type ArtifactContext struct {
}

func init() {
web.RegisterHandleTypeProvider[*ArtifactContext](func(req *http.Request) web.ResponseStatusProvider {
web.RegisterResponseStatusProvider[*ArtifactContext](func(req *http.Request) web_types.ResponseStatusProvider {
return req.Context().Value(artifactContextKey).(*ArtifactContext)
})
}
Expand Down
5 changes: 2 additions & 3 deletions routers/api/packages/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package packages

import (
gocontext "context"
"net/http"
"regexp"
"strings"
Expand Down Expand Up @@ -96,7 +95,7 @@ func verifyAuth(r *web.Route, authMethods []auth.Method) {

// CommonRoutes provide endpoints for most package managers (except containers - see below)
// These are mounted on `/api/packages` (not `/api/v1/packages`)
func CommonRoutes(ctx gocontext.Context) *web.Route {
func CommonRoutes() *web.Route {
r := web.NewRoute()

r.Use(context.PackageContexter())
Expand Down Expand Up @@ -590,7 +589,7 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
// ContainerRoutes provides endpoints that implement the OCI API to serve containers
// These have to be mounted on `/v2/...` to comply with the OCI spec:
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md
func ContainerRoutes(ctx gocontext.Context) *web.Route {
func ContainerRoutes() *web.Route {
r := web.NewRoute()

r.Use(context.PackageContexter())
Expand Down
Loading

0 comments on commit 4ba6d51

Please sign in to comment.