request.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package processing
  2. import (
  3. "context"
  4. "errors"
  5. "net/http"
  6. "github.com/imgproxy/imgproxy/v3/fetcher"
  7. "github.com/imgproxy/imgproxy/v3/handlers"
  8. "github.com/imgproxy/imgproxy/v3/headerwriter"
  9. "github.com/imgproxy/imgproxy/v3/ierrors"
  10. "github.com/imgproxy/imgproxy/v3/imagetype"
  11. "github.com/imgproxy/imgproxy/v3/monitoring"
  12. "github.com/imgproxy/imgproxy/v3/monitoring/stats"
  13. "github.com/imgproxy/imgproxy/v3/options"
  14. "github.com/imgproxy/imgproxy/v3/server"
  15. "github.com/imgproxy/imgproxy/v3/vips"
  16. )
  17. // request holds the parameters and state for a single request request
  18. type request struct {
  19. HandlerContext
  20. reqID string
  21. req *http.Request
  22. rw http.ResponseWriter
  23. config *Config
  24. po *options.ProcessingOptions
  25. imageURL string
  26. monitoringMeta monitoring.Meta
  27. hwr *headerwriter.Request
  28. }
  29. // execute handles the actual processing logic
  30. func (r *request) execute(ctx context.Context) error {
  31. // Check if we can save the resulting image
  32. canSave := vips.SupportsSave(r.po.Format) ||
  33. r.po.Format == imagetype.Unknown ||
  34. r.po.Format == imagetype.SVG
  35. if !canSave {
  36. return handlers.NewCantSaveError(r.po.Format)
  37. }
  38. // Acquire queue semaphore (if enabled)
  39. releaseQueueSem, err := r.Semaphores().AcquireQueue()
  40. if err != nil {
  41. return err
  42. }
  43. defer releaseQueueSem()
  44. // Acquire processing semaphore
  45. releaseProcessingSem, err := r.acquireProcessingSem(ctx)
  46. if err != nil {
  47. return err
  48. }
  49. defer releaseProcessingSem()
  50. // Deal with processing image counter
  51. stats.IncImagesInProgress()
  52. defer stats.DecImagesInProgress()
  53. // Response status code is OK by default
  54. statusCode := http.StatusOK
  55. // Request headers
  56. imgRequestHeaders := r.makeImageRequestHeaders()
  57. // create download options
  58. do := r.makeDownloadOptions(ctx, imgRequestHeaders)
  59. // Fetch image actual
  60. originData, originHeaders, err := r.fetchImage(ctx, do)
  61. if err == nil {
  62. defer originData.Close() // if any originData has been opened, we need to close it
  63. }
  64. // Check that image detection didn't take too long
  65. if terr := server.CheckTimeout(ctx); terr != nil {
  66. return ierrors.Wrap(terr, 0, ierrors.WithCategory(handlers.CategoryTimeout))
  67. }
  68. // Respond with NotModified if image was not modified
  69. var nmErr fetcher.NotModifiedError
  70. if errors.As(err, &nmErr) {
  71. r.hwr.SetOriginHeaders(nmErr.Headers())
  72. return r.respondWithNotModified()
  73. }
  74. // Prepare to write image response headers
  75. r.hwr.SetOriginHeaders(originHeaders)
  76. // If error is not related to NotModified, respond with fallback image and replace image data
  77. if err != nil {
  78. originData, statusCode, err = r.handleDownloadError(ctx, err)
  79. if err != nil {
  80. return err
  81. }
  82. }
  83. // Check if image supports load from origin format
  84. if !vips.SupportsLoad(originData.Format()) {
  85. return handlers.NewCantLoadError(originData.Format())
  86. }
  87. // Actually process the image
  88. result, err := r.processImage(ctx, originData)
  89. // Let's close resulting image data only if it differs from the source image data
  90. if result != nil && result.OutData != nil && result.OutData != originData {
  91. defer result.OutData.Close()
  92. }
  93. // First, check if the processing error wasn't caused by an image data error
  94. if derr := originData.Error(); derr != nil {
  95. return ierrors.Wrap(derr, 0, ierrors.WithCategory(handlers.CategoryDownload))
  96. }
  97. // If it wasn't, than it was a processing error
  98. if err != nil {
  99. return ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryProcessing))
  100. }
  101. // Write debug headers. It seems unlogical to move they to headerwriter since they're
  102. // not used anywhere else.
  103. err = r.writeDebugHeaders(result, originData)
  104. if err != nil {
  105. return ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryImageDataSize))
  106. }
  107. // Responde with actual image
  108. err = r.respondWithImage(statusCode, result.OutData)
  109. if err != nil {
  110. return err
  111. }
  112. return nil
  113. }