Browse Source

integration.Suite (#1518)

Victor Sokolov 3 weeks ago
parent
commit
01327c1cce
40 changed files with 184 additions and 180 deletions
  1. 1 1
      asyncbuffer/cond_test.go
  2. 1 6
      auximageprovider/static_provider_test.go
  3. 1 1
      cli/main.go
  4. 0 7
      config.go
  5. 10 0
      fetcher/config.go
  6. 8 3
      fetcher/fetcher.go
  7. 2 2
      fetcher/transport/azure/azure.go
  8. 1 1
      fetcher/transport/azure/azure_test.go
  9. 0 0
      fetcher/transport/azure/config.go
  10. 0 0
      fetcher/transport/common/common.go
  11. 6 6
      fetcher/transport/config.go
  12. 0 0
      fetcher/transport/fs/config.go
  13. 0 0
      fetcher/transport/fs/file_limiter.go
  14. 2 2
      fetcher/transport/fs/fs.go
  15. 1 1
      fetcher/transport/fs/fs_test.go
  16. 0 0
      fetcher/transport/gcs/config.go
  17. 2 2
      fetcher/transport/gcs/gcs.go
  18. 1 1
      fetcher/transport/gcs/gcs_test.go
  19. 0 0
      fetcher/transport/generichttp/config.go
  20. 0 0
      fetcher/transport/generichttp/generic_http.go
  21. 0 0
      fetcher/transport/notmodified/notmodified.go
  22. 0 0
      fetcher/transport/s3/config.go
  23. 1 1
      fetcher/transport/s3/s3.go
  24. 1 1
      fetcher/transport/s3/s3_test.go
  25. 0 0
      fetcher/transport/swift/config.go
  26. 2 2
      fetcher/transport/swift/swift.go
  27. 1 1
      fetcher/transport/swift/swift_test.go
  28. 6 6
      fetcher/transport/transport.go
  29. 1 1
      handlers/processing/request_methods.go
  30. 7 30
      handlers/stream/handler_test.go
  31. 1 8
      imagedata/image_data_test.go
  32. 10 8
      imgproxy.go
  33. 16 31
      integration/load_test.go
  34. 0 5
      integration/main_test.go
  35. 44 25
      integration/processing_handler_test.go
  36. 36 0
      integration/suite.go
  37. 0 1
      processing/processing.go
  38. 15 20
      processing/processing_test.go
  39. 5 0
      server/server.go
  40. 2 7
      svg/svg_test.go

+ 1 - 1
asyncbuffer/cond_test.go

@@ -18,7 +18,7 @@ func (s *TestCondSuite) SetupTest() {
 	s.cond = NewCond()
 }
 
-func (s *TestCondSuite) TeardownTest() {
+func (s *TestCondSuite) TearDownTest() {
 	if s.cond != nil {
 		s.cond.Close()
 	}

+ 1 - 6
auximageprovider/static_provider_test.go

@@ -16,7 +16,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/options"
-	"github.com/imgproxy/imgproxy/v3/transport"
 )
 
 type ImageProviderTestSuite struct {
@@ -168,12 +167,8 @@ func (s *ImageProviderTestSuite) TestNewProvider() {
 		},
 	}
 
-	trc := transport.NewDefaultConfig()
-	tr, err := transport.New(&trc)
-	s.Require().NoError(err)
-
 	fc := fetcher.NewDefaultConfig()
-	f, err := fetcher.New(tr, &fc)
+	f, err := fetcher.New(&fc)
 	s.Require().NoError(err)
 
 	idf := imagedata.NewFactory(f)

+ 1 - 1
cli/main.go

@@ -42,7 +42,7 @@ func run(ctx context.Context, cmd *cli.Command) error {
 		return err
 	}
 
-	if err := instance.StartServer(ctx); err != nil {
+	if err := instance.StartServer(ctx, nil); err != nil {
 		return err
 	}
 

+ 0 - 7
config.go

@@ -9,7 +9,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/headerwriter"
 	"github.com/imgproxy/imgproxy/v3/semaphores"
 	"github.com/imgproxy/imgproxy/v3/server"
-	"github.com/imgproxy/imgproxy/v3/transport"
 )
 
 // Config represents an instance configuration
@@ -18,7 +17,6 @@ type Config struct {
 	Semaphores        semaphores.Config
 	FallbackImage     auximageprovider.StaticConfig
 	WatermarkImage    auximageprovider.StaticConfig
-	Transport         transport.Config
 	Fetcher           fetcher.Config
 	ProcessingHandler processinghandler.Config
 	StreamHandler     stream.Config
@@ -32,7 +30,6 @@ func NewDefaultConfig() Config {
 		Semaphores:        semaphores.NewDefaultConfig(),
 		FallbackImage:     auximageprovider.NewDefaultStaticConfig(),
 		WatermarkImage:    auximageprovider.NewDefaultStaticConfig(),
-		Transport:         transport.NewDefaultConfig(),
 		Fetcher:           fetcher.NewDefaultConfig(),
 		ProcessingHandler: processinghandler.NewDefaultConfig(),
 		StreamHandler:     stream.NewDefaultConfig(),
@@ -66,10 +63,6 @@ func LoadConfigFromEnv(c *Config) (*Config, error) {
 		return nil, err
 	}
 
-	if _, err = transport.LoadConfigFromEnv(&c.Transport); err != nil {
-		return nil, err
-	}
-
 	if _, err = fetcher.LoadConfigFromEnv(&c.Fetcher); err != nil {
 		return nil, err
 	}

+ 10 - 0
fetcher/config.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/imgproxy/imgproxy/v3/config"
 	"github.com/imgproxy/imgproxy/v3/ensure"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport"
 	"github.com/imgproxy/imgproxy/v3/version"
 )
 
@@ -19,6 +20,9 @@ type Config struct {
 
 	// MaxRedirects is the maximum number of redirects to follow when fetching an image.
 	MaxRedirects int
+
+	// Transport holds the configuration for the transport layer.
+	Transport transport.Config
 }
 
 // NewDefaultConfig returns a new Config instance with default values.
@@ -27,6 +31,7 @@ func NewDefaultConfig() Config {
 		UserAgent:       "imgproxy/" + version.Version,
 		DownloadTimeout: 5 * time.Second,
 		MaxRedirects:    10,
+		Transport:       transport.NewDefaultConfig(),
 	}
 }
 
@@ -38,6 +43,11 @@ func LoadConfigFromEnv(c *Config) (*Config, error) {
 	c.DownloadTimeout = time.Duration(config.DownloadTimeout) * time.Second
 	c.MaxRedirects = config.MaxRedirects
 
+	_, err := transport.LoadConfigFromEnv(&c.Transport)
+	if err != nil {
+		return nil, err
+	}
+
 	return c, nil
 }
 

+ 8 - 3
fetcher/fetcher.go

@@ -7,9 +7,9 @@ import (
 	"net/http"
 	"time"
 
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
-	"github.com/imgproxy/imgproxy/v3/transport"
-	"github.com/imgproxy/imgproxy/v3/transport/common"
 )
 
 const (
@@ -24,11 +24,16 @@ type Fetcher struct {
 }
 
 // New creates a new ImageFetcher with the provided transport
-func New(transport *transport.Transport, config *Config) (*Fetcher, error) {
+func New(config *Config) (*Fetcher, error) {
 	if err := config.Validate(); err != nil {
 		return nil, err
 	}
 
+	transport, err := transport.New(&config.Transport)
+	if err != nil {
+		return nil, err
+	}
+
 	return &Fetcher{transport, config}, nil
 }
 

+ 2 - 2
transport/azure/azure.go → fetcher/transport/azure/azure.go

@@ -15,10 +15,10 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
 
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/notmodified"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/httprange"
-	"github.com/imgproxy/imgproxy/v3/transport/common"
-	"github.com/imgproxy/imgproxy/v3/transport/notmodified"
 )
 
 type transport struct {

+ 1 - 1
transport/azure/azure_test.go → fetcher/transport/azure/azure_test.go

@@ -11,8 +11,8 @@ import (
 	"github.com/stretchr/testify/suite"
 
 	"github.com/imgproxy/imgproxy/v3/config"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/generichttp"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
-	"github.com/imgproxy/imgproxy/v3/transport/generichttp"
 )
 
 type AzureTestSuite struct {

+ 0 - 0
transport/azure/config.go → fetcher/transport/azure/config.go


+ 0 - 0
transport/common/common.go → fetcher/transport/common/common.go


+ 6 - 6
transport/config.go → fetcher/transport/config.go

@@ -5,12 +5,12 @@ package transport
 import (
 	"github.com/imgproxy/imgproxy/v3/config"
 	"github.com/imgproxy/imgproxy/v3/ensure"
-	"github.com/imgproxy/imgproxy/v3/transport/azure"
-	"github.com/imgproxy/imgproxy/v3/transport/fs"
-	"github.com/imgproxy/imgproxy/v3/transport/gcs"
-	"github.com/imgproxy/imgproxy/v3/transport/generichttp"
-	"github.com/imgproxy/imgproxy/v3/transport/s3"
-	"github.com/imgproxy/imgproxy/v3/transport/swift"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/azure"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/fs"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/gcs"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/generichttp"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/s3"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/swift"
 )
 
 // Config represents configuration of the transport package

+ 0 - 0
transport/fs/config.go → fetcher/transport/fs/config.go


+ 0 - 0
transport/fs/file_limiter.go → fetcher/transport/fs/file_limiter.go


+ 2 - 2
transport/fs/fs.go → fetcher/transport/fs/fs.go

@@ -13,10 +13,10 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/notmodified"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/httprange"
-	"github.com/imgproxy/imgproxy/v3/transport/common"
-	"github.com/imgproxy/imgproxy/v3/transport/notmodified"
 )
 
 type transport struct {

+ 1 - 1
transport/fs/fs_test.go → fetcher/transport/fs/fs_test.go

@@ -24,7 +24,7 @@ func (s *FsTestSuite) SetupSuite() {
 	wd, err := os.Getwd()
 	s.Require().NoError(err)
 
-	fsRoot := filepath.Join(wd, "..", "..", "testdata")
+	fsRoot := filepath.Join(wd, "..", "..", "..", "testdata")
 
 	fi, err := os.Stat(filepath.Join(fsRoot, "test1.png"))
 	s.Require().NoError(err)

+ 0 - 0
transport/gcs/config.go → fetcher/transport/gcs/config.go


+ 2 - 2
transport/gcs/gcs.go → fetcher/transport/gcs/gcs.go

@@ -14,11 +14,11 @@ import (
 	raw "google.golang.org/api/storage/v1"
 	htransport "google.golang.org/api/transport/http"
 
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/notmodified"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/httprange"
 	"github.com/imgproxy/imgproxy/v3/ierrors"
-	"github.com/imgproxy/imgproxy/v3/transport/common"
-	"github.com/imgproxy/imgproxy/v3/transport/notmodified"
 )
 
 // For tests

+ 1 - 1
transport/gcs/gcs_test.go → fetcher/transport/gcs/gcs_test.go

@@ -10,8 +10,8 @@ import (
 	"github.com/fsouza/fake-gcs-server/fakestorage"
 	"github.com/stretchr/testify/suite"
 
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/generichttp"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
-	"github.com/imgproxy/imgproxy/v3/transport/generichttp"
 )
 
 func getFreePort() (int, error) {

+ 0 - 0
transport/generichttp/config.go → fetcher/transport/generichttp/config.go


+ 0 - 0
transport/generichttp/generic_http.go → fetcher/transport/generichttp/generic_http.go


+ 0 - 0
transport/notmodified/notmodified.go → fetcher/transport/notmodified/notmodified.go


+ 0 - 0
transport/s3/config.go → fetcher/transport/s3/config.go


+ 1 - 1
transport/s3/s3.go → fetcher/transport/s3/s3.go

@@ -20,9 +20,9 @@ import (
 	"github.com/aws/aws-sdk-go-v2/service/s3"
 	"github.com/aws/aws-sdk-go-v2/service/sts"
 
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/ierrors"
-	"github.com/imgproxy/imgproxy/v3/transport/common"
 )
 
 type s3Client interface {

+ 1 - 1
transport/s3/s3_test.go → fetcher/transport/s3/s3_test.go

@@ -15,7 +15,7 @@ import (
 	"github.com/johannesboyne/gofakes3/backend/s3mem"
 	"github.com/stretchr/testify/suite"
 
-	"github.com/imgproxy/imgproxy/v3/transport/generichttp"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/generichttp"
 )
 
 type S3TestSuite struct {

+ 0 - 0
transport/swift/config.go → fetcher/transport/swift/config.go


+ 2 - 2
transport/swift/swift.go → fetcher/transport/swift/swift.go

@@ -10,9 +10,9 @@ import (
 	"github.com/ncw/swift/v2"
 
 	"github.com/imgproxy/imgproxy/v3/config"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/notmodified"
 	"github.com/imgproxy/imgproxy/v3/ierrors"
-	"github.com/imgproxy/imgproxy/v3/transport/common"
-	"github.com/imgproxy/imgproxy/v3/transport/notmodified"
 )
 
 type transport struct {

+ 1 - 1
transport/swift/swift_test.go → fetcher/transport/swift/swift_test.go

@@ -10,7 +10,7 @@ import (
 	"github.com/ncw/swift/v2/swifttest"
 	"github.com/stretchr/testify/suite"
 
-	"github.com/imgproxy/imgproxy/v3/transport/generichttp"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/generichttp"
 )
 
 const (

+ 6 - 6
transport/transport.go → fetcher/transport/transport.go

@@ -5,13 +5,13 @@ package transport
 import (
 	"net/http"
 
-	"github.com/imgproxy/imgproxy/v3/transport/generichttp"
+	"github.com/imgproxy/imgproxy/v3/fetcher/transport/generichttp"
 
-	azureTransport "github.com/imgproxy/imgproxy/v3/transport/azure"
-	fsTransport "github.com/imgproxy/imgproxy/v3/transport/fs"
-	gcsTransport "github.com/imgproxy/imgproxy/v3/transport/gcs"
-	s3Transport "github.com/imgproxy/imgproxy/v3/transport/s3"
-	swiftTransport "github.com/imgproxy/imgproxy/v3/transport/swift"
+	azureTransport "github.com/imgproxy/imgproxy/v3/fetcher/transport/azure"
+	fsTransport "github.com/imgproxy/imgproxy/v3/fetcher/transport/fs"
+	gcsTransport "github.com/imgproxy/imgproxy/v3/fetcher/transport/gcs"
+	s3Transport "github.com/imgproxy/imgproxy/v3/fetcher/transport/s3"
+	swiftTransport "github.com/imgproxy/imgproxy/v3/fetcher/transport/swift"
 )
 
 // Transport is a wrapper around http.Transport which allows to track registered protocols

+ 1 - 1
handlers/processing/request_methods.go

@@ -154,7 +154,7 @@ func (r *request) getFallbackImage(
 // processImage calls actual image processing
 func (r *request) processImage(ctx context.Context, originData imagedata.ImageData) (*processing.Result, error) {
 	defer monitoring.StartProcessingSegment(ctx, r.monitoringMeta.Filter(monitoring.MetaProcessingOptions))()
-	return processing.ProcessImage(ctx, originData, r.po, r.handler.watermarkImage, r.handler.idf)
+	return processing.ProcessImage(ctx, originData, r.po, r.handler.watermarkImage)
 }
 
 // writeDebugHeaders writes debug headers (X-Origin-*, X-Result-*) to the response

+ 7 - 30
handlers/stream/handler_test.go

@@ -20,7 +20,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/headerwriter"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/options"
-	"github.com/imgproxy/imgproxy/v3/transport"
 )
 
 const (
@@ -49,15 +48,9 @@ func (s *HandlerTestSuite) SetupTest() {
 	config.Reset()
 	config.AllowLoopbackSourceAddresses = true
 
-	trc, err := transport.LoadConfigFromEnv(nil)
-	s.Require().NoError(err)
-
-	tr, err := transport.New(trc)
-	s.Require().NoError(err)
-
 	fc := fetcher.NewDefaultConfig()
 
-	fetcher, err := fetcher.New(tr, &fc)
+	fetcher, err := fetcher.New(&fc)
 	s.Require().NoError(err)
 
 	cfg := NewDefaultConfig()
@@ -349,16 +342,10 @@ func (s *HandlerTestSuite) TestHandlerCacheControl() {
 			}))
 			defer ts.Close()
 
-			trc, err := transport.LoadConfigFromEnv(nil)
-			s.Require().NoError(err)
-
-			// Create new handler with updated config for each test
-			tr, err := transport.New(trc)
+			fc, err := fetcher.LoadConfigFromEnv(nil)
 			s.Require().NoError(err)
 
-			fc := fetcher.NewDefaultConfig()
-
-			fetcher, err := fetcher.New(tr, &fc)
+			fetcher, err := fetcher.New(fc)
 			s.Require().NoError(err)
 
 			cfg := NewDefaultConfig()
@@ -446,15 +433,10 @@ func (s *HandlerTestSuite) TestHandlerErrorResponse() {
 
 // TestHandlerCookiePassthrough tests the cookie passthrough behavior of the streaming service.
 func (s *HandlerTestSuite) TestHandlerCookiePassthrough() {
-	trc, err := transport.LoadConfigFromEnv(nil)
-	s.Require().NoError(err)
-
-	// Create new handler with updated config
-	tr, err := transport.New(trc)
+	fc, err := fetcher.LoadConfigFromEnv(nil)
 	s.Require().NoError(err)
 
-	fc := fetcher.NewDefaultConfig()
-	fetcher, err := fetcher.New(tr, &fc)
+	fetcher, err := fetcher.New(fc)
 	s.Require().NoError(err)
 
 	cfg := NewDefaultConfig()
@@ -506,15 +488,10 @@ func (s *HandlerTestSuite) TestHandlerCanonicalHeader() {
 	defer ts.Close()
 
 	for _, sc := range []bool{true, false} {
-		trc, err := transport.LoadConfigFromEnv(nil)
-		s.Require().NoError(err)
-
-		// Create new handler with updated config
-		tr, err := transport.New(trc)
+		fc, err := fetcher.LoadConfigFromEnv(nil)
 		s.Require().NoError(err)
 
-		fc := fetcher.NewDefaultConfig()
-		fetcher, err := fetcher.New(tr, &fc)
+		fetcher, err := fetcher.New(fc)
 		s.Require().NoError(err)
 
 		cfg := NewDefaultConfig()

+ 1 - 8
imagedata/image_data_test.go

@@ -22,7 +22,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/ierrors"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
 	"github.com/imgproxy/imgproxy/v3/testutil"
-	"github.com/imgproxy/imgproxy/v3/transport"
 )
 
 type ImageDataTestSuite struct {
@@ -70,16 +69,10 @@ func (s *ImageDataTestSuite) SetupSuite() {
 		rw.Write(data)
 	}))
 
-	ctr, err := transport.LoadConfigFromEnv(nil)
-	s.Require().NoError(err)
-
-	ts, err := transport.New(ctr)
-	s.Require().NoError(err)
-
 	c, err := fetcher.LoadConfigFromEnv(nil)
 	s.Require().NoError(err)
 
-	fetcher, err := fetcher.New(ts, c)
+	fetcher, err := fetcher.New(c)
 	s.Require().NoError(err)
 
 	s.factory = NewFactory(fetcher)

+ 10 - 8
imgproxy.go

@@ -2,6 +2,7 @@ package imgproxy
 
 import (
 	"context"
+	"net"
 	"time"
 
 	"github.com/imgproxy/imgproxy/v3/auximageprovider"
@@ -15,7 +16,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/monitoring/prometheus"
 	"github.com/imgproxy/imgproxy/v3/semaphores"
 	"github.com/imgproxy/imgproxy/v3/server"
-	"github.com/imgproxy/imgproxy/v3/transport"
 )
 
 const (
@@ -43,12 +43,7 @@ func New(ctx context.Context, config *Config) (*Imgproxy, error) {
 		return nil, err
 	}
 
-	ts, err := transport.New(&config.Transport)
-	if err != nil {
-		return nil, err
-	}
-
-	fetcher, err := fetcher.New(ts, &config.Fetcher)
+	fetcher, err := fetcher.New(&config.Fetcher)
 	if err != nil {
 		return nil, err
 	}
@@ -123,7 +118,9 @@ func (i *Imgproxy) BuildRouter() (*server.Router, error) {
 }
 
 // Start runs the imgproxy server. This function blocks until the context is cancelled.
-func (i *Imgproxy) StartServer(ctx context.Context) error {
+// If hasStarted is not nil, it will be notified with the server address once
+// the server is ready or about to be ready to accept requests.
+func (i *Imgproxy) StartServer(ctx context.Context, hasStarted chan net.Addr) error {
 	go i.startMemoryTicker(ctx)
 
 	ctx, cancel := context.WithCancel(ctx)
@@ -143,6 +140,11 @@ func (i *Imgproxy) StartServer(ctx context.Context) error {
 	}
 	defer s.Shutdown(context.Background())
 
+	if hasStarted != nil {
+		hasStarted <- s.Addr
+		close(hasStarted)
+	}
+
 	<-ctx.Done()
 
 	return nil

+ 16 - 31
integration/load_test.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"image/png"
 	"io"
+	"net"
 	"net/http"
 	"os"
 	"path"
@@ -27,25 +28,33 @@ const (
 )
 
 type LoadTestSuite struct {
-	suite.Suite
-	ctx            context.Context
-	cancel         context.CancelFunc
+	Suite
 	testData       *testutil.TestDataProvider
 	testImagesPath string
+
+	addr         net.Addr
+	stopImgproxy context.CancelFunc
 }
 
 // SetupSuite starts imgproxy instance server
 func (s *LoadTestSuite) SetupSuite() {
 	s.testData = testutil.NewTestDataProvider(s.T())
 	s.testImagesPath = s.testData.Path("test-images")
-	s.ctx, s.cancel = context.WithCancel(s.T().Context())
 
-	s.startImgproxy(s.ctx)
+	c, err := imgproxy.LoadConfigFromEnv(nil)
+	s.Require().NoError(err)
+
+	c.Fetcher.Transport.Local.Root = s.testImagesPath
+	config.MaxAnimationFrames = 999
+	config.DevelopmentErrorsMode = true
+
+	// In this test we start the single imgproxy server for all test cases
+	s.addr, s.stopImgproxy = s.StartImgproxy(c)
 }
 
 // TearDownSuite stops imgproxy instance server
 func (s *LoadTestSuite) TearDownSuite() {
-	s.cancel()
+	s.stopImgproxy()
 }
 
 // testLoadFolder fetches images iterates over images in the specified folder,
@@ -108,7 +117,7 @@ func (s *LoadTestSuite) testLoadFolder(folder string) {
 
 // fetchImage fetches an image from the imgproxy server
 func (s *LoadTestSuite) fetchImage(path string) []byte {
-	url := fmt.Sprintf("http://%s:%d/%s", bindHost, bindPort, path)
+	url := fmt.Sprintf("http://%s/%s", s.addr.String(), path)
 
 	resp, err := http.Get(url)
 	s.Require().NoError(err, "Failed to fetch image from %s", url)
@@ -122,30 +131,6 @@ func (s *LoadTestSuite) fetchImage(path string) []byte {
 	return bytes
 }
 
-func (s *LoadTestSuite) startImgproxy(ctx context.Context) *imgproxy.Imgproxy {
-	c, err := imgproxy.LoadConfigFromEnv(nil)
-	s.Require().NoError(err)
-
-	c.Server.Bind = ":" + fmt.Sprintf("%d", bindPort)
-	c.Transport.Local.Root = s.testImagesPath
-	c.Server.LogMemStats = true
-
-	config.MaxAnimationFrames = 999
-	config.DevelopmentErrorsMode = true
-
-	i, err := imgproxy.New(ctx, c)
-	s.Require().NoError(err)
-
-	go func() {
-		err = i.StartServer(ctx)
-		if err != nil {
-			s.T().Errorf("Imgproxy server exited with error: %v", err)
-		}
-	}()
-
-	return i
-}
-
 // TestLoadSaveToPng ensures that our load pipeline works,
 // including standard and custom loaders. For each source image
 // in the folder, it does the passthrough request through imgproxy:

+ 0 - 5
integration/main_test.go

@@ -7,11 +7,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3"
 )
 
-const (
-	bindPort = 9090 // Port to bind imgproxy to
-	bindHost = "localhost"
-)
-
 // TestMain performs global setup/teardown for the integration tests.
 func TestMain(m *testing.M) {
 	imgproxy.Init()

+ 44 - 25
integration/processing_handler_test.go

@@ -1,6 +1,7 @@
 package integration
 
 import (
+	"bytes"
 	"fmt"
 	"io"
 	"net/http"
@@ -13,10 +14,10 @@ import (
 	"github.com/imgproxy/imgproxy/v3"
 	"github.com/imgproxy/imgproxy/v3/config"
 	"github.com/imgproxy/imgproxy/v3/config/configurators"
+	"github.com/imgproxy/imgproxy/v3/fetcher"
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
-	"github.com/imgproxy/imgproxy/v3/server"
 	"github.com/imgproxy/imgproxy/v3/svg"
 	"github.com/imgproxy/imgproxy/v3/testutil"
 	"github.com/imgproxy/imgproxy/v3/vips"
@@ -26,12 +27,15 @@ import (
 
 // ProcessingHandlerTestSuite is a test suite for testing image processing handler
 type ProcessingHandlerTestSuite struct {
-	suite.Suite
+	Suite
 
 	testData *testutil.TestDataProvider
-	config   testutil.LazyObj[*imgproxy.Config]
-	router   testutil.LazyObj[*server.Router]
-	imgproxy testutil.LazyObj[*imgproxy.Imgproxy]
+
+	// NOTE: lazy obj is required here because in the specific tests we sometimes
+	// change the config values in config.go. Config instantiation should
+	// happen afterwards. It is done via lazy obj. When all config values will be moved
+	// to imgproxy.Config struct, this can be removed.
+	config testutil.LazyObj[*imgproxy.Config]
 }
 
 func (s *ProcessingHandlerTestSuite) SetupSuite() {
@@ -42,7 +46,7 @@ func (s *ProcessingHandlerTestSuite) SetupSuite() {
 	s.testData = testutil.NewTestDataProvider(s.T())
 }
 
-func (s *ProcessingHandlerTestSuite) TeardownSuite() {
+func (s *ProcessingHandlerTestSuite) TearDownSuite() {
 	logrus.SetOutput(os.Stdout)
 }
 
@@ -52,19 +56,11 @@ func (s *ProcessingHandlerTestSuite) setupObjs() {
 		c, err := imgproxy.LoadConfigFromEnv(nil)
 		s.Require().NoError(err)
 
-		c.Transport.Local.Root = s.testData.Root()
-		c.Transport.HTTP.ClientKeepAliveTimeout = 0
+		c.Fetcher.Transport.Local.Root = s.testData.Root()
+		c.Fetcher.Transport.HTTP.ClientKeepAliveTimeout = 0
 
 		return c, nil
 	})
-
-	s.imgproxy = testutil.NewLazyObj(s.T(), func() (*imgproxy.Imgproxy, error) {
-		return imgproxy.New(s.T().Context(), s.config())
-	})
-
-	s.router = testutil.NewLazyObj(s.T(), func() (*server.Router, error) {
-		return s.imgproxy().BuildRouter()
-	})
 }
 
 func (s *ProcessingHandlerTestSuite) SetupTest() {
@@ -82,18 +78,35 @@ func (s *ProcessingHandlerTestSuite) SetupSubTest() {
 	s.setupObjs()
 }
 
-// GET performs a GET request to the given path and returns the response recorder
+// GET performs a GET request to the imageproxy real server
+// NOTE: Do not forget to move this to Suite in case of need in other future test suites
 func (s *ProcessingHandlerTestSuite) GET(path string, header ...http.Header) *http.Response {
-	req := httptest.NewRequest(http.MethodGet, path, nil)
-	rw := httptest.NewRecorder()
-
-	if len(header) > 0 {
-		req.Header = header[0]
+	// In this test we start the imgproxy server instance per request
+	addr, stopServer := s.StartImgproxy(s.config())
+	defer stopServer()
+
+	url := fmt.Sprintf("http://%s%s", addr.String(), path)
+
+	// Perform GET request to an url
+	req, _ := http.NewRequest("GET", url, nil)
+	for h := range header {
+		for k, v := range header[h] {
+			req.Header.Set(k, v[0]) // only first value will go to the request
+		}
 	}
 
-	s.router().ServeHTTP(rw, req)
+	// Do the request
+	resp, err := http.DefaultClient.Do(req)
+	s.Require().NoError(err)
+
+	// Read the entire body into memory and replace the original body with memory reader
+	// to avoid the defer
+	bodyBytes, err := io.ReadAll(resp.Body)
+	s.Require().NoError(err)
+	resp.Body.Close()
+	resp.Body = io.NopCloser(bytes.NewReader(bodyBytes))
 
-	return rw.Result()
+	return resp
 }
 
 func (s *ProcessingHandlerTestSuite) TestSignatureValidationFailure() {
@@ -267,7 +280,13 @@ func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() {
 
 	s.Require().Equal(http.StatusOK, res.StatusCode)
 
-	data, err := s.imgproxy().ImageDataFactory.NewFromBytes(s.testData.Read("test1.svg"))
+	c := fetcher.NewDefaultConfig()
+	f, err := fetcher.New(&c)
+	s.Require().NoError(err)
+
+	idf := imagedata.NewFactory(f)
+
+	data, err := idf.NewFromBytes(s.testData.Read("test1.svg"))
 	s.Require().NoError(err)
 
 	expected, err := svg.Sanitize(data)

+ 36 - 0
integration/suite.go

@@ -0,0 +1,36 @@
+package integration
+
+import (
+	"context"
+	"net"
+
+	"github.com/imgproxy/imgproxy/v3"
+	"github.com/stretchr/testify/suite"
+)
+
+type Suite struct {
+	suite.Suite
+}
+
+// StartImgproxy starts imgproxy instance for the tests
+// Returns instance, instance address and stop function
+func (s *Suite) StartImgproxy(c *imgproxy.Config) (net.Addr, context.CancelFunc) {
+	ctx, cancel := context.WithCancel(s.T().Context())
+
+	c.Server.Bind = ":0"
+	c.Server.LogMemStats = true
+
+	i, err := imgproxy.New(ctx, c)
+	s.Require().NoError(err)
+
+	addrCh := make(chan net.Addr)
+
+	go func() {
+		err = i.StartServer(s.T().Context(), addrCh)
+		if err != nil {
+			s.T().Errorf("Imgproxy stopped with error: %v", err)
+		}
+	}()
+
+	return <-addrCh, cancel
+}

+ 0 - 1
processing/processing.go

@@ -85,7 +85,6 @@ func ProcessImage(
 	imgdata imagedata.ImageData,
 	po *options.ProcessingOptions,
 	watermarkProvider auximageprovider.Provider,
-	idf *imagedata.Factory,
 ) (*Result, error) {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()

+ 15 - 20
processing/processing_test.go

@@ -16,7 +16,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/ierrors"
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/options"
-	"github.com/imgproxy/imgproxy/v3/transport"
 	"github.com/imgproxy/imgproxy/v3/vips"
 )
 
@@ -37,12 +36,8 @@ func (s *ProcessingTestSuite) SetupSuite() {
 
 	logrus.SetOutput(io.Discard)
 
-	trc := transport.NewDefaultConfig()
-	tr, err := transport.New(&trc)
-	s.Require().NoError(err)
-
 	fc := fetcher.NewDefaultConfig()
-	f, err := fetcher.New(tr, &fc)
+	f, err := fetcher.New(&fc)
 	s.Require().NoError(err)
 
 	s.idf = imagedata.NewFactory(f)
@@ -94,7 +89,7 @@ func (s *ProcessingTestSuite) TestResizeToFit() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -133,7 +128,7 @@ func (s *ProcessingTestSuite) TestResizeToFitEnlarge() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -177,7 +172,7 @@ func (s *ProcessingTestSuite) TestResizeToFitExtend() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -221,7 +216,7 @@ func (s *ProcessingTestSuite) TestResizeToFitExtendAR() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -259,7 +254,7 @@ func (s *ProcessingTestSuite) TestResizeToFill() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -298,7 +293,7 @@ func (s *ProcessingTestSuite) TestResizeToFillEnlarge() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -344,7 +339,7 @@ func (s *ProcessingTestSuite) TestResizeToFillExtend() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -390,7 +385,7 @@ func (s *ProcessingTestSuite) TestResizeToFillExtendAR() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -428,7 +423,7 @@ func (s *ProcessingTestSuite) TestResizeToFillDown() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -467,7 +462,7 @@ func (s *ProcessingTestSuite) TestResizeToFillDownEnlarge() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -513,7 +508,7 @@ func (s *ProcessingTestSuite) TestResizeToFillDownExtend() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -557,7 +552,7 @@ func (s *ProcessingTestSuite) TestResizeToFillDownExtendAR() {
 			po.Width = tc.width
 			po.Height = tc.height
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
 
@@ -986,7 +981,7 @@ func (s *ProcessingTestSuite) TestResultSizeLimit() {
 			po.Rotate = tc.rotate
 			po.Padding = tc.padding
 
-			result, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+			result, err := ProcessImage(context.Background(), imgdata, po, nil)
 
 			s.Require().NoError(err)
 			s.Require().NotNil(result)
@@ -1001,7 +996,7 @@ func (s *ProcessingTestSuite) TestImageResolutionTooLarge() {
 	po.SecurityOptions.MaxSrcResolution = 1
 
 	imgdata := s.openFile("test2.jpg")
-	_, err := ProcessImage(context.Background(), imgdata, po, nil, s.idf)
+	_, err := ProcessImage(context.Background(), imgdata, po, nil)
 
 	s.Require().Error(err)
 	s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())

+ 5 - 0
server/server.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	golog "log"
+	"net"
 	"net/http"
 
 	log "github.com/sirupsen/logrus"
@@ -22,6 +23,7 @@ const (
 type Server struct {
 	router *Router
 	server *http.Server
+	Addr   net.Addr
 }
 
 // Start starts the http server. cancel is called in case server failed to start, but it happened
@@ -42,6 +44,8 @@ func Start(cancel context.CancelFunc, router *Router) (*Server, error) {
 		"", 0,
 	)
 
+	addr := l.Addr()
+
 	srv := &http.Server{
 		Handler:        router,
 		ReadTimeout:    router.config.ReadRequestTimeout,
@@ -68,6 +72,7 @@ func Start(cancel context.CancelFunc, router *Router) (*Server, error) {
 	return &Server{
 		router: router,
 		server: srv,
+		Addr:   addr,
 	}, nil
 }
 

+ 2 - 7
svg/svg_test.go

@@ -11,7 +11,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/fetcher"
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/testutil"
-	"github.com/imgproxy/imgproxy/v3/transport"
 )
 
 type SvgTestSuite struct {
@@ -22,12 +21,8 @@ type SvgTestSuite struct {
 func (s *SvgTestSuite) SetupSuite() {
 	config.Reset()
 
-	trc := transport.NewDefaultConfig()
-	tr, err := transport.New(&trc)
-	s.Require().NoError(err)
-
-	fc := fetcher.NewDefaultConfig()
-	f, err := fetcher.New(tr, &fc)
+	c := fetcher.NewDefaultConfig()
+	f, err := fetcher.New(&c)
 	s.Require().NoError(err)
 
 	s.idf = imagedata.NewFactory(f)