# Salemove.HttpClient

[](https://hex.pm/packages/salemove_http_client)
[](http://hexdocs.pm/salemove_http_client)
Elixir HTTP client for JSON services built on top of [tesla](https://github.com/teamon/tesla).
## Installation
The package can be installed by adding `salemove_http_client` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:salemove_http_client, "~> 6.0"},
{:finch, "~> 0.23"}
]
end
```
Documentation can be found at [https://hexdocs.pm/salemove_http_client](https://hexdocs.pm/salemove_http_client).
## Usage
```elixir
defmodule GihubClient do
use Salemove.HttpClient,
base_url: "https://api.github.com/"
end
```
Requests are made through `Tesla.Adapter.Finch` by default, backed by a Finch pool that
this library starts itself (named `Salemove.HttpClient.Finch`) — no supervision tree
changes are needed. The pool is only started when `:finch` is available and the adapter
configured via the `:salemove_http_client` application environment resolves to
`Tesla.Adapter.Finch` (the default); applications configured with another adapter don't
run an unused pool.
The pool can be tuned via the `:finch_pools` application environment.
Pay attention to setting a pool's size; it's a hard cap on concurrent requests per scheme,
host and port.
The following custom configuration is an example that would make no difference because
it matches this library's defaults.
If you pass any Tesla options, **none of the defaults** (except for `:finch_pools`) **will apply**.
If you pass `:finch_pools`, the default `:finch_pools` value **won't apply**.
```elixir
config :salemove_http_client,
adapter: Tesla.Adapter.Finch,
adapter_options: [
name: Salemove.HttpClient.Finch,
receive_timeout: 4500
],
retry: false,
finch_pools: %{
default: [conn_max_idle_time: 30_000, conn_opts: [transport_opts: [timeout: 1_500]]]
}
```
## Migrating from 5.x to 6.0
The default adapter changed from `Tesla.Adapter.Hackney` to `Tesla.Adapter.Finch`, and the
library now starts its own default Finch pool, so no supervision tree changes are needed.
### Add finch to your dependencies
Finch is an optional dependency of this library. To use the default adapter, add it yourself:
```elixir
{:finch, "~> 0.23"}
```
If tesla was already compiled before finch was added, recompile it so the Finch adapter is
included: `mix deps.clean tesla && mix deps.compile tesla`. Without finch, the default pool
is not started and requests fail.
For HTTPS, make sure `castore` is in your dependency tree —
Mint uses it for CA certificates and raises on HTTPS requests without it:
```elixir
{:castore, "~> 1.0"}
```
### TLS peer verification is now on by default
The previous hackney default (no `ssl_options`) performed **no** certificate
verification. Finch/Mint **verify the peer by default** against the `castore` CA
bundle, so requests to endpoints served with a private or internal CA — which
silently succeeded under 5.x — will now fail. Trust the CA on the default pool:
```elixir
config :salemove_http_client,
finch_pools: %{default: [conn_opts: [transport_opts: [cacertfile: "/path/to/ca.pem"]]]}
```
or, if you must, opt out of verification:
```elixir
config :salemove_http_client,
finch_pools: %{default: [conn_opts: [transport_opts: [verify: :verify_none]]]}
```
### Consider a custom pool size
Finch creates separate pools for each `{scheme, host, port}` combination and each pool
has a hard limit on concurrent requests, dictated mainly by the pool's `:size`.
This library uses Finch's default `:size`.
If you want to avoid full pools, consult your service's metrics and set the `:size`
comfortably above the max observed count of concurrent outgoing requests.
```elixir
config :salemove_http_client,
finch_pools: %{default: [size: 200], smaller_kind_of_pool: [size: 20]}
```
### Audit your `adapter_options` — they are NOT translated
Adapter options are passed to the adapter verbatim and are adapter-specific.
`Tesla.Adapter.Finch` **silently ignores options it does not recognise**, so hackney-style
options left over from 5.x do not fail loudly — they simply stop having any effect.
Check every `config :my_app, MyClient` entry and every `use Salemove.HttpClient` option
list:
| Hackney option (5.x) | With the Finch adapter (6.0) |
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `recv_timeout: 4500` | Rename to `receive_timeout: 4500`. If left unrenamed, requests silently run with Finch's default receive timeout of 15 000 ms instead. |
| `connect_timeout: 1500` | Remove; configure at pool level via `:finch_pools` (`conn_opts: [transport_opts: [timeout: 1_500]]`). The previous 1500 ms default is preserved in the default pool. |
| `ssl_options: [...]` | Remove; configure at pool level via `:finch_pools` (`conn_opts: [transport_opts: [...]]`). Note that Finch/Mint verify TLS peers by default, using CA certificates from the `castore` package — see "Add finch to your dependencies" above. |
| `pool: :my_pool` | Remove; Finch selects pools by request URL. For a dedicated pool, start your own named Finch instance (see below). |
| `proxy: ...` | Not supported per-request; see the proxy section below. |
The `:salemove_http_client` application environment is now read at request time, so it can
be set in `config/runtime.exs` and config changes no longer require recompiling the
dependency.
You also need to act if any of the following applies:
* **You use proxies** (the `:proxy` option or `HTTP_PROXY`/`HTTPS_PROXY` environment
variables) — the Finch adapter does not support per-request proxies, and proxy
configuration is silently ignored when it is used. Either configure the proxy on
the Finch pool:
```elixir
config :salemove_http_client,
finch_pools: %{
default: [conn_opts: [proxy: {:http, "proxy.example.com", 3128, []}]]
}
```
or keep using the hackney adapter for those clients (see below).
* **You want to keep using hackney** — add a patched hackney to your own dependencies and
select the adapter explicitly. Hackney 1.x has unpatched security vulnerabilities; use
4.x (this may require `override: true` while other dependencies still constrain hackney
to 1.x):
```elixir
# mix.exs
{:hackney, "~> 4.1", override: true}
# config
config :salemove_http_client,
adapter: Tesla.Adapter.Hackney,
adapter_options: [connect_timeout: 1500, recv_timeout: 4500]
```
* **You need separate pools per client** (different TLS settings, pool sizes, or proxy
targets) — start your own named Finch instance in your supervision tree and point the
client at it:
```elixir
# application.ex
{Finch, name: MyApp.SlackFinch, pools: %{default: [size: 10]}}
# config
config :my_app, MyApp.SlackClient,
adapter_options: [name: MyApp.SlackFinch]
```
Note that any options passed to `:finch_pools` will override this library's default pool settings
(see [Usage](#usage)).
## Migrating from 1.x to 2.0
Module config is now deep merged with base salemove_http_client config, so when upgrading, make sure that calls to Salemove HTTP Client don't rely on the configuration being shallow merged.
### Example
```elixir
config :foo, Some.Module,
adapter_options: [
connect_timeout: 8000
]
config :salemove_http_client,
adapter_options: [
ssl_options: [verify: :verify_none]
]
```
and as a result of
```elixir
use Salemove.HttpClient, Application.fetch_env!(:foo, Some.Module)
```
with the older version the configuration would have been:
```elixir
adapter_options: [
connect_timeout: 8000
]
```
with 2.x and upwards it is:
```elixir
adapter_options: [
ssl_options: [verify: :verify_none],
connect_timeout: 8000
]
```
To disable stats now, you can just set `stats` value to `false` in the module configuration.
## Migrating from 0.x to 1.0
Most changes are due to changes in Tesla HTTP client. Migrating guide for tesla can be seen at https://github.com/teamon/tesla/wiki/0.x-to-1.0-Migration-Guide.
### Changes specific to Salemove HTTP Client
* `Salemove.HttpClient.ConnectionError` struct no longer has a field `message`. The error message can be fetched using `Exception.message/1`.
## License
MIT License, Copyright (c) 2017 SaleMove