Skip to content

Commit 4da1d97

Browse files
authored
Refactor HTTP request context (#17979)
1 parent 9d943bf commit 4da1d97

26 files changed

+138
-176
lines changed

modules/context/api.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -181,24 +181,24 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
181181
links := genAPILinks(ctx.Req.URL, total, pageSize, ctx.FormInt("page"))
182182

183183
if len(links) > 0 {
184-
ctx.Header().Set("Link", strings.Join(links, ","))
184+
ctx.RespHeader().Set("Link", strings.Join(links, ","))
185185
ctx.AppendAccessControlExposeHeaders("Link")
186186
}
187187
}
188188

189189
// SetTotalCountHeader set "X-Total-Count" header
190190
func (ctx *APIContext) SetTotalCountHeader(total int64) {
191-
ctx.Header().Set("X-Total-Count", fmt.Sprint(total))
191+
ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
192192
ctx.AppendAccessControlExposeHeaders("X-Total-Count")
193193
}
194194

195195
// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
196196
func (ctx *APIContext) AppendAccessControlExposeHeaders(names ...string) {
197-
val := ctx.Header().Get("Access-Control-Expose-Headers")
197+
val := ctx.RespHeader().Get("Access-Control-Expose-Headers")
198198
if len(val) != 0 {
199-
ctx.Header().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
199+
ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
200200
} else {
201-
ctx.Header().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
201+
ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
202202
}
203203
}
204204

modules/context/context.go

+60-97
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"context"
1010
"crypto/sha256"
1111
"encoding/hex"
12-
"fmt"
1312
"html"
1413
"html/template"
1514
"io"
@@ -156,6 +155,7 @@ func (ctx *Context) GetErrMsg() string {
156155
}
157156

158157
// HasError returns true if error occurs in form validation.
158+
// Attention: this function changes ctx.Data and ctx.Flash
159159
func (ctx *Context) HasError() bool {
160160
hasErr, ok := ctx.Data["HasError"]
161161
if !ok {
@@ -191,29 +191,25 @@ func (ctx *Context) RedirectToFirst(location ...string) {
191191
ctx.Redirect(setting.AppSubURL + "/")
192192
}
193193

194-
// HTML calls Context.HTML and converts template name to string.
194+
// HTML calls Context.HTML and renders the template to HTTP response
195195
func (ctx *Context) HTML(status int, name base.TplName) {
196196
log.Debug("Template: %s", name)
197-
var startTime = time.Now()
197+
tmplStartTime := time.Now()
198198
ctx.Data["TmplLoadTimes"] = func() string {
199-
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
199+
return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
200200
}
201201
if err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data); err != nil {
202202
if status == http.StatusInternalServerError && name == base.TplName("status/500") {
203-
ctx.PlainText(http.StatusInternalServerError, []byte("Unable to find status/500 template"))
203+
ctx.PlainText(http.StatusInternalServerError, "Unable to find status/500 template")
204204
return
205205
}
206206
ctx.ServerError("Render failed", err)
207207
}
208208
}
209209

210-
// HTMLString render content to a string but not http.ResponseWriter
211-
func (ctx *Context) HTMLString(name string, data interface{}) (string, error) {
210+
// RenderToString renders the template content to a string
211+
func (ctx *Context) RenderToString(name base.TplName, data map[string]interface{}) (string, error) {
212212
var buf strings.Builder
213-
var startTime = time.Now()
214-
ctx.Data["TmplLoadTimes"] = func() string {
215-
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
216-
}
217213
err := ctx.Render.HTML(&buf, 200, string(name), data)
218214
return buf.String(), err
219215
}
@@ -229,51 +225,47 @@ func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}
229225
}
230226

