about summary refs log tree commit diff
path: root/src/libstore/download.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore/download.cc')
-rw-r--r--src/libstore/download.cc68
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;