mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-10 20:22:31 +02:00
144 lines
4.0 KiB
Go
144 lines
4.0 KiB
Go
package processing
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/fetcher"
|
|
"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
|
|
reqID string
|
|
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
|
|
func (r *request) execute(ctx context.Context) error {
|
|
// Check if we can save the resulting image
|
|
canSave := vips.SupportsSave(r.po.Format) ||
|
|
r.po.Format == imagetype.Unknown ||
|
|
r.po.Format == imagetype.SVG
|
|
|
|
if !canSave {
|
|
return handlers.NewCantSaveError(r.po.Format)
|
|
}
|
|
|
|
// Acquire queue semaphore (if enabled)
|
|
releaseQueueSem, err := r.semaphores.AcquireQueue()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer releaseQueueSem()
|
|
|
|
// Acquire processing semaphore
|
|
releaseProcessingSem, err := r.acquireProcessingSem(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer releaseProcessingSem()
|
|
|
|
// Deal with processing image counter
|
|
stats.IncImagesInProgress()
|
|
defer stats.DecImagesInProgress()
|
|
|
|
// Response status code is OK by default
|
|
statusCode := http.StatusOK
|
|
|
|
// Request headers
|
|
imgRequestHeaders := r.makeImageRequestHeaders()
|
|
|
|
// create download options
|
|
do := r.makeDownloadOptions(ctx, imgRequestHeaders)
|
|
|
|
// Fetch image actual
|
|
originData, originHeaders, err := r.fetchImage(ctx, do)
|
|
if err == nil {
|
|
defer originData.Close() // if any originData has been opened, we need to close it
|
|
}
|
|
|
|
// Check that image detection didn't take too long
|
|
if terr := server.CheckTimeout(ctx); terr != nil {
|
|
return ierrors.Wrap(terr, 0, ierrors.WithCategory(handlers.CategoryTimeout))
|
|
}
|
|
|
|
// Respond with NotModified if image was not modified
|
|
var nmErr fetcher.NotModifiedError
|
|
|
|
if errors.As(err, &nmErr) {
|
|
r.hwr.SetOriginHeaders(nmErr.Headers())
|
|
|
|
return r.respondWithNotModified()
|
|
}
|
|
|
|
// Prepare to write image response headers
|
|
r.hwr.SetOriginHeaders(originHeaders)
|
|
|
|
// If error is not related to NotModified, respond with fallback image and replace image data
|
|
if err != nil {
|
|
originData, statusCode, err = r.handleDownloadError(ctx, err)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Check if image supports load from origin format
|
|
if !vips.SupportsLoad(originData.Format()) {
|
|
return handlers.NewCantLoadError(originData.Format())
|
|
}
|
|
|
|
// Actually process the image
|
|
result, err := r.processImage(ctx, originData)
|
|
|
|
// Let's close resulting image data only if it differs from the source image data
|
|
if result != nil && result.OutData != nil && result.OutData != originData {
|
|
defer result.OutData.Close()
|
|
}
|
|
|
|
// First, check if the processing error wasn't caused by an image data error
|
|
if derr := originData.Error(); derr != nil {
|
|
return ierrors.Wrap(derr, 0, ierrors.WithCategory(handlers.CategoryDownload))
|
|
}
|
|
|
|
// If it wasn't, than it was a processing error
|
|
if err != nil {
|
|
return ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryProcessing))
|
|
}
|
|
|
|
// Write debug headers. It seems unlogical to move they to headerwriter since they're
|
|
// not used anywhere else.
|
|
err = r.writeDebugHeaders(result, originData)
|
|
if err != nil {
|
|
return ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryImageDataSize))
|
|
}
|
|
|
|
// Responde with actual image
|
|
err = r.respondWithImage(statusCode, result.OutData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|