|
|
@ -26,38 +26,69 @@ needs_update(Dir, {pkg, _Name, Vsn}) -> |
|
|
|
true |
|
|
|
end. |
|
|
|
|
|
|
|
download(_Dir, {pkg, Name, Vsn}, State) -> |
|
|
|
download(TmpDir, Pkg={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), |
|
|
|
CachePath = filename:join(PackageDir, Package), |
|
|
|
Url = string:join([CDN, Package], "/"), |
|
|
|
cached_download(TmpDir, CachePath, Pkg, Url, etag(CachePath), State). |
|
|
|
|
|
|
|
case request(Url, etag(Path)) of |
|
|
|
cached_download(TmpDir, CachePath, Pkg, Url, ETag, State) -> |
|
|
|
case request(Url, ETag) of |
|
|
|
{ok, cached} -> |
|
|
|
{tarball, Path}; |
|
|
|
{ok, Binary, EtagHeader} -> |
|
|
|
file:write_file(Path, Binary), |
|
|
|
Etag = etag(Path), |
|
|
|
case EtagHeader =:= Etag of |
|
|
|
true -> |
|
|
|
{tarball, Path}; |
|
|
|
false -> |
|
|
|
?DEBUG("Bad md5sum for ~s of ~s comparing to ~s sent by server", |
|
|
|
[Path, Etag, EtagHeader]), |
|
|
|
throw(bad_etag) |
|
|
|
end; |
|
|
|
serve_from_cache(TmpDir, CachePath, Pkg, State); |
|
|
|
{ok, Body, NewETag} -> |
|
|
|
serve_from_download(TmpDir, CachePath, Pkg, NewETag, Body, State); |
|
|
|
error when ETag =/= false -> |
|
|
|
?DEBUG("Download ~s error, using ~s from cache", [Url, CachePath]), |
|
|
|
serve_from_cache(TmpDir, CachePath, Pkg, State); |
|
|
|
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 |
|
|
|
throw(request_failed) |
|
|
|
end. |
|
|
|
|
|
|
|
serve_from_cache(TmpDir, CachePath, Pkg, State) -> |
|
|
|
{Files, Contents, Version, Meta} = extract(TmpDir, CachePath), |
|
|
|
case checksums(Pkg, Files, Contents, Version, Meta, State) of |
|
|
|
{Chk, Chk, Chk} -> |
|
|
|
ok = erl_tar:extract({binary, Contents}, [{cwd, TmpDir}, compressed]), |
|
|
|
{ok, true}; |
|
|
|
{_Bin, Chk, Chk} -> |
|
|
|
?PRV_ERROR({failed_extract, CachePath}); |
|
|
|
{Chk, _Reg, Chk} -> |
|
|
|
?PRV_ERROR({bad_registry_checksum, CachePath}); |
|
|
|
{_Bin, _Reg, _Tar} -> |
|
|
|
?PRV_ERROR({bad_checksum, CachePath}) |
|
|
|
end. |
|
|
|
|
|
|
|
serve_from_download(TmpDir, CachePath, Package, ETag, Binary, State) -> |
|
|
|
?DEBUG("Writing ~p to cache at ~s", [Package, CachePath]), |
|
|
|
file:write_file(CachePath, Binary), |
|
|
|
case etag(CachePath) of |
|
|
|
ETag -> |
|
|
|
serve_from_cache(TmpDir, CachePath, Package, State); |
|
|
|
FileETag -> |
|
|
|
?DEBUG("Download ETag ~s doesn't match cached ETag ~s", [ETag, FileETag]), |
|
|
|
?PRV_ERROR({bad_download, CachePath}) |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
extract(TmpDir, CachePath) -> |
|
|
|
ec_file:mkdir_p(TmpDir), |
|
|
|
{ok, Files} = erl_tar:extract(CachePath, [memory]), |
|
|
|
{"contents.tar.gz", Contents} = lists:keyfind("contents.tar.gz", 1, Files), |
|
|
|
{"VERSION", Version} = lists:keyfind("VERSION", 1, Files), |
|
|
|
{"metadata.config", Meta} = lists:keyfind("metadata.config", 1, Files), |
|
|
|
{Files, Contents, Version, Meta}. |
|
|
|
|
|
|
|
checksums(Pkg, Files, Contents, Version, Meta, State) -> |
|
|
|
Blob = <<Version/binary, Meta/binary, Contents/binary>>, |
|
|
|
<<X:256/big-unsigned>> = crypto:hash(sha256, Blob), |
|
|
|
BinChecksum = list_to_binary(string:to_upper(lists:flatten(io_lib:format("~64.16.0b", [X])))), |
|
|
|
RegistryChecksum = rebar_packages:registry_sum(Pkg, State), |
|
|
|
{"CHECKSUM", TarChecksum} = lists:keyfind("CHECKSUM", 1, Files), |
|
|
|
{BinChecksum, RegistryChecksum, TarChecksum}. |
|
|
|
|
|
|
|
make_vsn(_) -> |
|
|
|
{error, "Replacing version of type pkg not supported."}. |
|
|
|
|
|
|
@ -77,8 +108,8 @@ request(Url, ETag) -> |
|
|
|
[{relaxed, true}], |
|
|
|
[{body_format, binary}]) of |
|
|
|
{ok, {{_Version, 200, _Reason}, Headers, Body}} -> |
|
|
|
{"etag", ETag1} = lists:keyfind("etag", 1, Headers), |
|
|
|
?DEBUG("Successfully downloaded ~s", [Url]), |
|
|
|
{"etag", ETag1} = lists:keyfind("etag", 1, Headers), |
|
|
|
{ok, Body, string:strip(ETag1, both, $")}; |
|
|
|
{ok, {{_Version, 304, _Reason}, _Headers, _Body}} -> |
|
|
|
?DEBUG("Cached copy of ~s still valid", [Url]), |
|
|
|