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.

[View source] [View on GitHub]

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.

[View on GitHub]

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

[View source] [View on GitHub]

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

[View source] [View on GitHub]

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)
[View source] [View on GitHub]

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

def skip_ssrf_protection?(uri)
  @uploader.skip_ssrf_protection
end