mirror of
https://github.com/gogs/gogs.git
synced 2025-05-08 16:41:10 +00:00
We used to handle SSH and HTTP push separately which produces duplicated code, but now with post-receive hook, the process is unified to one single place and much cleaner. Thus, UpdateTask struct is removed. Narrow down the range of Git HTTP routes to reduce condufsing HTTP Basic Authentication window popup on browser. By detecting <old-commit, new-commit, ref-name> inside post-receive hook, Git HTTP doesn't need to read the whole content body anymore, which completely solve the RAM problem reported in #636.
203 lines
5.7 KiB
Go
203 lines
5.7 KiB
Go
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package context
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-macaron/cache"
|
|
"github.com/go-macaron/csrf"
|
|
"github.com/go-macaron/i18n"
|
|
"github.com/go-macaron/session"
|
|
log "gopkg.in/clog.v1"
|
|
"gopkg.in/macaron.v1"
|
|
|
|
"github.com/gogits/gogs/models"
|
|
"github.com/gogits/gogs/modules/auth"
|
|
"github.com/gogits/gogs/modules/base"
|
|
"github.com/gogits/gogs/modules/setting"
|
|
)
|
|
|
|
// Context represents context of a request.
|
|
type Context struct {
|
|
*macaron.Context
|
|
Cache cache.Cache
|
|
csrf csrf.CSRF
|
|
Flash *session.Flash
|
|
Session session.Store
|
|
|
|
User *models.User
|
|
IsSigned bool
|
|
IsBasicAuth bool
|
|
|
|
Repo *Repository
|
|
Org *Organization
|
|
}
|
|
|
|
// HasError returns true if error occurs in form validation.
|
|
func (ctx *Context) HasApiError() bool {
|
|
hasErr, ok := ctx.Data["HasError"]
|
|
if !ok {
|
|
return false
|
|
}
|
|
return hasErr.(bool)
|
|
}
|
|
|
|
func (ctx *Context) GetErrMsg() string {
|
|
return ctx.Data["ErrorMsg"].(string)
|
|
}
|
|
|
|
// HasError returns true if error occurs in form validation.
|
|
func (ctx *Context) HasError() bool {
|
|
hasErr, ok := ctx.Data["HasError"]
|
|
if !ok {
|
|
return false
|
|
}
|
|
ctx.Flash.ErrorMsg = ctx.Data["ErrorMsg"].(string)
|
|
ctx.Data["Flash"] = ctx.Flash
|
|
return hasErr.(bool)
|
|
}
|
|
|
|
// HasValue returns true if value of given name exists.
|
|
func (ctx *Context) HasValue(name string) bool {
|
|
_, ok := ctx.Data[name]
|
|
return ok
|
|
}
|
|
|
|
// HTML calls Context.HTML and converts template name to string.
|
|
func (ctx *Context) HTML(status int, name base.TplName) {
|
|
log.Trace("Template: %s", name)
|
|
ctx.Context.HTML(status, string(name))
|
|
}
|
|
|
|
// RenderWithErr used for page has form validation but need to prompt error to users.
|
|
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) {
|
|
if form != nil {
|
|
auth.AssignForm(form, ctx.Data)
|
|
}
|
|
ctx.Flash.ErrorMsg = msg
|
|
ctx.Data["Flash"] = ctx.Flash
|
|
ctx.HTML(200, tpl)
|
|
}
|
|
|
|
// Handle handles and logs error by given status.
|
|
func (ctx *Context) Handle(status int, title string, err error) {
|
|
switch status {
|
|
case 404:
|
|
ctx.Data["Title"] = "Page Not Found"
|
|
case 500:
|
|
ctx.Data["Title"] = "Internal Server Error"
|
|
log.Error(4, "%s: %v", title, err)
|
|
if !setting.ProdMode || (ctx.IsSigned && ctx.User.IsAdmin) {
|
|
ctx.Data["ErrorMsg"] = err
|
|
}
|
|
}
|
|
ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
|
|
}
|
|
|
|
// NotFound simply renders the 404 page.
|
|
func (ctx *Context) NotFound() {
|
|
ctx.Handle(404, "", nil)
|
|
}
|
|
|
|
// NotFoundOrServerError use error check function to determine if the error
|
|
// is about not found. It responses with 404 status code for not found error,
|
|
// or error context description for logging purpose of 500 server error.
|
|
func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool, err error) {
|
|
if errck(err) {
|
|
ctx.NotFound()
|
|
return
|
|
}
|
|
|
|
ctx.Handle(500, title, err)
|
|
}
|
|
|
|
func (ctx *Context) HandleText(status int, title string) {
|
|
ctx.PlainText(status, []byte(title))
|
|
}
|
|
|
|
func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
|
|
modtime := time.Now()
|
|
for _, p := range params {
|
|
switch v := p.(type) {
|
|
case time.Time:
|
|
modtime = v
|
|
}
|
|
}
|
|
ctx.Resp.Header().Set("Content-Description", "File Transfer")
|
|
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
|
|
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
|
|
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
|
|
ctx.Resp.Header().Set("Expires", "0")
|
|
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
|
ctx.Resp.Header().Set("Pragma", "public")
|
|
http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
|
|
}
|
|
|
|
// Contexter initializes a classic context for a request.
|
|
func Contexter() macaron.Handler {
|
|
return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {
|
|
ctx := &Context{
|
|
Context: c,
|
|
Cache: cache,
|
|
csrf: x,
|
|
Flash: f,
|
|
Session: sess,
|
|
Repo: &Repository{
|
|
PullRequest: &PullRequest{},
|
|
},
|
|
Org: &Organization{},
|
|
}
|
|
|
|
if len(setting.HTTP.AccessControlAllowOrigin) > 0 {
|
|
ctx.Header().Set("Access-Control-Allow-Origin", setting.HTTP.AccessControlAllowOrigin)
|
|
}
|
|
|
|
// Compute current URL for real-time change language.
|
|
ctx.Data["Link"] = setting.AppSubUrl + strings.TrimSuffix(ctx.Req.URL.Path, "/")
|
|
|
|
ctx.Data["PageStartTime"] = time.Now()
|
|
|
|
// Get user from session if logined.
|
|
ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Context, ctx.Session)
|
|
|
|
if ctx.User != nil {
|
|
ctx.IsSigned = true
|
|
ctx.Data["IsSigned"] = ctx.IsSigned
|
|
ctx.Data["SignedUser"] = ctx.User
|
|
ctx.Data["SignedUserID"] = ctx.User.ID
|
|
ctx.Data["SignedUserName"] = ctx.User.Name
|
|
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
|
} else {
|
|
ctx.Data["SignedUserID"] = 0
|
|
ctx.Data["SignedUserName"] = ""
|
|
}
|
|
|
|
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
|
|
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
|
|
if err := ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
|
ctx.Handle(500, "ParseMultipartForm", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx.Data["CsrfToken"] = x.GetToken()
|
|
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)
|
|
log.Trace("Session ID: %s", sess.ID())
|
|
log.Trace("CSRF Token: %v", ctx.Data["CsrfToken"])
|
|
|
|
ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
|
|
ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
|
|
ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
|
|
|
|
c.Map(ctx)
|
|
}
|
|
}
|