mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-12-23 22:11:10 +02:00
164 lines
3.8 KiB
Go
164 lines
3.8 KiB
Go
package imagefetcher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"reflect"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/errwrap"
|
|
"github.com/imgproxy/imgproxy/v3/security"
|
|
)
|
|
|
|
const msgSourceImageIsUnreachable = "Source image is unreachable"
|
|
|
|
type (
|
|
RequestError struct{ error }
|
|
RequestSchemeError struct{ error }
|
|
PartialResponseError struct{ error }
|
|
ResponseStatusError struct{ error }
|
|
TooManyRedirectsError struct{ error }
|
|
RequestCanceledError struct{ error }
|
|
RequestTimeoutError struct{ error }
|
|
)
|
|
|
|
type NotModifiedError struct {
|
|
headers http.Header
|
|
}
|
|
|
|
type httpError interface {
|
|
Timeout() bool
|
|
}
|
|
|
|
func newRequestError(err error) error {
|
|
return errwrap.From(
|
|
RequestError{err}, 1,
|
|
).
|
|
WithStatusCode(http.StatusNotFound).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newRequestSchemeError(scheme string) error {
|
|
return errwrap.From(
|
|
RequestSchemeError{fmt.Errorf("unknown scheme: %s", scheme)}, 1,
|
|
).
|
|
WithStatusCode(http.StatusNotFound).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newPartialResponseError(msg string) error {
|
|
return errwrap.From(
|
|
PartialResponseError{errors.New(msg)}, 1,
|
|
).
|
|
WithStatusCode(http.StatusNotFound).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newResponseStatusError(status int, body string) error {
|
|
var err error
|
|
|
|
if len(body) > 0 {
|
|
err = fmt.Errorf("status: %d; %s", status, body)
|
|
} else {
|
|
err = fmt.Errorf("status: %d", status)
|
|
}
|
|
|
|
statusCode := http.StatusNotFound
|
|
if status >= 500 {
|
|
statusCode = http.StatusInternalServerError
|
|
}
|
|
|
|
return errwrap.From(
|
|
ResponseStatusError{err}, 1,
|
|
).
|
|
WithStatusCode(statusCode).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newTooManyRedirectsError(n int) error {
|
|
return errwrap.From(
|
|
TooManyRedirectsError{fmt.Errorf("stopped after %d redirects", n)}, 1,
|
|
).
|
|
WithStatusCode(http.StatusNotFound).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newRequestCanceledError(err error) error {
|
|
// stack 2
|
|
return errwrap.Wrapf(
|
|
RequestCanceledError{err},
|
|
"the image request is cancelled",
|
|
).WithStatusCode(499).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newImageRequestTimeoutError(err error) error {
|
|
return errwrap.From(
|
|
RequestTimeoutError{err},
|
|
2,
|
|
).
|
|
WithStatusCode(http.StatusGatewayTimeout).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func newNotModifiedError(headers http.Header) error {
|
|
return errwrap.From(
|
|
NotModifiedError{headers},
|
|
1,
|
|
).
|
|
WithStatusCode(http.StatusNotModified).
|
|
WithPublicMessage("Not modified").
|
|
WithShouldReport(false)
|
|
}
|
|
|
|
func (e NotModifiedError) Error() string { return "not modified" }
|
|
|
|
func (e NotModifiedError) Headers() http.Header {
|
|
return e.headers
|
|
}
|
|
|
|
// Is performs comparison of two notModifiedError instances.
|
|
// Any error should be Comparable, http.Header is not comparable,
|
|
// hence, we need to compare headers manually.
|
|
func (nm NotModifiedError) Is(target error) bool {
|
|
m, ok := target.(NotModifiedError)
|
|
return ok && reflect.DeepEqual(nm.headers, m.headers)
|
|
}
|
|
|
|
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 newRequestCanceledError(err)
|
|
case errors.As(err, &secArrdErr):
|
|
return errwrap.From(err, 1).
|
|
WithStatusCode(404).
|
|
WithPublicMessage(msgSourceImageIsUnreachable).
|
|
WithShouldReport(false)
|
|
default:
|
|
if httpErr, ok := err.(httpError); ok {
|
|
isTimeout = httpErr.Timeout()
|
|
}
|
|
}
|
|
|
|
if isTimeout {
|
|
return errwrap.From(newImageRequestTimeoutError(err), 1)
|
|
}
|
|
|
|
// shift stack by 1 (NOTE: START WRAPPING FROM ORIGIN)
|
|
return errwrap.Wrap(err)
|
|
}
|