IMG-13: separate http.Headers from ImageData (#1475)

* Separate headers from ImageData

* processing.Result
This commit is contained in:
Victor Sokolov
2025-08-06 16:46:35 +02:00
committed by GitHub
parent 52ecfeaf8b
commit f7a13c99de
15 changed files with 198 additions and 187 deletions

View File

@@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"hash" "hash"
"io" "io"
"net/http"
"net/textproto" "net/textproto"
"strings" "strings"
"sync" "sync"
@@ -107,9 +108,9 @@ func (h *Handler) ImageEtagExpected() string {
return h.imgEtagExpected return h.imgEtagExpected
} }
func (h *Handler) SetActualImageData(imgdata imagedata.ImageData) (bool, error) { func (h *Handler) SetActualImageData(imgdata imagedata.ImageData, headers http.Header) (bool, error) {
var haveActualImgETag bool var haveActualImgETag bool
h.imgEtagActual = imgdata.Headers().Get(httpheaders.Etag) h.imgEtagActual = headers.Get(httpheaders.Etag)
haveActualImgETag = len(h.imgEtagActual) > 0 haveActualImgETag = len(h.imgEtagActual) > 0
// Just in case server didn't check ETag properly and returned the same one // Just in case server didn't check ETag properly and returned the same one

View File

@@ -2,6 +2,7 @@ package etag
import ( import (
"io" "io"
"net/http"
"os" "os"
"strings" "strings"
"testing" "testing"
@@ -23,9 +24,11 @@ const (
type EtagTestSuite struct { type EtagTestSuite struct {
suite.Suite suite.Suite
po *options.ProcessingOptions po *options.ProcessingOptions
imgWithETag imagedata.ImageData imgWithETag imagedata.ImageData
imgWithoutETag imagedata.ImageData imgWithEtagHeaders http.Header
imgWithoutETag imagedata.ImageData
imgWithoutEtagHeaders http.Header
h Handler h Handler
} }
@@ -39,9 +42,11 @@ func (s *EtagTestSuite) SetupSuite() {
imgWithETag, err := imagedata.NewFromBytes(d) imgWithETag, err := imagedata.NewFromBytes(d)
s.Require().NoError(err) s.Require().NoError(err)
imgWithETag.Headers().Add(httpheaders.Etag, `"loremipsumdolor"`) s.imgWithEtagHeaders = make(http.Header)
s.imgWithEtagHeaders.Add(httpheaders.Etag, `"loremipsumdolor"`)
imgWithoutETag, err := imagedata.NewFromBytes(d) imgWithoutETag, err := imagedata.NewFromBytes(d)
s.imgWithoutEtagHeaders = make(http.Header)
s.Require().NoError(err) s.Require().NoError(err)
s.imgWithETag = imgWithETag s.imgWithETag = imgWithETag
@@ -59,14 +64,14 @@ func (s *EtagTestSuite) SetupTest() {
func (s *EtagTestSuite) TestGenerateActualReq() { func (s *EtagTestSuite) TestGenerateActualReq() {
s.h.SetActualProcessingOptions(s.po) s.h.SetActualProcessingOptions(s.po)
s.h.SetActualImageData(s.imgWithETag) s.h.SetActualImageData(s.imgWithETag, s.imgWithEtagHeaders)
s.Require().Equal(etagReq, s.h.GenerateActualETag()) s.Require().Equal(etagReq, s.h.GenerateActualETag())
} }
func (s *EtagTestSuite) TestGenerateActualData() { func (s *EtagTestSuite) TestGenerateActualData() {
s.h.SetActualProcessingOptions(s.po) s.h.SetActualProcessingOptions(s.po)
s.h.SetActualImageData(s.imgWithoutETag) s.h.SetActualImageData(s.imgWithoutETag, s.imgWithoutEtagHeaders)
s.Require().Equal(etagData, s.h.GenerateActualETag()) s.Require().Equal(etagData, s.h.GenerateActualETag())
} }
@@ -102,7 +107,7 @@ func (s *EtagTestSuite) TestImageETagExpectedPresent() {
s.h.ParseExpectedETag(etagReq) s.h.ParseExpectedETag(etagReq)
//nolint:testifylint // False-positive expected-actual //nolint:testifylint // False-positive expected-actual
s.Require().Equal(s.imgWithETag.Headers().Get(httpheaders.Etag), s.h.ImageEtagExpected()) s.Require().Equal(s.imgWithEtagHeaders.Get(httpheaders.Etag), s.h.ImageEtagExpected())
} }
func (s *EtagTestSuite) TestImageETagExpectedBlank() { func (s *EtagTestSuite) TestImageETagExpectedBlank() {
@@ -113,7 +118,7 @@ func (s *EtagTestSuite) TestImageETagExpectedBlank() {
func (s *EtagTestSuite) TestImageDataCheckDataToDataSuccess() { func (s *EtagTestSuite) TestImageDataCheckDataToDataSuccess() {
s.h.ParseExpectedETag(etagData) s.h.ParseExpectedETag(etagData)
s.Require().True(s.h.SetActualImageData(s.imgWithoutETag)) s.Require().True(s.h.SetActualImageData(s.imgWithoutETag, s.imgWithoutEtagHeaders))
} }
func (s *EtagTestSuite) TestImageDataCheckDataToDataFailure() { func (s *EtagTestSuite) TestImageDataCheckDataToDataFailure() {
@@ -121,12 +126,12 @@ func (s *EtagTestSuite) TestImageDataCheckDataToDataFailure() {
wrongEtag := etagData[:i] + `/Dwrongimghash"` wrongEtag := etagData[:i] + `/Dwrongimghash"`
s.h.ParseExpectedETag(wrongEtag) s.h.ParseExpectedETag(wrongEtag)
s.Require().False(s.h.SetActualImageData(s.imgWithoutETag)) s.Require().False(s.h.SetActualImageData(s.imgWithoutETag, s.imgWithoutEtagHeaders))
} }
func (s *EtagTestSuite) TestImageDataCheckDataToReqSuccess() { func (s *EtagTestSuite) TestImageDataCheckDataToReqSuccess() {
s.h.ParseExpectedETag(etagData) s.h.ParseExpectedETag(etagData)
s.Require().True(s.h.SetActualImageData(s.imgWithETag)) s.Require().True(s.h.SetActualImageData(s.imgWithETag, s.imgWithEtagHeaders))
} }
func (s *EtagTestSuite) TestImageDataCheckDataToReqFailure() { func (s *EtagTestSuite) TestImageDataCheckDataToReqFailure() {
@@ -134,19 +139,19 @@ func (s *EtagTestSuite) TestImageDataCheckDataToReqFailure() {
wrongEtag := etagData[:i] + `/Dwrongimghash"` wrongEtag := etagData[:i] + `/Dwrongimghash"`
s.h.ParseExpectedETag(wrongEtag) s.h.ParseExpectedETag(wrongEtag)
s.Require().False(s.h.SetActualImageData(s.imgWithETag)) s.Require().False(s.h.SetActualImageData(s.imgWithETag, s.imgWithEtagHeaders))
} }
func (s *EtagTestSuite) TestImageDataCheckReqToDataFailure() { func (s *EtagTestSuite) TestImageDataCheckReqToDataFailure() {
s.h.ParseExpectedETag(etagReq) s.h.ParseExpectedETag(etagReq)
s.Require().False(s.h.SetActualImageData(s.imgWithoutETag)) s.Require().False(s.h.SetActualImageData(s.imgWithoutETag, s.imgWithoutEtagHeaders))
} }
func (s *EtagTestSuite) TestETagBusterFailure() { func (s *EtagTestSuite) TestETagBusterFailure() {
config.ETagBuster = "busted" config.ETagBuster = "busted"
s.h.ParseExpectedETag(etagReq) s.h.ParseExpectedETag(etagReq)
s.Require().False(s.h.SetActualImageData(s.imgWithoutETag)) s.Require().False(s.h.SetActualImageData(s.imgWithoutETag, s.imgWithoutEtagHeaders))
} }
func TestEtag(t *testing.T) { func TestEtag(t *testing.T) {

View File

@@ -61,4 +61,9 @@ const (
XForwardedHost = "X-Forwarded-Host" XForwardedHost = "X-Forwarded-Host"
XForwardedProto = "X-Forwarded-Proto" XForwardedProto = "X-Forwarded-Proto"
XFrameOptions = "X-Frame-Options" XFrameOptions = "X-Frame-Options"
XOriginWidth = "X-Origin-Width"
XOriginHeight = "X-Origin-Height"
XResultWidth = "X-Result-Width"
XResultHeight = "X-Result-Height"
XOriginContentLength = "X-Origin-Content-Length"
) )

View File

@@ -37,7 +37,9 @@ func initDownloading() error {
return nil return nil
} }
func download(ctx context.Context, imageURL string, opts DownloadOptions, secopts security.Options) (ImageData, error) { func download(ctx context.Context, imageURL string, opts DownloadOptions, secopts security.Options) (ImageData, http.Header, error) {
h := make(http.Header)
// We use this for testing // We use this for testing
if len(redirectAllRequestsTo) > 0 { if len(redirectAllRequestsTo) > 0 {
imageURL = redirectAllRequestsTo imageURL = redirectAllRequestsTo
@@ -45,16 +47,19 @@ func download(ctx context.Context, imageURL string, opts DownloadOptions, secopt
req, err := Fetcher.BuildRequest(ctx, imageURL, opts.Header, opts.CookieJar) req, err := Fetcher.BuildRequest(ctx, imageURL, opts.Header, opts.CookieJar)
if err != nil { if err != nil {
return nil, err return nil, h, err
} }
defer req.Cancel() defer req.Cancel()
res, err := req.FetchImage() res, err := req.FetchImage()
if res != nil {
h = res.Header.Clone()
}
if err != nil { if err != nil {
if res != nil { if res != nil {
res.Body.Close() res.Body.Close()
} }
return nil, err return nil, h, err
} }
res, err = security.LimitResponseSize(res, secopts) res, err = security.LimitResponseSize(res, secopts)
@@ -62,22 +67,15 @@ func download(ctx context.Context, imageURL string, opts DownloadOptions, secopt
defer res.Body.Close() defer res.Body.Close()
} }
if err != nil { if err != nil {
return nil, err return nil, h, err
} }
imgdata, err := readAndCheckImage(res.Body, int(res.ContentLength), secopts) imgdata, err := readAndCheckImage(res.Body, int(res.ContentLength), secopts)
if err != nil { if err != nil {
return nil, ierrors.Wrap(err, 0) return nil, h, ierrors.Wrap(err, 0)
} }
// NOTE: This will be removed in the future in favor of headers/image data separation return imgdata, h, nil
for k, v := range res.Header {
for _, v := range v {
imgdata.Headers().Add(k, v)
}
}
return imgdata, nil
} }
func RedirectAllRequestsTo(u string) { func RedirectAllRequestsTo(u string) {

View File

@@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"io" "io"
"net/http"
"os" "os"
"github.com/imgproxy/imgproxy/v3/imagemeta" "github.com/imgproxy/imgproxy/v3/imagemeta"
@@ -12,22 +11,13 @@ import (
"github.com/imgproxy/imgproxy/v3/security" "github.com/imgproxy/imgproxy/v3/security"
) )
// NewFromBytesWithFormat creates a new ImageData instance from the provided format, // NewFromBytesWithFormat creates a new ImageData instance from the provided format
// http headers and byte slice. // and byte slice.
func NewFromBytesWithFormat(format imagetype.Type, b []byte, headers http.Header) ImageData { func NewFromBytesWithFormat(format imagetype.Type, b []byte) ImageData {
var h http.Header
if headers == nil {
h = make(http.Header)
} else {
h = headers.Clone()
}
return &imageDataBytes{ return &imageDataBytes{
data: b, data: b,
format: format, format: format,
headers: h, cancel: nil,
cancel: nil,
} }
} }
@@ -40,7 +30,7 @@ func NewFromBytes(b []byte) (ImageData, error) {
return nil, err return nil, err
} }
return NewFromBytesWithFormat(meta.Format(), b, nil), nil return NewFromBytesWithFormat(meta.Format(), b), nil
} }
// NewFromPath creates a new ImageData from an os.File // NewFromPath creates a new ImageData from an os.File

View File

@@ -15,8 +15,9 @@ import (
) )
var ( var (
Watermark ImageData Watermark ImageData
FallbackImage ImageData FallbackImage ImageData
FallbackImageHeaders http.Header // Headers for the fallback image
) )
type ImageData interface { type ImageData interface {
@@ -25,17 +26,12 @@ type ImageData interface {
Format() imagetype.Type // Format returns the image format from the metadata (shortcut) Format() imagetype.Type // Format returns the image format from the metadata (shortcut)
Size() (int, error) // Size returns the size of the image data in bytes Size() (int, error) // Size returns the size of the image data in bytes
AddCancel(context.CancelFunc) // AddCancel attaches a cancel function to the image data AddCancel(context.CancelFunc) // AddCancel attaches a cancel function to the image data
// This will be removed in the future
Headers() http.Header // Headers returns the HTTP headers of the image data, will be removed in the future
} }
// imageDataBytes represents image data stored in a byte slice in memory // imageDataBytes represents image data stored in a byte slice in memory
type imageDataBytes struct { type imageDataBytes struct {
format imagetype.Type format imagetype.Type
data []byte data []byte
headers http.Header
cancel []context.CancelFunc cancel []context.CancelFunc
cancelOnce sync.Once cancelOnce sync.Once
} }
@@ -66,10 +62,6 @@ func (d *imageDataBytes) Size() (int, error) {
return len(d.data), nil return len(d.data), nil
} }
func (d *imageDataBytes) Headers() http.Header {
return d.headers
}
func (d *imageDataBytes) AddCancel(cancel context.CancelFunc) { func (d *imageDataBytes) AddCancel(cancel context.CancelFunc) {
d.cancel = append(d.cancel, cancel) d.cancel = append(d.cancel, cancel)
} }
@@ -113,7 +105,7 @@ func loadWatermark() error {
} }
case len(config.WatermarkURL) > 0: case len(config.WatermarkURL) > 0:
Watermark, err = Download(context.Background(), config.WatermarkURL, "watermark", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions()) Watermark, _, err = Download(context.Background(), config.WatermarkURL, "watermark", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions())
if err != nil { if err != nil {
return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't download from URL")) return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't download from URL"))
} }
@@ -140,7 +132,7 @@ func loadFallbackImage() (err error) {
} }
case len(config.FallbackImageURL) > 0: case len(config.FallbackImageURL) > 0:
FallbackImage, err = Download(context.Background(), config.FallbackImageURL, "fallback image", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions()) FallbackImage, FallbackImageHeaders, err = Download(context.Background(), config.FallbackImageURL, "fallback image", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions())
if err != nil { if err != nil {
return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't download from URL")) return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't download from URL"))
} }
@@ -149,21 +141,17 @@ func loadFallbackImage() (err error) {
FallbackImage = nil FallbackImage = nil
} }
if FallbackImage != nil && err == nil && config.FallbackImageTTL > 0 {
FallbackImage.Headers().Set("Fallback-Image", "1")
}
return err return err
} }
func Download(ctx context.Context, imageURL, desc string, opts DownloadOptions, secopts security.Options) (ImageData, error) { func Download(ctx context.Context, imageURL, desc string, opts DownloadOptions, secopts security.Options) (ImageData, http.Header, error) {
imgdata, err := download(ctx, imageURL, opts, secopts) imgdata, h, err := download(ctx, imageURL, opts, secopts)
if err != nil { if err != nil {
return nil, ierrors.Wrap( return nil, h, ierrors.Wrap(
err, 0, err, 0,
ierrors.WithPrefix(fmt.Sprintf("Can't download %s", desc)), ierrors.WithPrefix(fmt.Sprintf("Can't download %s", desc)),
) )
} }
return imgdata, nil return imgdata, h, nil
} }

View File

@@ -91,7 +91,7 @@ func (s *ImageDataTestSuite) SetupTest() {
} }
func (s *ImageDataTestSuite) TestDownloadStatusOK() { func (s *ImageDataTestSuite) TestDownloadStatusOK() {
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(imgdata) s.Require().NotNil(imgdata)
@@ -158,7 +158,7 @@ func (s *ImageDataTestSuite) TestDownloadStatusPartialContent() {
s.Run(tc.name, func() { s.Run(tc.name, func() {
s.header.Set("Content-Range", tc.contentRange) s.header.Set("Content-Range", tc.contentRange)
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
if tc.expectErr { if tc.expectErr {
s.Require().Error(err) s.Require().Error(err)
@@ -178,7 +178,7 @@ func (s *ImageDataTestSuite) TestDownloadStatusNotFound() {
s.data = []byte("Not Found") s.data = []byte("Not Found")
s.header.Set("Content-Type", "text/plain") s.header.Set("Content-Type", "text/plain")
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
@@ -190,7 +190,7 @@ func (s *ImageDataTestSuite) TestDownloadStatusForbidden() {
s.data = []byte("Forbidden") s.data = []byte("Forbidden")
s.header.Set("Content-Type", "text/plain") s.header.Set("Content-Type", "text/plain")
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
@@ -202,7 +202,7 @@ func (s *ImageDataTestSuite) TestDownloadStatusInternalServerError() {
s.data = []byte("Internal Server Error") s.data = []byte("Internal Server Error")
s.header.Set("Content-Type", "text/plain") s.header.Set("Content-Type", "text/plain")
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(500, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(500, ierrors.Wrap(err, 0).StatusCode())
@@ -216,7 +216,7 @@ func (s *ImageDataTestSuite) TestDownloadUnreachable() {
serverURL := fmt.Sprintf("http://%s", l.Addr().String()) serverURL := fmt.Sprintf("http://%s", l.Addr().String())
imgdata, err := Download(context.Background(), serverURL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), serverURL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(500, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(500, ierrors.Wrap(err, 0).StatusCode())
@@ -226,7 +226,7 @@ func (s *ImageDataTestSuite) TestDownloadUnreachable() {
func (s *ImageDataTestSuite) TestDownloadInvalidImage() { func (s *ImageDataTestSuite) TestDownloadInvalidImage() {
s.data = []byte("invalid") s.data = []byte("invalid")
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())
@@ -236,7 +236,7 @@ func (s *ImageDataTestSuite) TestDownloadInvalidImage() {
func (s *ImageDataTestSuite) TestDownloadSourceAddressNotAllowed() { func (s *ImageDataTestSuite) TestDownloadSourceAddressNotAllowed() {
config.AllowLoopbackSourceAddresses = false config.AllowLoopbackSourceAddresses = false
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
@@ -246,7 +246,7 @@ func (s *ImageDataTestSuite) TestDownloadSourceAddressNotAllowed() {
func (s *ImageDataTestSuite) TestDownloadImageTooLarge() { func (s *ImageDataTestSuite) TestDownloadImageTooLarge() {
config.MaxSrcResolution = 1 config.MaxSrcResolution = 1
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())
@@ -256,7 +256,7 @@ func (s *ImageDataTestSuite) TestDownloadImageTooLarge() {
func (s *ImageDataTestSuite) TestDownloadImageFileTooLarge() { func (s *ImageDataTestSuite) TestDownloadImageFileTooLarge() {
config.MaxSrcFileSize = 1 config.MaxSrcFileSize = 1
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode()) s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())
@@ -275,7 +275,7 @@ func (s *ImageDataTestSuite) TestDownloadGzip() {
s.data = buf.Bytes() s.data = buf.Bytes()
s.header.Set("Content-Encoding", "gzip") s.header.Set("Content-Encoding", "gzip")
imgdata, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions()) imgdata, _, err := Download(context.Background(), s.server.URL, "Test image", DownloadOptions{}, security.DefaultOptions())
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(imgdata) s.Require().NotNil(imgdata)

View File

@@ -49,7 +49,7 @@ func readAndCheckImage(r io.Reader, contentLength int, secopts security.Options)
return nil, imagefetcher.WrapError(err) return nil, imagefetcher.WrapError(err)
} }
i := NewFromBytesWithFormat(meta.Format(), buf.Bytes(), nil) i := NewFromBytesWithFormat(meta.Format(), buf.Bytes())
i.AddCancel(cancel) i.AddCancel(cancel)
return i, nil return i, nil
} }

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"runtime" "runtime"
"strconv"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -79,7 +78,7 @@ func ValidatePreferredFormats() error {
} }
if len(filtered) == 0 { if len(filtered) == 0 {
return errors.New("No supported preferred formats specified") return errors.New("no supported preferred formats specified")
} }
config.PreferredFormats = filtered config.PreferredFormats = filtered
@@ -248,7 +247,15 @@ func saveImageToFitBytes(ctx context.Context, po *options.ProcessingOptions, img
} }
} }
func ProcessImage(ctx context.Context, imgdata imagedata.ImageData, po *options.ProcessingOptions) (imagedata.ImageData, error) { type Result struct {
OutData imagedata.ImageData
OriginWidth int
OriginHeight int
ResultWidth int
ResultHeight int
}
func ProcessImage(ctx context.Context, imgdata imagedata.ImageData, po *options.ProcessingOptions) (*Result, error) {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@@ -358,12 +365,15 @@ func ProcessImage(ctx context.Context, imgdata imagedata.ImageData, po *options.
outData, err = img.Save(po.Format, po.GetQuality()) outData, err = img.Save(po.Format, po.GetQuality())
} }
if err == nil { if err != nil {
outData.Headers().Set("X-Origin-Width", strconv.Itoa(originWidth)) return nil, err
outData.Headers().Set("X-Origin-Height", strconv.Itoa(originHeight))
outData.Headers().Set("X-Result-Width", strconv.Itoa(img.Width()))
outData.Headers().Set("X-Result-Height", strconv.Itoa(img.Height()))
} }
return outData, err return &Result{
OutData: outData,
OriginWidth: originWidth,
OriginHeight: originHeight,
ResultWidth: img.Width(),
ResultHeight: img.Height(),
}, nil
} }

View File

@@ -49,14 +49,10 @@ func (s *ProcessingTestSuite) openFile(name string) imagedata.ImageData {
return imagedata return imagedata
} }
func (s *ProcessingTestSuite) checkSize(imgdata imagedata.ImageData, width, height int) { func (s *ProcessingTestSuite) checkSize(r *Result, width, height int) {
img := new(vips.Image) s.Require().NotNil(r)
err := img.Load(imgdata, 1, 1, 1) s.Require().Equal(width, r.ResultWidth, "Width mismatch")
s.Require().NoError(err) s.Require().Equal(height, r.ResultHeight, "Height mismatch")
defer img.Clear()
s.Require().Equal(width, img.Width(), "Width mismatch")
s.Require().Equal(height, img.Height(), "Height mismatch")
} }
func (s *ProcessingTestSuite) TestResizeToFit() { func (s *ProcessingTestSuite) TestResizeToFit() {
@@ -88,11 +84,11 @@ func (s *ProcessingTestSuite) TestResizeToFit() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -127,11 +123,11 @@ func (s *ProcessingTestSuite) TestResizeToFitEnlarge() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -171,11 +167,11 @@ func (s *ProcessingTestSuite) TestResizeToFitExtend() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -215,11 +211,11 @@ func (s *ProcessingTestSuite) TestResizeToFitExtendAR() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -253,11 +249,11 @@ func (s *ProcessingTestSuite) TestResizeToFill() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -292,11 +288,11 @@ func (s *ProcessingTestSuite) TestResizeToFillEnlarge() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -338,11 +334,11 @@ func (s *ProcessingTestSuite) TestResizeToFillExtend() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -384,11 +380,11 @@ func (s *ProcessingTestSuite) TestResizeToFillExtendAR() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -422,11 +418,11 @@ func (s *ProcessingTestSuite) TestResizeToFillDown() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -461,11 +457,11 @@ func (s *ProcessingTestSuite) TestResizeToFillDownEnlarge() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -507,11 +503,11 @@ func (s *ProcessingTestSuite) TestResizeToFillDownExtend() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -551,11 +547,11 @@ func (s *ProcessingTestSuite) TestResizeToFillDownExtendAR() {
po.Width = tc.width po.Width = tc.width
po.Height = tc.height po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(outImgdata) s.Require().NotNil(result)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }
@@ -980,11 +976,12 @@ func (s *ProcessingTestSuite) TestResultSizeLimit() {
po.Rotate = tc.rotate po.Rotate = tc.rotate
po.Padding = tc.padding po.Padding = tc.padding
outImgdata, err := ProcessImage(context.Background(), imgdata, po) result, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight) s.Require().NoError(err)
s.Require().NotNil(result)
s.checkSize(result, tc.outWidth, tc.outHeight)
}) })
} }
} }

View File

@@ -125,7 +125,31 @@ func setCanonical(rw http.ResponseWriter, originURL string) {
} }
} }
func respondWithImage(reqID string, r *http.Request, rw http.ResponseWriter, statusCode int, resultData imagedata.ImageData, po *options.ProcessingOptions, originURL string, originData imagedata.ImageData) { func writeOriginContentLengthDebugHeader(ctx context.Context, rw http.ResponseWriter, originData imagedata.ImageData) {
if !config.EnableDebugHeaders {
return
}
size, err := originData.Size()
if err != nil {
checkErr(ctx, "image_data_size", err)
}
rw.Header().Set(httpheaders.XOriginContentLength, strconv.Itoa(size))
}
func writeDebugHeaders(rw http.ResponseWriter, result *processing.Result) {
if !config.EnableDebugHeaders || result == nil {
return
}
rw.Header().Set(httpheaders.XOriginWidth, strconv.Itoa(result.OriginWidth))
rw.Header().Set(httpheaders.XOriginHeight, strconv.Itoa(result.OriginHeight))
rw.Header().Set(httpheaders.XResultWidth, strconv.Itoa(result.ResultWidth))
rw.Header().Set(httpheaders.XResultHeight, strconv.Itoa(result.ResultHeight))
}
func respondWithImage(reqID string, r *http.Request, rw http.ResponseWriter, statusCode int, resultData imagedata.ImageData, po *options.ProcessingOptions, originURL string, originData imagedata.ImageData, originHeaders http.Header) {
var contentDisposition string var contentDisposition string
if len(po.Filename) > 0 { if len(po.Filename) > 0 {
contentDisposition = resultData.Format().ContentDisposition(po.Filename, po.ReturnAttachment) contentDisposition = resultData.Format().ContentDisposition(po.Filename, po.ReturnAttachment)
@@ -133,35 +157,22 @@ func respondWithImage(reqID string, r *http.Request, rw http.ResponseWriter, sta
contentDisposition = resultData.Format().ContentDispositionFromURL(originURL, po.ReturnAttachment) contentDisposition = resultData.Format().ContentDispositionFromURL(originURL, po.ReturnAttachment)
} }
rw.Header().Set("Content-Type", resultData.Format().Mime()) rw.Header().Set(httpheaders.ContentType, resultData.Format().Mime())
rw.Header().Set("Content-Disposition", contentDisposition) rw.Header().Set(httpheaders.ContentDisposition, contentDisposition)
setCacheControl(rw, po.Expires, originData.Headers()) setCacheControl(rw, po.Expires, originHeaders)
setLastModified(rw, originData.Headers()) setLastModified(rw, originHeaders)
setVary(rw) setVary(rw)
setCanonical(rw, originURL) setCanonical(rw, originURL)
if config.EnableDebugHeaders { rw.Header().Set(httpheaders.ContentSecurityPolicy, "script-src 'none'")
originSize, err := originData.Size()
if err != nil {
checkErr(r.Context(), "image_data_size", err)
}
rw.Header().Set("X-Origin-Content-Length", strconv.Itoa(originSize))
rw.Header().Set("X-Origin-Width", resultData.Headers().Get("X-Origin-Width"))
rw.Header().Set("X-Origin-Height", resultData.Headers().Get("X-Origin-Height"))
rw.Header().Set("X-Result-Width", resultData.Headers().Get("X-Result-Width"))
rw.Header().Set("X-Result-Height", resultData.Headers().Get("X-Result-Height"))
}
rw.Header().Set("Content-Security-Policy", "script-src 'none'")
resultSize, err := resultData.Size() resultSize, err := resultData.Size()
if err != nil { if err != nil {
checkErr(r.Context(), "image_data_size", err) checkErr(r.Context(), "image_data_size", err)
} }
rw.Header().Set("Content-Length", strconv.Itoa(resultSize)) rw.Header().Set(httpheaders.ContentLength, strconv.Itoa(resultSize))
rw.WriteHeader(statusCode) rw.WriteHeader(statusCode)
_, err = io.Copy(rw, resultData.Reader()) _, err = io.Copy(rw, resultData.Reader())
@@ -348,7 +359,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
statusCode := http.StatusOK statusCode := http.StatusOK
originData, err := func() (imagedata.ImageData, error) { originData, originHeaders, err := func() (imagedata.ImageData, http.Header, error) {
defer metrics.StartDownloadingSegment(ctx, metrics.Meta{ defer metrics.StartDownloadingSegment(ctx, metrics.Meta{
metrics.MetaSourceImageURL: metricsMeta[metrics.MetaSourceImageURL], metrics.MetaSourceImageURL: metricsMeta[metrics.MetaSourceImageURL],
metrics.MetaSourceImageOrigin: metricsMeta[metrics.MetaSourceImageOrigin], metrics.MetaSourceImageOrigin: metricsMeta[metrics.MetaSourceImageOrigin],
@@ -412,17 +423,22 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
} }
originData = imagedata.FallbackImage originData = imagedata.FallbackImage
originHeaders = imagedata.FallbackImageHeaders.Clone()
if config.FallbackImageTTL > 0 {
originHeaders.Set("Fallback-Image", "1")
}
} }
checkErr(ctx, "timeout", router.CheckTimeout(ctx)) checkErr(ctx, "timeout", router.CheckTimeout(ctx))
if config.ETagEnabled && statusCode == http.StatusOK { if config.ETagEnabled && statusCode == http.StatusOK {
imgDataMatch, terr := etagHandler.SetActualImageData(originData) imgDataMatch, terr := etagHandler.SetActualImageData(originData, originHeaders)
if terr == nil { if terr == nil {
rw.Header().Set("ETag", etagHandler.GenerateActualETag()) rw.Header().Set("ETag", etagHandler.GenerateActualETag())
if imgDataMatch && etagHandler.ProcessingOptionsMatch() { if imgDataMatch && etagHandler.ProcessingOptionsMatch() {
respondWithNotModified(reqID, r, rw, po, imageURL, originData.Headers()) respondWithNotModified(reqID, r, rw, po, imageURL, originHeaders)
return return
} }
} }
@@ -444,11 +460,13 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
defer sanitized.Close() defer sanitized.Close()
respondWithImage(reqID, r, rw, statusCode, sanitized, po, imageURL, originData) writeOriginContentLengthDebugHeader(ctx, rw, originData)
respondWithImage(reqID, r, rw, statusCode, sanitized, po, imageURL, originData, originHeaders)
return return
} }
respondWithImage(reqID, r, rw, statusCode, originData, po, imageURL, originData) writeOriginContentLengthDebugHeader(ctx, rw, originData)
respondWithImage(reqID, r, rw, statusCode, originData, po, imageURL, originData, originHeaders)
return return
} }
@@ -467,7 +485,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
)) ))
} }
resultData, err := func() (imagedata.ImageData, error) { result, err := func() (*processing.Result, error) {
defer metrics.StartProcessingSegment(ctx, metrics.Meta{ defer metrics.StartProcessingSegment(ctx, metrics.Meta{
metrics.MetaProcessingOptions: metricsMeta[metrics.MetaProcessingOptions], metrics.MetaProcessingOptions: metricsMeta[metrics.MetaProcessingOptions],
})() })()
@@ -475,9 +493,12 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
}() }()
checkErr(ctx, "processing", err) checkErr(ctx, "processing", err)
defer resultData.Close() defer result.OutData.Close()
checkErr(ctx, "timeout", router.CheckTimeout(ctx)) checkErr(ctx, "timeout", router.CheckTimeout(ctx))
respondWithImage(reqID, r, rw, statusCode, resultData, po, imageURL, originData) writeDebugHeaders(rw, result)
writeOriginContentLengthDebugHeader(ctx, rw, originData)
respondWithImage(reqID, r, rw, statusCode, result.OutData, po, imageURL, originData, originHeaders)
} }

View File

@@ -106,7 +106,7 @@ func (s *ProcessingHandlerTestSuite) readImageData(imgdata imagedata.ImageData)
return data return data
} }
func (s *ProcessingHandlerTestSuite) sampleETagData(imgETag string) (string, imagedata.ImageData, string) { func (s *ProcessingHandlerTestSuite) sampleETagData(imgETag string) (string, imagedata.ImageData, http.Header, string) {
poStr := "rs:fill:4:4" poStr := "rs:fill:4:4"
po := options.NewProcessingOptions() po := options.NewProcessingOptions()
@@ -115,16 +115,17 @@ func (s *ProcessingHandlerTestSuite) sampleETagData(imgETag string) (string, ima
po.Height = 4 po.Height = 4
imgdata := s.readTestImageData("test1.png") imgdata := s.readTestImageData("test1.png")
headers := make(http.Header)
if len(imgETag) != 0 { if len(imgETag) != 0 {
imgdata.Headers().Set(httpheaders.Etag, imgETag) headers.Set(httpheaders.Etag, imgETag)
} }
var h etag.Handler var h etag.Handler
h.SetActualProcessingOptions(po) h.SetActualProcessingOptions(po)
h.SetActualImageData(imgdata) h.SetActualImageData(imgdata, headers)
return poStr, imgdata, h.GenerateActualETag() return poStr, imgdata, headers, h.GenerateActualETag()
} }
func (s *ProcessingHandlerTestSuite) TestRequest() { func (s *ProcessingHandlerTestSuite) TestRequest() {
@@ -412,12 +413,12 @@ func (s *ProcessingHandlerTestSuite) TestETagDisabled() {
func (s *ProcessingHandlerTestSuite) TestETagReqNoIfNotModified() { func (s *ProcessingHandlerTestSuite) TestETagReqNoIfNotModified() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, etag := s.sampleETagData("loremipsumdolor") poStr, _, headers, etag := s.sampleETagData("loremipsumdolor")
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.Empty(r.Header.Get("If-None-Match")) s.Empty(r.Header.Get("If-None-Match"))
rw.Header().Set("ETag", imgdata.Headers().Get(httpheaders.Etag)) rw.Header().Set("ETag", headers.Get(httpheaders.Etag))
rw.WriteHeader(200) rw.WriteHeader(200)
rw.Write(s.readTestFile("test1.png")) rw.Write(s.readTestFile("test1.png"))
})) }))
@@ -433,7 +434,7 @@ func (s *ProcessingHandlerTestSuite) TestETagReqNoIfNotModified() {
func (s *ProcessingHandlerTestSuite) TestETagDataNoIfNotModified() { func (s *ProcessingHandlerTestSuite) TestETagDataNoIfNotModified() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, etag := s.sampleETagData("") poStr, imgdata, _, etag := s.sampleETagData("")
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.Empty(r.Header.Get("If-None-Match")) s.Empty(r.Header.Get("If-None-Match"))
@@ -453,10 +454,10 @@ func (s *ProcessingHandlerTestSuite) TestETagDataNoIfNotModified() {
func (s *ProcessingHandlerTestSuite) TestETagReqMatch() { func (s *ProcessingHandlerTestSuite) TestETagReqMatch() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, etag := s.sampleETagData(`"loremipsumdolor"`) poStr, _, headers, etag := s.sampleETagData(`"loremipsumdolor"`)
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.Equal(imgdata.Headers().Get(httpheaders.Etag), r.Header.Get(httpheaders.IfNoneMatch)) s.Equal(headers.Get(httpheaders.Etag), r.Header.Get(httpheaders.IfNoneMatch))
rw.WriteHeader(304) rw.WriteHeader(304)
})) }))
@@ -475,7 +476,7 @@ func (s *ProcessingHandlerTestSuite) TestETagReqMatch() {
func (s *ProcessingHandlerTestSuite) TestETagDataMatch() { func (s *ProcessingHandlerTestSuite) TestETagDataMatch() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, etag := s.sampleETagData("") poStr, imgdata, _, etag := s.sampleETagData("")
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.Empty(r.Header.Get("If-None-Match")) s.Empty(r.Header.Get("If-None-Match"))
@@ -498,13 +499,13 @@ func (s *ProcessingHandlerTestSuite) TestETagDataMatch() {
func (s *ProcessingHandlerTestSuite) TestETagReqNotMatch() { func (s *ProcessingHandlerTestSuite) TestETagReqNotMatch() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, actualETag := s.sampleETagData(`"loremipsumdolor"`) poStr, imgdata, headers, actualETag := s.sampleETagData(`"loremipsumdolor"`)
_, _, expectedETag := s.sampleETagData(`"loremipsum"`) _, _, _, expectedETag := s.sampleETagData(`"loremipsum"`)
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.Equal(`"loremipsum"`, r.Header.Get("If-None-Match")) s.Equal(`"loremipsum"`, r.Header.Get("If-None-Match"))
rw.Header().Set("ETag", imgdata.Headers().Get(httpheaders.Etag)) rw.Header().Set("ETag", headers.Get(httpheaders.Etag))
rw.WriteHeader(200) rw.WriteHeader(200)
rw.Write(s.readImageData(imgdata)) rw.Write(s.readImageData(imgdata))
})) }))
@@ -523,7 +524,7 @@ func (s *ProcessingHandlerTestSuite) TestETagReqNotMatch() {
func (s *ProcessingHandlerTestSuite) TestETagDataNotMatch() { func (s *ProcessingHandlerTestSuite) TestETagDataNotMatch() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, actualETag := s.sampleETagData("") poStr, imgdata, _, actualETag := s.sampleETagData("")
// Change the data hash // Change the data hash
expectedETag := actualETag[:strings.IndexByte(actualETag, '/')] + "/Dasdbefj" expectedETag := actualETag[:strings.IndexByte(actualETag, '/')] + "/Dasdbefj"
@@ -548,14 +549,14 @@ func (s *ProcessingHandlerTestSuite) TestETagDataNotMatch() {
func (s *ProcessingHandlerTestSuite) TestETagProcessingOptionsNotMatch() { func (s *ProcessingHandlerTestSuite) TestETagProcessingOptionsNotMatch() {
config.ETagEnabled = true config.ETagEnabled = true
poStr, imgdata, actualETag := s.sampleETagData("") poStr, imgdata, headers, actualETag := s.sampleETagData("")
// Change the processing options hash // Change the processing options hash
expectedETag := "abcdefj" + actualETag[strings.IndexByte(actualETag, '/'):] expectedETag := "abcdefj" + actualETag[strings.IndexByte(actualETag, '/'):]
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.Empty(r.Header.Get("If-None-Match")) s.Empty(r.Header.Get("If-None-Match"))
rw.Header().Set("ETag", imgdata.Headers().Get(httpheaders.Etag)) rw.Header().Set("ETag", headers.Get(httpheaders.Etag))
rw.WriteHeader(200) rw.WriteHeader(200)
rw.Write(s.readImageData(imgdata)) rw.Write(s.readImageData(imgdata))
})) }))

