1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-02-02 11:34:20 +02:00

Better expected errors handling

This commit is contained in:
DarthSim 2018-11-20 18:53:44 +06:00
parent 4187646052
commit 6997e585ef
5 changed files with 41 additions and 32 deletions

View File

@ -4,7 +4,6 @@ import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"image"
"io"
@ -25,11 +24,13 @@ var (
imageTypeCtxKey = ctxKey("imageType")
imageDataCtxKey = ctxKey("imageData")
errSourceDimensionsTooBig = errors.New("Source image dimensions are too big")
errSourceResolutionTooBig = errors.New("Source image resolution are too big")
errSourceImageTypeNotSupported = errors.New("Source image type not supported")
errSourceDimensionsTooBig = newError(422, "Source image dimensions are too big", "Invalid source image")
errSourceResolutionTooBig = newError(422, "Source image resolution are too big", "Invalid source image")
errSourceImageTypeNotSupported = newError(422, "Source image type not supported", "Invalid source image")
)
const msgSourceImageIsUnreachable = "Source image is unreachable"
var downloadBufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
@ -78,7 +79,7 @@ func checkDimensions(width, height int) error {
func checkTypeAndDimensions(r io.Reader) (imageType, error) {
imgconf, imgtypeStr, err := image.DecodeConfig(r)
if err != nil {
return imageTypeUnknown, err
return imageTypeUnknown, errSourceImageTypeNotSupported
}
imgtype, imgtypeOk := imageTypes[imgtypeStr]
@ -105,12 +106,14 @@ func readAndCheckImage(ctx context.Context, res *http.Response) (context.Context
return ctx, cancel, err
}
if _, err = buf.ReadFrom(res.Body); err == nil {
ctx = context.WithValue(ctx, imageTypeCtxKey, imgtype)
ctx = context.WithValue(ctx, imageDataCtxKey, buf)
if _, err = buf.ReadFrom(res.Body); err != nil {
return ctx, cancel, newError(404, err.Error(), msgSourceImageIsUnreachable)
}
return ctx, cancel, err
ctx = context.WithValue(ctx, imageTypeCtxKey, imgtype)
ctx = context.WithValue(ctx, imageDataCtxKey, buf)
return ctx, cancel, nil
}
func downloadImage(ctx context.Context) (context.Context, context.CancelFunc, error) {
@ -127,20 +130,21 @@ func downloadImage(ctx context.Context) (context.Context, context.CancelFunc, er
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return ctx, func() {}, err
return ctx, func() {}, newError(404, err.Error(), msgSourceImageIsUnreachable)
}
req.Header.Set("User-Agent", conf.UserAgent)
res, err := downloadClient.Do(req)
if err != nil {
return ctx, func() {}, err
return ctx, func() {}, newError(404, err.Error(), msgSourceImageIsUnreachable)
}
defer res.Body.Close()
if res.StatusCode != 200 {
body, _ := ioutil.ReadAll(res.Body)
return ctx, func() {}, fmt.Errorf("Can't download image; Status: %d; %s", res.StatusCode, string(body))
msg := fmt.Sprintf("Can't download image; Status: %d; %s", res.StatusCode, string(body))
return ctx, func() {}, newError(404, msg, msgSourceImageIsUnreachable)
}
return readAndCheckImage(ctx, res)

View File

@ -13,17 +13,17 @@ type imgproxyError struct {
PublicMessage string
}
func (e imgproxyError) Error() string {
func (e *imgproxyError) Error() string {
return e.Message
}
func newError(status int, msg string, pub string) imgproxyError {
return imgproxyError{status, msg, pub}
func newError(status int, msg string, pub string) *imgproxyError {
return &imgproxyError{status, msg, pub}
}
func newUnexpectedError(err error, skip int) imgproxyError {
func newUnexpectedError(err error, skip int) *imgproxyError {
msg := fmt.Sprintf("Unexpected error: %s\n%s", err, stacktrace(skip+1))
return imgproxyError{500, msg, "Internal error"}
return &imgproxyError{500, msg, "Internal error"}
}
func stacktrace(skip int) string {

View File

@ -9,8 +9,6 @@ import (
"sync"
)
var errNotModified = newError(304, "Not modified", "Not modified")
type eTagCalc struct {
hash hash.Hash
enc *json.Encoder

View File

@ -141,13 +141,16 @@ const (
processingOptionsCtxKey = ctxKey("processingOptions")
urlTokenPlain = "plain"
maxClientHintDPR = 8
msgForbidden = "Forbidden"
msgInvalidURL = "Invalid URL"
)
var (
errInvalidImageURL = errors.New("Invalid image url")
errInvalidURLEncoding = errors.New("Invalid url encoding")
errInvalidPath = errors.New("Invalid path")
errResultingImageFormatIsNotSupported = errors.New("Resulting image format is not supported")
errInvalidPath = newError(404, "Invalid path", msgInvalidURL)
)
func (it imageType) String() string {
@ -836,7 +839,7 @@ func parsePath(ctx context.Context, r *http.Request) (context.Context, error) {
if !conf.AllowInsecure {
if err := validatePath(parts[0], strings.TrimPrefix(path, fmt.Sprintf("/%s", parts[0]))); err != nil {
return ctx, err
return ctx, newError(403, err.Error(), msgForbidden)
}
}
@ -858,13 +861,13 @@ func parsePath(ctx context.Context, r *http.Request) (context.Context, error) {
}
if err != nil {
return ctx, err
return ctx, newError(404, err.Error(), msgInvalidURL)
}
ctx = context.WithValue(ctx, imageURLCtxKey, imageURL)
ctx = context.WithValue(ctx, processingOptionsCtxKey, po)
return ctx, err
return ctx, nil
}
func getImageURL(ctx context.Context) string {

View File

@ -135,7 +135,7 @@ func respondWithImage(ctx context.Context, reqID string, r *http.Request, rw htt
logResponse(200, fmt.Sprintf("[%s] Processed in %s: %s; %+v", reqID, getTimerSince(ctx), getImageURL(ctx), po))
}
func respondWithError(reqID string, rw http.ResponseWriter, err imgproxyError) {
func respondWithError(reqID string, rw http.ResponseWriter, err *imgproxyError) {
logResponse(err.StatusCode, fmt.Sprintf("[%s] %s", reqID, err.Message))
rw.WriteHeader(err.StatusCode)
@ -147,6 +147,11 @@ func respondWithOptions(reqID string, rw http.ResponseWriter) {
rw.WriteHeader(200)
}
func respondWithNotModified(reqID string, rw http.ResponseWriter) {
logResponse(200, fmt.Sprintf("[%s] Not modified", reqID))
rw.WriteHeader(304)
}
func prepareAuthHeaderMust() []byte {
if len(authHeaderMust) == 0 {
authHeaderMust = []byte(fmt.Sprintf("Bearer %s", conf.Secret))
@ -180,11 +185,9 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
defer func() {
if rerr := recover(); rerr != nil {
if err, ok := rerr.(error); ok {
if err != errNotModified {
reportError(err, r)
}
reportError(err, r)
if ierr, ok := err.(imgproxyError); ok {
if ierr, ok := err.(*imgproxyError); ok {
respondWithError(reqID, rw, ierr)
} else {
respondWithError(reqID, rw, newUnexpectedError(err, 4))
@ -239,7 +242,7 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
ctx, err := parsePath(ctx, r)
if err != nil {
panic(newError(404, err.Error(), "Invalid image url"))
panic(err)
}
ctx, downloadcancel, err := downloadImage(ctx)
@ -251,7 +254,7 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if prometheusEnabled {
incrementPrometheusErrorsTotal("download")
}
panic(newError(404, err.Error(), "Image is unreachable"))
panic(err)
}
checkTimeout(ctx)
@ -261,7 +264,8 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("ETag", eTag)
if eTag == r.Header.Get("If-None-Match") {
panic(errNotModified)
respondWithNotModified(reqID, rw)
return
}
}
@ -275,7 +279,7 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if prometheusEnabled {
incrementPrometheusErrorsTotal("processing")
}
panic(newError(500, err.Error(), "Error occurred while processing image"))
panic(err)
}
checkTimeout(ctx)