Packages
erlcloud
3.3.7
3.8.3
3.8.2
3.8.1
3.7.6
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.8
3.6.7
3.6.5
3.6.4
3.6.3
3.6.2
3.6.1
3.6.0
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.5
3.4.3
3.4.1
3.4.0
3.3.9
3.3.8
3.3.7
3.3.6
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.18
3.2.17
3.2.16
3.2.15
3.2.14
3.2.13
3.2.12
3.2.11
3.2.10
3.2.7
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.17
3.1.16
3.1.14
3.1.13
3.1.12
3.1.11
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.2
2.2.1
2.2.0
2.1.0
2.0.5
2.0.4
2.0.3
2.0.0
0.13.10
0.13.9
0.13.8
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.0
0.12.0
0.11.0
0.9.2
0.9.2-rc.1
0.9.1
0.9.0
AWS APIs library for Erlang
Current section
Files
Jump to
Current section
Files
src/erlcloud_httpc.erl
%% @author Ransom Richardson <ransom@ransomr.net>
%% @doc
%%
%% HTTP client abstraction for erlcloud. Simplifies changing http clients.
%% API matches lhttpc, except Config is passed instead of options for
%% future cusomizability.
%%
%% @end
-module(erlcloud_httpc).
-include("erlcloud_aws.hrl").
-export([request/6]).
-type request_fun() ::
lhttpc | httpc | hackney |
{module(), atom()} |
fun((string(),
head | get | put | post | trace | options | delete,
list({binary(), binary()}),
binary(), pos_integer(), #aws_config{}) ->
{ok, {{pos_integer(), string()},
list({string(), string()}), binary()}} |
{error, any()}).
-export_type([request_fun/0]).
request(URL, Method, Hdrs, Body, Timeout,
#aws_config{http_client = lhttpc} = Config) ->
request_lhttpc(URL, Method, Hdrs, Body, Timeout, Config);
request(URL, Method, Hdrs, Body, Timeout,
#aws_config{http_client = httpc} = Config) ->
request_httpc(URL, Method, Hdrs, Body, Timeout, Config);
request(URL, Method, Hdrs, Body, Timeout,
#aws_config{http_client = hackney} = Config) ->
request_hackney(URL, Method, Hdrs, Body, Timeout, Config);
request(URL, Method, Hdrs, Body, Timeout,
#aws_config{http_client = {M, F}} = Config)
when is_atom(M), is_atom(F) ->
M:F(URL, Method, Hdrs, Body, Timeout, Config);
request(URL, Method, Hdrs, Body, Timeout,
#aws_config{http_client = F} = Config)
when is_function(F, 6) ->
F(URL, Method, Hdrs, Body, Timeout, Config).
request_lhttpc(URL, Method, Hdrs, Body, Timeout, #aws_config{lhttpc_pool = undefined, http_proxy = undefined}) ->
lhttpc:request(URL, Method, Hdrs, Body, Timeout, []);
request_lhttpc(URL, Method, Hdrs, Body, Timeout, #aws_config{http_proxy = HttpProxy, lhttpc_pool = undefined}) ->
LHttpcOpts = [{proxy, HttpProxy}],
lhttpc:request(URL, Method, Hdrs, Body, Timeout, LHttpcOpts);
request_lhttpc(URL, Method, Hdrs, Body, Timeout, #aws_config{lhttpc_pool = Pool, http_proxy = undefined}) ->
LHttpcOpts = [{pool, Pool}, {pool_ensure, true}],
lhttpc:request(URL, Method, Hdrs, Body, Timeout, LHttpcOpts);
request_lhttpc(URL, Method, Hdrs, Body, Timeout, #aws_config{lhttpc_pool = Pool, http_proxy = HttpProxy}) ->
LHttpcOpts = [{pool, Pool}, {pool_ensure, true}, {proxy, HttpProxy}],
lhttpc:request(URL, Method, Hdrs, Body, Timeout, LHttpcOpts).
%% Guard clause protects against empty bodied requests from being
%% unable to find a matching httpc:request call.
request_httpc(URL, Method, Hdrs, <<>>, Timeout, _Config)
when (Method =:= options) orelse
(Method =:= get) orelse
(Method =:= head) orelse
(Method =:= delete) orelse
(Method =:= trace) ->
HdrsStr = [{to_list_string(K), to_list_string(V)} || {K, V} <- Hdrs],
response_httpc(httpc:request(Method, {URL, HdrsStr},
[{timeout, Timeout}],
[{body_format, binary}]));
request_httpc(URL, Method, Hdrs, Body, Timeout, _Config) ->
HdrsStr = [{to_list_string(K), to_list_string(V)} || {K, V} <- Hdrs],
{"content-type", ContentType} = lists:keyfind("content-type", 1, HdrsStr),
response_httpc(httpc:request(Method,
{URL, HdrsStr,
ContentType, Body},
[{timeout, Timeout}],
[{body_format, binary}])).
request_hackney(URL, Method, Hdrs, Body, Timeout,
#aws_config{hackney_pool = Pool,
hackney_client_options = #hackney_client_options{
insecure = Insecure,
proxy = Proxy,
proxy_auth = ProxyAuth}}
) ->
BinURL = to_binary(URL),
BinHdrs = [{to_binary(K), to_binary(V)} || {K, V} <- Hdrs],
PoolOpt = if Pool =:= undefined ->
[];
true ->
[{pool, Pool}]
end,
HttpProxyOpt = [{proxy, Proxy}, {proxy_auth, ProxyAuth}],
response_hackney(hackney:request(Method,
BinURL, BinHdrs,
Body,
[{recv_timeout, Timeout}, {insecure, Insecure}] ++
PoolOpt ++ HttpProxyOpt)).
response_httpc({ok, {{_HTTPVer, Status, StatusLine}, Headers, Body}}) ->
{ok, {{Status, StatusLine}, Headers, Body}};
response_httpc({error, _} = Error) ->
Error.
response_hackney({ok, Status, Hdrs}) ->
HdrsStr = header_str(Hdrs),
{ok, {{Status, undefined}, HdrsStr, undefined}};
response_hackney({ok, Status, Hdrs, Ref}) ->
case hackney:body(Ref) of
{ok, Body} ->
HdrsStr = header_str(Hdrs),
{ok, {{Status, undefined}, HdrsStr, Body}};
{error, Reason} when Status >= 200, Status =< 299 ->
{error, {hackney_error, Reason}};
{error, _} ->
HdrsStr = header_str(Hdrs),
{ok, {{Status, undefined}, HdrsStr, undefined}}
end;
response_hackney({error, _} = Error) ->
Error.
header_str(Hdrs) ->
[{string:to_lower(to_list_string(K)), to_list_string(V)} || {K, V} <- Hdrs].
to_list_string(Val) when erlang:is_binary(Val) ->
erlang:binary_to_list(Val);
to_list_string(Val) when erlang:is_list(Val) ->
Val.
to_binary(Val) when erlang:is_list(Val) ->
erlang:list_to_binary(Val);
to_binary(Val) when erlang:is_binary(Val) ->
Val.