fetcher.go 2.5 KB

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