123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- 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/options"
- "github.com/imgproxy/imgproxy/v3/options/keys"
- "github.com/imgproxy/imgproxy/v3/security"
- "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
- opts *options.Options
- secops security.Options
- imageURL string
- monitoringMeta monitoring.Meta
- }
- // execute handles the actual processing logic
- func (r *request) execute(ctx context.Context) error {
- outFormat := options.Get(r.opts, keys.Format, imagetype.Unknown)
- // Check if we can save the resulting image
- canSave := vips.SupportsSave(outFormat) ||
- outFormat == imagetype.Unknown ||
- outFormat == imagetype.SVG
- if !canSave {
- return handlers.NewCantSaveError(outFormat)
- }
- // Acquire worker
- releaseWorker, err := r.acquireWorker(ctx)
- if err != nil {
- return err
- }
- defer releaseWorker()
- // Deal with processing image counter
- r.Monitoring().Stats().IncImagesInProgress()
- defer r.Monitoring().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 r.wrapDownloadingErr(derr)
- }
- // 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 err
- }
- // Responde with actual image
- err = r.respondWithImage(statusCode, result.OutData)
- if err != nil {
- return err
- }
- return nil
- }
|