diff options
Diffstat (limited to 'src/libstore/download.cc')
-rw-r--r-- | src/libstore/download.cc | 68 |
1 files changed, 50 insertions, 18 deletions
diff --git a/src/libstore/download.cc b/src/libstore/download.cc index cf3929cadd65..ed7e124d25f4 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -8,6 +8,7 @@ #include <curl/curl.h> #include <iostream> +#include <thread> namespace nix { @@ -31,7 +32,7 @@ struct CurlDownloader : public Downloader { CURL * curl; ref<std::string> data; - string etag, status, expectedETag; + string etag, status, expectedETag, effectiveUrl; struct curl_slist * requestHeaders; @@ -194,11 +195,24 @@ struct CurlDownloader : public Downloader if (res != CURLE_OK) { Error err = httpStatus == 404 ? NotFound : - httpStatus == 403 ? Forbidden : Misc; - throw DownloadError(err, format("unable to download ‘%1%’: %2% (%3%)") - % url % curl_easy_strerror(res) % res); + httpStatus == 403 ? Forbidden : + (httpStatus == 408 || httpStatus == 500 || httpStatus == 503 + || httpStatus == 504 || httpStatus == 522 || httpStatus == 524 + || res == CURLE_COULDNT_RESOLVE_HOST) ? Transient : + Misc; + if (res == CURLE_HTTP_RETURNED_ERROR && httpStatus != -1) + throw DownloadError(err, format("unable to download ‘%s’: HTTP error %d") + % url % httpStatus); + else + throw DownloadError(err, format("unable to download ‘%s’: %s (%d)") + % url % curl_easy_strerror(res) % res); } + char *effectiveUrlCStr; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effectiveUrlCStr); + if (effectiveUrlCStr) + effectiveUrl = effectiveUrlCStr; + if (httpStatus == 304) return false; return true; @@ -206,14 +220,27 @@ struct CurlDownloader : public Downloader DownloadResult download(string url, const DownloadOptions & options) override { - DownloadResult res; - if (fetch(resolveUri(url), options)) { - res.cached = false; - res.data = data; - } else - res.cached = true; - res.etag = etag; - return res; + size_t attempt = 0; + + while (true) { + try { + DownloadResult res; + if (fetch(resolveUri(url), options)) { + res.cached = false; + res.data = data; + } else + res.cached = true; + res.effectiveUrl = effectiveUrl; + res.etag = etag; + return res; + } catch (DownloadError & e) { + attempt++; + if (e.error != Transient || attempt >= options.tries) throw; + auto ms = options.baseRetryTimeMs * (1 << (attempt - 1)); + printMsg(lvlError, format("warning: %s; retrying in %d ms") % e.what() % ms); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + } + } } }; @@ -222,13 +249,14 @@ ref<Downloader> makeDownloader() return make_ref<CurlDownloader>(); } -Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpack, const Hash & expectedHash) +Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpack, string name, const Hash & expectedHash, string * effectiveUrl) { auto url = resolveUri(url_); - string name; - auto p = url.rfind('/'); - if (p != string::npos) name = string(url, p + 1); + if (name == "") { + auto p = url.rfind('/'); + if (p != string::npos) name = string(url, p + 1); + } Path expectedStorePath; if (expectedHash) { @@ -259,9 +287,11 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa auto ss = tokenizeString<vector<string>>(readFile(dataFile), "\n"); if (ss.size() >= 3 && ss[0] == url) { time_t lastChecked; - if (string2Int(ss[2], lastChecked) && lastChecked + ttl >= time(0)) + if (string2Int(ss[2], lastChecked) && lastChecked + ttl >= time(0)) { skip = true; - else if (!ss[1].empty()) { + if (effectiveUrl) + *effectiveUrl = url_; + } else if (!ss[1].empty()) { printMsg(lvlDebug, format("verifying previous ETag ‘%1%’") % ss[1]); expectedETag = ss[1]; } @@ -276,6 +306,8 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa DownloadOptions options; options.expectedETag = expectedETag; auto res = download(url, options); + if (effectiveUrl) + *effectiveUrl = res.effectiveUrl; if (!res.cached) { ValidPathInfo info; |