fetcher.go 2.5 KB

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