1
0

request.go 4.0 KB

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