register the panic-recover handler after the activity logger

This commit is contained in:
Gani Georgiev
2024-10-18 13:47:10 +03:00
parent dbc074ee9a
commit 6f2fe91da5
6 changed files with 83 additions and 43 deletions

View File

@@ -28,9 +28,10 @@ func NewRouter(app core.App) (*router.Router[*core.RequestEvent], error) {
// register default middlewares
pbRouter.Bind(activityLogger())
pbRouter.Bind(panicRecover())
pbRouter.Bind(rateLimit())
pbRouter.Bind(loadAuthToken())
pbRouter.Bind(securityHeaders())
pbRouter.Bind(rateLimit())
pbRouter.Bind(BodyLimit(DefaultMaxBodySize))
apiGroup := pbRouter.Group("/api")

View File

@@ -1,10 +1,12 @@
package apis
import (
"errors"
"fmt"
"log/slog"
"net/http"
"net/url"
"runtime"
"slices"
"strings"
"time"
@@ -29,11 +31,14 @@ const (
DefaultWWWRedirectMiddlewarePriority = -99999
DefaultWWWRedirectMiddlewareId = "pbWWWRedirect"
DefaultActivityLoggerMiddlewarePriority = DefaultRateLimitMiddlewarePriority - 30
DefaultActivityLoggerMiddlewarePriority = DefaultRateLimitMiddlewarePriority - 40
DefaultActivityLoggerMiddlewareId = "pbActivityLogger"
DefaultSkipSuccessActivityLogMiddlewareId = "pbSkipSuccessActivityLog"
DefaultEnableAuthIdActivityLog = "pbEnableAuthIdActivityLog"
DefaultPanicRecoverMiddlewarePriority = DefaultRateLimitMiddlewarePriority - 30
DefaultPanicRecoverMiddlewareId = "pbPanicRecover"
DefaultLoadAuthTokenMiddlewarePriority = DefaultRateLimitMiddlewarePriority - 20
DefaultLoadAuthTokenMiddlewareId = "pbLoadAuthToken"
@@ -252,6 +257,39 @@ func wwwRedirect(redirectHosts []string) *hook.Handler[*core.RequestEvent] {
}
}
// panicRecover returns a default panic-recover handler.
func panicRecover() *hook.Handler[*core.RequestEvent] {
return &hook.Handler[*core.RequestEvent]{
Id: DefaultPanicRecoverMiddlewareId,
Priority: DefaultPanicRecoverMiddlewarePriority,
Func: func(e *core.RequestEvent) (err error) {
// panic-recover
defer func() {
recoverResult := recover()
if recoverResult == nil {
return
}
recoverErr, ok := recoverResult.(error)
if !ok {
recoverErr = fmt.Errorf("%v", recoverResult)
} else if errors.Is(recoverErr, http.ErrAbortHandler) {
// don't recover ErrAbortHandler so the response to the client can be aborted
panic(recoverResult)
}
stack := make([]byte, 2<<10) // 2 KB
length := runtime.Stack(stack, true)
err = e.InternalServerError("", fmt.Errorf("[PANIC RECOVER] %w %s", recoverErr, stack[:length]))
}()
err = e.Next()
return err
},
}
}
// securityHeaders middleware adds common security headers to the response.
//
// This middleware is registered by default for all routes.

View File

@@ -9,6 +9,45 @@ import (
"github.com/pocketbase/pocketbase/tests"
)
func TestPanicRecover(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "panic from route",
Method: http.MethodGet,
URL: "/my/test",
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
panic("123")
})
},
ExpectedStatus: 500,
ExpectedContent: []string{`"data":{}`},
ExpectedEvents: map[string]int{"*": 0},
},
{
Name: "panic from middleware",
Method: http.MethodGet,
URL: "/my/test",
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
return e.String(http.StatusOK, "test")
}).BindFunc(func(e *core.RequestEvent) error {
panic(123)
})
},
ExpectedStatus: 500,
ExpectedContent: []string{`"data":{}`},
ExpectedEvents: map[string]int{"*": 0},
},
}
for _, scenario := range scenarios {
scenario.Test(t)
}
}
func TestRequireGuestOnly(t *testing.T) {
t.Parallel()