mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-10 20:22:31 +02:00
187 lines
4.4 KiB
Go
187 lines
4.4 KiB
Go
package fetcher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/ierrors"
|
|
"github.com/imgproxy/imgproxy/v3/security"
|
|
)
|
|
|
|
const msgSourceImageIsUnreachable = "Source image is unreachable"
|
|
|
|
type (
|
|
ImageRequestError struct{ error }
|
|
ImageRequstSchemeError string
|
|
ImagePartialResponseError string
|
|
ImageResponseStatusError string
|
|
ImageTooManyRedirectsError string
|
|
ImageRequestCanceledError struct{ error }
|
|
ImageRequestTimeoutError struct{ error }
|
|
|
|
NotModifiedError struct {
|
|
headers http.Header
|
|
}
|
|
|
|
httpError interface {
|
|
Timeout() bool
|
|
}
|
|
)
|
|
|
|
func newImageRequestError(err error) error {
|
|
return ierrors.Wrap(
|
|
ImageRequestError{err},
|
|
1,
|
|
ierrors.WithStatusCode(http.StatusNotFound),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImageRequestError) Unwrap() error {
|
|
return e.error
|
|
}
|
|
|
|
func newImageRequstSchemeError(scheme string) error {
|
|
return ierrors.Wrap(
|
|
ImageRequstSchemeError(fmt.Sprintf("Unknown scheme: %s", scheme)),
|
|
1,
|
|
ierrors.WithStatusCode(http.StatusNotFound),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImageRequstSchemeError) Error() string { return string(e) }
|
|
|
|
func newImagePartialResponseError(msg string) error {
|
|
return ierrors.Wrap(
|
|
ImagePartialResponseError(msg),
|
|
1,
|
|
ierrors.WithStatusCode(http.StatusNotFound),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImagePartialResponseError) Error() string { return string(e) }
|
|
|
|
func newImageResponseStatusError(status int, body string) error {
|
|
var msg string
|
|
|
|
if len(body) > 0 {
|
|
msg = fmt.Sprintf("Status: %d; %s", status, body)
|
|
} else {
|
|
msg = fmt.Sprintf("Status: %d", status)
|
|
}
|
|
|
|
statusCode := 404
|
|
if status >= 500 {
|
|
statusCode = 500
|
|
}
|
|
|
|
return ierrors.Wrap(
|
|
ImageResponseStatusError(msg),
|
|
1,
|
|
ierrors.WithStatusCode(statusCode),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImageResponseStatusError) Error() string { return string(e) }
|
|
|
|
func newImageTooManyRedirectsError(n int) error {
|
|
return ierrors.Wrap(
|
|
ImageTooManyRedirectsError(fmt.Sprintf("Stopped after %d redirects", n)),
|
|
1,
|
|
ierrors.WithStatusCode(http.StatusNotFound),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImageTooManyRedirectsError) Error() string { return string(e) }
|
|
|
|
func newImageRequestCanceledError(err error) error {
|
|
return ierrors.Wrap(
|
|
ImageRequestCanceledError{err},
|
|
2,
|
|
ierrors.WithStatusCode(499),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImageRequestCanceledError) Error() string {
|
|
return fmt.Sprintf("The image request is cancelled: %s", e.error)
|
|
}
|
|
|
|
func (e ImageRequestCanceledError) Unwrap() error { return e.error }
|
|
|
|
func newImageRequestTimeoutError(err error) error {
|
|
return ierrors.Wrap(
|
|
ImageRequestTimeoutError{err},
|
|
2,
|
|
ierrors.WithStatusCode(http.StatusGatewayTimeout),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e ImageRequestTimeoutError) Error() string {
|
|
return fmt.Sprintf("The image request timed out: %s", e.error)
|
|
}
|
|
|
|
func (e ImageRequestTimeoutError) Unwrap() error { return e.error }
|
|
|
|
func newNotModifiedError(headers http.Header) error {
|
|
return ierrors.Wrap(
|
|
NotModifiedError{headers},
|
|
1,
|
|
ierrors.WithStatusCode(http.StatusNotModified),
|
|
ierrors.WithPublicMessage("Not modified"),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
}
|
|
|
|
func (e NotModifiedError) Error() string { return "Not modified" }
|
|
|
|
func (e NotModifiedError) Headers() http.Header {
|
|
return e.headers
|
|
}
|
|
|
|
// NOTE: make private when we remove download functions from imagedata package
|
|
func WrapError(err error) error {
|
|
isTimeout := false
|
|
|
|
var secArrdErr security.SourceAddressError
|
|
|
|
switch {
|
|
case errors.Is(err, context.DeadlineExceeded):
|
|
isTimeout = true
|
|
case errors.Is(err, context.Canceled):
|
|
return newImageRequestCanceledError(err)
|
|
case errors.As(err, &secArrdErr):
|
|
return ierrors.Wrap(
|
|
err,
|
|
1,
|
|
ierrors.WithStatusCode(404),
|
|
ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
|
|
ierrors.WithShouldReport(false),
|
|
)
|
|
default:
|
|
if httpErr, ok := err.(httpError); ok {
|
|
isTimeout = httpErr.Timeout()
|
|
}
|
|
}
|
|
|
|
if isTimeout {
|
|
return newImageRequestTimeoutError(err)
|
|
}
|
|
|
|
return ierrors.Wrap(err, 1)
|
|
}
|