1
0

request.go 3.9 KB

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