Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
wxiaoguang committed Dec 22, 2024
1 parent a09ea2f commit 0d6322a
Show file tree
Hide file tree
Showing 25 changed files with 268 additions and 267 deletions.
68 changes: 20 additions & 48 deletions modules/gitrepo/gitrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
Expand Down Expand Up @@ -38,63 +39,34 @@ func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository,

// contextKey is a value for use with context.WithValue.
type contextKey struct {
name string
}

// RepositoryContextKey is a context key. It is used with context.Value() to get the current Repository for the context
var RepositoryContextKey = &contextKey{"repository"}

// RepositoryFromContext attempts to get the repository from the context
func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository {
value := ctx.Value(RepositoryContextKey)
if value == nil {
return nil
}

if gitRepo, ok := value.(*git.Repository); ok && gitRepo != nil {
if gitRepo.Path == repoPath(repo) {
return gitRepo
}
}

return nil
repoPath string
}

// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) {
gitRepo := repositoryFromContext(ctx, repo)
if gitRepo != nil {
return gitRepo, util.NopCloser{}, nil
reqCtx := reqctx.GetRequestContext(ctx)
if reqCtx != nil {
gitRepo, err := RepositoryFromRequestContextOrOpen(reqCtx, repo)
return gitRepo, util.NopCloser{}, err
}

gitRepo, err := OpenRepository(ctx, repo)
return gitRepo, gitRepo, err
}

