From 84ec23420db7f044fa78c439d9c289b42bd7d8c2 Mon Sep 17 00:00:00 2001 From: "tom.cat" Date: Tue, 15 Mar 2016 19:24:48 +0900 Subject: [PATCH] =?UTF-8?q?404=20=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 23 ++--- middleware/recovery/middleware.go | 163 ++++++++++++++++++++++++++++++ util/ldap.go | 1 - 3 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 middleware/recovery/middleware.go diff --git a/main.go b/main.go index ad393db..f18e05d 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( "time" "amuz.es/go/mnemonics/middleware/logging" + "amuz.es/go/mnemonics/middleware/recovery" "amuz.es/go/mnemonics/middleware/secure" "amuz.es/go/mnemonics/middleware/session" "amuz.es/go/mnemonics/middleware/static" @@ -41,29 +42,23 @@ func main() { setMaxProcs() store := session.NewCookieStore([]byte(util.GetRandomString(128))) - //gin.SetMode(gin.ReleaseMode) + gin.SetMode(gin.ReleaseMode) r := gin.New() // middleware settings r.Use(logging.Ginrus(time.RFC3339, true)) - - r.Use(gin.Recovery()) + // r.Use(recovery.Recovery()) // not found handler - r.NoRoute(func(c *gin.Context) { - code := http.StatusNotFound - c.HTML(code, "page_404.html", template.Context{ - "statusCode": code, - "statusMessage": http.StatusText(code), - "path": c.Request.URL.Path, - "method": c.Request.Method}) - }) + r.NoRoute(recovery.RecoveryHttpError(http.StatusNotFound)) + r.NoMethod(recovery.RecoveryHttpError(http.StatusMethodNotAllowed)) // suburl process path := r.Group("/") - + path.Use(recovery.RecoveryHTML()) // handlers api := path.Group("/api") + api.Use(recovery.RecoveryJSON()) api.Use(secure.Secure(secure.Options{ AllowedHosts: []string{}, @@ -78,9 +73,9 @@ func main() { BrowserXssFilter: true, ContentSecurityPolicy: "default-src 'self'", })) - api.Use(session.Sessions("mysession", store)) - api.GET("", route.Me) + api.Use(session.Sessions("mnemonics", store)) + api.GET("/auth", route.Me) path.GET("/self", route.Self) path.GET("/metric", route.Metric) path.GET("/health", func(c *gin.Context) { diff --git a/middleware/recovery/middleware.go b/middleware/recovery/middleware.go new file mode 100644 index 0000000..1c522d9 --- /dev/null +++ b/middleware/recovery/middleware.go @@ -0,0 +1,163 @@ +package recovery + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" + "runtime" + "strings" + + "amuz.es/go/mnemonics/middleware/template" + "amuz.es/go/mnemonics/util" + + "github.com/Sirupsen/logrus" + "github.com/gin-gonic/gin" +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +func recoveryInternal(code int, c *gin.Context, err error) map[string]interface{} { + logger := util.Log() + + stack := stack(3) + httprequest, _ := httputil.DumpRequest(c.Request, false) + path := c.Request.URL.Path + + info := map[string]interface{}{ + "statusCode": code, + "statusMessage": fmt.Sprintf("%s : %s", http.StatusText(code), err), + "path": path} + + detailInfo := map[string]interface{}{ + "statusCode": code, + "statusMessage": fmt.Sprintf("%s : %s", http.StatusText(code), err), + "path": path, + "method": c.Request.Method, + "request": string(httprequest), + "stack": string(stack), + "error": string(err.(error).Error())} + + logger.WithFields(logrus.Fields(detailInfo)).Error("[Recovery] panic recovered:\n") + return info +} + +func recoveryError(code int, c *gin.Context) map[string]interface{} { + path := c.Request.URL.Path + return map[string]interface{}{ + "statusCode": code, + "statusMessage": http.StatusText(code), + "path": path} + +} + +func RecoveryHttpError(code int) gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + info := recoveryError(code, c) + if strings.HasPrefix(c.Request.URL.Path, "/api") { + c.JSON(code, gin.H(info)) + } else { + c.HTML(code, "page_404.html", template.Context(info)) + } + c.Abort() + } + }() + c.Next() + } +} +func RecoveryJSON() gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + code := http.StatusInternalServerError + info := recoveryInternal(code, c, err.(error)) + c.JSON(code, gin.H(info)) + c.Abort() + } + }() + c.Next() + } +} + +func RecoveryHTML() gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + code := http.StatusInternalServerError + info := recoveryInternal(code, c, err.(error)) + c.HTML(code, "page_404.html", template.Context(info)) + c.Abort() + } + }() + c.Next() + } +} + +// stack returns a nicely formated stack frame, skipping skip frames +func stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contains dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { + name = name[lastslash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} diff --git a/util/ldap.go b/util/ldap.go index b79fec7..fb8c442 100644 --- a/util/ldap.go +++ b/util/ldap.go @@ -142,7 +142,6 @@ func (l ldapSource) Bind(uid string, password pwdStor) { dn := fmt.Sprintf("%s=%s,%s", l.user.uniqueAttributeName, uid, l.user.dn) err := l.connection.Bind(dn, password.GetPlainPassword()).(*ldap.Error) if err != nil { - Log().Errorf("dddd %d", err.ResultCode) panic(err) } }