Class: CarrierWave::Downloader::Base

Inherits:
Object
  • Object
show all
Includes:
Utilities::Uri
Defined in:
lib/carrierwave/downloader/base.rb

Constant Summary

Constants included from Utilities::Uri

Utilities::Uri::NON_ASCII, Utilities::Uri::PATH_SAFE, Utilities::Uri::PATH_UNSAFE

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uploader) ⇒ Base

Returns a new instance of Base.



13
14
15
# File 'lib/carrierwave/downloader/base.rb', line 13

def initialize(uploader)
  @uploader = uploader
end

Instance Attribute Details

#uploaderObject (readonly)

Returns the value of attribute uploader.



11
12
13
# File 'lib/carrierwave/downloader/base.rb', line 11

def uploader
  @uploader
end

Instance Method Details

#download(url, remote_headers = {}) ⇒ Object

Downloads a file from given URL and returns a RemoteFile.

Parameters

url (String)

The URL where the remote file is stored

remote_headers (Hash)

Request headers



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/carrierwave/downloader/base.rb', line 25

def download(url, remote_headers = {})
  @current_download_retry_count = 0
  headers = remote_headers.
    reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
  uri = process_uri(url.to_s)
  begin
    if skip_ssrf_protection?(uri)
      response = OpenURI.open_uri(process_uri(url.to_s), headers)
    else
      request = nil
      if ::SsrfFilter::VERSION.to_f < 1.1
        response = SsrfFilter.get(uri, headers: headers) do |req|
          request = req
        end
      else
        response = SsrfFilter.get(uri, headers: headers, request_proc: ->(req) { request = req }) do |res|
          res.body # ensure to read body
        end
      end
      response.uri = request.uri
      response.value
    end
  rescue StandardError => e
    if @current_download_retry_count < @uploader.download_retry_count
      @current_download_retry_count += 1
      sleep @uploader.download_retry_wait_time
      retry
    else
      raise CarrierWave::DownloadError, "could not download file: #{e.message}"
    end
  end
  CarrierWave::Downloader::RemoteFile.new(response)
end

#process_uri(source) ⇒ Object

Processes the given URL by parsing it, and escaping if necessary. Public to allow overriding.

Parameters

url (String)

The URL where the remote file is stored



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/carrierwave/downloader/base.rb', line 66

def process_uri(source)
  uri = Addressable::URI.parse(source)
  uri.host = uri.normalized_host
  # Perform decode first, as the path is likely to be already encoded
  uri.path = encode_path(decode_uri(uri.path)) if uri.path =~ CarrierWave::Utilities::Uri::PATH_UNSAFE
  uri.query = encode_non_ascii(uri.query) if uri.query
  uri.fragment = encode_non_ascii(uri.fragment) if uri.fragment
  URI.parse(uri.to_s)
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError
  raise CarrierWave::DownloadError, "couldn't parse URL: #{source}"
end

#skip_ssrf_protection?(uri) ⇒ Boolean

If this returns true, SSRF protection will be bypassed. You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.

Parameters

uri (URI)

The URI where the remote file is stored

Examples

class CarrierWave::Downloader::CustomDownloader < CarrierWave::Downloader::Base
  def skip_ssrf_protection?(uri)
    uri.hostname == 'localhost' && uri.port == 80
  end
end

my_uploader.downloader = CarrierWave::Downloader::CustomDownloader

Returns:

  • (Boolean)


96
97
98
# File 'lib/carrierwave/downloader/base.rb', line 96

def skip_ssrf_protection?(uri)
  @uploader.skip_ssrf_protection
end