if Code.ensure_loaded?(:ibrowse) do
defmodule Tesla.Adapter.Ibrowse do
@moduledoc """
Adapter for [ibrowse](https://github.com/cmullaparthi/ibrowse).
Remember to add `{:ibrowse, "~> 4.2"}` to dependencies (and `:ibrowse` to applications in `mix.exs`)
Also, you need to recompile tesla after adding `:ibrowse` dependency:
```elixir
mix deps.clean tesla
mix deps.compile tesla
```
## Examples
```elixir
# set globally in config/config.exs
config :tesla, :adapter, Tesla.Adapter.Ibrowse
# set per module
defmodule MyClient do
def client do
Tesla.client([], Tesla.Adapter.Ibrowse)
end
end
```
"""
@behaviour Tesla.Adapter
import Tesla.Adapter.Shared, only: [stream_to_fun: 1, next_chunk: 1]
alias Tesla.Multipart
@impl Tesla.Adapter
def call(env, opts) do
with {:ok, status, headers, body} <- request(env, opts) do
{:ok,
%{
env
| status: format_status(status),
headers: format_headers(headers),
body: format_body(body)
}}
end
end
defp format_status(status) when is_list(status) do
status |> to_string() |> String.to_integer()
end
defp format_headers(headers) do
for {key, value} <- headers do
{String.downcase(to_string(key)), to_string(value)}
end
end
defp format_body(data) when is_list(data), do: IO.iodata_to_binary(data)
defp format_body(data) when is_binary(data), do: data
defp request(env, opts) do
body = env.body || []
handle(
request(
Tesla.build_url(env) |> to_charlist(),
env.headers,
env.method,
body,
Tesla.Adapter.opts(env, opts)
)
)
end
defp request(url, headers, method, %Multipart{} = mp, opts) do
headers = headers ++ Multipart.headers(mp)
body = stream_to_fun(Multipart.body(mp))
request(url, headers, method, body, opts)
end
defp request(url, headers, method, %Stream{} = body, opts) do
fun = stream_to_fun(body)
request(url, headers, method, fun, opts)
end
defp request(url, headers, method, body, opts) when is_function(body) do
body = {&next_chunk/1, body}
opts = Keyword.put(opts, :transfer_encoding, :chunked)
request(url, headers, method, body, opts)
end
defp request(url, headers, method, body, opts) do
{timeout, opts} = opts |> Keyword.pop(:timeout, 30_000)
:ibrowse.send_req(url, headers, method, body, opts, timeout)
end
defp handle({:error, {:conn_failed, error}}), do: error
defp handle(response), do: response
end
end