diff --git a/Library/Homebrew/dependency_collector.rb b/Library/Homebrew/dependency_collector.rb index 9a418a1c2e..bdd8a463d3 100644 --- a/Library/Homebrew/dependency_collector.rb +++ b/Library/Homebrew/dependency_collector.rb @@ -63,6 +63,10 @@ class DependencyCollector Dependency.new("git", tags) end + def brewed_curl_dep_if_needed(tags) + Dependency.new("curl", tags) + end + def subversion_dep_if_needed(tags) return if Utils::Svn.available? @@ -139,7 +143,10 @@ class DependencyCollector tags << :build << :test strategy = spec.download_strategy - if strategy <= CurlDownloadStrategy + if strategy <= HomebrewCurlDownloadStrategy + brewed_curl_dep_if_needed(tags) + parse_url_spec(spec.url, tags) + elsif strategy <= CurlDownloadStrategy parse_url_spec(spec.url, tags) elsif strategy <= GitDownloadStrategy git_dep_if_needed(tags) diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index e3abf6f321..5659934f3b 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -524,7 +524,11 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy raise CurlDownloadStrategyError, url end - curl_download resolved_url, to: temporary_path, timeout: timeout + _curl_download resolved_url, temporary_path, timeout + end + + def _curl_download(resolved_url, to, timeout) + curl_download resolved_url, to: to, timeout: timeout end # Curl options to be always passed to curl, @@ -559,6 +563,19 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy end end +# Strategy for downloading a file using homebrew's curl. +# +# @api public +class HomebrewCurlDownloadStrategy < CurlDownloadStrategy + private + + def _curl_download(resolved_url, to, timeout) + raise HomebrewCurlDownloadStrategyError, url unless Formula["curl"].any_version_installed? + + curl_download resolved_url, to: to, timeout: timeout, use_homebrew_curl: true + end +end + # Strategy for downloading a file from an GitHub Packages URL. # # @api public @@ -1368,6 +1385,7 @@ class DownloadStrategyDetector when :bzr then BazaarDownloadStrategy when :svn then SubversionDownloadStrategy when :curl then CurlDownloadStrategy + when :homebrew_curl then HomebrewCurlDownloadStrategy when :cvs then CVSDownloadStrategy when :post then CurlPostDownloadStrategy when :fossil then FossilDownloadStrategy diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 0c64230035..b8c5d4c622 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -602,6 +602,13 @@ class CurlDownloadStrategyError < RuntimeError end end +# Raised in {HomebrewCurlDownloadStrategy#fetch}. +class HomebrewCurlDownloadStrategyError < CurlDownloadStrategyError + def initialize(url) + super "Homebrew-installed `curl` is not installed for: #{url}" + end +end + # Raised by {Kernel#safe_system} in `utils.rb`. class ErrorDuringExecution < RuntimeError extend T::Sig diff --git a/Library/Homebrew/formula_auditor.rb b/Library/Homebrew/formula_auditor.rb index 9a1eb5def5..277f2fdf4a 100644 --- a/Library/Homebrew/formula_auditor.rb +++ b/Library/Homebrew/formula_auditor.rb @@ -531,7 +531,8 @@ module Homebrew ra = ResourceAuditor.new( spec, spec_name, - online: @online, strict: @strict, only: @only, except: except + online: @online, strict: @strict, only: @only, except: except, + use_homebrew_curl: spec.using == :homebrew_curl ).audit ra.problems.each do |message| problem "#{name}: #{message}" diff --git a/Library/Homebrew/resource_auditor.rb b/Library/Homebrew/resource_auditor.rb index 39c348a7ea..967815bf06 100644 --- a/Library/Homebrew/resource_auditor.rb +++ b/Library/Homebrew/resource_auditor.rb @@ -22,7 +22,8 @@ module Homebrew @strict = options[:strict] @only = options[:only] @except = options[:except] - @problems = [] + @use_homebrew_curl = options[:use_homebrew_curl] + @problems = [] end def audit @@ -110,7 +111,14 @@ module Homebrew strategy = DownloadStrategyDetector.detect(url, using) if strategy <= CurlDownloadStrategy && !url.start_with?("file") - if (http_content_problem = curl_check_http_content(url, "source URL", specs: specs)) + + raise HomebrewCurlDownloadStrategyError, url if + strategy <= HomebrewCurlDownloadStrategy && !Formula["curl"].any_version_installed? + + if (http_content_problem = curl_check_http_content(url, + "source URL", + specs: specs, + use_homebrew_curl: @use_homebrew_curl)) problem http_content_problem end elsif strategy <= GitDownloadStrategy diff --git a/Library/Homebrew/utils/curl.rb b/Library/Homebrew/utils/curl.rb index 517875628c..2241e1687f 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -14,7 +14,9 @@ module Utils module_function - def curl_executable + def curl_executable(use_homebrew_curl: false) + return Formula["curl"].opt_bin/"curl" if use_homebrew_curl + @curl ||= [ ENV["HOMEBREW_CURL"], which("curl"), @@ -63,7 +65,8 @@ module Utils def curl_with_workarounds( *args, - secrets: nil, print_stdout: nil, print_stderr: nil, debug: nil, verbose: nil, env: {}, timeout: nil, **options + secrets: nil, print_stdout: nil, print_stderr: nil, debug: nil, + verbose: nil, env: {}, timeout: nil, use_homebrew_curl: false, **options ) end_time = Time.now + timeout if timeout @@ -77,7 +80,7 @@ module Utils # SSL_CERT_FILE can be incorrectly set by users or portable-ruby and screw # with SSL downloads so unset it here. - result = system_command curl_executable, + result = system_command curl_executable(use_homebrew_curl: use_homebrew_curl), args: curl_args(*args, **options), env: { "SSL_CERT_FILE" => nil }.merge(env), timeout: end_time&.remaining, @@ -173,7 +176,7 @@ module Utils end def curl_check_http_content(url, url_type, specs: {}, user_agents: [:default], - check_content: false, strict: false) + check_content: false, strict: false, use_homebrew_curl: false) return unless url.start_with? "http" secure_url = url.sub(/\Ahttp:/, "https:") @@ -183,7 +186,7 @@ module Utils user_agents.each do |user_agent| secure_details = begin curl_http_content_headers_and_checksum(secure_url, specs: specs, hash_needed: true, - user_agent: user_agent) + user_agent: user_agent, use_homebrew_curl: use_homebrew_curl) rescue Timeout::Error next end @@ -199,7 +202,8 @@ module Utils details = nil user_agents.each do |user_agent| details = - curl_http_content_headers_and_checksum(url, specs: specs, hash_needed: hash_needed, user_agent: user_agent) + curl_http_content_headers_and_checksum(url, specs: specs, hash_needed: hash_needed, + user_agent: user_agent, use_homebrew_curl: use_homebrew_curl) break if http_status_ok?(details[:status]) end @@ -264,7 +268,8 @@ module Utils "The #{url_type} #{url} may be able to use HTTPS rather than HTTP. Please verify it in a browser." end - def curl_http_content_headers_and_checksum(url, specs: {}, hash_needed: false, user_agent: :default) + def curl_http_content_headers_and_checksum(url, specs: {}, hash_needed: false, + user_agent: :default, use_homebrew_curl: false) file = Tempfile.new.tap(&:close) specs = specs.flat_map { |option, argument| ["--#{option.to_s.tr("_", "-")}", argument] } @@ -272,7 +277,7 @@ module Utils output, _, status = curl_output( *specs, "--dump-header", "-", "--output", file.path, "--location", "--connect-timeout", "15", "--max-time", max_time, "--retry-max-time", max_time, url, - user_agent: user_agent + user_agent: user_agent, use_homebrew_curl: use_homebrew_curl ) status_code = :unknown