Skip to content

Commit ba072ae

Browse files
committed
refactor context to add a BaseContext and reduce private context struct size
1 parent b59875a commit ba072ae

File tree

5 files changed

+184
-105
lines changed

5 files changed

+184
-105
lines changed

modules/context/api.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,9 @@ func APIContexter() func(http.Handler) http.Handler {
272272
var locale = middleware.Locale(w, req)
273273
var ctx = APIContext{
274274
Context: &Context{
275-
Resp: NewResponse(w),
276-
Data: map[string]interface{}{},
277-
Locale: locale,
278-
Session: session.GetSession(req),
275+
BaseContext: NewBaseContext(w, req, map[string]interface{}{}),
276+
Locale: locale,
277+
Session: session.GetSession(req),
279278
Repo: &Repository{
280279
PullRequest: &PullRequest{},
281280
},

modules/context/base.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// Copyright 2021 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package context
6+
7+
import (
8+
"net/http"
9+
"net/url"
10+
"strconv"
11+
"strings"
12+
"time"
13+
14+
"code.gitea.io/gitea/modules/json"
15+
"code.gitea.io/gitea/modules/log"
16+
"code.gitea.io/gitea/modules/util"
17+
18+
chi "github.com/go-chi/chi/v5"
19+
)
20+
21+
type BaseContext struct {
22+
Resp ResponseWriter
23+
Req *http.Request
24+
Data map[string]interface{}
25+
}
26+
27+
func NewBaseContext(resp http.ResponseWriter, req *http.Request, data map[string]interface{}) *BaseContext {
28+
return &BaseContext{
29+
Resp: NewResponse(resp),
30+
Req: req,
31+
Data: data,
32+
}
33+
}
34+
35+
// GetData returns the data
36+
func (ctx *BaseContext) GetData() map[string]interface{} {
37+
return ctx.Data
38+
}
39+
40+
// HasValue returns true if value of given name exists.
41+
func (ctx *BaseContext) HasValue(name string) bool {
42+
_, ok := ctx.Data[name]
43+
return ok
44+
}
45+
46+
// Header returns a header
47+
func (ctx *BaseContext) Header() http.Header {
48+
return ctx.Resp.Header()
49+
}
50+
51+
// RemoteAddr returns the client machie ip address
52+
func (ctx *BaseContext) RemoteAddr() string {
53+
return ctx.Req.RemoteAddr
54+
}
55+
56+
// Params returns the param on route
57+
func (ctx *BaseContext) Params(p string) string {
58+
s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":")))
59+
return s
60+
}
61+
62+
// ParamsInt64 returns the param on route as int64
63+
func (ctx *BaseContext) ParamsInt64(p string) int64 {
64+
v, _ := strconv.ParseInt(ctx.Params(p), 10, 64)
65+
return v
66+
}
67+
68+
// SetParams set params into routes
69+
func (ctx *BaseContext) SetParams(k, v string) {
70+
chiCtx := chi.RouteContext(ctx)
71+
chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
72+
}
73+
74+
// Write writes data to webbrowser
75+
func (ctx *BaseContext) Write(bs []byte) (int, error) {
76+
return ctx.Resp.Write(bs)
77+
}
78+
79+
// Written returns true if there are something sent to web browser
80+
func (ctx *BaseContext) Written() bool {
81+
return ctx.Resp.Status() > 0
82+
}
83+
84+
// Status writes status code
85+
func (ctx *BaseContext) Status(status int) {
86+
ctx.Resp.WriteHeader(status)
87+
}
88+
89+
// Deadline is part of the interface for context.Context and we pass this to the request context
90+
func (ctx *BaseContext) Deadline() (deadline time.Time, ok bool) {
91+
return ctx.Req.Context().Deadline()
92+
}
93+
94+
// Done is part of the interface for context.Context and we pass this to the request context
95+
func (ctx *BaseContext) Done() <-chan struct{} {
96+
return ctx.Req.Context().Done()
97+
}
98+
99+
// Err is part of the interface for context.Context and we pass this to the request context
100+
func (ctx *BaseContext) Err() error {
101+
return ctx.Req.Context().Err()
102+
}
103+
104+
// Value is part of the interface for context.Context and we pass this to the request context
105+
func (ctx *BaseContext) Value(key interface{}) interface{} {
106+
return ctx.Req.Context().Value(key)
107+
}
108+
109+
// FIXME: We should differ Query and Form, currently we just use form as query
110+
// Currently to be compatible with macaron, we keep it.
111+
112+
// Query returns request form as string with default
113+
func (ctx *BaseContext) Query(key string, defaults ...string) string {
114+
return (*Forms)(ctx.Req).MustString(key, defaults...)
115+
}
116+
117+
// QueryTrim returns request form as string with default and trimmed spaces
118+
func (ctx *BaseContext) QueryTrim(key string, defaults ...string) string {
119+
return (*Forms)(ctx.Req).MustTrimmed(key, defaults...)
120+
}
121+
122+
// QueryStrings returns request form as strings with default
123+
func (ctx *BaseContext) QueryStrings(key string, defaults ...[]string) []string {
124+
return (*Forms)(ctx.Req).MustStrings(key, defaults...)
125+
}
126+
127+
// QueryInt returns request form as int with default
128+
func (ctx *BaseContext) QueryInt(key string, defaults ...int) int {
129+
return (*Forms)(ctx.Req).MustInt(key, defaults...)
130+
}
131+
132+
// QueryInt64 returns request form as int64 with default
133+
func (ctx *BaseContext) QueryInt64(key string, defaults ...int64) int64 {
134+
return (*Forms)(ctx.Req).MustInt64(key, defaults...)
135+
}
136+
137+
// QueryBool returns request form as bool with default
138+
func (ctx *BaseContext) QueryBool(key string, defaults ...bool) bool {
139+
return (*Forms)(ctx.Req).MustBool(key, defaults...)
140+
}
141+
142+
// QueryOptionalBool returns request form as OptionalBool with default
143+
func (ctx *BaseContext) QueryOptionalBool(key string, defaults ...util.OptionalBool) util.OptionalBool {
144+
return (*Forms)(ctx.Req).MustOptionalBool(key, defaults...)
145+
}
146+
147+
// JSON render content as JSON
148+
func (ctx *BaseContext) JSON(status int, content interface{}) {
149+
ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
150+
ctx.Resp.WriteHeader(status)
151+
if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
152+
log.Error("Render JSON failed: %v", err)
153+
ctx.Status(500)
154+
}
155+
}
156+
157+
// PlainText render content as plain text
158+
func (ctx *BaseContext) PlainText(status int, bs []byte) {
159+
ctx.Resp.WriteHeader(status)
160+
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
161+
if _, err := ctx.Resp.Write(bs); err != nil {
162+
log.Error("Render PlainText failed: %v", err)
163+
ctx.Status(500)
164+
}
165+
}

