cover/Elixir.WalletIntegrations.Http.HttcClient.html

1 defmodule WalletIntegrations.Http do
2 @moduledoc """
3 HTTP client behaviour for provider adapter requests.
4
5 Isolates the HTTP transport layer for testability and provider substitution.
6 """
7
8 @callback request(
9 method :: :get | :post | :put | :delete,
10 url :: String.t(),
11 headers :: [{String.t(), String.t()}],
12 body :: String.t(),
13 opts :: keyword()
14 ) ::
15 {:ok, status_code :: non_neg_integer(), headers :: list(), body :: String.t()}
16 | {:error, reason :: term()}
17
18 @doc "Resolve the configured HTTP client module."
19 @spec client() :: module()
20 def client do
21 Application.get_env(:wallet_integrations, :http_client, WalletIntegrations.Http.HttcClient)
22 end
23 end
24
25 defmodule WalletIntegrations.Http.HttcClient do
26 @moduledoc """
27 OTP :httpc-backed HTTP client.
28
29 Uses :inets and :ssl from OTP stdlib — no external HTTP dependency required.
30 Configured with connect/read timeouts from RetryPolicy defaults.
31 """
32
33 @behaviour WalletIntegrations.Http
34
35 @impl true
36 def request(method, url, headers, body, opts) do
37
:-(
connect_timeout = Keyword.get(opts, :connect_timeout, 5_000)
38
:-(
recv_timeout = Keyword.get(opts, :recv_timeout, 15_000)
39
40
:-(
url_charlist = String.to_charlist(url)
41
42
:-(
httpc_headers =
43
:-(
Enum.map(headers, fn {k, v} ->
44 {String.to_charlist(k), String.to_charlist(v)}
45 end)
46
47
:-(
request =
48 case method do
49
:-(
:get ->
50 {url_charlist, httpc_headers}
51
52 _ ->
53
:-(
content_type =
54 headers
55 |> Enum.find_value("application/json", fn
56
:-(
{"content-type", v} -> v
57
:-(
{"Content-Type", v} -> v
58
:-(
_ -> false
59 end)
60
61
:-(
{url_charlist, httpc_headers, String.to_charlist(content_type),
62 String.to_charlist(body)}
63 end
64
65
:-(
http_opts = [
66 timeout: recv_timeout,
67 connect_timeout: connect_timeout,
68 ssl: [verify: :verify_peer, cacerts: :public_key.cacerts_get()]
69 ]
70
71
:-(
result =
72 try do
73
:-(
:httpc.request(method, request, http_opts, [])
74 rescue
75
:-(
e -> {:error, e}
76 catch
77
:-(
:exit, reason -> {:error, {:exit, reason}}
78 end
79
80
:-(
case result do
81 {:ok, {{_http_ver, status_code, _reason_phrase}, resp_headers, resp_body}} ->
82
:-(
headers_decoded = Enum.map(resp_headers, fn {k, v} ->
83
:-(
{to_string(k), to_string(v)}
84 end)
85
:-(
{:ok, status_code, headers_decoded, to_string(resp_body)}
86
87
:-(
{:error, reason} ->
88 {:error, reason}
89 end
90 end
91 end
Line Hits Source