Skip to content

Commit 4b6d07d

Browse files
committed
feat(libstore/s3-binary-cache-store): implement createMultipartUpload()
POST to key with `?uploads` query parameter, optionally set `Content-Encoding` header, parse `uploadId` from XML response using regex
1 parent ac8b1ef commit 4b6d07d

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

src/libstore/s3-binary-cache-store.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <cassert>
66
#include <ranges>
7+
#include <regex>
78

89
namespace nix {
910

@@ -27,6 +28,15 @@ class S3BinaryCacheStore : public virtual HttpBinaryCacheStore
2728
private:
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+
4891
void S3BinaryCacheStore::abortMultipartUpload(std::string_view key, std::string_view uploadId)
4992
{
5093
auto req = makeRequest(key);

0 commit comments

Comments
 (0)