fetcher.go 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  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/httpheaders"
  9. "github.com/imgproxy/imgproxy/v3/transport"
  10. "github.com/imgproxy/imgproxy/v3/transport/common"
  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(transport *transport.Transport, config *Config) (*Fetcher, error) {
  23. if err := config.Validate(); err != nil {
  24. return nil, err
  25. }
  26. return &Fetcher{transport, config}, nil
  27. }
  28. // checkRedirect is a method that checks if the number of redirects exceeds the maximum allowed
  29. func (f *Fetcher) checkRedirect(req *http.Request, via []*http.Request) error {
  30. redirects := len(via)
  31. if redirects >= f.config.MaxRedirects {
  32. return newImageTooManyRedirectsError(redirects)
  33. }
  34. return nil
  35. }
  36. // newHttpClient returns new HTTP client
  37. func (f *Fetcher) newHttpClient() *http.Client {
  38. return &http.Client{
  39. Transport: f.transport.Transport(), // Connection pool is there
  40. CheckRedirect: f.checkRedirect,
  41. }
  42. }
  43. // NewImageFetcherRequest creates a new ImageFetcherRequest with the provided context, URL, headers, and cookie jar
  44. func (f *Fetcher) BuildRequest(ctx context.Context, url string, header http.Header, jar http.CookieJar) (*Request, error) {
  45. url = common.EscapeURL(url)
  46. // Set request timeout and get cancel function
  47. ctx, cancel := context.WithTimeout(ctx, f.config.DownloadTimeout)
  48. req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
  49. if err != nil {
  50. cancel()
  51. return nil, newImageRequestError(err)
  52. }
  53. // Check if the URL scheme is supported
  54. if !f.transport.IsProtocolRegistered(req.URL.Scheme) {
  55. cancel()
  56. return nil, newImageRequstSchemeError(req.URL.Scheme)
  57. }
  58. // Add cookies from the jar to the request (if any)
  59. if jar != nil {
  60. for _, cookie := range jar.Cookies(req.URL) {
  61. req.AddCookie(cookie)
  62. }
  63. }
  64. // Set user agent header
  65. req.Header.Set(httpheaders.UserAgent, f.config.UserAgent)
  66. // Set headers
  67. httpheaders.CopyToRequest(header, req)
  68. return &Request{f, req, cancel}, nil
  69. }