modules/context/context.go

Lines changed: 8 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
user_model "code.gitea.io/gitea/models/user"
2525
"code.gitea.io/gitea/modules/base"
2626
mc "code.gitea.io/gitea/modules/cache"
27-
"code.gitea.io/gitea/modules/json"
2827
"code.gitea.io/gitea/modules/log"
2928
"code.gitea.io/gitea/modules/setting"
3029
"code.gitea.io/gitea/modules/templates"
@@ -49,11 +48,9 @@ type Render interface {
4948

5049
// Context represents context of a request.
5150
type Context struct {
52-
Resp ResponseWriter
53-
Req *http.Request
54-
Data map[string]interface{} // data used by MVC templates
51+
*BaseContext
5552
PageData map[string]interface{} // data used by JavaScript modules in one page, it's `window.config.pageData`
56-
Render Render
53+
Render Render
5754
translation.Locale
5855
Cache cache.Cache
5956
csrf CSRF
@@ -166,12 +163,6 @@ func (ctx *Context) HasError() bool {
166163
return hasErr.(bool)
167164
}
168165

169-
// HasValue returns true if value of given name exists.
170-
func (ctx *Context) HasValue(name string) bool {
171-
_, ok := ctx.Data[name]
172-
return ok
173-
}
174-
175166
// RedirectToFirst redirects to first not empty URL
176167
func (ctx *Context) RedirectToFirst(location ...string) {
177168
for _, loc := range location {
@@ -377,15 +368,6 @@ func (ctx *Context) Error(status int, contents ...string) {
377368
http.Error(ctx.Resp, v, status)
378369
}
379370

380-
// JSON render content as JSON
381-
func (ctx *Context) JSON(status int, content interface{}) {
382-
ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
383-
ctx.Resp.WriteHeader(status)
384-
if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
385-
ctx.ServerError("Render JSON failed", err)
386-
}
387-
}
388-
389371
// Redirect redirect the request
390372
func (ctx *Context) Redirect(location string, status ...int) {
391373
code := http.StatusFound
@@ -483,64 +465,6 @@ func (ctx *Context) GetCookieFloat64(name string) float64 {
483465
return v
484466
}
485467

486-
// RemoteAddr returns the client machie ip address
487-
func (ctx *Context) RemoteAddr() string {
488-
return ctx.Req.RemoteAddr
489-
}
490-
491-
// Params returns the param on route
492-
func (ctx *Context) Params(p string) string {
493-
s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":")))
494-
return s
495-
}
496-
497-
// ParamsInt64 returns the param on route as int64
498-
func (ctx *Context) ParamsInt64(p string) int64 {
499-
v, _ := strconv.ParseInt(ctx.Params(p), 10, 64)
500-
return v
501-
}
502-
503-
// SetParams set params into routes
504-
func (ctx *Context) SetParams(k, v string) {
505-
chiCtx := chi.RouteContext(ctx)
506-
chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
507-
}
508-
509-
// Write writes data to webbrowser
510-
func (ctx *Context) Write(bs []byte) (int, error) {
511-
return ctx.Resp.Write(bs)
512-
}
513-
514-
// Written returns true if there are something sent to web browser
515-
func (ctx *Context) Written() bool {
516-
return ctx.Resp.Status() > 0
517-
}
518-
519-
// Status writes status code
520-
func (ctx *Context) Status(status int) {
521-
ctx.Resp.WriteHeader(status)
522-
}
523-
524-
// Deadline is part of the interface for context.Context and we pass this to the request context
525-
func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
526-
return ctx.Req.Context().Deadline()
527-
}
528-
529-
// Done is part of the interface for context.Context and we pass this to the request context
530-
func (ctx *Context) Done() <-chan struct{} {
531-
return ctx.Req.Context().Done()
532-
}
533-
534-
// Err is part of the interface for context.Context and we pass this to the request context
535-
func (ctx *Context) Err() error {
536-
return ctx.Req.Context().Err()
537-
}
538-
539-
// Value is part of the interface for context.Context and we pass this to the request context
540-
func (ctx *Context) Value(key interface{}) interface{} {
541-
return ctx.Req.Context().Value(key)
542-
}
543-
544468
// Handler represents a custom handler
545469
type Handler func(*Context)
546470

