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.cc82
1 files changed, 65 insertions, 17 deletions
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 9bf3e13aa9da..822e9a8db867 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -6,8 +6,18 @@
 
 #include <curl/curl.h>
 
+#include <iostream>
+
+
 namespace nix {
 
+double getTime()
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec + (tv.tv_usec / 1000000.0);
+}
+
 struct Curl
 {
     CURL * curl;
@@ -16,6 +26,10 @@ struct Curl
 
     struct curl_slist * requestHeaders;
 
+    bool showProgress;
+    double prevProgressTime{0}, startTime{0};
+    unsigned int moveBack{1};
+
     static size_t writeCallback(void * contents, size_t size, size_t nmemb, void * userp)
     {
         Curl & c(* (Curl *) userp);
@@ -56,11 +70,30 @@ struct Curl
         return realSize;
     }
 
-    static int progressCallback(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow)
+    int progressCallback(double dltotal, double dlnow)
     {
+        if (showProgress) {
+            double now = getTime();
+            if (prevProgressTime <= now - 1) {
+                string s = (format(" [%1$.0f/%2$.0f KiB, %3$.1f KiB/s]")
+                    % (dlnow / 1024.0)
+                    % (dltotal / 1024.0)
+                    % (now == startTime ? 0 : dlnow / 1024.0 / (now - startTime))).str();
+                std::cerr << "\e[" << moveBack << "D" << s;
+                moveBack = s.size();
+                std::cerr.flush();
+                prevProgressTime = now;
+            }
+        }
         return _isInterrupted;
     }
 
+    static int progressCallback_(void * userp, double dltotal, double dlnow, double ultotal, double ulnow)
+    {
+        Curl & c(* (Curl *) userp);
+        return c.progressCallback(dltotal, dlnow);
+    }
+
     Curl()
     {
         requestHeaders = 0;
@@ -69,7 +102,6 @@ struct Curl
         if (!curl) throw Error("unable to initialize curl");
 
         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-        curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
         curl_easy_setopt(curl, CURLOPT_USERAGENT, ("Nix/" + nixVersion).c_str());
         curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
 
@@ -79,7 +111,8 @@ struct Curl
         curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
         curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) &curl);
 
-        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
+        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback_);
+        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void *) &curl);
         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
     }
 
@@ -89,10 +122,19 @@ struct Curl
         if (requestHeaders) curl_slist_free_all(requestHeaders);
     }
 
-    bool fetch(const string & url, const string & expectedETag = "")
+    bool fetch(const string & url, const DownloadOptions & options)
     {
+        showProgress = options.forceProgress || isatty(STDERR_FILENO);
+
         curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
 
+        if (options.verifyTLS)
+            curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
+        else {
+            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
+        }
+
         data.clear();
 
         if (requestHeaders) {
@@ -100,16 +142,25 @@ struct Curl
             requestHeaders = 0;
         }
 
-        if (!expectedETag.empty()) {
-            this->expectedETag = expectedETag;
-            requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + expectedETag).c_str());
+        if (!options.expectedETag.empty()) {
+            this->expectedETag = options.expectedETag;
+            requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + options.expectedETag).c_str());
         }
 
         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeaders);
 
+        if (showProgress) {
+            std::cerr << (format("downloading ‘%1%’... ") % url);
+            std::cerr.flush();
+            startTime = getTime();
+        }
+
         CURLcode res = curl_easy_perform(curl);
+        if (showProgress)
+            //std::cerr << "\e[" << moveBack << "D\e[K\n";
+            std::cerr << "\n";
         checkInterrupt();
-        if (res == CURLE_WRITE_ERROR && etag == expectedETag) return false;
+        if (res == CURLE_WRITE_ERROR && etag == options.expectedETag) return false;
         if (res != CURLE_OK)
             throw DownloadError(format("unable to download ‘%1%’: %2% (%3%)")
                 % url % curl_easy_strerror(res) % res);
@@ -123,11 +174,11 @@ struct Curl
 };
 
 
-DownloadResult downloadFile(string url, string expectedETag)
+DownloadResult downloadFile(string url, const DownloadOptions & options)
 {
     DownloadResult res;
     Curl curl;
-    if (curl.fetch(url, expectedETag)) {
+    if (curl.fetch(url, options)) {
         res.cached = false;
         res.data = curl.data;
     } else
@@ -178,13 +229,10 @@ Path downloadFileCached(const string & url, bool unpack)
 
     if (!skip) {
 
-        if (storePath.empty())
-            printMsg(lvlInfo, format("downloading ‘%1%’...") % url);
-        else
-            printMsg(lvlInfo, format("checking ‘%1%’...") % url);
-
         try {
-            auto res = downloadFile(url, expectedETag);
+            DownloadOptions options;
+            options.expectedETag = expectedETag;
+            auto res = downloadFile(url, options);
 
             if (!res.cached)
                 storePath = store->addTextToStore(name, res.data, PathSet(), false);
@@ -192,7 +240,7 @@ Path downloadFileCached(const string & url, bool unpack)
             assert(!storePath.empty());
             replaceSymlink(storePath, fileLink);
 
-            writeFile(dataFile, url + "\n" + res.etag + "\n" + int2String(time(0)) + "\n");
+            writeFile(dataFile, url + "\n" + res.etag + "\n" + std::to_string(time(0)) + "\n");
         } catch (DownloadError & e) {
             if (storePath.empty()) throw;
             printMsg(lvlError, format("warning: %1%; using cached result") % e.msg());