Packages
tesla
1.12.0
1.20.0
1.18.3
1.18.2
1.18.1
1.18.0
1.17.0
1.16.0
1.15.3
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.3
1.12.2
1.12.1
1.12.0
1.11.2
1.11.1
1.11.0
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.1
retired
1.8.0
1.7.0
1.6.1
1.6.0
1.5.1
1.5.0
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.0
1.0.0
1.0.0-beta.1
0.10.0
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.2.2
0.2.1
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
HTTP client library, with support for middleware and multiple adapters.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/tesla/adapter/finch.ex
if Code.ensure_loaded?(Finch) do
defmodule Tesla.Adapter.Finch do
@moduledoc """
Adapter for [finch](https://github.com/sneako/finch).
Remember to add `{:finch, "~> 0.14.0"}` to dependencies. Also, you need to
recompile tesla after adding the `:finch` dependency:
```
mix deps.clean tesla
mix compile
```
## Examples
In order to use Finch, you must start it and provide a `:name`. For example,
in your supervision tree:
```elixir
children = [
{Finch, name: MyFinch}
]
```
You must provide the same name to this adapter:
```
# set globally in config/config.exs
config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}
# set per module
defmodule MyClient do
use Tesla
adapter Tesla.Adapter.Finch, name: MyFinch
end
```
## Adapter specific options
* `:name` - The `:name` provided to Finch (**required**).
## [Finch options](https://hexdocs.pm/finch/Finch.html#request/3)
* `:pool_timeout` - This timeout is applied when a connection is checked
out from the pool. Default value is `5_000`.
* `:receive_timeout` - The maximum time to wait for a response before
returning an error. Default value is `15_000`.
"""
@behaviour Tesla.Adapter
alias Tesla.Multipart
@defaults [
receive_timeout: 15_000
]
@impl Tesla.Adapter
def call(%Tesla.Env{} = env, opts) do
opts = Tesla.Adapter.opts(@defaults, env, opts)
name = Keyword.fetch!(opts, :name)
url = Tesla.build_url(env.url, env.query)
req_opts = Keyword.take(opts, [:pool_timeout, :receive_timeout])
req = build(env.method, url, env.headers, env.body)
case request(req, name, req_opts, opts) do
{:ok, %Finch.Response{status: status, headers: headers, body: body}} ->
{:ok, %Tesla.Env{env | status: status, headers: headers, body: body}}
{:error, %Mint.TransportError{reason: reason}} ->
{:error, reason}
{:error, reason} ->
{:error, reason}
end
end
defp build(method, url, headers, %Multipart{} = mp) do
headers = headers ++ Multipart.headers(mp)
body = Multipart.body(mp)
build(method, url, headers, body)
end
defp build(method, url, headers, %Stream{} = body_stream) do
build(method, url, headers, {:stream, body_stream})
end
defp build(method, url, headers, body_stream_fun) when is_function(body_stream_fun) do
build(method, url, headers, {:stream, body_stream_fun})
end
defp build(method, url, headers, body) do
Finch.build(method, url, headers, body)
end
defp request(req, name, req_opts, opts) do
case opts[:response] do
:stream -> stream(req, name, req_opts)
nil -> Finch.request(req, name, req_opts)
other -> raise "Unknown response option: #{inspect(other)}"
end
end
defp stream(req, name, opts) do
owner = self()
ref = make_ref()
fun = fn
{:status, status}, _acc -> status
{:headers, headers}, status -> send(owner, {ref, {:status, status, headers}})
{:data, data}, _acc -> send(owner, {ref, {:data, data}})
end
task =
Task.async(fn ->
case Finch.stream(req, name, nil, fun, opts) do
{:ok, _acc} -> send(owner, {ref, :eof})
{:error, error} -> send(owner, {ref, {:error, error}})
end
end)
receive do
{^ref, {:status, status, headers}} ->
body =
Stream.unfold(nil, fn _ ->
receive do
{^ref, {:data, data}} ->
{data, nil}
{^ref, :eof} ->
Task.await(task)
nil
after
opts[:receive_timeout] ->
Task.shutdown(task, :brutal_kill)
nil
end
end)
{:ok, %Finch.Response{status: status, headers: headers, body: body}}
after
opts[:receive_timeout] ->
{:error, :timeout}
end
end
end
end