Class: CurlDownloadStrategy
- Inherits:
-
AbstractFileDownloadStrategy
- Object
- AbstractDownloadStrategy
- AbstractFileDownloadStrategy
- CurlDownloadStrategy
- Includes:
- Utils::Curl
- Defined in:
- download_strategy/curl_download_strategy.rb
Overview
Strategy for downloading files using curl.
Direct Known Subclasses
CurlApacheMirrorDownloadStrategy, CurlGitHubPackagesDownloadStrategy, CurlPostDownloadStrategy, Homebrew::API::SourceDownloadStrategy, HomebrewCurlDownloadStrategy, NoUnzipCurlDownloadStrategy, PyPIDownloadStrategy
Constant Summary collapse
- URLMetadata =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
url, basename, time, file_size, content_type, is_redirection
T.type_alias { [String, String, T.nilable(Time), T.nilable(Integer), T.nilable(String), T::Boolean] }
Instance Attribute Summary collapse
- #mirrors ⇒ Array<String> readonly private
Attributes inherited from AbstractDownloadStrategy
Instance Method Summary collapse
- #clear_cache ⇒ void private
-
#fetch(timeout: nil) ⇒ void
Download and cache the file at AbstractFileDownloadStrategy#cached_location.
- #initialize(url, name, version, **meta) ⇒ void constructor private
- #resolved_time_file_size(timeout: nil) ⇒ Array<([Time, nil], Integer)> private
- #total_size ⇒ Integer? private
Methods included from Utils::Curl
clear_path_cache, curl, curl_args, curl_check_http_content, curl_download, curl_executable, curl_headers, curl_http_content_headers_and_checksum, curl_output, curl_path, curl_response_follow_redirections, curl_response_last_location, curl_supports_fail_with_body?, curl_supports_tls13?, curl_version, curl_with_workarounds, http_status_ok?, parse_curl_output, parse_curl_response, url_protected_by_cloudflare?, url_protected_by_incapsula?
Methods included from SystemCommand::Mixin
#system_command, #system_command!
Methods included from Utils::Output::Mixin
#issue_reporting_message, #odebug, #odeprecated, #odie, #odisabled, #ofail, #oh1, #oh1_title, #ohai, #ohai_title, #onoe, #opoo, #opoo_outside_github_actions, #pretty_deprecated, #pretty_disabled, #pretty_duration, #pretty_install_status, #pretty_installed, #pretty_outdated, #pretty_uninstalled, #pretty_upgradable
Methods inherited from AbstractFileDownloadStrategy
#basename, #cached_location, #create_symlink_to_cached_download, #fetched_size, #symlink_location, #temporary_path
Methods inherited from AbstractDownloadStrategy
#basename, #cached_location, #fetched_size, #ohai, #quiet!, #quiet?, #source_modified_time, #source_revision, #stage
Methods included from Context
current, current=, #debug?, #quiet?, #verbose?, #with_context
Constructor Details
#initialize(url, name, version, **meta) ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'download_strategy/curl_download_strategy.rb', line 17 def initialize(url, name, version, **) @try_partial = T.let(true, T::Boolean) @mirrors = T.let(.fetch(:mirrors, []), T::Array[String]) @file_size = T.let(nil, T.nilable(Integer)) @last_modified = T.let(nil, T.nilable(Time)) # Merge `:header` with `:headers`. if (header = .delete(:header)) [:headers] ||= [] [:headers] << header end super end |
Instance Attribute Details
#mirrors ⇒ Array<String> (readonly)
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
14 15 16 |
# File 'download_strategy/curl_download_strategy.rb', line 14 def mirrors @mirrors end |
Instance Method Details
#clear_cache ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
This method returns an undefined value.
130 131 132 133 |
# File 'download_strategy/curl_download_strategy.rb', line 130 def clear_cache super rm_rf(temporary_path) end |
#fetch(timeout: nil) ⇒ void
This method returns an undefined value.
Download and cache the file at AbstractFileDownloadStrategy#cached_location.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'download_strategy/curl_download_strategy.rb', line 36 def fetch(timeout: nil) end_time = Time.now + timeout if timeout download_lock = DownloadLock.new(temporary_path) begin download_lock.lock urls = [url, *mirrors] if (domain = Homebrew::EnvConfig.artifact_domain) artifact_urls = urls.map do |u| u.sub(%r{^https?://#{GitHubPackages::URL_DOMAIN}/}o, "#{domain.chomp("/")}/") end urls = if Homebrew::EnvConfig.artifact_domain_no_fallback? artifact_urls else # Interleave: try artifact domain first, then original for each URL that was rewritten. combined = [] artifact_urls.zip(urls).each do |artifact_url, original_url| combined << artifact_url combined << original_url if original_url != artifact_url end combined end end begin url = T.must(urls.shift) ohai "Downloading #{url}" cached_location_valid = cached_location.exist? resolved_url, _, last_modified, @file_size, content_type, is_redirection = begin resolve_url_basename_time_file_size(url, timeout: Utils::Timer.remaining!(end_time)) rescue ErrorDuringExecution raise unless cached_location_valid end @last_modified = last_modified # Authorization is no longer valid after redirects [:headers]&.delete_if { |header| header.start_with?("Authorization") } if is_redirection # The cached location is no longer fresh if either: # - Last-Modified value is newer than the file's timestamp # - Content-Length value is different than the file's size if cached_location_valid && (!content_type.is_a?(String) || !content_type.start_with?("text/")) if last_modified && last_modified > cached_location.mtime ohai "Ignoring #{cached_location}", "Cached modified time #{cached_location.mtime.iso8601} is before " \ "Last-Modified header: #{last_modified.iso8601}" cached_location_valid = false end if @file_size&.nonzero? && @file_size != cached_location.size ohai "Ignoring #{cached_location}", "Cached size #{cached_location.size} differs from " \ "Content-Length header: #{@file_size}" cached_location_valid = false end end if cached_location_valid puts "Already downloaded: #{cached_location}" else begin _fetch(url:, resolved_url: T.must(resolved_url), timeout: Utils::Timer.remaining!(end_time)) rescue ErrorDuringExecution => e raise CurlDownloadStrategyError.new(url, e.stderr.strip) end cached_location.dirname.mkpath temporary_path.rename(cached_location.to_s) end create_symlink_to_cached_download(cached_location) rescue CurlDownloadStrategyError raise if urls.empty? puts "Trying a mirror..." retry rescue Timeout::Error => e raise Timeout::Error, "Timed out downloading #{self.url}: #{e}" end ensure download_lock.unlock(unlink: true) end end |
#resolved_time_file_size(timeout: nil) ⇒ Array<([Time, nil], Integer)>
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
136 137 138 139 |
# File 'download_strategy/curl_download_strategy.rb', line 136 def resolved_time_file_size(timeout: nil) _, _, time, file_size, = resolve_url_basename_time_file_size(url, timeout:) [time, T.must(file_size)] end |
#total_size ⇒ Integer?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
125 126 127 |
# File 'download_strategy/curl_download_strategy.rb', line 125 def total_size @file_size end |