diff --git a/middleware/recovery.go b/middleware/recovery.go new file mode 100644 index 0000000..ad74dc0 --- /dev/null +++ b/middleware/recovery.go @@ -0,0 +1,54 @@ +package middleware + +import ( + "fmt" + "net/http" + + "git.dev.m-and-m.ovh/mderasse/gocommon/commonctx" +) + +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 + log := commonctx.GetLogger(r.Context()) + if log == nil { + fmt.Print("Impossible to log the panic as the logger is not part of the context\n") + } else { + // Probably have to catch a stacktrace + Info about the error + log.Error("A Panic happened and has been caught") + } + // execute the recovery function + rm.respFunc(w, r) + } + }() + rm.handler.ServeHTTP(w, r) +} + +// 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) *recoveryMiddleware { + 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) + w.Write([]byte( + fmt.Sprintf("{\"code\":%d,\"message\":\"An error happened during the execution of your request.\"}", http.StatusInternalServerError), + )) + } + } + return recovery +}