mirror of
				https://github.com/grafana/grafana.git
				synced 2025-02-25 18:55:37 -06:00 
			
		
		
		
	middleware: recovery handles panics in all handlers
Also, changes the order of the middleware so that recovery is after the gzip middleware. Otherwise, a 200 OK is returned instead of a 500 error.
This commit is contained in:
		@@ -146,12 +146,13 @@ func (hs *HttpServer) newMacaron() *macaron.Macaron {
 | 
			
		||||
	m := macaron.New()
 | 
			
		||||
 | 
			
		||||
	m.Use(middleware.Logger())
 | 
			
		||||
	m.Use(middleware.Recovery())
 | 
			
		||||
 | 
			
		||||
	if setting.EnableGzip {
 | 
			
		||||
		m.Use(middleware.Gziper())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m.Use(middleware.Recovery())
 | 
			
		||||
 | 
			
		||||
	for _, route := range plugins.StaticRoutes {
 | 
			
		||||
		pluginRoute := path.Join("/public/plugins/", route.PluginId)
 | 
			
		||||
		hs.log.Debug("Plugins: Adding route", "route", pluginRoute, "dir", route.Directory)
 | 
			
		||||
 
 | 
			
		||||
@@ -363,6 +363,7 @@ type scenarioContext struct {
 | 
			
		||||
	respJson       map[string]interface{}
 | 
			
		||||
	handlerFunc    handlerFunc
 | 
			
		||||
	defaultHandler macaron.Handler
 | 
			
		||||
	url            string
 | 
			
		||||
 | 
			
		||||
	req *http.Request
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -123,23 +123,22 @@ func Recovery() macaron.Handler {
 | 
			
		||||
					c.Data["ErrorMsg"] = string(stack)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				c.HTML(500, "500")
 | 
			
		||||
				ctx, ok := c.Data["ctx"].(*Context)
 | 
			
		||||
 | 
			
		||||
				// // Lookup the current responsewriter
 | 
			
		||||
				// val := c.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil)))
 | 
			
		||||
				// res := val.Interface().(http.ResponseWriter)
 | 
			
		||||
				//
 | 
			
		||||
				// // respond with panic message while in development mode
 | 
			
		||||
				// var body []byte
 | 
			
		||||
				// if setting.Env == setting.DEV {
 | 
			
		||||
				// 	res.Header().Set("Content-Type", "text/html")
 | 
			
		||||
				// 	body = []byte(fmt.Sprintf(panicHtml, err, err, stack))
 | 
			
		||||
				// }
 | 
			
		||||
				//
 | 
			
		||||
				// res.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
				// if nil != body {
 | 
			
		||||
				// 	res.Write(body)
 | 
			
		||||
				// }
 | 
			
		||||
				if ok && ctx.IsApiRequest() {
 | 
			
		||||
					resp := make(map[string]interface{})
 | 
			
		||||
					resp["message"] = "Internal Server Error - Check the Grafana server logs for the detailed error message."
 | 
			
		||||
 | 
			
		||||
					if c.Data["ErrorMsg"] != nil {
 | 
			
		||||
						resp["error"] = fmt.Sprintf("%v - %v", c.Data["Title"], c.Data["ErrorMsg"])
 | 
			
		||||
					} else {
 | 
			
		||||
						resp["error"] = c.Data["Title"]
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					c.JSON(500, resp)
 | 
			
		||||
				} else {
 | 
			
		||||
					c.HTML(500, "500")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								pkg/middleware/recovery_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								pkg/middleware/recovery_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-macaron/session"
 | 
			
		||||
	"github.com/grafana/grafana/pkg/bus"
 | 
			
		||||
	. "github.com/smartystreets/goconvey/convey"
 | 
			
		||||
	"gopkg.in/macaron.v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestRecoveryMiddleware(t *testing.T) {
 | 
			
		||||
	Convey("Given an api route that panics", t, func() {
 | 
			
		||||
		apiUrl := "/api/whatever"
 | 
			
		||||
		recoveryScenario("recovery middleware should return json", apiUrl, func(sc *scenarioContext) {
 | 
			
		||||
			sc.handlerFunc = PanicHandler
 | 
			
		||||
			sc.fakeReq("GET", apiUrl).exec()
 | 
			
		||||
			sc.req.Header.Add("content-type", "application/json")
 | 
			
		||||
 | 
			
		||||
			So(sc.resp.Code, ShouldEqual, 500)
 | 
			
		||||
			So(sc.respJson["message"], ShouldStartWith, "Internal Server Error - Check the Grafana server logs for the detailed error message.")
 | 
			
		||||
			So(sc.respJson["error"], ShouldStartWith, "Server Error")
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	Convey("Given a non-api route that panics", t, func() {
 | 
			
		||||
		apiUrl := "/whatever"
 | 
			
		||||
		recoveryScenario("recovery middleware should return html", apiUrl, func(sc *scenarioContext) {
 | 
			
		||||
			sc.handlerFunc = PanicHandler
 | 
			
		||||
			sc.fakeReq("GET", apiUrl).exec()
 | 
			
		||||
 | 
			
		||||
			So(sc.resp.Code, ShouldEqual, 500)
 | 
			
		||||
			So(sc.resp.Header().Get("content-type"), ShouldEqual, "text/html; charset=UTF-8")
 | 
			
		||||
			So(sc.resp.Body.String(), ShouldContainSubstring, "<title>Grafana - Error</title>")
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PanicHandler(c *Context) {
 | 
			
		||||
	panic("Handler has panicked")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func recoveryScenario(desc string, url string, fn scenarioFunc) {
 | 
			
		||||
	Convey(desc, func() {
 | 
			
		||||
		defer bus.ClearBusHandlers()
 | 
			
		||||
 | 
			
		||||
		sc := &scenarioContext{
 | 
			
		||||
			url: url,
 | 
			
		||||
		}
 | 
			
		||||
		viewsPath, _ := filepath.Abs("../../public/views")
 | 
			
		||||
 | 
			
		||||
		sc.m = macaron.New()
 | 
			
		||||
		sc.m.Use(Recovery())
 | 
			
		||||
 | 
			
		||||
		sc.m.Use(macaron.Renderer(macaron.RenderOptions{
 | 
			
		||||
			Directory: viewsPath,
 | 
			
		||||
			Delims:    macaron.Delims{Left: "[[", Right: "]]"},
 | 
			
		||||
		}))
 | 
			
		||||
 | 
			
		||||
		sc.m.Use(GetContextHandler())
 | 
			
		||||
		// mock out gc goroutine
 | 
			
		||||
		startSessionGC = func() {}
 | 
			
		||||
		sc.m.Use(Sessioner(&session.Options{}))
 | 
			
		||||
		sc.m.Use(OrgRedirect())
 | 
			
		||||
		sc.m.Use(AddDefaultResponseHeaders())
 | 
			
		||||
 | 
			
		||||
		sc.defaultHandler = func(c *Context) {
 | 
			
		||||
			sc.context = c
 | 
			
		||||
			if sc.handlerFunc != nil {
 | 
			
		||||
				sc.handlerFunc(sc.context)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sc.m.Get(url, sc.defaultHandler)
 | 
			
		||||
 | 
			
		||||
		fn(sc)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user