Skip to content

Commit d7059a4

Browse files
committed
Add support for checksums
1 parent 156b9c3 commit d7059a4

35 files changed

+547
-30
lines changed

Src/ProviderTests/Multipart/MultipartTests.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Genbox.ProviderTests.Misc;
1+
using System.Security.Cryptography;
2+
using System.Text;
3+
using Genbox.ProviderTests.Misc;
24
using Genbox.SimpleS3.Core.Abstracts;
35
using Genbox.SimpleS3.Core.Common.Helpers;
46
using Genbox.SimpleS3.Core.Enums;
@@ -206,7 +208,7 @@ public async Task MultipartUpload(S3Provider provider, string bucket, ISimpleCli
206208

207209
CompleteMultipartUploadResponse completeResp = await client.CompleteMultipartUploadAsync(bucket, objectKey, initResp.UploadId, [new S3PartInfo(uploadResp1.ETag, 1), new S3PartInfo(uploadResp2.ETag, 2)]);
208210
Assert.Equal(200, completeResp.StatusCode);
209-
Assert.NotNull(uploadResp2.ETag);
211+
Assert.NotNull(completeResp.ETag);
210212

211213
if (provider == S3Provider.AmazonS3)
212214
{
@@ -292,4 +294,40 @@ public async Task MultipartViaExtensions(S3Provider _, string bucket, ISimpleCli
292294

293295
Assert.Equal(10, count);
294296
}
297+
298+
[Theory]
299+
[MultipleProviders(S3Provider.AmazonS3)]
300+
public async Task MultipartChecksum(S3Provider provider, string bucket, ISimpleClient client)
301+
{
302+
const string objectKey = nameof(MultipartChecksum);
303+
304+
CreateMultipartUploadResponse initResp = await client.CreateMultipartUploadAsync(bucket, objectKey, r =>
305+
{
306+
r.ChecksumAlgorithm = ChecksumAlgorithm.Sha1;
307+
r.ChecksumType = ChecksumType.Composite;
308+
});
309+
310+
Assert.Equal(200, initResp.StatusCode);
311+
Assert.Equal(ChecksumType.Composite, initResp.ChecksumType);
312+
Assert.Equal(ChecksumAlgorithm.Sha1, initResp.ChecksumAlgorithm);
313+
314+
byte[] data = "hello world"u8.ToArray();
315+
await using MemoryStream ms = new MemoryStream(data);
316+
317+
byte[] checksum = SHA1.HashData(data);
318+
UploadPartResponse uploadResp = await client.UploadPartAsync(bucket, objectKey, 1, initResp.UploadId, ms, r =>
319+
{
320+
r.ChecksumAlgorithm = ChecksumAlgorithm.Sha1;
321+
r.Checksum = checksum;
322+
});
323+
Assert.Equal(200, uploadResp.StatusCode);
324+
Assert.Equal(ChecksumAlgorithm.Sha1, uploadResp.ChecksumAlgorithm);
325+
Assert.Equal(checksum, uploadResp.Checksum);
326+
327+
CompleteMultipartUploadResponse completeResp = await client.CompleteMultipartUploadAsync(bucket, objectKey, initResp.UploadId, [new S3PartInfo(uploadResp.ETag, 1, checksum, ChecksumAlgorithm.Sha1)]);
328+
Assert.Equal(200, completeResp.StatusCode);
329+
Assert.Equal(ChecksumType.Composite, completeResp.ChecksumType);
330+
Assert.Equal(ChecksumAlgorithm.Sha1, completeResp.ChecksumAlgorithm);
331+
Assert.NotNull(completeResp.Checksum);
332+
}
295333
}

Src/ProviderTests/Objects/HeadObjectTests.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
using Genbox.HttpBuilders.Enums;
1+
using System.Security.Cryptography;
2+
using Genbox.HttpBuilders.Enums;
23
using Genbox.ProviderTests.Misc;
34
using Genbox.SimpleS3.Core.Abstracts;
5+
using Genbox.SimpleS3.Core.Enums;
46
using Genbox.SimpleS3.Core.Extensions;
57
using Genbox.SimpleS3.Core.Network.Responses.Objects;
68
using Genbox.SimpleS3.Utility.Shared;
@@ -95,4 +97,28 @@ public async Task HeadObjectWebsiteRedirect(S3Provider _, string bucket, ISimple
9597
Assert.Equal(200, headResp.StatusCode);
9698
Assert.Equal("https://google.com", headResp.WebsiteRedirectLocation);
9799
}
100+
101+
[Theory]
102+
[MultipleProviders(S3Provider.AmazonS3)]
103+
public async Task HeadObjectChecksum(S3Provider _, string bucket, ISimpleClient client)
104+
{
105+
byte[] data = [1, 2, 3, 4];
106+
byte[] checksum = SHA1.HashData(data);
107+
108+
PutObjectResponse putResp = await client.PutObjectDataAsync(bucket, nameof(HeadObjectChecksum), data, r =>
109+
{
110+
r.ChecksumAlgorithm = ChecksumAlgorithm.Sha1;
111+
r.Checksum = checksum;
112+
});
113+
114+
Assert.Equal(200, putResp.StatusCode);
115+
Assert.Equal(checksum, putResp.Checksum);
116+
Assert.Equal(ChecksumType.FullObject, putResp.ChecksumType);
117+
118+
HeadObjectResponse resp = await client.HeadObjectAsync(bucket, nameof(HeadObjectChecksum), r => r.EnableChecksum = true);
119+
Assert.Equal(200, resp.StatusCode);
120+
Assert.Equal(ChecksumType.FullObject, resp.ChecksumType);
121+
Assert.Equal(ChecksumAlgorithm.Sha1, resp.ChecksumAlgorithm);
122+
Assert.Equal(checksum, resp.Checksum);
123+
}
98124
}

