Current section

Files

Jump to
hex_core src hex_http_httpc.erl
Raw

src/hex_http_httpc.erl

%% @doc
%% httpc-based implementation of {@link hex_http} contract.
%%
%% Configuration keys:
%%
%% * `profile' - the name of the profile, defaults to `default'. See
%% {@link httpc:set_options/2} for more information on setting
%% options on profiles.
%%
%% * `http_options' - a list of HTTP options, defaults to `[]'. See
%% {@link httpc:request/5} for a list of available HTTP options.
-module(hex_http_httpc).
-behaviour(hex_http).
-export([request/5, request_to_file/6]).
%%====================================================================
%% API functions
%%====================================================================
request(Method, URI, ReqHeaders, Body, AdapterConfig) when is_binary(URI) ->
Profile = maps:get(profile, AdapterConfig, default),
HTTPOptions = http_options(URI, AdapterConfig),
Request = build_request(URI, ReqHeaders, Body),
case httpc:request(Method, Request, HTTPOptions, [{body_format, binary}], Profile) of
{ok, {{_, StatusCode, _}, RespHeaders, RespBody}} ->
RespHeaders2 = load_headers(RespHeaders),
{ok, {StatusCode, RespHeaders2, RespBody}};
{error, Reason} ->
{error, Reason}
end.
request_to_file(Method, URI, ReqHeaders, Body, Filename, AdapterConfig) when is_binary(URI) ->
Profile = maps:get(profile, AdapterConfig, default),
HTTPOptions = http_options(URI, AdapterConfig),
Request = build_request(URI, ReqHeaders, Body),
case
httpc:request(
Method,
Request,
HTTPOptions,
[{stream, unicode:characters_to_list(Filename)}],
Profile
)
of
{ok, saved_to_file} ->
{ok, {200, #{}}};
{ok, {{_, StatusCode, _}, RespHeaders, _RespBody}} ->
RespHeaders2 = load_headers(RespHeaders),
{ok, {StatusCode, RespHeaders2}};
{error, Reason} ->
{error, Reason}
end.
%%====================================================================
%% Internal functions
%%====================================================================
%% @private
http_options(URI, AdapterConfig) ->
HTTPOptions0 = maps:get(http_options, AdapterConfig, []),
HTTPS =
case URI of
<<"https", _/binary>> -> true;
_ -> false
end,
SSLOpts0 = proplists:get_value(ssl, HTTPOptions0),
if
HTTPS == true andalso SSLOpts0 == undefined ->
try
[
{ssl, [
{verify, verify_peer},
{cacerts, public_key:cacerts_get()},
{depth, 3},
{customize_hostname_check, [
{match_fun, public_key:pkix_verify_hostname_match_fun(https)}
]}
]}
| HTTPOptions0
]
catch
_:_ ->
io:format(
"[hex_http_httpc] using default ssl options which are insecure.~n"
"Configure your adapter with: "
"{hex_http_httpc, #{http_options => [{ssl, SslOpts}]}}~n"
"or upgrade Erlang/OTP to OTP-25 or later.~n"
),
HTTPOptions0
end;
true ->
HTTPOptions0
end.
%% @private
build_request(URI, ReqHeaders, Body) ->
build_request2(binary_to_list(URI), dump_headers(ReqHeaders), Body).
%% @private
build_request2(URI, ReqHeaders, undefined) ->
{URI, ReqHeaders};
build_request2(URI, ReqHeaders, {ContentType, Body}) ->
{URI, ReqHeaders, ContentType, Body}.
%% @private
dump_headers(Map) ->
maps:fold(
fun(K, V, Acc) ->
[{binary_to_list(K), binary_to_list(V)} | Acc]
end,
[],
Map
).
%% @private
load_headers(List) ->
lists:foldl(
fun({K, V}, Acc) ->
maps:put(list_to_binary(K), list_to_binary(V), Acc)
end,
#{},
List
).