Class: BSV::Storage::Downloader

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/storage/downloader.rb

Overview

Downloads UHRP-addressed content from distributed storage hosts.

Resolution: queries the ls_uhrp lookup service via a Overlay::LookupResolver, decodes each PushDrop output to extract the host URL (field) and expiry timestamp (field, varint). Expired entries are silently dropped.

Download: fetches each resolved URL in turn. Any HTTP 4xx/5xx, empty body, or network exception is treated as a failed host and the next URL is tried. This matches the TS SDK contract — no special treatment of 401/402/403.

HTTP redirects are not followed (Net::HTTP default). This is intentional in v1.

Download URLs are taken directly from the overlay; no private-IP filter is applied here. (The LookupResolver applies SSRF filtering to SLAP-discovered infrastructure hosts — content URLs are end-user data and are not subject to the same filter.)

Instance Method Summary collapse

Constructor Details

#initialize(network_preset: :mainnet, lookup_resolver: nil, http_client: nil, timeout: 30) ⇒ Downloader

Returns a new instance of Downloader.

Parameters:

  • network_preset (Symbol) (defaults to: :mainnet)

    :mainnet, :testnet, or :local

  • lookup_resolver (BSV::Overlay::LookupResolver) (defaults to: nil)

    injectable resolver (nil = default)

  • http_client (#call, nil) (defaults to: nil)

    injectable HTTP client for testing. Must respond to .call(url_string) and return an object exposing #code (Integer), #body (binary String), and #[](header_name) for header access. Default: a thin Net::HTTP.get_response wrapper.

  • timeout (Integer) (defaults to: 30)

    per-request timeout in seconds



35
36
37
38
39
# File 'lib/bsv/storage/downloader.rb', line 35

def initialize(network_preset: :mainnet, lookup_resolver: nil, http_client: nil, timeout: 30)
  @lookup_resolver = lookup_resolver || BSV::Overlay::LookupResolver.new(network_preset: network_preset)
  @http_client     = http_client
  @timeout         = timeout
end

Instance Method Details

#download(uhrp_url) ⇒ BSV::Storage::DownloadResult

Download the content addressed by uhrp_url.

Validates the URL, resolves it to a list of hosts, then attempts each host in order. Verifies the SHA-256 hash of the downloaded body against the hash embedded in the UHRP URL. Returns on the first successful match.

Parameters:

  • uhrp_url (String)

    UHRP URL

Returns:

Raises:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/bsv/storage/downloader.rb', line 78

def download(uhrp_url)
  raise ArgumentError, 'Invalid parameter UHRP url' unless BSV::Storage::Utils.valid_url?(uhrp_url)

  urls = resolve(uhrp_url)
  raise DownloadError, 'No one currently hosts this file!' if urls.empty?

  expected_hash = BSV::Storage::Utils.get_hash_from_url(uhrp_url)

  urls.each do |url|
    result = attempt_download(url, expected_hash)
    return result if result
  end

  raise DownloadError, "Unable to download content from #{uhrp_url}"
end

#resolve(uhrp_url) ⇒ Array<String>

Resolve a UHRP URL to a list of HTTP(S) download URLs.

Queries the ls_uhrp lookup service, decodes each PushDrop output, drops expired entries, and returns the remaining URLs.

Parameters:

  • uhrp_url (String)

    UHRP URL (with or without uhrp:// prefix)

Returns:

  • (Array<String>)

    resolved HTTP(S) download URLs

Raises:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/bsv/storage/downloader.rb', line 49

def resolve(uhrp_url)
  question = BSV::Overlay::LookupQuestion.new(
    service: 'ls_uhrp',
    query: { 'uhrpUrl' => BSV::Storage::Utils.normalize_url(uhrp_url) }
  )
  answer = @lookup_resolver.query(question)
  raise DownloadError, 'Lookup answer must be an output list' unless answer.type == 'output-list'

  current_time = Time.now.to_i
  urls = []

  answer.outputs.each do |output|
    url = decode_output_url(output, current_time)
    urls << url if url
  end

  urls
end