Class: CurlDownloadStrategy

Inherits:
AbstractFileDownloadStrategy show all
Includes:
Utils::Curl
Defined in:
download_strategy.rb

Overview

Strategy for downloading files using curl.

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, is_redirection

T.type_alias { [String, String, T.nilable(Time), T.nilable(Integer), T::Boolean] }

Instance Attribute Summary collapse

Attributes inherited from AbstractDownloadStrategy

#cache, #url

Instance Method Summary collapse

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, url_protected_by_cloudflare?, url_protected_by_incapsula?

Methods included from SystemCommand::Mixin

#system_command, #system_command!

Methods inherited from AbstractFileDownloadStrategy

#basename, #cached_location, #symlink_location, #temporary_path

Methods inherited from AbstractDownloadStrategy

#basename, #cached_location, #quiet!, #quiet?, #source_modified_time, #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.

Parameters:



437
438
439
440
441
442
443
444
445
446
447
448
# File 'download_strategy.rb', line 437

def initialize(url, name, version, **meta)
  @try_partial = T.let(true, T::Boolean)
  @mirrors = T.let(meta.fetch(:mirrors, []), T::Array[String])

  # Merge `:header` with `:headers`.
  if (header = meta.delete(:header))
    meta[:headers] ||= []
    meta[:headers] << header
  end

  super
end

Instance Attribute Details

#mirrorsArray<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.

Returns:



434
435
436
# File 'download_strategy.rb', line 434

def mirrors
  @mirrors
end

Instance Method Details

#clear_cachevoid

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.



523
524
525
526
# File 'download_strategy.rb', line 523

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.

Parameters:

  • timeout (Float, Integer, nil) (defaults to: nil)


454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
# File 'download_strategy.rb', line 454

def fetch(timeout: nil)
  end_time = Time.now + timeout if timeout

  download_lock = DownloadLock.new(temporary_path)
  begin
    download_lock.lock

    urls = [url, *mirrors]

    begin
      url = T.must(urls.shift)

      if (domain = Homebrew::EnvConfig.artifact_domain)
        url = url.sub(%r{^https?://#{GitHubPackages::URL_DOMAIN}/}o, "#{domain.chomp("/")}/")
        urls = [] if Homebrew::EnvConfig.artifact_domain_no_fallback?
      end

      ohai "Downloading #{url}"

      cached_location_valid = cached_location.exist?
      v = version
      cached_location_valid = false if v.is_a?(Cask::DSL::Version) && v.latest?

      resolved_url, _, last_modified, file_size, is_redirection = begin
        resolve_url_basename_time_file_size(url, timeout: Utils::Timer.remaining!(end_time))
      rescue ErrorDuringExecution
        raise unless cached_location_valid
      end

      # Authorization is no longer valid after redirects
      meta[: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
      cached_location_valid = if cached_location_valid
        newer_last_modified = last_modified && last_modified > cached_location.mtime
        different_file_size = file_size&.nonzero? && file_size != cached_location.size
        !(newer_last_modified || different_file_size)
      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
          raise CurlDownloadStrategyError, url
        end
        cached_location.dirname.mkpath
        temporary_path.rename(cached_location.to_s)
      end

      symlink_location.dirname.mkpath
      FileUtils.ln_s cached_location.relative_path_from(symlink_location.dirname), symlink_location, force: true
    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.

Parameters:

  • timeout (Float, Integer, nil) (defaults to: nil)

Returns:



529
530
531
532
# File 'download_strategy.rb', line 529

def resolved_time_file_size(timeout: nil)
  _, _, time, file_size = resolve_url_basename_time_file_size(url, timeout:)
  [time, T.must(file_size)]
end