浏览代码

Streamer request

Viktor Sokolov 2 月之前
父节点
当前提交
b4459da738
共有 6 个文件被更改,包括 66 次插入65 次删除
  1. 24 24
      headerwriter/writer.go
  2. 11 11
      headerwriter/writer_test.go
  3. 2 12
      imagestreamer/service.go
  4. 18 6
      imagestreamer/streamer.go
  5. 1 2
      imagestreamer/streamer_test.go
  6. 10 10
      processing_handler.go

+ 24 - 24
headerwriter/writer.go

@@ -44,9 +44,9 @@ func (w *Writer) SetMaxAge(maxAge int) {
 	}
 }
 
-// WriteIsFallbackImage sets the Fallback-Image header to
+// SetIsFallbackImage sets the Fallback-Image header to
 // indicate that the fallback image was used.
-func (w *Writer) WriteIsFallbackImage() {
+func (w *Writer) SetIsFallbackImage() {
 	w.res.Set("Fallback-Image", "1")
 }
 
@@ -73,8 +73,8 @@ func (w *Writer) SetMaxAgeFromExpires(expires *time.Time) {
 	}
 }
 
-// WriteLastModified sets the Last-Modified header from request
-func (w *Writer) WriteLastModified() {
+// SetLastModified sets the Last-Modified header from request
+func (w *Writer) SetLastModified() {
 	if !w.config.LastModifiedEnabled {
 		return
 	}
@@ -87,8 +87,8 @@ func (w *Writer) WriteLastModified() {
 	w.res.Set(httpheaders.LastModified, val)
 }
 
-// WriteVary sets the Vary header
-func (w *Writer) WriteVary() {
+// SetVary sets the Vary header
+func (w *Writer) SetVary() {
 	vary := make([]string, 0)
 
 	if w.config.SetVaryAccept {
@@ -129,15 +129,15 @@ func (w *Writer) CopyFrom(headers http.Header, only []string) {
 	}
 }
 
-// WriteContentLength sets the Content-Length header
-func (w *Writer) WriteContentLength(contentLength int) {
+// SetContentLength sets the Content-Length header
+func (w *Writer) SetContentLength(contentLength int) {
 	if contentLength > 0 {
 		w.res.Set(httpheaders.ContentLength, strconv.Itoa(contentLength))
 	}
 }
 
-// WriteContentDisposition sets the Content-Disposition header
-func (w *Writer) WriteContentDisposition(filename, ext string, returnAttachment bool) {
+// SetContentDisposition sets the Content-Disposition header
+func (w *Writer) SetContentDisposition(filename, ext string, returnAttachment bool) {
 	disposition := "inline"
 
 	if returnAttachment {
@@ -149,13 +149,13 @@ func (w *Writer) WriteContentDisposition(filename, ext string, returnAttachment
 	w.res.Set(httpheaders.ContentDisposition, value)
 }
 
-func (w *Writer) WriteContentType(mime string) {
+func (w *Writer) SetContentType(mime string) {
 	w.res.Set(httpheaders.ContentType, mime)
 }
 
 // writeCanonical sets the Link header with the canonical URL.
 // It is mandatory for any response if enabled in the configuration.
-func (b *Writer) WriteCanonical() {
+func (b *Writer) SetCanonical() {
 	if !b.config.SetCanonicalHeader {
 		return
 	}
@@ -166,13 +166,13 @@ func (b *Writer) WriteCanonical() {
 	}
 }
 
-// writeCacheControlNoCache sets the Cache-Control header to no-cache (default).
-func (w *Writer) writeCacheControlNoCache() {
+// setCacheControlNoCache sets the Cache-Control header to no-cache (default).
+func (w *Writer) setCacheControlNoCache() {
 	w.res.Set(httpheaders.CacheControl, "no-cache")
 }
 
-// writeCacheControlMaxAge sets the Cache-Control header with max-age.
-func (w *Writer) writeCacheControlMaxAge() {
+// setCacheControlMaxAge sets the Cache-Control header with max-age.
+func (w *Writer) setCacheControlMaxAge() {
 	maxAge := w.maxAge
 
 	if maxAge <= 0 {
@@ -184,9 +184,9 @@ func (w *Writer) writeCacheControlMaxAge() {
 	}
 }
 
-// writeCacheControlPassthrough sets the Cache-Control header from the request
+// setCacheControlPassthrough sets the Cache-Control header from the request
 // if passthrough is enabled in the configuration.
-func (w *Writer) writeCacheControlPassthrough() bool {
+func (w *Writer) setCacheControlPassthrough() bool {
 	if !w.config.CacheControlPassthrough || w.maxAge > 0 {
 		return false
 	}
@@ -205,20 +205,20 @@ func (w *Writer) writeCacheControlPassthrough() bool {
 	return false
 }
 
-// writeCSP sets the Content-Security-Policy header to prevent script execution.
-func (w *Writer) writeCSP() {
+// setCSP sets the Content-Security-Policy header to prevent script execution.
+func (w *Writer) setCSP() {
 	w.res.Set("Content-Security-Policy", "script-src 'none'")
 }
 
 // Write writes the headers to the response writer
 func (w *Writer) Write(rw http.ResponseWriter) {
-	w.writeCacheControlNoCache()
+	w.setCacheControlNoCache()
 
-	if !w.writeCacheControlPassthrough() {
-		w.writeCacheControlMaxAge()
+	if !w.setCacheControlPassthrough() {
+		w.setCacheControlMaxAge()
 	}
 
-	w.writeCSP()
+	w.setCSP()
 
 	for key, values := range w.res {
 		for _, value := range values {

+ 11 - 11
headerwriter/writer_test.go

@@ -89,7 +89,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 				DefaultTTL:         3600,
 			},
 			fn: func(w *Writer) {
-				w.WriteCanonical()
+				w.SetCanonical()
 			},
 		},
 		{
@@ -118,7 +118,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 				DefaultTTL:         3600,
 			},
 			fn: func(w *Writer) {
-				w.WriteCanonical()
+				w.SetCanonical()
 			},
 		},
 		{
@@ -136,7 +136,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 				DefaultTTL:          3600,
 			},
 			fn: func(w *Writer) {
-				w.WriteLastModified()
+				w.SetLastModified()
 			},
 		},
 		{
@@ -180,7 +180,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 				SetVaryAccept:     true,
 			},
 			fn: func(w *Writer) {
-				w.WriteVary()
+				w.SetVary()
 			},
 		},
 		{
@@ -223,7 +223,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 			},
 			config: Config{},
 			fn: func(w *Writer) {
-				w.WriteContentLength(123)
+				w.SetContentLength(123)
 			},
 		},
 		{
@@ -236,7 +236,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 			},
 			config: Config{},
 			fn: func(w *Writer) {
-				w.WriteContentDisposition("file", ".txt", false)
+				w.SetContentDisposition("file", ".txt", false)
 			},
 		},
 		{
@@ -249,7 +249,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 			},
 			config: Config{},
 			fn: func(w *Writer) {
-				w.WriteContentDisposition("file", ".txt", true)
+				w.SetContentDisposition("file", ".txt", true)
 			},
 		},
 		{
@@ -262,7 +262,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 			},
 			config: Config{},
 			fn: func(w *Writer) {
-				w.WriteContentType("image/png")
+				w.SetContentType("image/png")
 			},
 		},
 		{
@@ -275,7 +275,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 			},
 			config: Config{},
 			fn: func(w *Writer) {
-				w.WriteIsFallbackImage()
+				w.SetIsFallbackImage()
 			},
 		},
 		{
@@ -319,7 +319,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 				SetVaryAccept: true,
 			},
 			fn: func(w *Writer) {
-				w.WriteVary()
+				w.SetVary()
 			},
 		},
 		{
@@ -334,7 +334,7 @@ func (s *HeaderWriterSuite) TestHeaderCases() {
 				EnableClientHints: true,
 			},
 			fn: func(w *Writer) {
-				w.WriteVary()
+				w.SetVary()
 			},
 		},
 	}

+ 2 - 12
imagestreamer/service.go

@@ -6,7 +6,6 @@ import (
 
 	"github.com/imgproxy/imgproxy/v3/headerwriter"
 	"github.com/imgproxy/imgproxy/v3/imagefetcher"
-	"github.com/imgproxy/imgproxy/v3/options"
 )
 
 // Service is a struct which stores dependencies for the image streaming service
@@ -16,28 +15,19 @@ type Service struct {
 	headerWriterFactory *headerwriter.Factory
 }
 
-// Request holds parameters for the image streaming options
-// NOTE: This struct looks like it could be reused in the processing handler as a request context
-type Request struct {
-	UserRequest       *http.Request              // Original user request to imgproxy
-	ImageURL          string                     // URL of the image to be streamed
-	ReqID             string                     // Unique identifier for the request
-	ProcessingOptions *options.ProcessingOptions // Processing options for the image
-	Rw                http.ResponseWriter        // Response writer to write the streamed image
-}
-
 // NewService creates a new service instance with the provided configuration
 func NewService(config *Config, fetcher *imagefetcher.Fetcher, headerWriterFactory *headerwriter.Factory) *Service {
 	return &Service{config: config, fetcher: fetcher, headerWriterFactory: headerWriterFactory}
 }
 
 // Stream streams the image based on the provided request
-func (f *Service) Stream(ctx context.Context, r *Request) {
+func (f *Service) Stream(ctx context.Context, r *Request, rr http.ResponseWriter) {
 	s := &streamer{
 		fetcher:             f.fetcher,
 		headerWriterFactory: f.headerWriterFactory,
 		config:              f.config,
 		p:                   r,
+		rw:                  rr,
 	}
 
 	s.Stream(ctx)

+ 18 - 6
imagestreamer/streamer.go

@@ -13,6 +13,7 @@ import (
 	"github.com/imgproxy/imgproxy/v3/imagefetcher"
 	"github.com/imgproxy/imgproxy/v3/metrics"
 	"github.com/imgproxy/imgproxy/v3/metrics/stats"
+	"github.com/imgproxy/imgproxy/v3/options"
 	"github.com/imgproxy/imgproxy/v3/router"
 	"github.com/imgproxy/imgproxy/v3/stemext"
 	log "github.com/sirupsen/logrus"
@@ -25,19 +26,30 @@ const (
 var (
 	// streamBufPool is a sync.Pool for reusing byte slices used for streaming
 	streamBufPool = sync.Pool{
-		New: func() interface{} {
+		New: func() any {
 			buf := make([]byte, streamBufferSize)
 			return &buf
 		},
 	}
 )
 
+// Request represents an image request that will be processed by the image streamer
+// NOTE: This struct will be used as a base for the image request in the processing handler.
+// Here it's temporary.
+type Request struct {
+	UserRequest       *http.Request              // Original user request to imgproxy
+	ImageURL          string                     // URL of the image to be streamed
+	ReqID             string                     // Unique identifier for the request
+	ProcessingOptions *options.ProcessingOptions // Processing options for the image
+}
+
 // streamer handles image passthrough requests, allowing images to be streamed directly
 type streamer struct {
 	fetcher             *imagefetcher.Fetcher // Fetcher instance to handle image fetching
 	headerWriterFactory *headerwriter.Factory // Factory for creating header writers
 	config              *Config               // Configuration for the streamer
 	p                   *Request              // Streaming request
+	rw                  http.ResponseWriter   // Response writer to write the streamed image
 }
 
 // Stream handles the image passthrough request, streaming the image directly to the response writer
@@ -96,13 +108,13 @@ func (s *streamer) getPassthroughRequestHeaders() http.Header {
 
 // writeHeaders writes the headers to the response writer
 func (s *streamer) writeHeaders(r *imagefetcher.Request, res *http.Response) {
-	hw := s.headerWriterFactory.NewHeaderWriter(s.p.Rw.Header(), r.URL().String())
+	hw := s.headerWriterFactory.NewHeaderWriter(s.rw.Header(), r.URL().String())
 
 	// Copy the response headers to the header writer
 	hw.Copy(s.config.KeepResponseHeaders)
 
 	// Set the Content-Length header
-	hw.WriteContentLength(int(res.ContentLength))
+	hw.SetContentLength(int(res.ContentLength))
 
 	// Try to set correct Content-Disposition file name and extension
 	if res.StatusCode >= 200 && res.StatusCode < 300 {
@@ -115,10 +127,10 @@ func (s *streamer) writeHeaders(r *imagefetcher.Request, res *http.Response) {
 			StemExt()
 
 		// Write the Content-Disposition header
-		hw.WriteContentDisposition(stem, ext, s.p.ProcessingOptions.ReturnAttachment)
+		hw.SetContentDisposition(stem, ext, s.p.ProcessingOptions.ReturnAttachment)
 	}
 
-	hw.Write(s.p.Rw)
+	hw.Write(s.rw)
 }
 
 // sendData copies the image data from the response body to the response writer
@@ -126,7 +138,7 @@ func (s *streamer) sendData(res *http.Response) {
 	buf := streamBufPool.Get().(*[]byte)
 	defer streamBufPool.Put(buf)
 
-	_, copyerr := io.CopyBuffer(s.p.Rw, res.Body, *buf)
+	_, copyerr := io.CopyBuffer(s.rw, res.Body, *buf)
 
 	router.LogResponse(
 		s.p.ReqID, s.p.UserRequest, res.StatusCode, nil,

+ 1 - 2
imagestreamer/streamer_test.go

@@ -80,10 +80,9 @@ func (s *StreamerTestSuite) TestStreamer() {
 		ImageURL:          s.ts.URL + testFilePath,
 		ReqID:             "test-req-id",
 		ProcessingOptions: po,
-		Rw:                rr,
 	}
 
-	s.service.Stream(context.Background(), &p)
+	s.service.Stream(context.Background(), &p, rr)
 
 	// Check response body
 	respBody := rr.Body.Bytes()

+ 10 - 10
processing_handler.go

@@ -56,10 +56,10 @@ func respondWithImage(hw *headerwriter.Writer, reqID string, r *http.Request, rw
 		StemExtWithFallback()
 
 	hw.SetMaxAgeFromExpires(po.Expires)
-	hw.WriteContentDisposition(stem, ext, po.ReturnAttachment)
-	hw.WriteContentType(resultData.Type.Mime())
-	hw.WriteLastModified()
-	hw.WriteVary()
+	hw.SetContentDisposition(stem, ext, po.ReturnAttachment)
+	hw.SetContentType(resultData.Type.Mime())
+	hw.SetLastModified()
+	hw.SetVary()
 
 	// TODO: think about moving this to the headerwriter
 	if config.EnableDebugHeaders {
@@ -70,8 +70,8 @@ func respondWithImage(hw *headerwriter.Writer, reqID string, r *http.Request, rw
 		rw.Header().Set("X-Result-Height", resultData.Headers["X-Result-Height"])
 	}
 
-	hw.WriteContentLength(len(resultData.Data))
-	hw.WriteCanonical()
+	hw.SetContentLength(len(resultData.Data))
+	hw.SetCanonical()
 	hw.Write(rw)
 
 	rw.WriteHeader(statusCode)
@@ -99,7 +99,7 @@ func respondWithImage(hw *headerwriter.Writer, reqID string, r *http.Request, rw
 
 func respondWithNotModified(hw *headerwriter.Writer, reqID string, r *http.Request, rw http.ResponseWriter, po *options.ProcessingOptions, originURL string, originHeaders map[string]string) {
 	hw.SetMaxAgeFromExpires(po.Expires)
-	hw.WriteVary()
+	hw.SetVary()
 	hw.Write(rw)
 
 	rw.WriteHeader(304)
@@ -180,10 +180,9 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
 			ImageURL:          imageURL,
 			ReqID:             reqID,
 			ProcessingOptions: po,
-			Rw:                rw,
 		}
 
-		sf.Stream(ctx, &p)
+		sf.Stream(ctx, &p, rw)
 		return
 	}
 
@@ -209,6 +208,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
 		}
 	}
 
+	// ???
 	if config.LastModifiedEnabled {
 		if modifiedSince := r.Header.Get("If-Modified-Since"); len(modifiedSince) != 0 {
 			imgRequestHeader.Set("If-Modified-Since", modifiedSince)
@@ -319,7 +319,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
 		hw.SetMaxAge(config.FallbackImageTTL)
 
 		if config.FallbackImageTTL > 0 {
-			hw.WriteIsFallbackImage()
+			hw.SetIsFallbackImage()
 		}
 
 		originData = imagedata.FallbackImage