request.go 3.7 KB

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