Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func main() {
}
// Setup
e := echo.New()
e.HTTPErrorHandler = revproxy.ErrorHandler
e.Pre(middleware.RequestID(), middleware.RemoveTrailingSlash(), revproxy.UiServerPathRewrite())
e.Use(middleware.Recover())
// Sentry middleware
Expand Down
63 changes: 63 additions & 0 deletions internal/revproxy/error_handler.go
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: maybe this file should be in internal/gwerrors/?

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package revproxy

import (
"errors"
"fmt"
"net/http"
"strings"

"github.com/labstack/echo/v4"
)

// The same error content as the data services API
const gwBaseErrorCode int = 6000
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 6000? (Do we have a rule for these numbers?)


type errorContent struct {
Code int `json:"code"`
Message string `json:"message"`
Detail string `json:"detail,omitempty"`
TraceId string `json:"trace_id,omitempty"`
}

type errorResponse struct {
Error errorContent `json:"error"`
}

// Adapted from https://echo.labstack.com/docs/error-handling
func ErrorHandler(err error, c echo.Context) {
if c.Response() != nil && c.Response().Committed {
return // response has been already sent to the client by handler or some middleware
}

accept := c.Request().Header.Get("Accept")
isHTML := strings.Contains(accept, echo.MIMETextHTML)
Comment on lines +32 to +33
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: weak content negotiation. But there is no standard library method to at least parse the accept header (not even try to perform content negotiation).
See: https://httpwg.org/specs/rfc9110.html#field.accept


// If the accept header is html then we fall back to the default handler (for now).
// If the acceptt header is not html or is blank we return json
if isHTML {
c.Echo().DefaultHTTPErrorHandler(err, c)
return
}

code := http.StatusInternalServerError
message := err.Error()
var he *echo.HTTPError
if errors.As(err, &he) { // find error in an error chain that implements HTTPError
if tmp := he.Code; tmp != 0 {
code = tmp
}
if msg := fmt.Sprintf("%v", he.Message); len(msg) > 0 {
message = msg
}
}

var cErr error
if c.Request().Method == http.MethodHead {
cErr = c.NoContent(code)
} else {
cErr = c.JSON(code, errorResponse{Error: errorContent{Code: gwBaseErrorCode + code, Message: message}})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we are not using Detail and TraceId. Should we open an issue to add them?

}
if cErr != nil {
c.Logger().Error("failed to send error page to client", "error", errors.Join(err, cErr))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use slog.Error() here. We configure the logger for slog but we don't set the Logger used by echo.

}
}
Loading