// repositoryFromContextPath attempts to get the repository from the context
func repositoryFromContextPath(ctx context.Context, path string) *git.Repository {
value := ctx.Value(RepositoryContextKey)
if value == nil {
return nil
// RepositoryFromRequestContextOrOpen opens the repository at the given relative path in the provided request context
// The repo will be automatically closed when the request context is done
func RepositoryFromRequestContextOrOpen(ctx *reqctx.RequestContext, repo Repository) (*git.Repository, error) {
ck := contextKey{repoPath(repo)}
if gitRepo := ctx.Value(ck); gitRepo != nil {
return gitRepo.(*git.Repository), nil
}

if repo, ok := value.(*git.Repository); ok && repo != nil {
if repo.Path == path {
return repo
}
gitRepo, err := git.OpenRepository(ctx, repoPath(repo))
if err != nil {
return nil, err
}

return nil
}

// RepositoryFromContextOrOpenPath attempts to get the repository from the context or just opens it
// Deprecated: Use RepositoryFromContextOrOpen instead
func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Repository, io.Closer, error) {
gitRepo := repositoryFromContextPath(ctx, path)
if gitRepo != nil {
return gitRepo, util.NopCloser{}, nil
}

gitRepo, err := git.OpenRepository(ctx, path)
return gitRepo, gitRepo, err
ctx.AddCleanUp(func() {
gitRepo.Close()
})
ctx.SetContextValue(ck, gitRepo)
return gitRepo, nil
}
12 changes: 4 additions & 8 deletions modules/gitrepo/walk_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,11 @@ import (
// WalkReferences walks all the references from the repository
// refname is empty, ObjectTag or ObjectBranch. All other values should be treated as equivalent to empty.
func WalkReferences(ctx context.Context, repo Repository, walkfn func(sha1, refname string) error) (int, error) {
gitRepo := repositoryFromContext(ctx, repo)
if gitRepo == nil {
var err error
gitRepo, err = OpenRepository(ctx, repo)
if err != nil {
return 0, err
}
defer gitRepo.Close()
gitRepo, closer, err := RepositoryFromContextOrOpen(ctx, repo)
if err != nil {
return 0, err
}
defer closer.Close()

i := 0
iter, err := gitRepo.GoGitRepo().References()
Expand Down
21 changes: 21 additions & 0 deletions modules/reqctx/ctxdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package reqctx

type ContextData map[string]any

type ContextDataStore interface {
GetData() ContextData
}

func (ds ContextData) GetData() ContextData {
return ds
}

func (ds ContextData) MergeFrom(other ContextData) ContextData {
for k, v := range other {
ds[k] = v
}
return ds
}
101 changes: 101 additions & 0 deletions modules/reqctx/reqctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package reqctx

import (
"context"
"sync"
"time"

"code.gitea.io/gitea/modules/process"
)

type RequestContextKeyType struct{}

var RequestContextKey RequestContextKeyType

// RequestContext is a short-lived context that is used to store request-specific data.
type RequestContext struct {
ctx context.Context
data ContextData

mu sync.RWMutex
values map[any]any

cleanUpFuncs []func()
}

func (r *RequestContext) Deadline() (deadline time.Time, ok bool) {
return r.ctx.Deadline()
}

func (r *RequestContext) Done() <-chan struct{} {
return r.ctx.Done()
}

func (r *RequestContext) Err() error {
return r.ctx.Err()
}

func (r *RequestContext) Value(key any) any {
if key == RequestContextKey {
return r
}
r.mu.RLock()
if v, ok := r.values[key]; ok {
r.mu.RUnlock()
return v
}
r.mu.RUnlock()
return r.ctx.Value(key)
}

func (r *RequestContext) SetContextValue(k, v any) {
r.mu.Lock()
r.values[k] = v
r.mu.Unlock()
}

// GetData and the underlying ContextData are not thread-safe, callers should ensure thread-safety.
func (r *RequestContext) GetData() ContextData {
if r.data == nil {
r.data = make(ContextData)
}
return r.data
}

func (r *RequestContext) AddCleanUp(f func()) {
r.mu.Lock()
r.cleanUpFuncs = append(r.cleanUpFuncs, f)
r.mu.Unlock()
}

func (r *RequestContext) cleanUp() {
for _, f := range r.cleanUpFuncs {
f()
}
}

func GetRequestContext(ctx context.Context) *RequestContext {
if req, ok := ctx.Value(RequestContextKey).(*RequestContext); ok {
return req
}
return nil
}

func NewRequestContext(parentCtx context.Context, profDesc string) (_ *RequestContext, finished func()) {
ctx, _, processFinished := process.GetManager().AddTypedContext(parentCtx, profDesc, process.RequestProcessType, true)
reqCtx := &RequestContext{ctx: ctx, values: make(map[any]any)}
return reqCtx, func() {
reqCtx.cleanUp()
processFinished()
}
}

// NewRequestContextForTest creates a new RequestContext for testing purposes
// It doesn't add the context to the process manager, nor do cleanup
func NewRequestContextForTest(parentCtx context.Context) *RequestContext {
reqCtx := &RequestContext{ctx: parentCtx, values: make(map[any]any)}
return reqCtx
}
37 changes: 6 additions & 31 deletions modules/web/middleware/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.GetRequestContext(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,
Expand Down
4 changes: 3 additions & 1 deletion modules/web/middleware/flash.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.ContextDataStore
url.Values
ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
}
Expand Down
5 changes: 3 additions & 2 deletions modules/web/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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.ContextDataStore, obj any) {
dataStore.GetData()["__form"] = obj
}

// GetForm returns the validate form information
func GetForm(dataStore middleware.ContextDataStore) any {
func GetForm(dataStore reqctx.ContextDataStore) any {
return dataStore.GetData()["__form"]
}

Expand Down
5 changes: 2 additions & 3 deletions routers/api/actions/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,10 @@ func ArtifactsRoutes(prefix string) *web.Router {
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) {
base, baseCleanUp := context.NewBaseContext(resp, req)
defer baseCleanUp()
base := context.NewBaseContext(resp, req)

ctx := &ArtifactContext{Base: base}
ctx.AppendContextValue(artifactContextKey, ctx)
ctx.SetContextValue(artifactContextKey, ctx)

// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
// we should verify the ACTIONS_RUNTIME_TOKEN
Expand Down
7 changes: 2 additions & 5 deletions routers/api/actions/artifactsv4.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,9 @@ type artifactV4Routes struct {
func ArtifactV4Contexter() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
base, baseCleanUp := context.NewBaseContext(resp, req)
defer baseCleanUp()

base := context.NewBaseContext(resp, req)
ctx := &ArtifactContext{Base: base}
ctx.AppendContextValue(artifactContextKey, ctx)

ctx.SetContextValue(artifactContextKey, ctx)
next.ServeHTTP(ctx.Resp, ctx.Req)
})
}
Expand Down
4 changes: 2 additions & 2 deletions routers/common/errpage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ import (
"testing"

"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/web/middleware"

"github.com/stretchr/testify/assert"
)

func TestRenderPanicErrorPage(t *testing.T) {
w := httptest.NewRecorder()
req := &http.Request{URL: &url.URL{}}
req = req.WithContext(middleware.WithContextData(context.Background()))
req = req.WithContext(reqctx.NewRequestContextForTest(context.Background()))
RenderPanicErrorPage(w, req, errors.New("fake panic error (for test only)"))
respContent := w.Body.String()
assert.Contains(t, respContent, `class="page-content status-page-500"`)
Expand Down
Loading

0 comments on commit 0d6322a

Please sign in to comment.