Преглед на файлове

Provide shared resources to handlers via HandlerContext interface

DarthSim преди 3 седмици
родител
ревизия
53645688fb
променени са 4 файла, в които са добавени 98 реда и са изтрити 82 реда
  1. 31 34
      handlers/processing/handler.go
  2. 4 7
      handlers/processing/request.go
  3. 14 12
      handlers/processing/request_methods.go
  4. 49 29
      imgproxy.go

+ 31 - 34
handlers/processing/handler.go

@@ -19,25 +19,27 @@ import (
 	"github.com/imgproxy/imgproxy/v3/semaphores"
 )
 
+// HandlerContext provides access to shared handler dependencies
+type HandlerContext interface {
+	HeaderWriter() *headerwriter.Writer
+	Semaphores() *semaphores.Semaphores
+	FallbackImage() auximageprovider.Provider
+	WatermarkImage() auximageprovider.Provider
+	ImageDataFactory() *imagedata.Factory
+}
+
 // Handler handles image processing requests
 type Handler struct {
-	hw             *headerwriter.Writer // Configured HeaderWriter instance
-	stream         *stream.Handler      // Stream handler for raw image streaming
-	config         *Config              // Handler configuration
-	semaphores     *semaphores.Semaphores
-	fallbackImage  auximageprovider.Provider
-	watermarkImage auximageprovider.Provider
-	idf            *imagedata.Factory
+	HandlerContext
+
+	stream *stream.Handler // Stream handler for raw image streaming
+	config *Config         // Handler configuration
 }
 
 // New creates new handler object
 func New(
+	hCtx HandlerContext,
 	stream *stream.Handler,
-	hw *headerwriter.Writer,
-	semaphores *semaphores.Semaphores,
-	fi auximageprovider.Provider,
-	wi auximageprovider.Provider,
-	idf *imagedata.Factory,
 	config *Config,
 ) (*Handler, error) {
 	if err := config.Validate(); err != nil {
@@ -45,13 +47,9 @@ func New(
 	}
 
 	return &Handler{
-		hw:             hw,
+		HandlerContext: hCtx,
 		config:         config,
 		stream:         stream,
-		semaphores:     semaphores,
-		fallbackImage:  fi,
-		watermarkImage: wi,
-		idf:            idf,
 	}, nil
 }
 
@@ -59,49 +57,48 @@ func New(
 func (h *Handler) Execute(
 	reqID string,
 	rw http.ResponseWriter,
-	imageRequest *http.Request,
+	req *http.Request,
 ) error {
 	// Increment the number of requests in progress
 	stats.IncRequestsInProgress()
 	defer stats.DecRequestsInProgress()
 
-	ctx := imageRequest.Context()
+	ctx := req.Context()
 
 	// Verify URL signature and extract image url and processing options
-	imageURL, po, mm, err := h.newRequest(ctx, imageRequest)
+	imageURL, po, mm, err := h.newRequest(ctx, req)
 	if err != nil {
 		return err
 	}
 
 	// if processing options indicate raw image streaming, stream it and return
 	if po.Raw {
-		return h.stream.Execute(ctx, imageRequest, imageURL, reqID, po, rw)
+		return h.stream.Execute(ctx, req, imageURL, reqID, po, rw)
 	}
 
-	req := &request{
-		handler:        h,
-		imageRequest:   imageRequest,
+	hReq := &request{
+		HandlerContext: h,
+
 		reqID:          reqID,
+		req:            req,
 		rw:             rw,
 		config:         h.config,
 		po:             po,
 		imageURL:       imageURL,
 		monitoringMeta: mm,
-		semaphores:     h.semaphores,
-		hwr:            h.hw.NewRequest(),
-		idf:            h.idf,
+		hwr:            h.HeaderWriter().NewRequest(),
 	}
 
-	return req.execute(ctx)
+	return hReq.execute(ctx)
 }
 
 // newRequest extracts image url and processing options from request URL and verifies them
 func (h *Handler) newRequest(
 	ctx context.Context,
-	imageRequest *http.Request,
+	req *http.Request,
 ) (string, *options.ProcessingOptions, monitoring.Meta, error) {
 	// let's extract signature and valid request path from a request
-	path, signature, err := handlers.SplitPathSignature(imageRequest)
+	path, signature, err := handlers.SplitPathSignature(req)
 	if err != nil {
 		return "", nil, nil, err
 	}
@@ -112,7 +109,7 @@ func (h *Handler) newRequest(
 	}
 
 	// parse image url and processing options
-	po, imageURL, err := options.ParsePath(path, imageRequest.Header)
+	po, imageURL, err := options.ParsePath(path, req.Header)
 	if err != nil {
 		return "", nil, nil, ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryPathParsing))
 	}
@@ -127,9 +124,9 @@ func (h *Handler) newRequest(
 	}
 
 	// set error reporting and monitoring context
-	errorreport.SetMetadata(imageRequest, "Source Image URL", imageURL)
-	errorreport.SetMetadata(imageRequest, "Source Image Origin", imageOrigin)
-	errorreport.SetMetadata(imageRequest, "Processing Options", po)
+	errorreport.SetMetadata(req, "Source Image URL", imageURL)
+	errorreport.SetMetadata(req, "Source Image Origin", imageOrigin)
+	errorreport.SetMetadata(req, "Processing Options", po)
 
 	monitoring.SetMetadata(ctx, mm)
 

+ 4 - 7
handlers/processing/request.go

@@ -9,29 +9,26 @@ import (
 	"github.com/imgproxy/imgproxy/v3/handlers"
 	"github.com/imgproxy/imgproxy/v3/headerwriter"
 	"github.com/imgproxy/imgproxy/v3/ierrors"
-	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
 	"github.com/imgproxy/imgproxy/v3/monitoring"
 	"github.com/imgproxy/imgproxy/v3/monitoring/stats"
 	"github.com/imgproxy/imgproxy/v3/options"
-	"github.com/imgproxy/imgproxy/v3/semaphores"
 	"github.com/imgproxy/imgproxy/v3/server"
 	"github.com/imgproxy/imgproxy/v3/vips"
 )
 
 // request holds the parameters and state for a single request request
 type request struct {
-	handler        *Handler
-	imageRequest   *http.Request
+	HandlerContext
+
 	reqID          string
+	req            *http.Request
 	rw             http.ResponseWriter
 	config         *Config
 	po             *options.ProcessingOptions
 	imageURL       string
 	monitoringMeta monitoring.Meta
-	semaphores     *semaphores.Semaphores
 	hwr            *headerwriter.Request
-	idf            *imagedata.Factory
 }
 
 // execute handles the actual processing logic
@@ -46,7 +43,7 @@ func (r *request) execute(ctx context.Context) error {
 	}
 
 	// Acquire queue semaphore (if enabled)
-	releaseQueueSem, err := r.semaphores.AcquireQueue()
+	releaseQueueSem, err := r.Semaphores().AcquireQueue()
 	if err != nil {
 		return err
 	}

+ 14 - 12
handlers/processing/request_methods.go

@@ -25,12 +25,12 @@ func (r *request) makeImageRequestHeaders() http.Header {
 
 	// If ETag is enabled, we forward If-None-Match header
 	if r.config.ETagEnabled {
-		h.Set(httpheaders.IfNoneMatch, r.imageRequest.Header.Get(httpheaders.IfNoneMatch))
+		h.Set(httpheaders.IfNoneMatch, r.req.Header.Get(httpheaders.IfNoneMatch))
 	}
 
 	// If LastModified is enabled, we forward If-Modified-Since header
 	if r.config.LastModifiedEnabled {
-		h.Set(httpheaders.IfModifiedSince, r.imageRequest.Header.Get(httpheaders.IfModifiedSince))
+		h.Set(httpheaders.IfModifiedSince, r.req.Header.Get(httpheaders.IfModifiedSince))
 	}
 
 	return h
@@ -40,7 +40,7 @@ func (r *request) makeImageRequestHeaders() http.Header {
 func (r *request) acquireProcessingSem(ctx context.Context) (context.CancelFunc, error) {
 	defer monitoring.StartQueueSegment(ctx)()
 
-	fn, err := r.semaphores.AcquireProcessing(ctx)
+	fn, err := r.Semaphores().AcquireProcessing(ctx)
 	if err != nil {
 		// We don't actually need to check timeout here,
 		// but it's an easy way to check if this is an actual timeout
@@ -76,13 +76,13 @@ func (r *request) fetchImage(ctx context.Context, do imagedata.DownloadOptions)
 	var err error
 
 	if r.config.CookiePassthrough {
-		do.CookieJar, err = cookies.JarFromRequest(r.imageRequest)
+		do.CookieJar, err = cookies.JarFromRequest(r.req)
 		if err != nil {
 			return nil, nil, ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryDownload))
 		}
 	}
 
-	return r.idf.DownloadAsync(ctx, r.imageURL, "source image", do)
+	return r.ImageDataFactory().DownloadAsync(ctx, r.imageURL, "source image", do)
 }
 
 // handleDownloadError replaces the image data with fallback image if needed
@@ -103,7 +103,7 @@ func (r *request) handleDownloadError(
 
 	// We didn't return, so we have to report error
 	if err.ShouldReport() {
-		errorreport.Report(err, r.imageRequest)
+		errorreport.Report(err, r.req)
 	}
 
 	log.
@@ -134,16 +134,18 @@ func (r *request) getFallbackImage(
 	ctx context.Context,
 	po *options.ProcessingOptions,
 ) (imagedata.ImageData, http.Header) {
-	if r.handler.fallbackImage == nil {
+	fbi := r.FallbackImage()
+
+	if fbi == nil {
 		return nil, nil
 	}
 
-	data, h, err := r.handler.fallbackImage.Get(ctx, po)
+	data, h, err := fbi.Get(ctx, po)
 	if err != nil {
 		log.Warning(err.Error())
 
 		if ierr := r.wrapDownloadingErr(err); ierr.ShouldReport() {
-			errorreport.Report(ierr, r.imageRequest)
+			errorreport.Report(ierr, r.req)
 		}
 
 		return nil, nil
@@ -155,7 +157,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)
+	return processing.ProcessImage(ctx, originData, r.po, r.WatermarkImage())
 }
 
 // writeDebugHeaders writes debug headers (X-Origin-*, X-Result-*) to the response
@@ -200,7 +202,7 @@ func (r *request) respondWithNotModified() error {
 	r.rw.WriteHeader(http.StatusNotModified)
 
 	server.LogResponse(
-		r.reqID, r.imageRequest, http.StatusNotModified, nil,
+		r.reqID, r.req, http.StatusNotModified, nil,
 		log.Fields{
 			"image_url":          r.imageURL,
 			"processing_options": r.po,
@@ -256,7 +258,7 @@ func (r *request) respondWithImage(statusCode int, resultData imagedata.ImageDat
 	}
 
 	server.LogResponse(
-		r.reqID, r.imageRequest, statusCode, ierr,
+		r.reqID, r.req, statusCode, ierr,
 		log.Fields{
 			"image_url":          r.imageURL,
 			"processing_options": r.po,

+ 49 - 29
imgproxy.go

@@ -34,14 +34,14 @@ type ImgproxyHandlers struct {
 
 // Imgproxy holds all the components needed for imgproxy to function
 type Imgproxy struct {
-	HeaderWriter     *headerwriter.Writer
-	Semaphores       *semaphores.Semaphores
-	FallbackImage    auximageprovider.Provider
-	WatermarkImage   auximageprovider.Provider
-	Fetcher          *fetcher.Fetcher
-	ImageDataFactory *imagedata.Factory
-	Handlers         ImgproxyHandlers
-	Config           *Config
+	headerWriter     *headerwriter.Writer
+	semaphores       *semaphores.Semaphores
+	fallbackImage    auximageprovider.Provider
+	watermarkImage   auximageprovider.Provider
+	fetcher          *fetcher.Fetcher
+	imageDataFactory *imagedata.Factory
+	handlers         ImgproxyHandlers
+	config           *Config
 }
 
 // New creates a new imgproxy instance
@@ -74,25 +74,25 @@ func New(ctx context.Context, config *Config) (*Imgproxy, error) {
 	}
 
 	imgproxy := &Imgproxy{
-		HeaderWriter:     headerWriter,
-		Semaphores:       semaphores,
-		FallbackImage:    fallbackImage,
-		WatermarkImage:   watermarkImage,
-		Fetcher:          fetcher,
-		ImageDataFactory: idf,
-		Config:           config,
+		headerWriter:     headerWriter,
+		semaphores:       semaphores,
+		fallbackImage:    fallbackImage,
+		watermarkImage:   watermarkImage,
+		fetcher:          fetcher,
+		imageDataFactory: idf,
+		config:           config,
 	}
 
-	imgproxy.Handlers.Health = healthhandler.New()
-	imgproxy.Handlers.Landing = landinghandler.New()
+	imgproxy.handlers.Health = healthhandler.New()
+	imgproxy.handlers.Landing = landinghandler.New()
 
-	imgproxy.Handlers.Stream, err = streamhandler.New(&config.Handlers.Stream, headerWriter, fetcher)
+	imgproxy.handlers.Stream, err = streamhandler.New(&config.Handlers.Stream, headerWriter, fetcher)
 	if err != nil {
 		return nil, err
 	}
 
-	imgproxy.Handlers.Processing, err = processinghandler.New(
-		imgproxy.Handlers.Stream, headerWriter, semaphores, fallbackImage, watermarkImage, idf, &config.Handlers.Processing,
+	imgproxy.handlers.Processing, err = processinghandler.New(
+		imgproxy, imgproxy.handlers.Stream, &config.Handlers.Processing,
 	)
 	if err != nil {
 		return nil, err
@@ -103,22 +103,22 @@ func New(ctx context.Context, config *Config) (*Imgproxy, error) {
 
 // BuildRouter sets up the HTTP routes and middleware
 func (i *Imgproxy) BuildRouter() (*server.Router, error) {
-	r, err := server.NewRouter(&i.Config.Server)
+	r, err := server.NewRouter(&i.config.Server)
 	if err != nil {
 		return nil, err
 	}
 
-	r.GET("/", i.Handlers.Landing.Execute)
-	r.GET("", i.Handlers.Landing.Execute)
+	r.GET("/", i.handlers.Landing.Execute)
+	r.GET("", i.handlers.Landing.Execute)
 
 	r.GET(faviconPath, r.NotFoundHandler).Silent()
-	r.GET(healthPath, i.Handlers.Health.Execute).Silent()
-	if i.Config.Server.HealthCheckPath != "" {
-		r.GET(i.Config.Server.HealthCheckPath, i.Handlers.Health.Execute).Silent()
+	r.GET(healthPath, i.handlers.Health.Execute).Silent()
+	if i.config.Server.HealthCheckPath != "" {
+		r.GET(i.config.Server.HealthCheckPath, i.handlers.Health.Execute).Silent()
 	}
 
 	r.GET(
-		"/*", i.Handlers.Processing.Execute,
+		"/*", i.handlers.Processing.Execute,
 		r.WithSecret, r.WithCORS, r.WithPanic, r.WithReportError, r.WithMonitoring,
 	)
 
@@ -163,7 +163,7 @@ func (i *Imgproxy) StartServer(ctx context.Context, hasStarted chan net.Addr) er
 
 // startMemoryTicker starts a ticker that periodically frees memory and optionally logs memory stats
 func (i *Imgproxy) startMemoryTicker(ctx context.Context) {
-	ticker := time.NewTicker(i.Config.Server.FreeMemoryInterval)
+	ticker := time.NewTicker(i.config.Server.FreeMemoryInterval)
 	defer ticker.Stop()
 
 	for {
@@ -173,9 +173,29 @@ func (i *Imgproxy) startMemoryTicker(ctx context.Context) {
 		case <-ticker.C:
 			memory.Free()
 
-			if i.Config.Server.LogMemStats {
+			if i.config.Server.LogMemStats {
 				memory.LogStats()
 			}
 		}
 	}
 }
+
+func (i *Imgproxy) HeaderWriter() *headerwriter.Writer {
+	return i.headerWriter
+}
+
+func (i *Imgproxy) Semaphores() *semaphores.Semaphores {
+	return i.semaphores
+}
+
+func (i *Imgproxy) FallbackImage() auximageprovider.Provider {
+	return i.fallbackImage
+}
+
+func (i *Imgproxy) WatermarkImage() auximageprovider.Provider {
+	return i.watermarkImage
+}
+
+func (i *Imgproxy) ImageDataFactory() *imagedata.Factory {
+	return i.imageDataFactory
+}