Src/ProviderTests/Objects/ListObjectVersionsTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using System.Security.Cryptography;
23
using Genbox.ProviderTests.Misc;
34
using Genbox.SimpleS3.Core.Abstracts;
45
using Genbox.SimpleS3.Core.Common.Helpers;
@@ -222,4 +223,31 @@ await CreateTempBucketAsync(provider, client, async tempBucket =>
222223
Assert.Equal(tempObjName, obj.ObjectKey);
223224
});
224225
}
226+
227+
[Theory]
228+
[MultipleProviders(S3Provider.AmazonS3)]
229+
public async Task ListObjectsChecksum(S3Provider provider, string _, ISimpleClient client)
230+
{
231+
await CreateTempBucketAsync(provider, client, async tempBucket =>
232+
{
233+
string tempObjName = "object-" + Guid.NewGuid();
234+
byte[] data = "hello world"u8.ToArray();
235+
byte[] checksum = SHA1.HashData(data);
236+
237+
PutObjectResponse putResp = await client.PutObjectDataAsync(tempBucket, tempObjName, data, r =>
238+
{
239+
r.ChecksumAlgorithm = ChecksumAlgorithm.Sha1;
240+
r.Checksum = checksum;
241+
});
242+
243+
Assert.Equal(200, putResp.StatusCode);
244+
245+
ListObjectVersionsResponse listResp = await client.ListObjectVersionsAsync(tempBucket);
246+
Assert.Equal(200, listResp.StatusCode);
247+
S3Version obj = Assert.Single(listResp.Versions);
248+
249+
Assert.Equal(ChecksumAlgorithm.Sha1, obj.ChecksumAlgorithm);
250+
Assert.Equal(ChecksumType.FullObject, obj.ChecksumType);
251+
});
252+
}
225253
}

Src/ProviderTests/Objects/ListObjectsTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Globalization;
2+
using System.Security.Cryptography;
3+
using System.Text;
24
using Genbox.ProviderTests.Misc;
35
using Genbox.SimpleS3.Core.Abstracts;
46
using Genbox.SimpleS3.Core.Common.Helpers;
@@ -178,4 +180,31 @@ await CreateTempBucketAsync(provider, client, async tempBucket =>
178180
Assert.Equal(tempObjName, obj.ObjectKey);
179181
});
180182
}
183+
184+
[Theory]
185+
[MultipleProviders(S3Provider.AmazonS3)]
186+
public async Task ListObjectsChecksum(S3Provider provider, string _, ISimpleClient client)
187+
{
188+
await CreateTempBucketAsync(provider, client, async tempBucket =>
189+
{
190+
string tempObjName = "object-" + Guid.NewGuid();
191+
byte[] data = "hello world"u8.ToArray();
192+
byte[] checksum = SHA1.HashData(data);
193+
194+
PutObjectResponse putResp = await client.PutObjectDataAsync(tempBucket, tempObjName, data, r =>
195+
{
196+
r.ChecksumAlgorithm = ChecksumAlgorithm.Sha1;
197+
r.Checksum = checksum;
198+
});
199+
200+
Assert.Equal(200, putResp.StatusCode);
201+
202+
ListObjectsResponse listResp = await client.ListObjectsAsync(tempBucket);
203+
Assert.Equal(200, listResp.StatusCode);
204+
S3Object obj = Assert.Single(listResp.Objects);
205+
206+
Assert.Equal(ChecksumAlgorithm.Sha1, obj.ChecksumAlgorithm);
207+
Assert.Equal(ChecksumType.FullObject, obj.ChecksumType);
208+
});
209+
}
181210
}

