aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2018-01-26 11:12:30 -0800
committerShea Levy <shea@shealevy.com>2018-01-26 11:12:30 -0800
commit1d5d277ac7bf8a4bc601358b38746005416e935e (patch)
treeb25687daa670e2d3605d99fe4761b9953e82c9de
parente09161d05cfbd7c6d4cf41a35765e3fe346ea181 (diff)
HttpBinaryCacheStore: Support upsertFile with PUT.
Some servers, such as Artifactory, allow uploading with PUT and BASIC auth. This allows nix copy to work to upload binaries to those servers. Worked on together with @adelbertc
-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 ef417685f..4b37826c4 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 f2d65ad8d..0a278a05e 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 057337685..93bd3e5d5 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,