123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- package processing
- import (
- "context"
- "errors"
- "net/http"
- "github.com/imgproxy/imgproxy/v3/fetcher"
- "github.com/imgproxy/imgproxy/v3/handlers"
- "github.com/imgproxy/imgproxy/v3/ierrors"
- "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/server"
- "github.com/imgproxy/imgproxy/v3/vips"
- )
- // request holds the parameters and state for a single request request
- type request struct {
- HandlerContext
- reqID string
- req *http.Request
- rw server.ResponseWriter
- config *Config
- po *options.ProcessingOptions
- imageURL string
- monitoringMeta monitoring.Meta
- }
- // 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.rw.SetOriginHeaders(nmErr.Headers())
- return r.respondWithNotModified()
- }
- // Prepare to write image response headers
- r.rw.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 responsewriter 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
- }
|