Src/ProviderTests/Objects/PutObjectTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using System.Security.Cryptography;
23
using Genbox.HttpBuilders.Enums;
34
using Genbox.ProviderTests.Misc;
45
using Genbox.SimpleS3.Core.Abstracts;
@@ -339,4 +340,28 @@ public async Task PutObjectWebsiteRedirect(S3Provider _, string bucket, ISimpleC
339340
Assert.Equal(200, resp.StatusCode);
340341
Assert.Equal("https://google.com", resp.WebsiteRedirectLocation);
341342
}
343+
344+
[Theory]
345+
[MultipleProviders(S3Provider.AmazonS3)]
346+
public async Task PutObjectChecksum(S3Provider _, string bucket, ISimpleClient client)
347+
{
348+
byte[] data = [1, 2, 3, 4];
349+
byte[] checksum = SHA1.HashData(data);
350+
351+
PutObjectResponse putResp = await client.PutObjectDataAsync(bucket, nameof(PutObjectChecksum), data, r =>
352+
{
353+
r.ChecksumAlgorithm = ChecksumAlgorithm.Sha1;
354+
r.Checksum = checksum;
355+
});
356+
357+
Assert.Equal(200, putResp.StatusCode);
358+
Assert.Equal(checksum, putResp.Checksum);
359+
Assert.Equal(ChecksumType.FullObject, putResp.ChecksumType);
360+
361+
GetObjectResponse resp = await client.GetObjectAsync(bucket, nameof(PutObjectChecksum), r => r.EnableChecksum = true);
362+
Assert.Equal(200, resp.StatusCode);
363+
Assert.Equal(ChecksumType.FullObject, resp.ChecksumType);
364+
Assert.Equal(ChecksumAlgorithm.Sha1, resp.ChecksumAlgorithm);
365+
Assert.Equal(checksum, resp.Checksum);
366+
}
342367
}

Src/SimpleS3.Core.Common/Misc/AmzHeaders.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ public static class AmzHeaders
3737
public const string XAmzVersionId = "x-amz-version-id";
3838
public const string XAmzExpiration = "x-amz-expiration";
3939

40+
public const string XAmzChecksumType = "x-amz-checksum-type";
41+
public const string XAmzChecksumMode = "x-amz-checksum-mode";
42+
43+
public const string XAmzChecksumAlgorithm = "x-amz-checksum-algorithm";
44+
public const string XAmzChecksum = "x-amz-checksum-"; //Needs to be combined with XAmzChecksumAlgorithm
45+
public const string XAmzChecksumCrc32 = "x-amz-checksum-crc32";
46+
public const string XAmzChecksumCrc32C = "x-amz-checksum-crc32c";
47+
public const string XAmzChecksumCrc64Nvme = "x-amz-checksum-crc64nvme";
48+
public const string XAmzChecksumSha1 = "x-amz-checksum-sha1";
49+
public const string XAmzChecksumSha256 = "x-amz-checksum-sha256";
50+
4051
#endregion
4152

