1
0
mirror of https://github.com/labstack/echo.git synced 2026-03-12 15:46:17 +02:00
Files
echo/httperror.go

163 lines
5.1 KiB
Go

// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
package echo
import (
"errors"
"fmt"
"net/http"
)
// Following errors can produce HTTP status code by implementing HTTPStatusCoder interface
var (
ErrBadRequest = &httpError{http.StatusBadRequest} // 400
ErrUnauthorized = &httpError{http.StatusUnauthorized} // 401
ErrForbidden = &httpError{http.StatusForbidden} // 403
ErrNotFound = &httpError{http.StatusNotFound} // 404
ErrMethodNotAllowed = &httpError{http.StatusMethodNotAllowed} // 405
ErrRequestTimeout = &httpError{http.StatusRequestTimeout} // 408
ErrStatusRequestEntityTooLarge = &httpError{http.StatusRequestEntityTooLarge} // 413
ErrUnsupportedMediaType = &httpError{http.StatusUnsupportedMediaType} // 415
ErrTooManyRequests = &httpError{http.StatusTooManyRequests} // 429
ErrInternalServerError = &httpError{http.StatusInternalServerError} // 500
ErrBadGateway = &httpError{http.StatusBadGateway} // 502
ErrServiceUnavailable = &httpError{http.StatusServiceUnavailable} // 503
)
// Following errors fall into 500 (InternalServerError) category
var (
ErrValidatorNotRegistered = errors.New("validator not registered")
ErrRendererNotRegistered = errors.New("renderer not registered")
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
ErrCookieNotFound = errors.New("cookie not found")
ErrInvalidCertOrKeyType = errors.New("invalid cert or key type, must be string or []byte")
ErrInvalidListenerNetwork = errors.New("invalid listener network")
)
// HTTPStatusCoder is interface that errors can implement to produce status code for HTTP response
type HTTPStatusCoder interface {
StatusCode() int
}
// StatusCode returns status code from error if it implements HTTPStatusCoder interface.
// If error does not implement the interface it returns 0.
func StatusCode(err error) int {
var sc HTTPStatusCoder
if errors.As(err, &sc) {
return sc.StatusCode()
}
return 0
}
// ResolveResponseStatus returns the Response and HTTP status code that should be (or has been) sent for rw,
// given an optional error.
//
// This function is useful for middleware and handlers that need to figure out the HTTP status
// code to return based on the error that occurred or what was set in the response.
//
// Precedence rules:
// 1. If the response has already been committed, the committed status wins (err is ignored).
// 2. Otherwise, start with 200 OK (net/http default if WriteHeader is never called).
// 3. If the response has a non-zero suggested status, use it.
// 4. If err != nil, it overrides the suggested status:
// - StatusCode(err) if non-zero
// - otherwise 500 Internal Server Error.
func ResolveResponseStatus(rw http.ResponseWriter, err error) (resp *Response, status int) {
resp, _ = UnwrapResponse(rw)
// once committed (sent to the client), the wire status is fixed; err cannot change it.
if resp != nil && resp.Committed {
if resp.Status == 0 {
// unlikely path, but fall back to net/http implicit default if handler never calls WriteHeader
return resp, http.StatusOK
}
return resp, resp.Status
}
// net/http implicit default if handler never calls WriteHeader.
status = http.StatusOK
// suggested status written from middleware/handlers, if present.
if resp != nil && resp.Status != 0 {
status = resp.Status
}
// error overrides suggested status (matches typical Echo error-handler semantics).
if err != nil {
if s := StatusCode(err); s != 0 {
status = s
} else {
status = http.StatusInternalServerError
}
}
return resp, status
}
// NewHTTPError creates new instance of HTTPError
func NewHTTPError(code int, message string) *HTTPError {
return &HTTPError{
Code: code,
Message: message,
}
}
// HTTPError represents an error that occurred while handling a request.
type HTTPError struct {
// Code is status code for HTTP response
Code int `json:"-"`
Message string `json:"message"`
err error
}
// StatusCode returns status code for HTTP response
func (he *HTTPError) StatusCode() int {
return he.Code
}
// Error makes it compatible with `error` interface.
func (he *HTTPError) Error() string {
msg := he.Message
if msg == "" {
msg = http.StatusText(he.Code)
}
if he.err == nil {
return fmt.Sprintf("code=%d, message=%v", he.Code, msg)
}
return fmt.Sprintf("code=%d, message=%v, err=%v", he.Code, msg, he.err.Error())
}
// Wrap eturns new HTTPError with given errors wrapped inside
func (he HTTPError) Wrap(err error) error {
return &HTTPError{
Code: he.Code,
Message: he.Message,
err: err,
}
}
func (he *HTTPError) Unwrap() error {
return he.err
}
type httpError struct {
code int
}
func (he httpError) StatusCode() int {
return he.code
}
func (he httpError) Error() string {
return http.StatusText(he.code) // does not include status code
}
func (he httpError) Wrap(err error) error {
return &HTTPError{
Code: he.code,
Message: http.StatusText(he.code),
err: err,
}
}