Browse Source

Apply new errors processing to more code parts

DarthSim 2 months ago
parent
commit
3550042a16

+ 6 - 1
cookies/cookies.go

@@ -10,8 +10,13 @@ import (
 	"golang.org/x/net/publicsuffix"
 
 	"github.com/imgproxy/imgproxy/v3/config"
+	"github.com/imgproxy/imgproxy/v3/ierrors"
 )
 
+type cookieError string
+
+func (e cookieError) Error() string { return string(e) }
+
 type anyCookieJarEntry struct {
 	Name   string
 	Value  string
@@ -75,7 +80,7 @@ func JarFromRequest(r *http.Request) (jar http.CookieJar, err error) {
 	if !config.CookiePassthroughAll {
 		if len(config.CookieBaseURL) > 0 {
 			if cookieBase, err = url.Parse(config.CookieBaseURL); err != nil {
-				return nil, fmt.Errorf("can't parse cookie base URL: %s", err)
+				return nil, ierrors.Wrap(cookieError(fmt.Sprintf("can't parse cookie base URL: %s", err)), 0)
 			}
 		}
 

+ 1 - 2
imagedata/download.go

@@ -3,7 +3,6 @@ package imagedata
 import (
 	"compress/gzip"
 	"context"
-	"fmt"
 	"io"
 	"net/http"
 	"net/http/cookiejar"
@@ -105,7 +104,7 @@ func initDownloading() error {
 		CheckRedirect: func(req *http.Request, via []*http.Request) error {
 			redirects := len(via)
 			if redirects >= config.MaxRedirects {
-				return fmt.Errorf("stopped after %d redirects", redirects)
+				return newImageTooManyRedirectsError(redirects)
 			}
 			return nil
 		},

+ 19 - 6
imagedata/errors.go

@@ -11,12 +11,13 @@ import (
 )
 
 type (
-	ImageRequestError         struct{ error }
-	ImageRequstSchemeError    string
-	ImagePartialResponseError string
-	ImageResponseStatusError  string
-	ImageRequestCanceledError struct{ error }
-	ImageRequestTimeoutError  struct{ error }
+	ImageRequestError          struct{ error }
+	ImageRequstSchemeError     string
+	ImagePartialResponseError  string
+	ImageResponseStatusError   string
+	ImageTooManyRedirectsError string
+	ImageRequestCanceledError  struct{ error }
+	ImageRequestTimeoutError   struct{ error }
 
 	NotModifiedError struct {
 		headers map[string]string
@@ -90,6 +91,18 @@ func newImageResponseStatusError(status int, body string) error {
 
 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},

+ 1 - 1
imagemeta/heif.go

@@ -185,7 +185,7 @@ func heifReadHldr(r io.Reader, boxDataSize uint64) error {
 	}
 
 	if !bytes.Equal(data[8:12], heifPict) {
-		return fmt.Errorf("Invalid handler. Expected: pict, actual: %s", data[8:12])
+		return newFormatError("HEIF", fmt.Sprintf("Invalid handler. Expected: pict, actual: %s", data[8:12]))
 	}
 
 	return nil

+ 7 - 8
options/url.go

@@ -2,7 +2,6 @@ package options
 
 import (
 	"encoding/base64"
-	"errors"
 	"fmt"
 	"net/url"
 	"strings"
@@ -35,11 +34,11 @@ func decodeBase64URL(parts []string) (string, string, error) {
 	urlParts := strings.Split(encoded, ".")
 
 	if len(urlParts[0]) == 0 {
-		return "", "", errors.New("Image URL is empty")
+		return "", "", newInvalidURLError("Image URL is empty")
 	}
 
 	if len(urlParts) > 2 {
-		return "", "", fmt.Errorf("Multiple formats are specified: %s", encoded)
+		return "", "", newInvalidURLError("Multiple formats are specified: %s", encoded)
 	}
 
 	if len(urlParts) == 2 && len(urlParts[1]) > 0 {
@@ -48,7 +47,7 @@ func decodeBase64URL(parts []string) (string, string, error) {
 
 	imageURL, err := base64.RawURLEncoding.DecodeString(strings.TrimRight(urlParts[0], "="))
 	if err != nil {
-		return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
+		return "", "", newInvalidURLError("Invalid url encoding: %s", encoded)
 	}
 
 	return preprocessURL(string(imageURL)), format, nil
@@ -61,11 +60,11 @@ func decodePlainURL(parts []string) (string, string, error) {
 	urlParts := strings.Split(encoded, "@")
 
 	if len(urlParts[0]) == 0 {
-		return "", "", errors.New("Image URL is empty")
+		return "", "", newInvalidURLError("Image URL is empty")
 	}
 
 	if len(urlParts) > 2 {
-		return "", "", fmt.Errorf("Multiple formats are specified: %s", encoded)
+		return "", "", newInvalidURLError("Multiple formats are specified: %s", encoded)
 	}
 
 	if len(urlParts) == 2 && len(urlParts[1]) > 0 {
@@ -74,7 +73,7 @@ func decodePlainURL(parts []string) (string, string, error) {
 
 	unescaped, err := url.PathUnescape(urlParts[0])
 	if err != nil {
-		return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
+		return "", "", newInvalidURLError("Invalid url encoding: %s", encoded)
 	}
 
 	return preprocessURL(unescaped), format, nil
@@ -82,7 +81,7 @@ func decodePlainURL(parts []string) (string, string, error) {
 
 func DecodeURL(parts []string) (string, string, error) {
 	if len(parts) == 0 {
-		return "", "", errors.New("Image URL is empty")
+		return "", "", newInvalidURLError("Image URL is empty")
 	}
 
 	if parts[0] == urlTokenPlain && len(parts) > 1 {

+ 25 - 0
processing/errors.go

@@ -0,0 +1,25 @@
+package processing
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/imgproxy/imgproxy/v3/ierrors"
+	"github.com/imgproxy/imgproxy/v3/imagetype"
+)
+
+type (
+	SaveFormatError string
+)
+
+func newSaveFormatError(format imagetype.Type) error {
+	return ierrors.Wrap(
+		SaveFormatError(fmt.Sprintf("Can't save %s, probably not supported by your libvips", format)),
+		1,
+		ierrors.WithStatusCode(http.StatusUnprocessableEntity),
+		ierrors.WithPublicMessage("Invalid URL"),
+		ierrors.WithShouldReport(false),
+	)
+}
+
+func (e SaveFormatError) Error() string { return string(e) }

+ 1 - 2
processing/processing.go

@@ -3,7 +3,6 @@ package processing
 import (
 	"context"
 	"errors"
-	"fmt"
 	"runtime"
 	"strconv"
 
@@ -301,7 +300,7 @@ func ProcessImage(ctx context.Context, imgdata *imagedata.ImageData, po *options
 	}
 
 	if !vips.SupportsSave(po.Format) {
-		return nil, fmt.Errorf("Can't save %s, probably not supported by your libvips", po.Format)
+		return nil, newSaveFormatError(po.Format)
 	}
 
 	if po.Format.SupportsAnimationSave() && animated {

+ 1 - 1
transport/azure/azure.go

@@ -109,7 +109,7 @@ func (t transport) RoundTrip(req *http.Request) (*http.Response, error) {
 	if r := req.Header.Get("Range"); len(r) != 0 {
 		start, end, err := httprange.Parse(r)
 		if err != nil {
-			return httprange.InvalidHTTPRangeResponse(req), err
+			return httprange.InvalidHTTPRangeResponse(req), nil
 		}
 
 		if end != 0 {

+ 2 - 1
transport/gcs/gcs.go

@@ -16,6 +16,7 @@ import (
 
 	"github.com/imgproxy/imgproxy/v3/config"
 	"github.com/imgproxy/imgproxy/v3/httprange"
+	"github.com/imgproxy/imgproxy/v3/ierrors"
 	defaultTransport "github.com/imgproxy/imgproxy/v3/transport"
 	"github.com/imgproxy/imgproxy/v3/transport/common"
 	"github.com/imgproxy/imgproxy/v3/transport/notmodified"
@@ -71,7 +72,7 @@ func New() (http.RoundTripper, error) {
 	client, err = storage.NewClient(context.Background(), opts...)
 
 	if err != nil {
-		return nil, fmt.Errorf("Can't create GCS client: %s", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("Can't create GCS client"))
 	}
 
 	return transport{client}, nil

+ 7 - 7
transport/s3/s3.go

@@ -3,7 +3,6 @@ package s3
 import (
 	"context"
 	"errors"
-	"fmt"
 	"io"
 	"net/http"
 	"strconv"
@@ -23,6 +22,7 @@ import (
 	"github.com/aws/aws-sdk-go-v2/service/sts"
 
 	"github.com/imgproxy/imgproxy/v3/config"
+	"github.com/imgproxy/imgproxy/v3/ierrors"
 	defaultTransport "github.com/imgproxy/imgproxy/v3/transport"
 	"github.com/imgproxy/imgproxy/v3/transport/common"
 )
@@ -48,7 +48,7 @@ type transport struct {
 func New() (http.RoundTripper, error) {
 	conf, err := awsConfig.LoadDefaultConfig(context.Background())
 	if err != nil {
-		return nil, fmt.Errorf("can't load AWS S3 config: %s", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("can't load AWS S3 config"))
 	}
 
 	trans, err := defaultTransport.New(false)
@@ -90,7 +90,7 @@ func New() (http.RoundTripper, error) {
 
 	client, err := createClient(conf, clientOptions)
 	if err != nil {
-		return nil, fmt.Errorf("can't create S3 client: %s", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("can't create S3 client"))
 	}
 
 	return &transport{
@@ -248,7 +248,7 @@ func (t *transport) getClient(ctx context.Context, bucket string) (s3Client, err
 
 	region, err := s3Manager.GetBucketRegion(ctx, t.defaultClient, bucket)
 	if err != nil {
-		return nil, fmt.Errorf("can't get bucket region: %s", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("can't get bucket region"))
 	}
 
 	if len(region) == 0 {
@@ -265,7 +265,7 @@ func (t *transport) getClient(ctx context.Context, bucket string) (s3Client, err
 
 	client, err = createClient(conf, t.clientOptions)
 	if err != nil {
-		return nil, fmt.Errorf("can't create regional S3 client: %s", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("can't create regional S3 client"))
 	}
 
 	t.clientsByRegion[region] = client
@@ -295,11 +295,11 @@ func createClient(conf aws.Config, opts []func(*s3.Options)) (s3Client, error) {
 func handleError(req *http.Request, err error) (*http.Response, error) {
 	var rerr *awsHttp.ResponseError
 	if !errors.As(err, &rerr) {
-		return nil, err
+		return nil, ierrors.Wrap(err, 0)
 	}
 
 	if rerr.Response == nil || rerr.Response.StatusCode < 100 || rerr.Response.StatusCode == 301 {
-		return nil, err
+		return nil, ierrors.Wrap(err, 0)
 	}
 
 	body := strings.NewReader(err.Error())

+ 3 - 3
transport/swift/swift.go

@@ -3,7 +3,6 @@ package swift
 import (
 	"context"
 	"errors"
-	"fmt"
 	"io"
 	"net/http"
 	"strings"
@@ -12,6 +11,7 @@ import (
 	"github.com/ncw/swift/v2"
 
 	"github.com/imgproxy/imgproxy/v3/config"
+	"github.com/imgproxy/imgproxy/v3/ierrors"
 	defaultTransport "github.com/imgproxy/imgproxy/v3/transport"
 	"github.com/imgproxy/imgproxy/v3/transport/common"
 	"github.com/imgproxy/imgproxy/v3/transport/notmodified"
@@ -44,7 +44,7 @@ func New() (http.RoundTripper, error) {
 	err = c.Authenticate(ctx)
 
 	if err != nil {
-		return nil, fmt.Errorf("swift authentication error: %s", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("swift authentication error"))
 	}
 
 	return transport{con: c}, nil
@@ -91,7 +91,7 @@ func (t transport) RoundTrip(req *http.Request) (resp *http.Response, err error)
 			}, nil
 		}
 
-		return nil, fmt.Errorf("error opening object: %v", err)
+		return nil, ierrors.Wrap(err, 0, ierrors.WithPrefix("error opening object"))
 	}
 
 	if config.ETagEnabled {

+ 8 - 13
vips/bmp.go

@@ -9,7 +9,6 @@ import "C"
 import (
 	"bytes"
 	"encoding/binary"
-	"errors"
 	"io"
 	"unsafe"
 
@@ -36,10 +35,6 @@ type bmpHeader struct {
 	colorImportant  uint32
 }
 
-// errBmpUnsupported means that the input BMP image uses a valid but unsupported
-// feature.
-var errBmpUnsupported = errors.New("unsupported BMP image")
-
 func readUint16(b []byte) uint16 {
 	return uint16(b[0]) | uint16(b[1])<<8
 }
@@ -264,7 +259,7 @@ Loop:
 // If topDown is false, the image rows will be read bottom-up.
 func (img *Image) decodeBmpRGB(r io.Reader, width, height, bands int, topDown, noAlpha bool) error {
 	if bands != 3 && bands != 4 {
-		return errBmpUnsupported
+		return newVipsError("unsupported BMP image")
 	}
 
 	imgBands := 3
@@ -381,13 +376,13 @@ func (img *Image) loadBmp(data []byte, noAlpha bool) error {
 	}
 
 	if string(b[:2]) != "BM" {
-		return errors.New("not a BMP image")
+		return newVipsError("not a BMP image")
 	}
 
 	offset := readUint32(b[10:14])
 	infoLen := readUint32(b[14:18])
 	if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
-		return errBmpUnsupported
+		return newVipsError("unsupported BMP image")
 	}
 
 	if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
@@ -405,14 +400,14 @@ func (img *Image) loadBmp(data []byte, noAlpha bool) error {
 		height, topDown = -height, true
 	}
 	if width <= 0 || height <= 0 {
-		return errBmpUnsupported
+		return newVipsError("unsupported BMP image")
 	}
 
 	// We only support 1 plane and 8, 24 or 32 bits per pixel
 	planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
 
 	if planes != 1 {
-		return errBmpUnsupported
+		return newVipsError("unsupported BMP image")
 	}
 
 	rle := false
@@ -447,10 +442,10 @@ func (img *Image) loadBmp(data []byte, noAlpha bool) error {
 		case bpp == 32 && rmask == 0xff0000 && gmask == 0xff00 && bmask == 0xff && amask == 0xff000000:
 			// Go ahead, it's a regular 32-bit image
 		default:
-			return errBmpUnsupported
+			return newVipsError("unsupported BMP image")
 		}
 	default:
-		return errBmpUnsupported
+		return newVipsError("unsupported BMP image")
 	}
 
 	var palette []Color
@@ -497,7 +492,7 @@ func (img *Image) loadBmp(data []byte, noAlpha bool) error {
 		return img.decodeBmpRGB(r, width, height, 4, topDown, noAlpha)
 	}
 
-	return errBmpUnsupported
+	return newVipsError("unsupported BMP image")
 }
 
 func (img *Image) saveAsBmp() (*imagedata.ImageData, error) {

+ 1 - 1
vips/color.go

@@ -18,7 +18,7 @@ func ColorFromHex(hexcolor string) (Color, error) {
 	c := Color{}
 
 	if !hexColorRegex.MatchString(hexcolor) {
-		return c, fmt.Errorf("Invalid hex color: %s", hexcolor)
+		return c, newColorError("Invalid hex color: %s", hexcolor)
 	}
 
 	if len(hexcolor) == 3 {

+ 21 - 2
vips/errors.go

@@ -1,13 +1,32 @@
 package vips
 
 import (
+	"fmt"
+
 	"github.com/imgproxy/imgproxy/v3/ierrors"
 )
 
-type VipsError string
+type (
+	VipsError  string
+	ColorError string
+)
 
 func newVipsError(msg string) error {
-	return ierrors.Wrap(VipsError(msg), 2)
+	return ierrors.Wrap(VipsError(msg), 1)
+}
+
+func newVipsErrorf(format string, args ...interface{}) error {
+	return ierrors.Wrap(VipsError(fmt.Sprintf(format, args...)), 1)
 }
 
 func (e VipsError) Error() string { return string(e) }
+
+func newColorError(format string, args ...interface{}) error {
+	return ierrors.Wrap(
+		ColorError(fmt.Sprintf(format, args...)),
+		1,
+		ierrors.WithShouldReport(false),
+	)
+}
+
+func (e ColorError) Error() string { return string(e) }

+ 2 - 4
vips/ico.go

@@ -7,8 +7,6 @@ import "C"
 import (
 	"bytes"
 	"encoding/binary"
-	"errors"
-	"fmt"
 	"unsafe"
 
 	"github.com/imgproxy/imgproxy/v3/imagedata"
@@ -43,7 +41,7 @@ func (img *Image) loadIco(data []byte, shrink int, scale float64, pages int) err
 	internalType = meta.Format()
 
 	if internalType == imagetype.ICO || !SupportsLoad(internalType) {
-		return fmt.Errorf("Can't load %s from ICO", internalType)
+		return newVipsErrorf("Can't load %s from ICO", internalType)
 	}
 
 	imgdata := imagedata.ImageData{
@@ -56,7 +54,7 @@ func (img *Image) loadIco(data []byte, shrink int, scale float64, pages int) err
 
 func (img *Image) saveAsIco() (*imagedata.ImageData, error) {
 	if img.Width() > 256 || img.Height() > 256 {
-		return nil, errors.New("Image dimensions is too big. Max dimension size for ICO is 256")
+		return nil, newVipsError("Image dimensions is too big. Max dimension size for ICO is 256")
 	}
 
 	var ptr unsafe.Pointer

+ 4 - 5
vips/vips.go

@@ -9,7 +9,6 @@ package vips
 import "C"
 import (
 	"context"
-	"errors"
 	"math"
 	"net/http"
 	"os"
@@ -69,7 +68,7 @@ func Init() error {
 
 	if err := C.vips_initialize(); err != 0 {
 		C.vips_shutdown()
-		return errors.New("unable to start vips!")
+		return newVipsError("unable to start vips!")
 	}
 
 	// Disable libvips cache. Since processing pipeline is fine tuned, we won't get much profit from it.
@@ -355,7 +354,7 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64,
 	case imagetype.TIFF:
 		err = C.vips_tiffload_go(data, dataSize, &tmp)
 	default:
-		return errors.New("Usupported image type to load")
+		return newVipsError("Usupported image type to load")
 	}
 	if err != 0 {
 		return Error()
@@ -376,7 +375,7 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64,
 
 func (img *Image) LoadThumbnail(imgdata *imagedata.ImageData) error {
 	if imgdata.Type != imagetype.HEIC && imgdata.Type != imagetype.AVIF {
-		return errors.New("Usupported image type to load thumbnail")
+		return newVipsError("Usupported image type to load thumbnail")
 	}
 
 	var tmp *C.VipsImage
@@ -428,7 +427,7 @@ func (img *Image) Save(imgtype imagetype.Type, quality int) (*imagedata.ImageDat
 	case imagetype.TIFF:
 		err = C.vips_tiffsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality))
 	default:
-		return nil, errors.New("Usupported image type to save")
+		return nil, newVipsError("Usupported image type to save")
 	}
 	if err != 0 {
 		cancel()