downloader.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Package imagedownloader provides a shared method for downloading any
  2. // images within imgproxy.
  3. package imagedownloader
  4. import (
  5. "context"
  6. "fmt"
  7. "net/http"
  8. "github.com/imgproxy/imgproxy/v3/ierrors"
  9. "github.com/imgproxy/imgproxy/v3/imagedatanew"
  10. "github.com/imgproxy/imgproxy/v3/imagefetcher"
  11. "github.com/imgproxy/imgproxy/v3/security"
  12. "github.com/imgproxy/imgproxy/v3/transport"
  13. )
  14. var (
  15. // Global downloader instance
  16. Fetcher *imagefetcher.Fetcher
  17. D *Downloader
  18. // For tests
  19. redirectAllRequestsTo string
  20. )
  21. type DownloadOptions struct {
  22. Header http.Header
  23. CookieJar http.CookieJar
  24. }
  25. // Downloader is responsible for downloading images and converting them to ImageData
  26. type Downloader struct {
  27. fetcher *imagefetcher.Fetcher
  28. }
  29. // NewDownloader creates a new Downloader with the provided fetcher and config
  30. func NewDownloader(fetcher *imagefetcher.Fetcher) *Downloader {
  31. return &Downloader{
  32. fetcher: fetcher,
  33. }
  34. }
  35. // Init initializes the global downloader
  36. func InitGlobalDownloader() error {
  37. ts, err := transport.NewTransport()
  38. if err != nil {
  39. return err
  40. }
  41. Fetcher, err := imagefetcher.NewFetcher(ts, imagefetcher.NewConfigFromEnv())
  42. if err != nil {
  43. return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't create image fetcher"))
  44. }
  45. D = NewDownloader(Fetcher)
  46. return nil
  47. }
  48. // Download downloads an image from the given URL and returns ImageData
  49. func (d *Downloader) Download(ctx context.Context, imageURL string, opts DownloadOptions, secopts security.Options) (imagedatanew.ImageData, error) {
  50. // We use this for testing
  51. if len(redirectAllRequestsTo) > 0 {
  52. imageURL = redirectAllRequestsTo
  53. }
  54. req, err := d.fetcher.BuildRequest(ctx, imageURL, opts.Header, opts.CookieJar)
  55. if err != nil {
  56. defer req.Cancel()
  57. return nil, err
  58. }
  59. res, err := req.FetchImage()
  60. if err != nil {
  61. if res != nil {
  62. res.Body.Close()
  63. }
  64. return nil, err
  65. }
  66. // Create factory with the provided security options for this request
  67. imgdata, err := imagedatanew.NewFromResponse(res, secopts)
  68. if err != nil {
  69. if res != nil {
  70. res.Body.Close()
  71. }
  72. return nil, err
  73. }
  74. return imgdata, nil
  75. }
  76. // DownloadWithDesc downloads an image from the given URL, gives error a description context and returns ImageData
  77. func (d *Downloader) DownloadWithDesc(ctx context.Context, imageURL, desc string, opts DownloadOptions, secopts security.Options) (imagedatanew.ImageData, error) {
  78. i, err := d.Download(ctx, imageURL, opts, secopts)
  79. if err != nil {
  80. return nil, ierrors.Wrap(
  81. err, 0,
  82. ierrors.WithPrefix(fmt.Sprintf("Can't download %s", desc)),
  83. )
  84. }
  85. return i, err
  86. }
  87. // Download downloads an image using the global downloader.
  88. // NOTE: This method uses globalDownloader instance. In the future, this will
  89. // be replaced with an instance everywhere.
  90. func Download(ctx context.Context, imageURL, desc string, opts DownloadOptions, secopts security.Options) (imagedatanew.ImageData, error) {
  91. return D.DownloadWithDesc(ctx, imageURL, desc, opts, secopts)
  92. }
  93. // RedirectAllRequestsTo redirects all requests to the given URL (for testing)
  94. func RedirectAllRequestsTo(u string) {
  95. redirectAllRequestsTo = u
  96. }
  97. // StopRedirectingRequests stops redirecting requests (for testing)
  98. func StopRedirectingRequests() {
  99. redirectAllRequestsTo = ""
  100. }