2024-01-16 19:08:14 +02:00
package api
import (
"encoding/json"
"net/http"
"runtime"
"github.com/gorilla/mux"
2024-02-01 06:34:07 +02:00
"zotregistry.dev/zot/pkg/log"
2024-01-16 19:08:14 +02:00
)
type Stack struct {
Frames [ ] Frame ` json:"stack" `
}
type Frame struct {
Name string ` json:"function" `
File string ` json:"file" `
Line int ` json:"line" `
}
// RecoveryHandler is a HTTP middleware that recovers from a panic.
// It logs the panic and its traceback in json format, writes http.StatusInternalServerError
// and continues to the next handler.
func RecoveryHandler ( log log . Logger ) mux . MiddlewareFunc {
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( response http . ResponseWriter , request * http . Request ) {
defer func ( ) {
if err := recover ( ) ; err != nil {
response . WriteHeader ( http . StatusInternalServerError )
stack := Stack {
Frames : getStacktrace ( ) ,
}
recoveredErr , ok := err . ( error )
if ok {
buf , err := json . Marshal ( stack )
if err == nil {
log . Error ( ) . Err ( recoveredErr ) . RawJSON ( "traceback" , buf ) . Msg ( "panic recovered" ) //nolint: check-logs
}
}
}
} ( )
// Process request
next . ServeHTTP ( response , request )
} )
}
}
func getStacktrace ( ) [ ] Frame {
stack := [ ] Frame { }
//nolint: varnamelen
pc := make ( [ ] uintptr , 64 )
n := runtime . Callers ( 0 , pc )
if n == 0 {
return [ ] Frame { }
}
// first three frames are from this file, don't need them.
pc = pc [ 3 : ]
frames := runtime . CallersFrames ( pc )
// loop to get frames.
for {
frame , more := frames . Next ( )
// store this frame
stack = append ( stack , Frame {
Name : frame . Function ,
File : frame . File ,
Line : frame . Line ,
} )
// check whether there are more frames to process after this one.
if ! more {
break
}
}
return stack
}