231227
// NotFound displays a 404 (Not Found) page and prints the given error, if any.
232-
func (ctx *Context) NotFound(title string, err error) {
233-
ctx.notFoundInternal(title, err)
228+
func (ctx *Context) NotFound(logMsg string, logErr error) {
229+
ctx.notFoundInternal(logMsg, logErr)
234230
}
235231

236-
func (ctx *Context) notFoundInternal(title string, err error) {
237-
if err != nil {
238-
log.ErrorWithSkip(2, "%s: %v", title, err)
232+
func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
233+
if logErr != nil {
234+
log.ErrorWithSkip(2, "%s: %v", logMsg, logErr)
239235
if !setting.IsProd {
240-
ctx.Data["ErrorMsg"] = err
236+
ctx.Data["ErrorMsg"] = logErr
241237
}
242238
}
243239

244-
// response simple meesage if Accept isn't text/html
245-
reqTypes, has := ctx.Req.Header["Accept"]
246-
if has && len(reqTypes) > 0 {
247-
notHTML := true
248-
for _, part := range reqTypes {
249-
if strings.Contains(part, "text/html") {
250-
notHTML = false
251-
break
252-
}
240+
// response simple message if Accept isn't text/html
241+
showHTML := false
242+
for _, part := range ctx.Req.Header["Accept"] {
243+
if strings.Contains(part, "text/html") {
244+
showHTML = true
245+
break
253246
}
247+
}
254248

255-
if notHTML {
256-
ctx.PlainText(404, []byte("Not found.\n"))
257-
return
258-
}
249+
if !showHTML {
250+
ctx.PlainText(http.StatusNotFound, "Not found.\n")
251+
return
259252
}
260253

261254
ctx.Data["IsRepo"] = ctx.Repo.Repository != nil
262255
ctx.Data["Title"] = "Page Not Found"
263256
ctx.HTML(http.StatusNotFound, base.TplName("status/404"))
264257
}
265258

266-
// ServerError displays a 500 (Internal Server Error) page and prints the given
267-
// error, if any.
268-
func (ctx *Context) ServerError(title string, err error) {
269-
ctx.serverErrorInternal(title, err)
259+
// ServerError displays a 500 (Internal Server Error) page and prints the given error, if any.
260+
func (ctx *Context) ServerError(logMsg string, logErr error) {
261+
ctx.serverErrorInternal(logMsg, logErr)
270262
}
271263

272-
func (ctx *Context) serverErrorInternal(title string, err error) {
273-
if err != nil {
274-
log.ErrorWithSkip(2, "%s: %v", title, err)
264+
func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
265+
if logErr != nil {
266+
log.ErrorWithSkip(2, "%s: %v", logMsg, logErr)
275267
if !setting.IsProd {
276-
ctx.Data["ErrorMsg"] = err
268+
ctx.Data["ErrorMsg"] = logErr
277269
}
278270
}
279271

@@ -282,37 +274,45 @@ func (ctx *Context) serverErrorInternal(title string, err error) {
282274
}
283275

284276
// NotFoundOrServerError use error check function to determine if the error
285-
// is about not found. It responses with 404 status code for not found error,
277+
// is about not found. It responds with 404 status code for not found error,
286278
// or error context description for logging purpose of 500 server error.
287-
func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool, err error) {
288-
if errck(err) {
289-
ctx.notFoundInternal(title, err)
279+
func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bool, err error) {
280+
if errCheck(err) {
281+
ctx.notFoundInternal(logMsg, err)
290282
return
291283
}
284+
ctx.serverErrorInternal(logMsg, err)
285+
}
292286

293-
ctx.serverErrorInternal(title, err)
287+
// PlainTextBytes renders bytes as plain text
288+
func (ctx *Context) PlainTextBytes(status int, bs []byte) {
289+
if (status/100 == 4) || (status/100 == 5) {
290+
log.Error("PlainTextBytes: %s", string(bs))
291+
}
292+
ctx.Resp.WriteHeader(status)
293+
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
294+
if _, err := ctx.Resp.Write(bs); err != nil {
295+
log.Error("Write bytes failed: %v", err)
296+
}
294297
}
295298

296-
// Header returns a header
297-
func (ctx *Context) Header() http.Header {
298-
return ctx.Resp.Header()
299+
// PlainText renders content as plain text
300+
func (ctx *Context) PlainText(status int, text string) {
301+
ctx.PlainTextBytes(status, []byte(text))
299302
}
300303

301-
// HandleText handles HTTP status code
302-
func (ctx *Context) HandleText(status int, title string) {
303-
if (status/100 == 4) || (status/100 == 5) {
304-
log.Error("%s", title)
305-
}
306-
ctx.PlainText(status, []byte(title))
304+
// RespHeader returns the response header
305+
func (ctx *Context) RespHeader() http.Header {
306+
return ctx.Resp.Header()
307307
}
308308

309309
// ServeContent serves content to http request
310310
func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
311-
modtime := time.Now()
311+
modTime := time.Now()
312312
for _, p := range params {
313313
switch v := p.(type) {
314314
case time.Time:
315-
modtime = v
315+
modTime = v
316316
}
317317
}
318318
ctx.Resp.Header().Set("Content-Description", "File Transfer")
@@ -323,16 +323,7 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
323323
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
324324
ctx.Resp.Header().Set("Pragma", "public")
325325
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
326-
http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r)
327-
}
328-
329-
// PlainText render content as plain text
330-
func (ctx *Context) PlainText(status int, bs []byte) {
331-
ctx.Resp.WriteHeader(status)
332-
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
333-
if _, err := ctx.Resp.Write(bs); err != nil {
334-
ctx.ServerError("Write bytes failed", err)
335-
}
326+
http.ServeContent(ctx.Resp, ctx.Req, name, modTime, r)
336327
}
337328

338329
// ServeFile serves given file to response.
@@ -386,7 +377,7 @@ func (ctx *Context) JSON(status int, content interface{}) {
386377
}
387378
}
388379

389-
// Redirect redirect the request
380+
// Redirect redirects the request
390381
func (ctx *Context) Redirect(location string, status ...int) {
391382
code := http.StatusFound
392383
if len(status) == 1 {
@@ -506,7 +497,7 @@ func (ctx *Context) SetParams(k, v string) {
506497
chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
507498
}
508499

509-
// Write writes data to webbrowser
500+
// Write writes data to web browser
510501
func (ctx *Context) Write(bs []byte) (int, error) {
511502
return ctx.Resp.Write(bs)
512503
}
@@ -544,10 +535,9 @@ func (ctx *Context) Value(key interface{}) interface{} {
544535
// Handler represents a custom handler
545536
type Handler func(*Context)
546537

547-
// enumerate all content
548-
var (
549-
contextKey interface{} = "default_context"
550-
)
538+
type contextKeyType struct{}
539+
540+
var contextKey interface{} = contextKeyType{}
551541

552542
// WithContext set up install context in request
553543
func WithContext(req *http.Request, ctx *Context) *http.Request {
@@ -570,31 +560,6 @@ func GetContextUser(req *http.Request) *user_model.User {
570560
return nil
571561
}
572562

573-
// SignedUserName returns signed user's name via context
574-
func SignedUserName(req *http.Request) string {
575-
if middleware.IsInternalPath(req) {
576-
return ""
577-
}
578-
if middleware.IsAPIPath(req) {
579-
ctx, ok := req.Context().Value(apiContextKey).(*APIContext)
580-
if ok {
581-
v := ctx.Data["SignedUserName"]
582-
if res, ok := v.(string); ok {
583-
return res
584-
}
585-
}
586-
} else {
587-
ctx, ok := req.Context().Value(contextKey).(*Context)
588-
if ok {
589-
v := ctx.Data["SignedUserName"]
590-
if res, ok := v.(string); ok {
591-
return res
592-
}
593-
}
594-
}
595-
return ""
596-
}
597-
598563
func getCsrfOpts() CsrfOptions {
599564
return CsrfOptions{
600565
Secret: setting.SecretKey,
@@ -727,8 +692,6 @@ func Contexter() func(next http.Handler) http.Handler {
727692

728693
ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken())
729694
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
730-
log.Debug("Session ID: %s", ctx.Session.ID())
731-
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
732695

733696
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
734697
ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome

modules/context/repo.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"context"
1010
"fmt"
1111
"io"
12+
"net/http"
1213
"net/url"
1314
"path"
1415
"strings"
@@ -305,14 +306,14 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
305306
username := ctx.Params(":username")
306307
reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
307308
if username == "" || reponame == "" {
308-
ctx.PlainText(400, []byte("invalid repository path"))
309+
ctx.PlainText(http.StatusBadRequest, "invalid repository path")
309310
return
310311
}
311-
ctx.PlainText(200, []byte(com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
312+
ctx.PlainText(http.StatusOK, com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
312313
map[string]string{
313314
"GoGetImport": ComposeGoGetImport(username, reponame),
314315
"CloneLink": repo_model.ComposeHTTPSCloneURL(username, reponame),
315-
})))
316+
}))
316317
}
317318

318319
// RedirectToRepo redirect to a differently-named repository
@@ -897,7 +898,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
897898
}
898899
// If short commit ID add canonical link header
899900
if len(refName) < 40 {
900-
ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
901+
ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
901902
util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
902903
}
903904
} else {

modules/templates/base.go

-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ func BaseVars() Vars {
4343
"EnableSwagger": setting.API.EnableSwagger,
4444
"EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,
4545
"PageStartTime": startTime,
46-
"TmplLoadTimes": func() string {
47-
return time.Since(startTime).String()
48-
},
4946
}
5047
}
5148

routers/api/v1/repo/commits.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,11 @@ func GetAllCommits(ctx *context.APIContext) {
205205
ctx.SetTotalCountHeader(commitsCountTotal)
206206

207207
// kept for backwards compatibility
208-
ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
209-
ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
210-
ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
211-
ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount))
212-
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
208+
ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
209+
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
210+
ctx.RespHeader().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
211+
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(pageCount))
212+
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
213213
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-Total", "X-PageCount", "X-HasMore")
214214

215215
ctx.JSON(http.StatusOK, &apiCommits)

routers/api/v1/repo/pull.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -1233,10 +1233,10 @@ func GetPullRequestCommits(ctx *context.APIContext) {
12331233
ctx.SetLinkHeader(totalNumberOfCommits, listOptions.PageSize)
12341234
ctx.SetTotalCountHeader(int64(totalNumberOfCommits))
12351235

1236-
ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
1237-
ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
1238-
ctx.Header().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
1239-
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
1236+
ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
1237+
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
1238+
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
1239+
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
12401240
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")
12411241

12421242
ctx.JSON(http.StatusOK, &apiCommits)

routers/api/v1/user/gpg_key.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func GetVerificationToken(ctx *context.APIContext) {
157157
// "$ref": "#/responses/notFound"
158158

159159
token := asymkey_model.VerificationToken(ctx.User, 1)
160-
ctx.PlainText(http.StatusOK, []byte(token))
160+
ctx.PlainText(http.StatusOK, token)
161161
}
162162

163163
// VerifyUserGPGKey creates new GPG key to given user by ID.

0 commit comments

Comments
 (0)