diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2021-01-05 21:05:40 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-05 21:05:40 +0800 |
commit | 15a475b7dbcf7923d9518dff7764b20e404eb774 (patch) | |
tree | 8789f1f82c5e41345b442df4e58120bdd5f8bade /routers/routes | |
parent | 126c9331d6d8789563fae5d5bac2196d63fee0e8 (diff) | |
download | gitea-15a475b7dbcf7923d9518dff7764b20e404eb774.tar.gz gitea-15a475b7dbcf7923d9518dff7764b20e404eb774.zip |
Fix recovery middleware to render gitea style page. (#13857)
* Some changes to fix recovery
* Move Recovery to middlewares
* Remove trace code
* Fix lint
* add session middleware and remove dependent on macaron for sso
* Fix panic 500 page rendering
* Fix bugs
* Fix fmt
* Fix vendor
* recover unnecessary change
* Fix lint and addd some comments about the copied codes.
* Use util.StatDir instead of com.StatDir
Co-authored-by: 6543 <6543@obermui.de>
Diffstat (limited to 'routers/routes')
-rw-r--r-- | routers/routes/chi.go | 32 | ||||
-rw-r--r-- | routers/routes/recovery.go | 105 |
2 files changed, 118 insertions, 19 deletions
diff --git a/routers/routes/chi.go b/routers/routes/chi.go index 00689441b7..c0ac88957e 100644 --- a/routers/routes/chi.go +++ b/routers/routes/chi.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/routers" + "gitea.com/go-chi/session" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/prometheus/client_golang/prometheus" @@ -37,7 +38,7 @@ type routerLoggerOptions struct { } // SignedUserName returns signed user's name via context -// FIXME currently no any data stored on gin.Context but macaron.Context, so this will +// FIXME currently no any data stored on chi.Context but macaron.Context, so this will // return "" before we remove macaron totally func SignedUserName(req *http.Request) string { if v, ok := req.Context().Value("SignedUserName").(string); ok { @@ -97,24 +98,6 @@ func LoggerHandler(level log.Level) func(next http.Handler) http.Handler { } } -// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. -// Although similar to macaron.Recovery() the main difference is that this error will be created -// with the gitea 500 page. -func Recovery() func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - defer func() { - if err := recover(); err != nil { - combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) - http.Error(w, combinedErr, 500) - } - }() - - next.ServeHTTP(w, req) - }) - } -} - func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { if storageSetting.ServeDirect { @@ -202,6 +185,17 @@ func NewChi() chi.Router { c.Use(LoggerHandler(setting.RouterLogLevel)) } } + c.Use(session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + Domain: setting.SessionConfig.Domain, + })) + c.Use(Recovery()) if setting.EnableAccessLog { setupAccessLogger(c) diff --git a/routers/routes/recovery.go b/routers/routes/recovery.go new file mode 100644 index 0000000000..870a219c41 --- /dev/null +++ b/routers/routes/recovery.go @@ -0,0 +1,105 @@ +// Copyright 2020 The Gitea 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 routes + +import ( + "fmt" + "net/http" + + "code.gitea.io/gitea/modules/auth/sso" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" + + "gitea.com/go-chi/session" + "github.com/unrolled/render" +) + +type dataStore struct { + Data map[string]interface{} +} + +func (d *dataStore) GetData() map[string]interface{} { + return d.Data +} + +// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. +// Although similar to macaron.Recovery() the main difference is that this error will be created +// with the gitea 500 page. +func Recovery() func(next http.Handler) http.Handler { + var isDevelopment = setting.RunMode != "prod" + return func(next http.Handler) http.Handler { + rnd := render.New(render.Options{ + Extensions: []string{".tmpl"}, + Directory: "templates", + Funcs: templates.NewFuncMap(), + Asset: templates.GetAsset, + AssetNames: templates.GetAssetNames, + IsDevelopment: isDevelopment, + }) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + defer func() { + // Why we need this? The first recover will try to render a beautiful + // error page for user, but the process can still panic again, then + // we have to just recover twice and send a simple error page that + // should not panic any more. + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error(combinedErr) + if isDevelopment { + http.Error(w, combinedErr, 500) + } else { + http.Error(w, http.StatusText(500), 500) + } + } + }() + + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error("%v", combinedErr) + + lc := middlewares.Locale(w, req) + sess := session.GetSession(req) + + var store = dataStore{ + Data: templates.Vars{ + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, + }, + } + + // Get user from session if logged in. + user, _ := sso.SignedInUser(req, &store, sess) + if user != nil { + store.Data["IsSigned"] = true + store.Data["SignedUser"] = user + store.Data["SignedUserID"] = user.ID + store.Data["SignedUserName"] = user.Name + store.Data["IsAdmin"] = user.IsAdmin + } else { + store.Data["SignedUserID"] = int64(0) + store.Data["SignedUserName"] = "" + } + + w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) + + if setting.RunMode != "prod" { + store.Data["ErrMsg"] = combinedErr + } + err := rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + if err != nil { + log.Error("%v", err) + } + } + }() + + next.ServeHTTP(w, req) + }) + } +} |