1
0

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