about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstore/download.cc28
-rw-r--r--src/libstore/download.hh6
-rw-r--r--src/libstore/http-binary-cache-store.cc8
3 files changed, 38 insertions, 4 deletions
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 3121e6441f44..a8bcab2f159d 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -22,6 +22,7 @@
 #include <thread>
 #include <cmath>
 #include <random>
+#include <algorithm>
 
 using namespace std::string_literals;
 
@@ -91,6 +92,8 @@ struct CurlDownloader : public Downloader
         {
             if (!request.expectedETag.empty())
                 requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
+            if (!request.mimeType.empty())
+                requestHeaders = curl_slist_append(requestHeaders, ("Content-Type: " + request.mimeType).c_str());
         }
 
         ~DownloadItem()
@@ -185,6 +188,22 @@ struct CurlDownloader : public Downloader
             return 0;
         }
 
+        size_t readOffset = 0;
+        int readCallback(char *buffer, size_t size, size_t nitems)
+        {
+            if (readOffset == request.data->length())
+                return 0;
+            auto count = std::min(size * nitems, request.data->length() - readOffset);
+            memcpy(buffer, request.data->data() + readOffset, count);
+            readOffset += count;
+            return count;
+        }
+
+        static int readCallbackWrapper(char *buffer, size_t size, size_t nitems, void * userp)
+        {
+            return ((DownloadItem *) userp)->readCallback(buffer, size, nitems);
+        }
+
         long lowSpeedTimeout = 300;
 
         void init()
@@ -225,6 +244,13 @@ struct CurlDownloader : public Downloader
             if (request.head)
                 curl_easy_setopt(req, CURLOPT_NOBODY, 1);
 
+            if (request.data) {
+                curl_easy_setopt(req, CURLOPT_UPLOAD, 1L);
+                curl_easy_setopt(req, CURLOPT_READFUNCTION, readCallbackWrapper);
+                curl_easy_setopt(req, CURLOPT_READDATA, this);
+                curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->length());
+            }
+
             if (request.verifyTLS) {
                 if (settings.caFile != "")
                     curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str());
@@ -265,7 +291,7 @@ struct CurlDownloader : public Downloader
             }
 
             if (code == CURLE_OK &&
-                (httpStatus == 200 || httpStatus == 304 || httpStatus == 226 /* FTP */ || httpStatus == 0 /* other protocol */))
+                (httpStatus == 200 || httpStatus == 201 || httpStatus == 204 || httpStatus == 304 || httpStatus == 226 /* FTP */ || httpStatus == 0 /* other protocol */))
             {
                 result.cached = httpStatus == 304;
                 done = true;
diff --git a/src/libstore/download.hh b/src/libstore/download.hh
index f2d65ad8d61d..0a278a05e0e1 100644
--- a/src/libstore/download.hh
+++ b/src/libstore/download.hh
@@ -18,9 +18,11 @@ struct DownloadRequest
     unsigned int baseRetryTimeMs = 250;
     ActivityId parentAct;
     bool decompress = true;
+    std::shared_ptr<std::string> data;
+    std::string mimeType;
 
-    DownloadRequest(const std::string & uri)
-        : uri(uri), parentAct(curActivity) { }
+    DownloadRequest(const std::string & uri, std::shared_ptr<std::string> data = nullptr, std::string mimeType = "")
+        : uri(uri), parentAct(curActivity), data(std::move(data)), mimeType(std::move(mimeType)) { }
 };
 
 struct DownloadResult
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
index 057337685791..93bd3e5d5982 100644
--- a/src/libstore/http-binary-cache-store.cc
+++ b/src/libstore/http-binary-cache-store.cc
@@ -67,7 +67,13 @@ protected:
         const std::string & data,
         const std::string & mimeType) override
     {
-        throw UploadToHTTP("uploading to an HTTP binary cache is not supported");
+        auto data_ = std::make_shared<string>(data);
+        auto req = DownloadRequest(cacheUri + "/" + path, data_, mimeType);
+        try {
+            getDownloader()->download(req);
+        } catch (DownloadError & e) {
+            throw UploadToHTTP(format("uploading to HTTP binary cache at %1% not supported: %2%") % cacheUri % e.msg());
+        }
     }
 
     void getFile(const std::string & path,