From 1d5d277ac7bf8a4bc601358b38746005416e935e Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Fri, 26 Jan 2018 11:12:30 -0800 Subject: 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 --- src/libstore/download.cc | 28 +++++++++++++++++++++++++++- src/libstore/download.hh | 6 ++++-- src/libstore/http-binary-cache-store.cc | 8 +++++++- 3 files changed, 38 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/libstore/download.cc b/src/libstore/download.cc index ef417685f1a7..4b37826c46cf 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -22,6 +22,7 @@ #include #include #include +#include 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 data; + std::string mimeType; - DownloadRequest(const std::string & uri) - : uri(uri), parentAct(curActivity) { } + DownloadRequest(const std::string & uri, std::shared_ptr 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(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, -- cgit 1.4.1