Current section

Files

Jump to
mob_dev lib mob_dev google_play http.ex
Raw

lib/mob_dev/google_play/http.ex

defmodule MobDev.GooglePlay.HTTP do
@moduledoc false
# Shared `:httpc` wrapper used by GooglePlay, CloudSetup, PlaySetup, and OAuth.
@type result :: {:ok, map()} | {:error, String.t()}
@spec get(String.t(), list()) :: result()
def get(url, headers), do: request(:get, url, headers, nil)
@spec post(String.t(), list(), String.t()) :: result()
def post(url, headers, body), do: request(:post, url, headers, body)
@spec put(String.t(), list(), String.t()) :: result()
def put(url, headers, body), do: request(:put, url, headers, body)
@spec json_headers(String.t()) :: list()
def json_headers(token) do
[{"authorization", "Bearer #{token}"}, {"content-type", "application/json"}]
end
@spec ensure_started!() :: :ok
def ensure_started! do
Application.ensure_all_started(:inets)
Application.ensure_all_started(:ssl)
:ok
end
@spec request(atom(), String.t(), list(), String.t() | nil) :: result()
def request(:get, url, headers, _body) do
host = URI.parse(url).host
httpc_headers = encode_headers(headers)
:httpc.request(
:get,
{to_charlist(url), httpc_headers},
[ssl: ssl_opts(host), timeout: 30_000, connect_timeout: 15_000],
body_format: :binary
)
|> decode_response()
end
def request(method, url, headers, body) do
{ct, rest} = pop_content_type(headers)
host = URI.parse(url).host
httpc_headers = encode_headers(rest)
body_bin = if is_binary(body), do: body, else: ""
:httpc.request(
method,
{to_charlist(url), httpc_headers, to_charlist(ct), body_bin},
[ssl: ssl_opts(host), timeout: 300_000, connect_timeout: 15_000],
body_format: :binary
)
|> decode_response()
end
defp decode_response({:ok, {{_, status, _}, _hdrs, resp}}) when status in 200..299 do
{:ok, Jason.decode!(resp)}
end
defp decode_response({:ok, {{_, status, _}, _hdrs, resp}}) do
msg =
case Jason.decode(resp) do
{:ok, %{"error" => %{"message" => m}}} -> m
{:ok, %{"error" => %{"errors" => [%{"message" => m} | _]}}} -> m
{:ok, %{"error_description" => m}} -> m
_ -> inspect(resp)
end
{:error, "HTTP #{status}: #{msg}"}
end
defp decode_response({:error, reason}) do
{:error, "HTTP request failed: #{inspect(reason)}"}
end
defp encode_headers(headers) do
Enum.map(headers, fn {k, v} -> {to_charlist(k), to_charlist(v)} end)
end
defp pop_content_type(headers) do
case Enum.split_with(headers, fn {k, _} -> String.downcase(k) == "content-type" end) do
{[{_, ct} | _], rest} -> {ct, rest}
{[], rest} -> {"application/json", rest}
end
end
defp ssl_opts(host) do
[
verify: :verify_peer,
cacerts: :public_key.cacerts_get(),
server_name_indication: to_charlist(host),
customize_hostname_check: [
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
]
]
end
end