Skip to content

Commit ef76b92

Browse files
committed
fix
1 parent a09ea2f commit ef76b92

24 files changed

+264
-259
lines changed

modules/gitrepo/gitrepo.go

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"code.gitea.io/gitea/modules/git"
13+
"code.gitea.io/gitea/modules/reqctx"
1314
"code.gitea.io/gitea/modules/setting"
1415
"code.gitea.io/gitea/modules/util"
1516
)
@@ -38,63 +39,34 @@ func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository,
3839

3940
// contextKey is a value for use with context.WithValue.
4041
type contextKey struct {
41-
name string
42-
}
43-
44-
// RepositoryContextKey is a context key. It is used with context.Value() to get the current Repository for the context
45-
var RepositoryContextKey = &contextKey{"repository"}
46-
47-
// RepositoryFromContext attempts to get the repository from the context
48-
func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository {
49-
value := ctx.Value(RepositoryContextKey)
50-
if value == nil {
51-
return nil
52-
}
53-
54-
if gitRepo, ok := value.(*git.Repository); ok && gitRepo != nil {
55-
if gitRepo.Path == repoPath(repo) {
56-
return gitRepo
57-
}
58-
}
59-
60-
return nil
42+
repoPath string
6143
}
6244

6345
// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
6446
func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) {
65-
gitRepo := repositoryFromContext(ctx, repo)
66-
if gitRepo != nil {
67-
return gitRepo, util.NopCloser{}, nil
47+
reqCtx := reqctx.GetRequestContext(ctx)
48+
if reqCtx != nil {
49+
gitRepo, err := RepositoryFromRequestContextOrOpen(reqCtx, repo)
50+
return gitRepo, util.NopCloser{}, err
6851
}
69-
7052
gitRepo, err := OpenRepository(ctx, repo)
7153
return gitRepo, gitRepo, err
7254
}
7355

74-
// repositoryFromContextPath attempts to get the repository from the context
75-
func repositoryFromContextPath(ctx context.Context, path string) *git.Repository {
76-
value := ctx.Value(RepositoryContextKey)
77-
if value == nil {
78-
return nil
56+
// RepositoryFromRequestContextOrOpen opens the repository at the given relative path in the provided request context
57+
// The repo will be automatically closed when the request context is done
58+
func RepositoryFromRequestContextOrOpen(ctx *reqctx.RequestContext, repo Repository) (*git.Repository, error) {
59+
ck := contextKey{repoPath(repo)}
60+
if gitRepo := ctx.Value(ck); gitRepo != nil {
61+
return gitRepo.(*git.Repository), nil
7962
}
80-
81-
if repo, ok := value.(*git.Repository); ok && repo != nil {
82-
if repo.Path == path {
83-
return repo
84-
}
63+
gitRepo, err := git.OpenRepository(ctx, repoPath(repo))
64+
if err != nil {
65+
return nil, err
8566
}
86-
87-
return nil
88-
}
89-
90-
// RepositoryFromContextOrOpenPath attempts to get the repository from the context or just opens it
91-
// Deprecated: Use RepositoryFromContextOrOpen instead
92-
func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Repository, io.Closer, error) {
93-
gitRepo := repositoryFromContextPath(ctx, path)
94-
if gitRepo != nil {
95-
return gitRepo, util.NopCloser{}, nil
96-
}
97-
98-
gitRepo, err := git.OpenRepository(ctx, path)
99-
return gitRepo, gitRepo, err
67+
ctx.AddCleanUp(func() {
68+
gitRepo.Close()
69+
})
70+
ctx.SetContextValue(ck, gitRepo)
71+
return gitRepo, nil
10072
}

modules/reqctx/ctxdata.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package reqctx
5+
6+
type ContextData map[string]any
7+
8+
type ContextDataStore interface {
9+
GetData() ContextData
10+
}
11+
12+
func (ds ContextData) GetData() ContextData {
13+
return ds
14+
}
15+
16+
func (ds ContextData) MergeFrom(other ContextData) ContextData {
17+
for k, v := range other {
18+
ds[k] = v
19+
}
20+
return ds
21+
}

modules/reqctx/reqctx.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package reqctx
5+
6+
import (
7+
"context"
8+
"sync"
9+
"time"
10+
11+
"code.gitea.io/gitea/modules/process"
12+
)
13+
14+
type RequestContextKeyType struct{}
15+
16+
var RequestContextKey RequestContextKeyType
17+
18+
// RequestContext is a short-lived context that is used to store request-specific data.
19+
type RequestContext struct {
20+
ctx context.Context
21+
data ContextData
22+
23+
mu sync.RWMutex
24+
values map[any]any
25+
26+
cleanUpFuncs []func()
27+
}
28+
29+
func (r *RequestContext) Deadline() (deadline time.Time, ok bool) {
30+
return r.ctx.Deadline()
31+
}
32+
33+
func (r *RequestContext) Done() <-chan struct{} {
34+
return r.ctx.Done()
35+
}
36+
37+
func (r *RequestContext) Err() error {
38+
return r.ctx.Err()
39+
}
40+
41+
func (r *RequestContext) Value(key any) any {
42+
if key == RequestContextKey {
43+
return r
44+
}
45+
r.mu.RLock()
46+
if v, ok := r.values[key]; ok {
47+
r.mu.RUnlock()
48+
return v
49+
}
50+
r.mu.RUnlock()
51+
return r.ctx.Value(key)
52+
}
53+
54+
func (r *RequestContext) SetContextValue(k, v any) {
55+
r.mu.Lock()
56+
r.values[k] = v
57+
r.mu.Unlock()
58+
}
59+
60+
// GetData and the underlying ContextData are not thread-safe, callers should ensure thread-safety.
61+
func (r *RequestContext) GetData() ContextData {
62+
if r.data == nil {
63+
r.data = make(ContextData)
64+
}
65+
return r.data
66+
}
67+
68+
func (r *RequestContext) AddCleanUp(f func()) {
69+
r.mu.Lock()
70+
r.cleanUpFuncs = append(r.cleanUpFuncs, f)
71+
r.mu.Unlock()
72+
}
73+
74+
func (r *RequestContext) cleanUp() {
75+
for _, f := range r.cleanUpFuncs {
76+
f()
77+
}
78+
}
79+
80+
func GetRequestContext(ctx context.Context) *RequestContext {
81+
if req, ok := ctx.Value(RequestContextKey).(*RequestContext); ok {
82+
return req
83+
}
84+
return nil
85+
}
86+
87+
func NewRequestContext(parentCtx context.Context, profDesc string) (_ *RequestContext, finished func()) {
88+
ctx, _, processFinished := process.GetManager().AddTypedContext(parentCtx, profDesc, process.RequestProcessType, true)
89+
reqCtx := &RequestContext{ctx: ctx, values: make(map[any]any)}
90+
return reqCtx, func() {
91+
reqCtx.cleanUp()
92+
processFinished()
93+
}
94+
}
95+
96+
// NewRequestContextForTest creates a new RequestContext for testing purposes
97+
// It doesn't add the context to the process manager, nor do cleanup
98+
func NewRequestContextForTest(parentCtx context.Context) *RequestContext {
99+
reqCtx := &RequestContext{ctx: parentCtx, values: make(map[any]any)}
100+
return reqCtx
101+
}

modules/web/middleware/data.go

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,21 @@ import (
77
"context"
88
"time"
99

10+
"code.gitea.io/gitea/modules/reqctx"
1011
"code.gitea.io/gitea/modules/setting"
1112
)
1213

13-
// ContextDataStore represents a data store
14-
type ContextDataStore interface {
15-
GetData() ContextData
16-
}
17-
18-
type ContextData map[string]any
19-
20-
func (ds ContextData) GetData() ContextData {
21-
return ds
22-
}
23-
24-
func (ds ContextData) MergeFrom(other ContextData) ContextData {
25-
for k, v := range other {
26-
ds[k] = v
27-
}
28-
return ds
29-
}
30-
3114
const ContextDataKeySignedUser = "SignedUser"
3215

33-
type contextDataKeyType struct{}
34-
35-
var contextDataKey contextDataKeyType
36-
37-
func WithContextData(c context.Context) context.Context {
38-
return context.WithValue(c, contextDataKey, make(ContextData, 10))
39-
}
40-
41-
func GetContextData(c context.Context) ContextData {
42-
if ds, ok := c.Value(contextDataKey).(ContextData); ok {
43-
return ds
16+
func GetContextData(c context.Context) reqctx.ContextData {
17+
if rc := reqctx.GetRequestContext(c); rc != nil {
18+
return rc.GetData()
4419
}
4520
return nil
4621
}
4722

48-
func CommonTemplateContextData() ContextData {
49-
return ContextData{
23+
func CommonTemplateContextData() reqctx.ContextData {
24+
return reqctx.ContextData{
5025
"IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
5126

5227
"ShowRegistrationButton": setting.Service.ShowRegistrationButton,

modules/web/middleware/flash.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77
"fmt"
88
"html/template"
99
"net/url"
10+
11+
"code.gitea.io/gitea/modules/reqctx"
1012
)
1113

1214
// Flash represents a one time data transfer between two requests.
1315
type Flash struct {
14-
DataStore ContextDataStore
16+
DataStore reqctx.ContextDataStore
1517
url.Values
1618
ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
1719
}

modules/web/route.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"code.gitea.io/gitea/modules/htmlutil"
13+
"code.gitea.io/gitea/modules/reqctx"
1314
"code.gitea.io/gitea/modules/setting"
1415
"code.gitea.io/gitea/modules/web/middleware"
1516

@@ -29,12 +30,12 @@ func Bind[T any](_ T) http.HandlerFunc {
2930
}
3031

3132
// SetForm set the form object
32-
func SetForm(dataStore middleware.ContextDataStore, obj any) {
33+
func SetForm(dataStore reqctx.ContextDataStore, obj any) {
3334
dataStore.GetData()["__form"] = obj
3435
}
3536

3637
// GetForm returns the validate form information
37-
func GetForm(dataStore middleware.ContextDataStore) any {
38+
func GetForm(dataStore reqctx.ContextDataStore) any {
3839
return dataStore.GetData()["__form"]
3940
}
4041

routers/api/actions/artifacts.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,10 @@ func ArtifactsRoutes(prefix string) *web.Router {
126126
func ArtifactContexter() func(next http.Handler) http.Handler {
127127
return func(next http.Handler) http.Handler {
128128
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
129-
base, baseCleanUp := context.NewBaseContext(resp, req)
130-
defer baseCleanUp()
129+
base := context.NewBaseContext(resp, req)
131130

132131
ctx := &ArtifactContext{Base: base}
133-
ctx.AppendContextValue(artifactContextKey, ctx)
132+
ctx.SetContextValue(artifactContextKey, ctx)
134133

135134
// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
136135
// we should verify the ACTIONS_RUNTIME_TOKEN

routers/api/actions/artifactsv4.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,9 @@ type artifactV4Routes struct {
126126
func ArtifactV4Contexter() func(next http.Handler) http.Handler {
127127
return func(next http.Handler) http.Handler {
128128
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
129-
base, baseCleanUp := context.NewBaseContext(resp, req)
130-
defer baseCleanUp()
131-
129+
base := context.NewBaseContext(resp, req)
132130
ctx := &ArtifactContext{Base: base}
133-
ctx.AppendContextValue(artifactContextKey, ctx)
134-
131+
ctx.SetContextValue(artifactContextKey, ctx)
135132
next.ServeHTTP(ctx.Resp, ctx.Req)
136133
})
137134
}

routers/common/errpage_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import (
1212
"testing"
1313

1414
"code.gitea.io/gitea/models/unittest"
15+
"code.gitea.io/gitea/modules/reqctx"
1516
"code.gitea.io/gitea/modules/test"
16-
"code.gitea.io/gitea/modules/web/middleware"
1717

1818
"github.com/stretchr/testify/assert"
1919
)
2020

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

0 commit comments

Comments
 (0)