You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

recovery.go 3.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // Copyright 2020 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. package routes
  5. import (
  6. "fmt"
  7. "net/http"
  8. "code.gitea.io/gitea/modules/auth/sso"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/middlewares"
  11. "code.gitea.io/gitea/modules/setting"
  12. "code.gitea.io/gitea/modules/templates"
  13. "gitea.com/go-chi/session"
  14. "github.com/unrolled/render"
  15. )
  16. type dataStore struct {
  17. Data map[string]interface{}
  18. }
  19. func (d *dataStore) GetData() map[string]interface{} {
  20. return d.Data
  21. }
  22. // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
  23. // Although similar to macaron.Recovery() the main difference is that this error will be created
  24. // with the gitea 500 page.
  25. func Recovery() func(next http.Handler) http.Handler {
  26. var isDevelopment = setting.RunMode != "prod"
  27. return func(next http.Handler) http.Handler {
  28. rnd := render.New(render.Options{
  29. Extensions: []string{".tmpl"},
  30. Directory: "templates",
  31. Funcs: templates.NewFuncMap(),
  32. Asset: templates.GetAsset,
  33. AssetNames: templates.GetAssetNames,
  34. IsDevelopment: isDevelopment,
  35. })
  36. return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  37. defer func() {
  38. // Why we need this? The first recover will try to render a beautiful
  39. // error page for user, but the process can still panic again, then
  40. // we have to just recover twice and send a simple error page that
  41. // should not panic any more.
  42. defer func() {
  43. if err := recover(); err != nil {
  44. combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2)))
  45. log.Error(combinedErr)
  46. if isDevelopment {
  47. http.Error(w, combinedErr, 500)
  48. } else {
  49. http.Error(w, http.StatusText(500), 500)
  50. }
  51. }
  52. }()
  53. if err := recover(); err != nil {
  54. combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2)))
  55. log.Error("%v", combinedErr)
  56. lc := middlewares.Locale(w, req)
  57. sess := session.GetSession(req)
  58. var store = dataStore{
  59. Data: templates.Vars{
  60. "Language": lc.Language(),
  61. "CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
  62. "i18n": lc,
  63. },
  64. }
  65. // Get user from session if logged in.
  66. user, _ := sso.SignedInUser(req, w, &store, sess)
  67. if user != nil {
  68. store.Data["IsSigned"] = true
  69. store.Data["SignedUser"] = user
  70. store.Data["SignedUserID"] = user.ID
  71. store.Data["SignedUserName"] = user.Name
  72. store.Data["IsAdmin"] = user.IsAdmin
  73. } else {
  74. store.Data["SignedUserID"] = int64(0)
  75. store.Data["SignedUserName"] = ""
  76. }
  77. w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
  78. if setting.RunMode != "prod" {
  79. store.Data["ErrMsg"] = combinedErr
  80. }
  81. err := rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data))
  82. if err != nil {
  83. log.Error("%v", err)
  84. }
  85. }
  86. }()
  87. next.ServeHTTP(w, req)
  88. })
  89. }
  90. }