@@ -645,7 +569,12 @@ func Contexter() func(next http.Handler) http.Handler {
645569
var startTime = time.Now()
646570
var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
647571
var ctx = Context{
648-
Resp: NewResponse(resp),
572+
BaseContext: NewBaseContext(resp, req, map[string]interface{}{
573+
"CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
574+
"PageStartTime": startTime,
575+
"Link": link,
576+
"RunModeIsProd": setting.IsProd,
577+
}),
649578
Cache: mc.GetCache(),
650579
Locale: locale,
651580
Link: link,
@@ -655,12 +584,6 @@ func Contexter() func(next http.Handler) http.Handler {
655584
PullRequest: &PullRequest{},
656585
},
657586
Org: &Organization{},
658-
Data: map[string]interface{}{
659-
"CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
660-
"PageStartTime": startTime,
661-
"Link": link,
662-
"RunModeIsProd": setting.IsProd,
663-
},
664587
}
665588
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
666589
ctx.PageData = map[string]interface{}{}

modules/context/private.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import (
1010
)
1111

1212
// PrivateContext represents a context for private routes
13-
type PrivateContext struct {
14-
*Context
15-
}
13+
type PrivateContext = BaseContext
1614

1715
var (
1816
privateContextKey interface{} = "default_private_context"
@@ -32,12 +30,7 @@ func GetPrivateContext(req *http.Request) *PrivateContext {
3230
func PrivateContexter() func(http.Handler) http.Handler {
3331
return func(next http.Handler) http.Handler {
3432
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
35-
ctx := &PrivateContext{
36-
Context: &Context{
37-
Resp: NewResponse(w),
38-
Data: map[string]interface{}{},
39-
},
40-
}
33+
ctx := NewBaseContext(w, req, map[string]interface{}{})
4134
ctx.Req = WithPrivateContext(req, ctx)
4235
next.ServeHTTP(ctx.Resp, ctx.Req)
4336
})

routers/install/install.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,7 @@ func Init(next http.Handler) http.Handler {
6565
var locale = middleware.Locale(resp, req)
6666
var startTime = time.Now()
6767
var ctx = context.Context{
68-
Resp: context.NewResponse(resp),
69-
Flash: &middleware.Flash{},
70-
Locale: locale,
71-
Render: rnd,
72-
Session: session.GetSession(req),
73-
Data: map[string]interface{}{
68+
BaseContext: context.NewBaseContext(resp, req, map[string]interface{}{
7469
"Title": locale.Tr("install.install"),
7570
"PageIsInstall": true,
7671
"DbTypeNames": getDbTypeNames(),
@@ -84,7 +79,11 @@ func Init(next http.Handler) http.Handler {
8479
return time.Since(startTime).String()
8580
},
8681
"PasswordHashAlgorithms": user_model.AvailableHashAlgorithms,
87-
},
82+
}),
83+
Flash: &middleware.Flash{},
84+
Locale: locale,
85+
Render: rnd,
86+
Session: session.GetSession(req),
8887
}
8988
for _, lang := range translation.AllLangs() {
9089
if lang.Lang == locale.Language() {

0 commit comments

Comments
 (0)