44
55#include < cassert>
66#include < ranges>
7+ #include < regex>
78
89namespace nix {
910
@@ -27,6 +28,15 @@ class S3BinaryCacheStore : public virtual HttpBinaryCacheStore
2728private:
2829 ref<S3BinaryCacheStoreConfig> s3Config;
2930
31+ /* *
32+ * Creates a multipart upload for large objects to S3.
33+ *
34+ * @see
35+ * https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html#API_CreateMultipartUpload_RequestSyntax
36+ */
37+ std::string createMultipartUpload (
38+ std::string_view key, std::string_view mimeType, std::optional<std::string_view> contentEncoding);
39+
3040 /* *
3141 * Abort a multipart upload
3242 *
@@ -45,6 +55,39 @@ void S3BinaryCacheStore::upsertFile(
4555 HttpBinaryCacheStore::upsertFile (path, istream, mimeType, sizeHint);
4656}
4757
58+ std::string S3BinaryCacheStore::createMultipartUpload (
59+ std::string_view key, std::string_view mimeType, std::optional<std::string_view> contentEncoding)
60+ {
61+ auto req = makeRequest (key);
62+
63+ // setupForS3() converts s3:// to https:// but strips query parameters
64+ // So we call it first, then add our multipart parameters
65+ req.setupForS3 ();
66+
67+ auto url = req.uri .parsed ();
68+ url.query [" uploads" ] = " " ;
69+ req.uri = VerbatimURL (url);
70+
71+ req.method = HttpMethod::POST;
72+ req.data = " " ;
73+ req.mimeType = mimeType;
74+
75+ if (contentEncoding) {
76+ req.headers .emplace_back (" Content-Encoding" , *contentEncoding);
77+ }
78+
79+ auto result = getFileTransfer ()->enqueueFileTransfer (req).get ();
80+
81+ std::regex uploadIdRegex (" <UploadId>([^<]+)</UploadId>" );
82+ std::smatch match;
83+
84+ if (std::regex_search (result.data , match, uploadIdRegex)) {
85+ return match[1 ];
86+ }
87+
88+ throw Error (" S3 CreateMultipartUpload response missing <UploadId>" );
89+ }
90+
4891void S3BinaryCacheStore::abortMultipartUpload (std::string_view key, std::string_view uploadId)
4992{
5093 auto req = makeRequest (key);
0 commit comments