request.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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 worker
  37. releaseWorker, err := r.acquireWorker(ctx)
  38. if err != nil {
  39. return err
  40. }
  41. defer releaseWorker()
  42. // Deal with processing image counter
  43. stats.IncImagesInProgress()
  44. defer stats.DecImagesInProgress()
  45. // Response status code is OK by default
  46. statusCode := http.StatusOK
  47. // Request headers
  48. imgRequestHeaders := r.makeImageRequestHeaders()
  49. // create download options
  50. do := r.makeDownloadOptions(ctx, imgRequestHeaders)
  51. // Fetch image actual
  52. originData, originHeaders, err := r.fetchImage(ctx, do)
  53. if err == nil {
  54. defer originData.Close() // if any originData has been opened, we need to close it
  55. }
  56. // Check that image detection didn't take too long
  57. if terr := server.CheckTimeout(ctx); terr != nil {
  58. return ierrors.Wrap(terr, 0, ierrors.WithCategory(handlers.CategoryTimeout))
  59. }
  60. // Respond with NotModified if image was not modified
  61. var nmErr fetcher.NotModifiedError
  62. if errors.As(err, &nmErr) {
  63. r.rw.SetOriginHeaders(nmErr.Headers())
  64. return r.respondWithNotModified()
  65. }
  66. // Prepare to write image response headers
  67. r.rw.SetOriginHeaders(originHeaders)
  68. // If error is not related to NotModified, respond with fallback image and replace image data
  69. if err != nil {
  70. originData, statusCode, err = r.handleDownloadError(ctx, err)
  71. if err != nil {
  72. return err
  73. }
  74. }
  75. // Check if image supports load from origin format
  76. if !vips.SupportsLoad(originData.Format()) {
  77. return handlers.NewCantLoadError(originData.Format())
  78. }
  79. // Actually process the image
  80. result, err := r.processImage(ctx, originData)
  81. // Let's close resulting image data only if it differs from the source image data
  82. if result != nil && result.OutData != nil && result.OutData != originData {
  83. defer result.OutData.Close()
  84. }
  85. // First, check if the processing error wasn't caused by an image data error
  86. if derr := originData.Error(); derr != nil {
  87. return r.wrapDownloadingErr(derr)
  88. }
  89. // If it wasn't, than it was a processing error
  90. if err != nil {
  91. return ierrors.Wrap(err, 0, ierrors.WithCategory(handlers.CategoryProcessing))
  92. }
  93. // Write debug headers. It seems unlogical to move they to responsewriter since they're
  94. // not used anywhere else.
  95. err = r.writeDebugHeaders(result, originData)
  96. if err != nil {
  97. return err
  98. }
  99. // Responde with actual image
  100. err = r.respondWithImage(statusCode, result.OutData)
  101. if err != nil {
  102. return err
  103. }
  104. return nil
  105. }