|
|
@@ -10,9 +10,12 @@ import (
|
|
|
"io"
|
|
|
"io/ioutil"
|
|
|
"log"
|
|
|
+ "net"
|
|
|
"net/http"
|
|
|
"net/http/httputil"
|
|
|
+ "os"
|
|
|
"runtime"
|
|
|
+ "syscall"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
@@ -37,16 +40,37 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc {
|
|
|
return func(c *Context) {
|
|
|
defer func() {
|
|
|
if err := recover(); err != nil {
|
|
|
+ // Check for a broken connection, as it is not really a
|
|
|
+ // condition that warrants a panic stack trace.
|
|
|
+ var brokenPipe bool
|
|
|
+ if ne, ok := err.(*net.OpError); ok {
|
|
|
+ if se, ok := ne.Err.(*os.SyscallError); ok {
|
|
|
+ if se.Err == syscall.EPIPE || se.Err == syscall.ECONNRESET {
|
|
|
+ brokenPipe = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
if logger != nil {
|
|
|
stack := stack(3)
|
|
|
- if IsDebugging() {
|
|
|
- httprequest, _ := httputil.DumpRequest(c.Request, false)
|
|
|
- logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
|
|
|
+ httprequest, _ := httputil.DumpRequest(c.Request, false)
|
|
|
+ if brokenPipe {
|
|
|
+ logger.Printf("%s\n%s%s", err, string(httprequest), reset)
|
|
|
+ } else if IsDebugging() {
|
|
|
+ logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s",
|
|
|
+ timeFormat(time.Now()), string(httprequest), err, stack, reset)
|
|
|
} else {
|
|
|
- logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s", timeFormat(time.Now()), err, stack, reset)
|
|
|
+ logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s",
|
|
|
+ timeFormat(time.Now()), err, stack, reset)
|
|
|
}
|
|
|
}
|
|
|
- c.AbortWithStatus(http.StatusInternalServerError)
|
|
|
+
|
|
|
+ // If the connection is dead, we can't write a status to it.
|
|
|
+ if brokenPipe {
|
|
|
+ c.Error(err.(error))
|
|
|
+ c.Abort()
|
|
|
+ } else {
|
|
|
+ c.AbortWithStatus(http.StatusInternalServerError)
|
|
|
+ }
|
|
|
}
|
|
|
}()
|
|
|
c.Next()
|