1
0

request.go 3.6 KB

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