1
0

request.go 3.6 KB

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