# Class BSV::Storage::Downloader <a id="class-BSV-Storage-Downloader"></a>

**Inherits:** `Object`

Downloads UHRP-addressed content from distributed storage hosts.

Resolution: queries the `ls_uhrp` lookup service via a
{BSV::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.)

## Public Instance Methods
### `download(uhrp_url)` <a id="method-i-download"></a> <a id="download-instance_method"></a>
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.
- **@param** `uhrp_url` [String] UHRP URL
- **@raise** [ArgumentError] if the URL is not a valid UHRP URL
- **@raise** [BSV::Storage::DownloadError] if no host yields content matching the hash
- **@return** [BSV::Storage::DownloadResult] downloaded data and MIME type

### `initialize(network_preset: = :mainnet, lookup_resolver: = nil, http_client: = nil, timeout: = 30)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
- **@param** `network_preset` [Symbol] :mainnet, :testnet, or :local
- **@param** `lookup_resolver` [BSV::Overlay::LookupResolver] injectable resolver (nil = default)
- **@param** `http_client` [#call, 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.
- **@param** `timeout` [Integer] per-request timeout in seconds
- **@return** [Downloader] a new instance of Downloader

### `resolve(uhrp_url)` <a id="method-i-resolve"></a> <a id="resolve-instance_method"></a>
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.
- **@param** `uhrp_url` [String] UHRP URL (with or without +uhrp://+ prefix)
- **@raise** [BSV::Storage::DownloadError] if the lookup answer is not an output-list
- **@return** [Array<String>] resolved HTTP(S) download URLs
