1
0

fetcher.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // fetcher is responsible for downloading images using HTTP requests through various protocols
  2. // defined in transport package
  3. package fetcher
  4. import (
  5. "context"
  6. "net/http"
  7. "time"
  8. "github.com/imgproxy/imgproxy/v3/fetcher/transport"
  9. "github.com/imgproxy/imgproxy/v3/fetcher/transport/common"
  10. "github.com/imgproxy/imgproxy/v3/httpheaders"
  11. )
  12. const (
  13. connectionLostError = "client connection lost" // Error message indicating a lost connection
  14. bounceDelay = 100 * time.Microsecond // Delay before retrying a request
  15. )
  16. // Fetcher is a struct that holds the HTTP client and transport for fetching images
  17. type Fetcher struct {
  18. transport *transport.Transport // Transport used for making HTTP requests
  19. config *Config // Configuration for the image fetcher
  20. }
  21. // New creates a new ImageFetcher with the provided transport
  22. func New(config *Config) (*Fetcher, error) {
  23. if err := config.Validate(); err != nil {
  24. return nil, err
  25. }
  26. transport, err := transport.New(&config.Transport)
  27. if err != nil {
  28. return nil, err
  29. }
  30. return &Fetcher{transport, config}, nil
  31. }
  32. // checkRedirect is a method that checks if the number of redirects exceeds the maximum allowed
  33. func (f *Fetcher) checkRedirect(req *http.Request, via []*http.Request) error {
  34. redirects := len(via)
  35. if redirects >= f.config.MaxRedirects {
  36. return newImageTooManyRedirectsError(redirects)
  37. }
  38. return nil
  39. }
  40. // newHttpClient returns new HTTP client
  41. func (f *Fetcher) newHttpClient() *http.Client {
  42. return &http.Client{
  43. Transport: f.transport.Transport(), // Connection pool is there
  44. CheckRedirect: f.checkRedirect,
  45. }
  46. }
  47. // NewImageFetcherRequest creates a new ImageFetcherRequest with the provided context, URL, headers, and cookie jar
  48. func (f *Fetcher) BuildRequest(ctx context.Context, url string, header http.Header, jar http.CookieJar) (*Request, error) {
  49. url = common.EscapeURL(url)
  50. // Set request timeout and get cancel function
  51. ctx, cancel := context.WithTimeout(ctx, f.config.DownloadTimeout)
  52. req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
  53. if err != nil {
  54. cancel()
  55. return nil, newImageRequestError(err)
  56. }
  57. // Check if the URL scheme is supported
  58. if !f.transport.IsProtocolRegistered(req.URL.Scheme) {
  59. cancel()
  60. return nil, newImageRequstSchemeError(req.URL.Scheme)
  61. }
  62. // Add cookies from the jar to the request (if any)
  63. if jar != nil {
  64. for _, cookie := range jar.Cookies(req.URL) {
  65. req.AddCookie(cookie)
  66. }
  67. }
  68. // Set user agent header
  69. req.Header.Set(httpheaders.UserAgent, f.config.UserAgent)
  70. // Set headers
  71. httpheaders.CopyToRequest(header, req)
  72. return &Request{f, req, cancel}, nil
  73. }