Packages
rebar3_hex
0.2.0
7.1.0
7.0.11
7.0.10
7.0.9
7.0.8
7.0.7
7.0.6
7.0.5
7.0.4
7.0.3
7.0.2
7.0.1
7.0.0
6.11.9
6.11.8
6.11.7
6.11.6
6.11.5
6.11.4
6.11.3
6.11.2
6.11.1
6.11.0
6.10.3
6.10.2
6.10.1
6.10.0
6.9.6
6.9.5
6.9.4
6.9.3
6.9.2
6.9.1
6.9.0
6.8.0
6.7.0
6.6.0
6.5.0
6.4.0
6.3.0
6.2.0
6.1.0
6.0.0
4.1.0
4.0.0
3.1.0
3.0.0
2.5.1
2.5.0
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
1.20.0
1.19.0
1.18.0
1.17.0
1.16.0
1.15.0
1.14.0
1.13.0
1.12.0
1.11.0
1.10.0
1.9.1
1.9.0
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.3
1.6.1
1.6.0
1.5.0
1.4.0
1.3.0
1.2.0
1.1.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Hex.pm plugin for rebar3
Current section
Files
Jump to
Current section
Files
src/rebar3_hex_http.erl
-module(rebar3_hex_http).
-export([get/2
,delete/2
,put/3
,post_json/3
,post/4
,encode/1]).
-include("rebar3_hex.hrl").
-include_lib("public_key/include/OTP-PUB-KEY.hrl").
-define(ENDPOINT, "/api").
get(Path, Auth) ->
case httpc:request(get, request(Path, Auth),
[{ssl, [ssl_opts(rebar3_hex_config:api_url())]}],
[{body_format, binary}]) of
{ok, {{_, 200, _}, _, RespBody}} ->
{ok, jsx:decode(RespBody)};
{ok, {{_, Status, _}, _, _}} ->
{error, Status}
end.
delete(Path, Auth) ->
case httpc:request(delete, request(Path, Auth)
,[{ssl, [ssl_opts(rebar3_hex_config:api_url())]}]
,[{body_format, binary}]) of
{ok, {{_, 204, _}, _, _}} ->
ok;
{ok, {{_, Status, _}, _, _}} ->
{error, Status}
end.
put(Path, Auth, Body) ->
case httpc:request(put, json_request(Path, Auth, Body)
,[{ssl, [ssl_opts(rebar3_hex_config:api_url())]}]
,[{body_format, binary}]) of
{ok, {{_, Status, _}, _, _}} when Status >= 200, Status =< 299 ->
ok;
{ok, {{_, _, _}, _, RespBody}} ->
{error, RespBody}
end.
post_json(Path, Auth, Body) ->
case httpc:request(post, json_request(Path, Auth, Body)
,[{ssl, [ssl_opts(rebar3_hex_config:api_url())]}]
,[{body_format, binary}]) of
{ok, {{_, Status, _}, _, RespBody}} when Status >= 200, Status =< 299 ->
{ok, jsx:decode(RespBody)};
{ok, {{_, Status, _}, _, _}} when Status >= 500->
{error, undefined_server_error};
{ok, {{_, _, _}, _, RespBody}} ->
{error, RespBody}
end.
post(Path, Auth, Tar, Size) ->
Body = fun(S) when S < byte_size(Tar) ->
NewSize = min(S + ?CHUNK, byte_size(Tar)),
Chunk = NewSize - S,
{ok, [binary:part(Tar, S, Chunk)], NewSize};
(_S) ->
eof
end,
case httpc:request(post, file_request(Path, Auth, Body, Size)
,[{ssl, [ssl_opts(rebar3_hex_config:api_url())]}]
,[{body_format, binary}]) of
{ok, {{_, Status, _}, _, _RespBody}} when Status >= 200, Status =< 299 ->
ok;
{ok, {{_, Status, _}, _RespHeaders, _RespBodyJson}} when Status >= 500->
{error, undefined_server_error};
{ok, {{_, Status, _}, _RespHeaders, RespBodyJson}} ->
{error, Status, jsx:decode(RespBodyJson)}
end.
%% Internal Functions
request(Path, Auth) ->
{ec_cnv:to_list(rebar3_hex_config:api_url()) ++ ec_cnv:to_list(filename:join(?ENDPOINT, Path))
,[{"authorization", ec_cnv:to_list(Auth)}]}.
json_request(Path, Auth, Body) ->
{ec_cnv:to_list(rebar3_hex_config:api_url()) ++ ec_cnv:to_list(filename:join(?ENDPOINT, Path))
,[{"authorization", ec_cnv:to_list(Auth)}]
,"application/json", jsx:encode(Body)}.
file_request(Path, Auth, Body, ContentLength) ->
{ec_cnv:to_list(rebar3_hex_config:api_url()) ++ ec_cnv:to_list(filename:join(?ENDPOINT, Path))
,[{"authorization", ec_cnv:to_list(Auth)}
,{"content-length", ContentLength}]
,"application/octet-stream", {Body, 0}}.
ssl_opts(Url) ->
{ok, {_, _, Hostname, _, _, _}} = http_uri:parse(ec_cnv:to_list(Url)),
VerifyFun = {fun ssl_verify_hostname:verify_fun/3, [{check_hostname, Hostname}]},
CACerts = cacerts(),
[{verify, verify_peer}, {depth, 2}, {cacerts, CACerts}
,{partial_chain, fun partial_chain/1}, {verify_fun, VerifyFun}].
partial_chain(Certs) ->
Certs = [{Cert, public_key:pkix_decode_cert(Cert, otp)} || Cert <- Certs],
CACerts = cacerts(),
CACerts1 = [public_key:pkix_decode_cert(Cert, otp) || Cert <- CACerts],
case ec_lists:find(fun({Der, Cert}) ->
check_cert(CACerts1, Der, Cert)
end, Certs) of
{ok, Trusted} ->
{trusted_ca, Trusted};
_ ->
unknown_ca
end.
extract_key(Cert) ->
((Cert#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subjectPublicKeyInfo)#'OTPSubjectPublicKeyInfo'.subjectPublicKey.
cacerts() ->
Pems = public_key:pem_decode(rebar3_hex_cacerts:cacerts()),
[Der || {'Certificate', Der, _} <- Pems].
check_cert(CACerts, Der, Cert) ->
lists:any(fun(CACert) ->
case public_key:pkix_is_issuer(Cert, CACert) of
true ->
Key = extract_key(CACert),
public_key:pkix_verify(Der, Key);
_ ->
false
end
end, CACerts).
encode(Term) ->
quote_plus(Term).
-define(PERCENT, 37). % $\%
-define(FULLSTOP, 46). % $\.
-define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
(C >= $a andalso C =< $f) orelse
(C >= $A andalso C =< $F))).
-define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse
(C >= $A andalso C =< $Z) orelse
(C >= $0 andalso C =< $9) orelse
(C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse
C =:= $_))).
hexdigit(C) when C < 10 -> $0 + C;
hexdigit(C) when C < 16 -> $A + (C - 10).
%% @spec quote_plus(atom() | integer() | string()) -> string()
%% @doc URL safe encoding of the given term.
quote_plus(Atom) when is_atom(Atom) ->
quote_plus(atom_to_list(Atom));
quote_plus(Int) when is_integer(Int) ->
quote_plus(integer_to_list(Int));
quote_plus(String) ->
quote_plus(String, []).
quote_plus([], Acc) ->
lists:reverse(Acc);
quote_plus([C | Rest], Acc) when ?QS_SAFE(C) ->
quote_plus(Rest, [C | Acc]);
quote_plus([$\s | Rest], Acc) ->
quote_plus(Rest, [$+ | Acc]);
quote_plus([C | Rest], Acc) ->
<<Hi:4, Lo:4>> = <<C>>,
quote_plus(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]).