Skip to content

Commit fc61e61

Browse files
committed
add content md5,sha256 check
1 parent 6b861cb commit fc61e61

File tree

6 files changed

+127
-4
lines changed

6 files changed

+127
-4
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/target
2-
2+
.DS_Store
33
.vscode
44
.idea
55
__pycache__/

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/s3s-e2e/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ http-body-util = "0.1.3"
2323
futures = { version = "0.3.31", default-features = false }
2424
bytes = "1.10.1"
2525
http-body = "1.0.1"
26+
md5 = "0.7.0"
27+
base64 = "0.22.0"
28+
sha2 = "0.10.8"
29+
hex-simd = "0.8.0"
30+
base64-simd = "0.8.0"
31+
2632

2733
[dependencies.aws-config]
2834
version = "1.8.7"

crates/s3s-e2e/src/advanced.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::case;
22

3+
use aws_sdk_s3::types::ChecksumAlgorithm;
34
use s3s_test::Result;
45
use s3s_test::TestFixture;
56
use s3s_test::TestSuite;
@@ -114,7 +115,13 @@ impl Multipart {
114115
let key = self.key.as_str();
115116

116117
// Create multipart upload
117-
let create_resp = s3.create_multipart_upload().bucket(bucket).key(key).send().await?;
118+
let create_resp = s3
119+
.create_multipart_upload()
120+
.checksum_algorithm(ChecksumAlgorithm::Crc32)
121+
.bucket(bucket)
122+
.key(key)
123+
.send()
124+
.await?;
118125

119126
let upload_id = create_resp.upload_id().unwrap();
120127

crates/s3s-e2e/src/basic.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::utils::*;
33

44
use std::sync::Arc;
55

6+
use base64::Engine as _;
67
use s3s_test::Result;
78
use s3s_test::TestFixture;
89
use s3s_test::TestSuite;
@@ -14,6 +15,8 @@ use aws_sdk_s3::primitives::SdkBody;
1415
use bytes::Bytes;
1516
use futures::StreamExt as _;
1617
use http_body_util::StreamBody;
18+
use sha2::Digest as _;
19+
use sha2::Sha256;
1720

1821
pub fn register(tcx: &mut TestContext) {
1922
case!(tcx, Basic, Essential, test_list_buckets);
@@ -25,6 +28,7 @@ pub fn register(tcx: &mut TestContext) {
2528
case!(tcx, Basic, Put, test_put_object_with_metadata);
2629
case!(tcx, Basic, Put, test_put_object_larger);
2730
case!(tcx, Basic, Put, test_put_object_with_checksum_algorithm);
31+
case!(tcx, Basic, Put, test_put_object_with_content_checksums);
2832
case!(tcx, Basic, Copy, test_copy_object);
2933
}
3034

@@ -435,6 +439,100 @@ impl Put {
435439

436440
Ok(())
437441
}
442+
443+
async fn test_put_object_with_content_checksums(self: Arc<Self>) -> Result {
444+
use base64::Engine;
445+
use sha2::{Digest, Sha256};
446+
447+
let s3 = &self.s3;
448+
let bucket = self.bucket.as_str();
449+
let key = "file-with-content-checksums";
450+
451+
// Create test content
452+
let content = "Hello, World! This is a test content for checksum validation. 你好世界!";
453+
let content_bytes = content.as_bytes();
454+
455+
// Calculate MD5 hash
456+
let md5_digest = md5::compute(content_bytes);
457+
let md5_hash = base64_simd::STANDARD.encode_to_string(md5_digest.as_ref());
458+
459+
// Test with Content-MD5
460+
s3.put_object()
461+
.bucket(bucket)
462+
.key(format!("{key}-md5"))
463+
.body(ByteStream::from_static(content_bytes))
464+
.content_md5(&md5_hash)
465+
.send()
466+
.await?;
467+
468+
// Test with different content sizes and MD5
469+
let large_content = "x".repeat(2048);
470+
let large_md5_digest = md5::compute(large_content.as_bytes());
471+
let large_md5_hash = base64::engine::general_purpose::STANDARD.encode(large_md5_digest.as_ref());
472+
473+
s3.put_object()
474+
.bucket(bucket)
475+
.key(format!("{key}-large"))
476+
.body(ByteStream::from(large_content.clone().into_bytes()))
477+
.content_md5(&large_md5_hash)
478+
.send()
479+
.await?;
480+
481+
// Test with empty content and MD5
482+
let empty_content = "";
483+
let empty_md5_digest = md5::compute(empty_content.as_bytes());
484+
let empty_md5_hash = base64::engine::general_purpose::STANDARD.encode(empty_md5_digest.as_ref());
485+
486+
s3.put_object()
487+
.bucket(bucket)
488+
.key(format!("{key}-empty"))
489+
.body(ByteStream::from_static(empty_content.as_bytes()))
490+
.content_md5(&empty_md5_hash)
491+
.send()
492+
.await?;
493+
494+
// Verify all objects were uploaded correctly
495+
for (suffix, expected_content) in [("md5", content), ("large", &large_content), ("empty", empty_content)] {
496+
let resp = s3.get_object().bucket(bucket).key(format!("{key}-{suffix}")).send().await?;
497+
498+
let body = resp.body.collect().await?;
499+
let body = String::from_utf8(body.to_vec())?;
500+
assert_eq!(body, expected_content);
501+
}
502+
503+
// Test with incorrect MD5 (should fail)
504+
let incorrect_md5 = base64::engine::general_purpose::STANDARD.encode(b"incorrect_md5_hash");
505+
let result = s3
506+
.put_object()
507+
.bucket(bucket)
508+
.key(format!("{key}-incorrect-md5"))
509+
.body(ByteStream::from_static(content_bytes))
510+
.content_md5(&incorrect_md5)
511+
.send()
512+
.await;
513+
514+
// This should fail with a checksum mismatch error
515+
assert!(result.is_err(), "Expected checksum mismatch error for incorrect MD5");
516+
517+
// Test with correct MD5 but wrong content (should fail)
518+
let wrong_content = "This is different content";
519+
let wrong_md5_digest = md5::compute(wrong_content.as_bytes());
520+
let wrong_md5_hash = base64::engine::general_purpose::STANDARD.encode(wrong_md5_digest.as_ref());
521+
522+
let result = s3
523+
.put_object()
524+
.bucket(bucket)
525+
.key(format!("{key}-wrong-content"))
526+
.body(ByteStream::from_static(content_bytes)) // Using original content
527+
.content_md5(&wrong_md5_hash) // But wrong MD5
528+
.send()
529+
.await;
530+
531+
// This should also fail
532+
assert!(result.is_err(), "Expected checksum mismatch error for wrong content with correct MD5");
533+
534+
Ok(())
535+
}
438536
}
439537

440538
// Copy test fixture

scripts/s3s-e2e.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
cargo build -p s3s-e2e --release
44

5-
export AWS_ACCESS_KEY_ID=minioadmin
6-
export AWS_SECRET_ACCESS_KEY=minioadmin
5+
export AWS_ACCESS_KEY_ID=rustfsadmin
6+
export AWS_SECRET_ACCESS_KEY=rustfsadmin
77
export AWS_REGION=us-east-1
8+
# export AWS_REGION=cn-north-1
89
export AWS_ENDPOINT_URL=http://localhost:9000
910

1011
if [ -z "$RUST_LOG" ]; then

0 commit comments

Comments
 (0)