mirror of
https://github.com/ente-io/ente.git
synced 2025-05-28 13:37:58 +00:00
89 lines
2.6 KiB
Go
89 lines
2.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"os"
|
|
"runtime/debug"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/gin-contrib/requestid"
|
|
"github.com/gin-gonic/gin"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// PanicRecover is similar to Gin's CustomRecoveryWithWriter but with custom logger.
|
|
// There's no easy way to plugin application logger instance & log custom attributes (like requestID)
|
|
func PanicRecover() gin.HandlerFunc {
|
|
return func(c *gin.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.
|
|
//
|
|
// Newer versions of gin might fix this (the PR is not yet
|
|
// merged as on writing):
|
|
// https://github.com/gin-gonic/gin/pull/2150
|
|
var brokenPipe bool
|
|
|
|
// Legacy check, not sure if it ever worked. Retaining this, can
|
|
// remove both when the gin PR is merged.
|
|
if ne, ok := err.(*net.OpError); ok {
|
|
if se, ok := ne.Err.(*os.SyscallError); ok {
|
|
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
|
|
brokenPipe = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Newer check. Also untested.
|
|
if !brokenPipe {
|
|
if re, ok := err.(error); ok {
|
|
if errors.Is(re, syscall.EPIPE) {
|
|
brokenPipe = true
|
|
}
|
|
}
|
|
}
|
|
|
|
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
|
requestData := strings.Split(string(httpRequest), "\r\n")
|
|
for idx, header := range requestData {
|
|
current := strings.Split(header, ":")
|
|
if current[0] == "Authorization" || current[0] == "X-Auth-Token" {
|
|
requestData[idx] = current[0] + ": *"
|
|
}
|
|
}
|
|
reqDataWithoutAuthHeaders := strings.Join(requestData, "\r\n")
|
|
var logWithAttributes = log.WithFields(log.Fields{
|
|
"request_data": reqDataWithoutAuthHeaders,
|
|
"req_id": requestid.Get(c),
|
|
"req_uri": c.Request.URL.Path,
|
|
"panic": err,
|
|
"broken_pipe": brokenPipe,
|
|
"stack": string(debug.Stack()),
|
|
})
|
|
if brokenPipe {
|
|
log.Warn("Panic Recovery: Broken pipe")
|
|
// If the connection is dead, we can't write a status to it.
|
|
c.Error(err.(error)) // nolint: errcheck
|
|
c.Abort()
|
|
return
|
|
}
|
|
if fmt.Sprintf("%v", err) == "client disconnected" {
|
|
// https://github.com/gin-gonic/gin/issues/2279#issuecomment-768349478
|
|
logWithAttributes.Warn("Client request cancelled")
|
|
c.Request.Context().Done()
|
|
} else {
|
|
logWithAttributes.Error("Recovery from Panic")
|
|
c.AbortWithStatus(http.StatusInternalServerError)
|
|
}
|
|
}
|
|
}()
|
|
c.Next()
|
|
}
|
|
}
|