fetcher.go 2.6 KB

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