View File

@@ -48,7 +48,6 @@ func Sanitize(data imagedata.ImageData) (imagedata.ImageData, error) {
newData := imagedata.NewFromBytesWithFormat( newData := imagedata.NewFromBytesWithFormat(
imagetype.SVG, imagetype.SVG,
buf.Bytes(), buf.Bytes(),
data.Headers(),
) )
newData.AddCancel(cancel) newData.AddCancel(cancel)

View File

@@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/imgproxy/imgproxy/v3/config" "github.com/imgproxy/imgproxy/v3/config"
"github.com/imgproxy/imgproxy/v3/httpheaders"
"github.com/imgproxy/imgproxy/v3/imagedata" "github.com/imgproxy/imgproxy/v3/imagedata"
"github.com/imgproxy/imgproxy/v3/testutil" "github.com/imgproxy/imgproxy/v3/testutil"
) )
@@ -32,8 +31,6 @@ func (s *SvgTestSuite) readTestFile(name string) imagedata.ImageData {
s.Require().NoError(err) s.Require().NoError(err)
d, err := imagedata.NewFromBytes(data) d, err := imagedata.NewFromBytes(data)
d.Headers().Set(httpheaders.ContentType, "image/svg+xml")
d.Headers().Set(httpheaders.CacheControl, "public, max-age=12345")
s.Require().NoError(err) s.Require().NoError(err)
return d return d
@@ -46,7 +43,6 @@ func (s *SvgTestSuite) TestSanitize() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), actual.Reader())) s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), actual.Reader()))
s.Require().Equal(origin.Headers(), actual.Headers())
} }
func TestSvg(t *testing.T) { func TestSvg(t *testing.T) {

View File

@@ -470,7 +470,7 @@ func (img *Image) Save(imgtype imagetype.Type, quality int) (imagedata.ImageData
b := ptrToBytes(ptr, int(imgsize)) b := ptrToBytes(ptr, int(imgsize))
i := imagedata.NewFromBytesWithFormat(imgtype, b, nil) i := imagedata.NewFromBytesWithFormat(imgtype, b)
i.AddCancel(cancel) i.AddCancel(cancel)
return i, nil return i, nil