# `NervesHubLink.Downloader`
[🔗](https://github.com/nerves-hub/nerves_hub_link/blob/v2.12.0/lib/nerves_hub_link/downloader.ex#L6)

Handles downloading files via HTTP.

Several interesting properties about the download are internally cached, such as:

  * the URI of the request
  * the total content amounts of bytes of the file being downloaded
  * the total amount of bytes downloaded at any given time

Using this information, it can restart a download using the
[`Range` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range).

This process's **only** focus is obtaining data reliably. It doesn't have any
side effects on the system.

You can configure various options related to how the `Downloader` handles timeouts,
disconnections, and other aspects of the retry logic by adding the following configuration
to your application's config file:

    config :nerves_hub_link, :retry_config,
      max_disconnects: 20,
      idle_timeout: 75_000,
      max_timeout: 10_800_000

For more information about the configuration options, see the [RetryConfig](`NervesHubLink.Downloader.RetryConfig`) module.

# `event_handler_fun`

```elixir
@type event_handler_fun() :: (handler_event() -&gt; any())
```

# `handler_event`

```elixir
@type handler_event() ::
  {:data, data :: binary(), percent_complete :: float()}
  | {:error, any()}
  | :complete
```

# `initialized_download`

```elixir
@type initialized_download() :: %NervesHubLink.Downloader{
  completed: term(),
  conn: Mint.HTTP.t(),
  content_length: non_neg_integer(),
  downloaded_length: non_neg_integer(),
  handler_fun: event_handler_fun(),
  http_opts: term(),
  max_timeout: term(),
  request_ref: reference(),
  response_headers: Mint.Types.headers(),
  resume_from_bytes: term(),
  retry_args: term(),
  retry_number: non_neg_integer(),
  retry_timeout: term(),
  status: nil | Mint.Types.status(),
  transport_opts: term(),
  uri: URI.t(),
  worst_case_timeout: term(),
  worst_case_timeout_remaining_ms: term()
}
```

# `option`

```elixir
@type option() ::
  {:resume_from_bytes, integer()}
  | {:retry_config, NervesHubLink.Downloader.RetryConfig.t()}
  | {:downloader_ssl, keyword()}
  | {:downloader_http_opts, keyword()}
```

# `options`

```elixir
@type options() :: [option()]
```

# `resume_rescheduled`

```elixir
@type resume_rescheduled() :: t()
```

# `retry_args`

```elixir
@type retry_args() :: NervesHubLink.Downloader.RetryConfig.t()
```

# `t`

```elixir
@type t() :: %NervesHubLink.Downloader{
  completed: boolean(),
  conn: nil | Mint.HTTP.t(),
  content_length: non_neg_integer(),
  downloaded_length: non_neg_integer(),
  handler_fun: event_handler_fun(),
  http_opts: keyword(),
  max_timeout: timer(),
  request_ref: nil | reference(),
  response_headers: Mint.Types.headers(),
  resume_from_bytes: nil | non_neg_integer(),
  retry_args: retry_args(),
  retry_number: non_neg_integer(),
  retry_timeout: nil | timer(),
  status: nil | Mint.Types.status(),
  transport_opts: keyword(),
  uri: nil | URI.t(),
  worst_case_timeout: nil | timer(),
  worst_case_timeout_remaining_ms: nil | non_neg_integer()
}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `start_download`

```elixir
@spec start_download(String.t() | URI.t(), event_handler_fun(), [option()]) ::
  GenServer.on_start()
```

Begins downloading a file at `url` handled by `fun`.

# Example

      iex> pid = self()
      #PID<0.110.0>
      iex> fun = fn {:data, data, _percent} -> File.write("index.html", data)
      ...> {:error, error} -> IO.puts("error streaming file: #{inspect(error)}")
      ...> :complete -> send pid, :complete
      ...> end
      #Function<44.97283095/1 in :erl_eval.expr/5>
      iex> NervesHubLink.Downloader.start_download("https://httpbin.com/", fun)
      {:ok, #PID<0.111.0>}
      iex> flush()
      :complete

---

*Consult [api-reference.md](api-reference.md) for complete listing*
