From e5d7d9e2aa742505f4314fd52cc33e437a3adbea Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Tue, 22 Sep 2026 13:18:55 +0100 Subject: [PATCH] fix: add recovery middleware --- pkg/fx/app.go | 3 +++ pkg/fx/app_test.go | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/pkg/fx/app.go b/pkg/fx/app.go index 9168b33..377d223 100644 --- a/pkg/fx/app.go +++ b/pkg/fx/app.go @@ -243,6 +243,9 @@ func validateRevocationPath(ctx context.Context, path []ucan.Delegation, didReso func newEchoServer(id identity.Identity, ucanServer *server.HTTPServer, revocations store.RevocationStore) *echo.Echo { e := echo.New() e.HideBanner = true + // Outermost middleware: a handler panic becomes a 500 rather than a + // dropped connection. + e.Use(middleware.Recover()) e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"*"}, AllowMethods: []string{echo.GET, echo.POST, echo.OPTIONS}, diff --git a/pkg/fx/app_test.go b/pkg/fx/app_test.go index 2f04f4d..68ec2d1 100644 --- a/pkg/fx/app_test.go +++ b/pkg/fx/app_test.go @@ -59,6 +59,21 @@ func TestEchoPublicRoutes(t *testing.T) { require.Equal(t, http.StatusOK, response.Code) } +// A handler panic must not escape to net/http, which drops the connection +// without ever writing a response. +func TestAHandlerPanicBecomesAServerError(t *testing.T) { + id, err := identity.New("", "") + require.NoError(t, err) + e := newEchoServer(id, server.NewHTTP(id), memory.New()) + e.GET("/panic", func(echo.Context) error { panic("handler exploded") }) + + response := httptest.NewRecorder() + require.NotPanics(t, func() { + e.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/panic", nil)) + }) + require.Equal(t, http.StatusInternalServerError, response.Code) +} + func TestParseFrom(t *testing.T) { from, err := parseFrom("0") require.NoError(t, err)