mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-12-23 22:11:10 +02:00
* Refactored errors * Make monitoring and errorreport accept `errctx.Error` instead of `error` * Add server.Error; Remove category from errctx; Make HTTP handlers respond with *server.Error * Remove stackSkip from errctx.Wrap; Add errctx.WrapWithStackSkip
102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/errctx"
|
|
)
|
|
|
|
const (
|
|
errCategoryUnexpected = "unexpected"
|
|
errCategoryTimeout = "timeout"
|
|
errCategorySecurity = "security"
|
|
)
|
|
|
|
type (
|
|
// Error represents an error returned by RouteHandler with additional category information.
|
|
// It intentionally does not implement the error interface to avoid using it as a regular error.
|
|
Error struct {
|
|
Err errctx.Error
|
|
Category string
|
|
}
|
|
|
|
RouteNotDefinedError struct{ *errctx.TextError }
|
|
RequestCancelledError struct{ *errctx.TextError }
|
|
RequestTimeoutError struct{ *errctx.TextError }
|
|
InvalidSecretError struct{ *errctx.TextError }
|
|
)
|
|
|
|
// NewError creates a new [Error] instance wrapping the given errctx.Error and category.
|
|
// If err is nil, it returns nil.
|
|
//
|
|
// If the error or any of its causes is [context.DeadlineExceeded] or [context.Canceled],
|
|
// the category is set to "timeout" regardless of the provided category.
|
|
func NewError(err errctx.Error, category string) *Error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// If the error or any of its causes is context.DeadlineExceeded or context.Canceled,
|
|
// enforce the timeout category.
|
|
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
|
category = errCategoryTimeout
|
|
}
|
|
|
|
return &Error{
|
|
Err: err,
|
|
Category: category,
|
|
}
|
|
}
|
|
|
|
func newRouteNotDefinedError(path string) errctx.Error {
|
|
return RouteNotDefinedError{errctx.NewTextError(
|
|
fmt.Sprintf("Route for %s is not defined", path),
|
|
1,
|
|
errctx.WithStatusCode(http.StatusNotFound),
|
|
errctx.WithPublicMessage("Not found"),
|
|
errctx.WithShouldReport(false),
|
|
)}
|
|
}
|
|
|
|
func newRequestCancelledError(after time.Duration) errctx.Error {
|
|
return RequestCancelledError{errctx.NewTextError(
|
|
fmt.Sprintf("Request was cancelled after %v", after),
|
|
1,
|
|
errctx.WithStatusCode(499),
|
|
errctx.WithPublicMessage("Cancelled"),
|
|
errctx.WithShouldReport(false),
|
|
)}
|
|
}
|
|
|
|
func (e RequestCancelledError) Unwrap() error {
|
|
return context.Canceled
|
|
}
|
|
|
|
func newRequestTimeoutError(after time.Duration) errctx.Error {
|
|
return RequestTimeoutError{errctx.NewTextError(
|
|
fmt.Sprintf("Request was timed out after %v", after),
|
|
1,
|
|
errctx.WithStatusCode(http.StatusServiceUnavailable),
|
|
errctx.WithPublicMessage("Gateway Timeout"),
|
|
errctx.WithShouldReport(false),
|
|
)}
|
|
}
|
|
|
|
func (e RequestTimeoutError) Unwrap() error {
|
|
return context.DeadlineExceeded
|
|
}
|
|
|
|
func newInvalidSecretError() errctx.Error {
|
|
return InvalidSecretError{errctx.NewTextError(
|
|
"Invalid secret",
|
|
1,
|
|
errctx.WithStatusCode(http.StatusForbidden),
|
|
errctx.WithPublicMessage("Forbidden"),
|
|
errctx.WithShouldReport(false),
|
|
)}
|
|
}
|