From 37dbd38e00bbe51d1aa2d46bf7a7f454034dd4a2 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Sun, 20 Aug 2023 09:36:43 -0700 Subject: echo: expose error handler --- TODO.md | 1 + echo/echo_default.go | 16 +++++++++++----- echo/error_handler.go | 39 ++++++++++++++++++++++++++------------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/TODO.md b/TODO.md index 4c794cf..ce21a0d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,2 +1,3 @@ [ ] Contribute crypto/ocsp/client.go to x/crypto [ ] secrets: add AEAD/AAD method for `associated_data` (base64 encoded) +[ ] echo: nicer HTML error messages (https://github.com/pkg/errors/blob/master/stack.go) diff --git a/echo/echo_default.go b/echo/echo_default.go index ff3c754..33ad015 100644 --- a/echo/echo_default.go +++ b/echo/echo_default.go @@ -71,9 +71,10 @@ type EchoConfig struct { type EchoWrapper struct { *echo.Echo - runner *service.AppRunner - autocert autocert.PrimingCertProvider - templateFS fs.FS + runner *service.AppRunner + autocert autocert.PrimingCertProvider + templateFS fs.FS + errorHandler ErrorHandler } // NewEchoWrapper creates a new instance of Echo and wraps it in an @@ -132,6 +133,10 @@ func (w *EchoWrapper) GetTemplateFS() fs.FS { return w.templateFS } +func (w *EchoWrapper) AddErrorHandler(h ContentErrorHandler, mime ...string) { + w.errorHandler.AddHandler(h, mime...) +} + func (w *EchoWrapper) Configure(c EchoConfig) error { w.configureAutocert(&c) @@ -207,12 +212,13 @@ func (w *EchoWrapper) configureTemplates(c *EchoConfig) error { return fmt.Errorf("Error loading template renderer: %w", err) } - w.HTTPErrorHandler = NewDefaultErrorHandler(tr).HandleError + w.errorHandler = NewDefaultErrorHandler(tr) w.Renderer = tr } else { - w.HTTPErrorHandler = NewNoHTMLErrorHandler().HandleError + w.errorHandler = NewNoHTMLErrorHandler() } + w.HTTPErrorHandler = w.errorHandler.HandleError w.templateFS = templates return nil } diff --git a/echo/error_handler.go b/echo/error_handler.go index 6874eff..a0bdd60 100644 --- a/echo/error_handler.go +++ b/echo/error_handler.go @@ -10,17 +10,22 @@ import ( "github.com/labstack/echo/v4" ) -// TODO: This should have some kind of HTML formatting of panics in debug mode - type ContentErrorHandler interface { Handle(echo.Context, *echo.HTTPError) error } +type ErrorHandler interface { + AddHandler(ContentErrorHandler, ...string) + HandleError(error, echo.Context) +} + type HTMLErrorHandler struct { r *TemplateRenderer fallback *template.Template } +var _ ContentErrorHandler = (*HTMLErrorHandler)(nil) + func NewHTMLErrorHandler(r *TemplateRenderer) *HTMLErrorHandler { t, err := template.New("").Parse("

Error

{{ .Message }}
\n") if err != nil { @@ -58,12 +63,16 @@ func (h *HTMLErrorHandler) Handle(c echo.Context, e *echo.HTTPError) error { type PlainTextErrorHandler struct{} +var _ ContentErrorHandler = (*PlainTextErrorHandler)(nil) + func (h *PlainTextErrorHandler) Handle(c echo.Context, e *echo.HTTPError) error { return c.String(e.Code, fmt.Sprintf("%s", e.Message)) } type JSONErrorHandler struct{} +var _ ContentErrorHandler = (*JSONErrorHandler)(nil) + func (h *JSONErrorHandler) Handle(c echo.Context, e *echo.HTTPError) error { code := e.Code message := e.Message @@ -80,28 +89,32 @@ func (h *JSONErrorHandler) Handle(c echo.Context, e *echo.HTTPError) error { type failsafeHandler struct{} +var _ ContentErrorHandler = (*failsafeHandler)(nil) + func (h *failsafeHandler) Handle(c echo.Context, e *echo.HTTPError) error { c.Echo().Logger.Error("Error while processing error page: %s", e) c.JSON(http.StatusInternalServerError, e) return nil } -type ErrorHandler struct { +type EchoErrorHandler struct { types []contenttype.MediaType typeMap map[string]ContentErrorHandler failsafeHandler ContentErrorHandler } -func NewErrorHandler() *ErrorHandler { - return &ErrorHandler{ +var _ ErrorHandler = (*EchoErrorHandler)(nil) + +func NewEchoErrorHandler() *EchoErrorHandler { + return &EchoErrorHandler{ types: []contenttype.MediaType{}, typeMap: map[string]ContentErrorHandler{}, failsafeHandler: &failsafeHandler{}, } } -func NewDefaultErrorHandler(tr *TemplateRenderer) *ErrorHandler { - h := NewErrorHandler() +func NewDefaultErrorHandler(tr *TemplateRenderer) *EchoErrorHandler { + h := NewEchoErrorHandler() // The order of these is important, especially when negotiating */* h.AddHandler(&JSONErrorHandler{}, "text/json", "application/json") @@ -111,8 +124,8 @@ func NewDefaultErrorHandler(tr *TemplateRenderer) *ErrorHandler { return h } -func NewNoHTMLErrorHandler() *ErrorHandler { - h := NewErrorHandler() +func NewNoHTMLErrorHandler() *EchoErrorHandler { + h := NewEchoErrorHandler() // The order of these is important, especially when negotiating */* h.AddHandler(&JSONErrorHandler{}, "text/json", "application/json") @@ -121,14 +134,14 @@ func NewNoHTMLErrorHandler() *ErrorHandler { return h } -func (h *ErrorHandler) AddHandler(eh ContentErrorHandler, contentTypes ...string) { +func (h *EchoErrorHandler) AddHandler(eh ContentErrorHandler, contentTypes ...string) { for _, ct := range contentTypes { h.types = append(h.types, contenttype.NewMediaType(ct)) h.typeMap[ct] = eh } } -func (h *ErrorHandler) castToHttpError(e error) *echo.HTTPError { +func (h *EchoErrorHandler) castToHttpError(e error) *echo.HTTPError { he, ok := e.(*echo.HTTPError) if ok { if he.Internal != nil { @@ -145,7 +158,7 @@ func (h *ErrorHandler) castToHttpError(e error) *echo.HTTPError { } } -func (h *ErrorHandler) negotiateContentType(r *http.Request) (ContentErrorHandler, error) { +func (h *EchoErrorHandler) negotiateContentType(r *http.Request) (ContentErrorHandler, error) { var err error ct, _, err := contenttype.GetAcceptableMediaType(r, h.types) @@ -163,7 +176,7 @@ func (h *ErrorHandler) negotiateContentType(r *http.Request) (ContentErrorHandle return handle, err } -func (h *ErrorHandler) HandleError(err error, c echo.Context) { +func (h *EchoErrorHandler) HandleError(err error, c echo.Context) { defer func() { if r := recover(); r != nil { if err, ok := r.(error); !ok { -- cgit v1.2.3