summaryrefslogtreecommitdiff
path: root/src/rebar_pkg_resource.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_pkg_resource.erl')
-rw-r--r--src/rebar_pkg_resource.erl70
1 files changed, 64 insertions, 6 deletions
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl
index 3b44fc8..92e6cd4 100644
--- a/src/rebar_pkg_resource.erl
+++ b/src/rebar_pkg_resource.erl
@@ -9,8 +9,11 @@
,needs_update/2
,make_vsn/1]).
+-include_lib("providers/include/providers.hrl").
-include("rebar.hrl").
+-define(DEFAULT_CDN, "https://s3.amazonaws.com/s3.hex.pm/tarballs").
+
lock(_AppDir, Source) ->
Source.
@@ -23,12 +26,67 @@ needs_update(Dir, {pkg, _Name, Vsn}) ->
true
end.
-download(Dir, {pkg, Name, Vsn}, State) ->
- TmpFile = filename:join(Dir, "package.tar.gz"),
- CDN = rebar_state:get(State, rebar_packages_cdn, "https://s3.amazonaws.com/s3.hex.pm/tarballs"),
- Url = string:join([CDN, binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>)], "/"),
- {ok, saved_to_file} = httpc:request(get, {Url, []}, [], [{stream, TmpFile}]),
- {tarball, TmpFile}.
+download(_Dir, {pkg, Name, Vsn}, State) ->
+ CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
+ PackageDir = hex_package_dir(CDN, State),
+
+ Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
+ Path = filename:join(PackageDir, Package),
+ Url = string:join([CDN, Package], "/"),
+
+ case request(Url, etag(Path)) of
+ {ok, cached} ->
+ {tarball, Path};
+ {ok, Binary} ->
+ file:write_file(Path, Binary),
+ {tarball, Path};
+ error ->
+ case filelib:is_regular(Path) of
+ true ->
+ ?DEBUG("Download ~s error, using ~s since it exists", [Url, Path]),
+ {tarball, Path};
+ false ->
+ throw(request_failed)
+ end
+ end.
make_vsn(_) ->
{error, "Replacing version of type pkg not supported."}.
+
+%% Use the shared hex package directory unless a non-default package repo is used
+hex_package_dir(?DEFAULT_CDN, _) ->
+ filename:join([rebar_dir:home_dir(), ".hex", "packages"]);
+hex_package_dir(CDN, State) ->
+ CacheDir = rebar_dir:global_cache_dir(State),
+ {ok, {_, _, Host, _, _, _}} = http_uri:parse(CDN),
+ CDNPath = filename:join(lists:reverse(string:tokens(Host, "."))),
+ PackageDir = filename:join([CacheDir, "hex", CDNPath, "packages"]),
+ ok = filelib:ensure_dir(filename:join(PackageDir, "placeholder")),
+ PackageDir.
+
+request(Url, ETag) ->
+ case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]},
+ [{relaxed, true}],
+ [{body_format, binary}]) of
+ {ok, {{_Version, 200, _Reason}, _Headers, Body}} ->
+ ?DEBUG("Successfully downloaded ~s", [Url]),
+ {ok, Body};
+ {ok, {{_Version, 304, _Reason}, _Headers, _Body}} ->
+ ?DEBUG("Cached copy of ~s still valid", [Url]),
+ {ok, cached};
+ {ok, {{_Version, Code, _Reason}, _Headers, _Body}} ->
+ ?DEBUG("Request to ~p failed: status code ~p", [Url, Code]),
+ error;
+ {error, Reason} ->
+ ?DEBUG("Request to ~p failed: ~p", [Url, Reason]),
+ error
+ end.
+
+etag(Path) ->
+ case file:read_file(Path) of
+ {ok, Binary} ->
+ <<X:128/big-unsigned-integer>> = crypto:hash(md5, Binary),
+ string:to_lower(lists:flatten(io_lib:format("~32.16.0b", [X])));
+ {error, _} ->
+ false
+ end.