Current section

Files

Jump to
oidcc src oidcc_http_util.erl
Raw

src/oidcc_http_util.erl

-module(oidcc_http_util).
-export([async_http/3, async_http/5]).
-export([sync_http/3, sync_http/5]).
-export([sync_http/4, sync_http/6]).
-export([uncompress_body_if_needed/2]).
-export([request_timeout/1]).
-include_lib("public_key/include/public_key.hrl").
sync_http(Method, Url, Header) ->
sync_http(Method, Url, Header, false).
sync_http(Method, Url, Header, ContentType, Body) ->
sync_http(Method, Url, Header, ContentType, Body, false).
sync_http(Method, Url, Header, UseCache) ->
perform_request(Method,
Url,
Header,
undefined,
undefined,
[{body_format, binary}],
UseCache).
sync_http(Method, Url, Header, ContentType, Body, UseCache) ->
perform_request(Method,
Url,
Header,
ContentType,
Body,
[{body_format, binary}],
UseCache).
async_http(Method, Url, Header) ->
perform_request(Method, Url, Header, undefined, undefined, [{sync, false}], false).
async_http(Method, Url, Header, ContentType, Body) ->
perform_request(Method, Url, Header, ContentType, Body, [{sync, false}], false).
request_timeout(Unit) ->
Timeout =
case application:get_env(oidcc, http_request_timeout, undefined) of
T when is_integer(T), T > 0 ->
T;
_ ->
300
end,
case Unit of
ms ->
Timeout * 1000;
s ->
Timeout
end.
perform_request(Method, Url, Header, ContentType, Body, Options, UseCache) ->
case options(Url) of
{ok, HttpOptions} ->
Request = gen_request(Url, Header, ContentType, Body),
perform_request_or_lookup_cache(Method, Request, HttpOptions, Options, UseCache);
Error ->
Error
end.
perform_request_or_lookup_cache(Method, Request, HttpOptions, Options, true) ->
case oidcc_http_cache:lookup_http_call(Method, Request) of
{ok, pending} ->
wait_for_cache(Method, Request);
{ok, Res} ->
Res;
{error, _} ->
request_or_wait(Method, Request, HttpOptions, Options)
end;
perform_request_or_lookup_cache(Method, Request, HttpOptions, Options, false) ->
perform_http_request(Method, Request, HttpOptions, Options).
perform_http_request(Method, Request, HttpOptions, Options) ->
Res = httpc:request(Method, Request, HttpOptions, Options),
normalize_result(Res).
request_or_wait(Method, Request, HttpOpts, Opts) ->
case oidcc_http_cache:enqueue_http_call(Method, Request) of
true ->
Result = perform_http_request(Method, Request, HttpOpts, Opts),
ok = oidcc_http_cache:cache_http_result(Method, Request, Result),
Result;
_ ->
wait_for_cache(Method, Request)
end.
wait_for_cache(Method, Request) ->
case oidcc_http_cache:lookup_http_call(Method, Request) of
{ok, pending} ->
timer:sleep(500),
wait_for_cache(Method, Request);
{ok, Result} ->
Result
end.
gen_request(Url, Header, undefined, undefined) ->
{normalize(Url), normalize_headers(Header)};
gen_request(Url, Header, ContentType, Body) ->
{normalize(Url), normalize_headers(Header), normalize(ContentType), Body}.
normalize_result({ok, {{_Proto, Status, _StatusName}, RespHeaders, Body}}) ->
{ok,
#{status => Status,
header => RespHeaders,
body => Body}};
normalize_result({ok, StreamId}) ->
{ok, StreamId};
normalize_result({error, _} = Error) ->
Error.
uncompress_body_if_needed(Body, Header) when is_list(Header) ->
Encoding = lists:keyfind(<<"content-encoding">>, 1, Header),
uncompress_body_if_needed(Body, Encoding);
uncompress_body_if_needed(Body, false) ->
{ok, Body};
uncompress_body_if_needed(Body, {_, <<"gzip">>}) ->
{ok, zlib:gunzip(Body)};
uncompress_body_if_needed(Body, {_, <<"deflate">>}) ->
Z = zlib:open(),
ok = zlib:inflateInit(Z),
{ok, zlib:inflate(Z, Body)};
uncompress_body_if_needed(_Body, {_, Compression}) ->
erlang:error({unsupported_encoding, Compression}).
options(Url) when is_list(Url) ->
#{scheme := Schema} = uri_string:parse(normalize(Url)),
BaseOptions = [{timeout, request_timeout(ms)}],
case Schema of
"http" ->
{ok, BaseOptions};
"https" ->
ssl_options(BaseOptions)
end;
options(Url) when is_binary(Url) ->
options(binary_to_list(Url)).
ssl_options(BaseOptions) ->
CaCert = application:get_env(oidcc, cacertfile),
Depth = application:get_env(oidcc, cert_depth, 1),
case CaCert of
{ok, CaCertFile} ->
{ok,
[{ssl,
[{verify, verify_peer},
{verify_fun, {fun ssl_verify_hostname:verify_fun/3, []}},
{customize_hostname_check,
[{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]},
{cacertfile, CaCertFile},
{depth, Depth}]}]
++ BaseOptions};
_ ->
{error, missing_cacertfile}
end.
normalize(L) when is_list(L) ->
L;
normalize(B) when is_binary(B) ->
binary_to_list(B).
normalize_headers(L) when is_list(L) ->
[normalize_header(K, V) || {K, V} <- L].
normalize_header(K, V) ->
{normalize(K), normalize(V)}.