65 lines
2.0 KiB
Go
65 lines
2.0 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"runtime/debug"
|
|
"strings"
|
|
|
|
"git.dev.m-and-m.ovh/mderasse/gocommon/commonctx"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type recoveryFuncSignature func(w http.ResponseWriter, r *http.Request)
|
|
|
|
type recoveryMiddleware struct {
|
|
handler http.Handler
|
|
respFunc recoveryFuncSignature
|
|
}
|
|
|
|
func (rm *recoveryMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
// log the error
|
|
ctx, log := commonctx.GetContextAndLoggerWithFields(r.Context(), logrus.Fields{
|
|
"error": err,
|
|
"stacktrace": fmt.Sprintf("%v\n%s", err, getStackrace()),
|
|
})
|
|
if log != nil {
|
|
log.Error("A Panic happened and has been caught")
|
|
}
|
|
r = r.WithContext(ctx)
|
|
// execute the recovery function
|
|
rm.respFunc(w, r)
|
|
}
|
|
}()
|
|
rm.handler.ServeHTTP(w, r)
|
|
}
|
|
|
|
func getStackrace() string {
|
|
stackLines := strings.Split(string(debug.Stack()), "\n")
|
|
// removing a few first line as they are either that function or the middleware
|
|
return strings.Join(stackLines[9:], "\n")
|
|
}
|
|
|
|
// NewRecoveryMiddleware will declare a new middleware on the provided handler. It will recover any panic and log it.
|
|
// By default, the middleware will return a default error object in json with a 500 Http code. That can be overide by providing a recoveryFunc.
|
|
func NewRecoveryMiddleware(h http.Handler, recoveryFunc recoveryFuncSignature) http.Handler {
|
|
recovery := &recoveryMiddleware{
|
|
handler: h,
|
|
respFunc: recoveryFunc,
|
|
}
|
|
if recovery.respFunc == nil {
|
|
recovery.respFunc = func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
//nolint:errcheck,gosec // we already are in a bad case scenario...
|
|
w.Write([]byte(
|
|
fmt.Sprintf("{\"code\":%d,\"message\":\"An error happened during the execution of your request.\"}", http.StatusInternalServerError),
|
|
))
|
|
}
|
|
}
|
|
return recovery
|
|
}
|