4253
#region ServerSideEncryption
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using Genbox.FastEnum;
3+
4+
namespace Genbox.SimpleS3.Core.Enums;
5+
6+
/// <summary>Indicates the algorithm that you want Amazon S3 to use to create the checksum for the object. For more information, see https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html</summary>
7+
[FastEnum]
8+
public enum ChecksumAlgorithm
9+
{
10+
Unknown = 0,
11+
[Display(Name = "CRC32")]Crc32,
12+
[Display(Name = "CRC32C")]Crc32C,
13+
[Display(Name = "SHA1")]Sha1,
14+
[Display(Name = "SHA256")]Sha256,
15+
[Display(Name = "CRC64NVME")]Crc64Nvme,
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using Genbox.FastEnum;
3+
4+
namespace Genbox.SimpleS3.Core.Enums;
5+
6+
/// <summary>Indicates the checksum type that you want Amazon S3 to use to calculate the object’s checksum value. For more information, see https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html</summary>
7+
[FastEnum]
8+
public enum ChecksumType
9+
{
10+
Unknown = 0,
11+
[Display(Name = "COMPOSITE")]Composite,
12+
[Display(Name = "FULL_OBJECT")]FullObject
13+
}

Src/SimpleS3.Core/Internals/Fluent/Upload.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Genbox.SimpleS3.Core.Common.Misc;
1111
using Genbox.SimpleS3.Core.Common.Validation;
1212
using Genbox.SimpleS3.Core.Enums;
13+
using Genbox.SimpleS3.Core.Network.Requests.Multipart;
1314
using Genbox.SimpleS3.Core.Network.Requests.Objects;
1415
using Genbox.SimpleS3.Core.Network.Responses.Multipart;
1516
using Genbox.SimpleS3.Core.Network.Responses.Objects;
@@ -190,10 +191,30 @@ public IUpload RemoveLegalHold()
190191

191192
public Task<CompleteMultipartUploadResponse> UploadMultipartAsync(Stream data, CancellationToken token = default)
192193
{
193-
_request.Method = HttpMethodType.POST;
194-
_request.Content = null;
195-
196-
return _multipartTransfer.MultipartUploadAsync(_request, data, token: token);
194+
//Create a request and copy over all the supported properties
195+
CreateMultipartUploadRequest req = new CreateMultipartUploadRequest(_request.BucketName, _request.ObjectKey);
196+
req.CacheControl = _request.CacheControl;
197+
req.ContentDisposition = _request.ContentDisposition;
198+
req.ContentType = _request.ContentType;
199+
req.ContentEncoding = _request.ContentEncoding;
200+
req.SseAlgorithm = _request.SseAlgorithm;
201+
req.SseKmsKeyId = _request.SseKmsKeyId;
202+
req.SseCustomerAlgorithm = _request.SseCustomerAlgorithm;
203+
req.SseCustomerKey = _request.SseCustomerKey;
204+
req.SseCustomerKeyMd5 = _request.SseCustomerKeyMd5;
205+
req.Metadata = _request.Metadata;
206+
req.StorageClass = _request.StorageClass;
207+
req.Tags = _request.Tags;
208+
req.Acl = _request.Acl;
209+
req.AclGrantRead = _request.AclGrantRead;
210+
req.AclGrantReadAcp = _request.AclGrantReadAcp;
211+
req.AclGrantWriteAcp = _request.AclGrantWriteAcp;
212+
req.AclGrantFullControl = _request.AclGrantFullControl;
213+
req.LockMode = _request.LockMode;
214+
req.LockRetainUntil = _request.LockRetainUntil;
215+
req.LockLegalHold = _request.LockLegalHold;
216+
217+
return _multipartTransfer.MultipartUploadAsync(req, data, token: token);
197218
}
198219

199220
public Task<PutObjectResponse> UploadAsync(Stream? data, CancellationToken token = default)

Src/SimpleS3.Core/Internals/Marshallers/Requests/GenericRequestMapper.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,5 +144,17 @@ public static void Map<T>(T req) where T : IRequest
144144

145145
if (req is IHasExpectedBucketOwner expectedBucketOwner && expectedBucketOwner.ExpectedBucketOwner != null)
146146
req.SetHeader(AmzHeaders.XAmzExpectedBucketOwner, expectedBucketOwner.ExpectedBucketOwner);
147+
148+
if (req is IHasChecksum checksum)
149+
{
150+
if (checksum.ChecksumType != ChecksumType.Unknown)
151+
req.SetHeader(AmzHeaders.XAmzChecksumType, checksum.ChecksumType.GetDisplayName());
152+
153+
if (checksum.ChecksumAlgorithm != ChecksumAlgorithm.Unknown)
154+
req.SetHeader(AmzHeaders.XAmzChecksumAlgorithm, checksum.ChecksumAlgorithm.GetDisplayName());
155+
}
156+
157+
if (req is IHasChecksumProperties checksumProps && checksumProps.ChecksumAlgorithm != ChecksumAlgorithm.Unknown && checksumProps.Checksum != null)
158+
req.SetHeader(AmzHeaders.XAmzChecksum + checksumProps.ChecksumAlgorithm.ToString().ToLowerInvariant(), Convert.ToBase64String(checksumProps.Checksum));
147159
}
148160
}

0 commit comments

Comments
 (0)