Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/services/s3/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ reqsign-aws-v4 = { version = "3.0.0", default-features = false }
reqsign-core = { version = "3.0.1", default-features = false }
reqsign-file-read-tokio = { version = "3.0.1", default-features = false }
serde = { workspace = true, features = ["derive"] }
sha2 = { workspace = true }
url = { workspace = true }

[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions core/services/s3/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ impl S3Builder {
///
/// Available options:
/// - "crc32c"
/// - "sha256"
/// - "md5"
pub fn checksum_algorithm(mut self, checksum_algorithm: &str) -> Self {
self.config.checksum_algorithm = Some(checksum_algorithm.to_string());
Expand Down Expand Up @@ -800,6 +801,7 @@ impl Builder for S3Builder {

let checksum_algorithm = match config.checksum_algorithm.as_deref() {
Some("crc32c") => Some(ChecksumAlgorithm::Crc32c),
Some("sha256") => Some(ChecksumAlgorithm::Sha256),
Some("md5") => Some(ChecksumAlgorithm::Md5),
None => None,
v => {
Expand Down
1 change: 1 addition & 0 deletions core/services/s3/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ pub struct S3Config {
///
/// Available options:
/// - "crc32c"
/// - "sha256"
/// - "md5"
///
/// <!-- @group Behavior -->
Expand Down
29 changes: 29 additions & 0 deletions core/services/s3/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ use reqsign_aws_v4::Credential;
use reqsign_core::{Context, Signer};
use serde::Deserialize;
use serde::Serialize;
use sha2::Digest as Sha2Digest;
use sha2::Sha256;

use opendal_core::raw::*;
use opendal_core::*;
Expand Down Expand Up @@ -125,6 +127,13 @@ fn format_crc32c_iter(body: Buffer) -> String {
BASE64_STANDARD.encode(crc.to_be_bytes())
}

fn format_sha256_iter(body: Buffer) -> String {
let mut hasher = Sha256::new();
body.for_each(|b| hasher.update(&b));

BASE64_STANDARD.encode(hasher.finalize())
}

impl Debug for S3Core {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("S3Core")
Expand Down Expand Up @@ -296,6 +305,7 @@ impl S3Core {
match self.checksum_algorithm {
None => None,
Some(ChecksumAlgorithm::Crc32c) => Some(format_crc32c_iter(body.clone())),
Some(ChecksumAlgorithm::Sha256) => Some(format_sha256_iter(body.clone())),
Some(ChecksumAlgorithm::Md5) => Some(format_content_md5_iter(body.clone())),
}
}
Expand Down Expand Up @@ -1406,6 +1416,8 @@ pub struct CompleteMultipartUploadRequestPart {
pub etag: String,
#[serde(rename = "ChecksumCRC32C", skip_serializing_if = "Option::is_none")]
pub checksum_crc32c: Option<String>,
#[serde(rename = "ChecksumSHA256", skip_serializing_if = "Option::is_none")]
pub checksum_sha256: Option<String>,
}

/// Output of `CompleteMultipartUpload` operation
Expand Down Expand Up @@ -1557,13 +1569,15 @@ pub struct ListObjectVersionsOutputDeleteMarker {

pub enum ChecksumAlgorithm {
Crc32c,
Sha256,
/// Mapping to the `Content-MD5` header from S3.
Md5,
}
impl ChecksumAlgorithm {
pub fn to_header_name(&self) -> HeaderName {
match self {
Self::Crc32c => HeaderName::from_static("x-amz-checksum-crc32c"),
Self::Sha256 => HeaderName::from_static("x-amz-checksum-sha256"),
Self::Md5 => HeaderName::from_static("content-md5"),
}
}
Expand All @@ -1575,6 +1589,7 @@ impl Display for ChecksumAlgorithm {
"{}",
match self {
Self::Crc32c => "CRC32C",
Self::Sha256 => "SHA256",
Self::Md5 => "MD5",
}
)
Expand All @@ -1588,6 +1603,20 @@ mod tests {

use super::*;

#[test]
fn test_format_sha256_iter() {
// Base64-encoded SHA256 of "hello".
assert_eq!(
format_sha256_iter(Buffer::from("hello")),
"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
);
// Base64-encoded SHA256 of an empty input.
assert_eq!(
format_sha256_iter(Buffer::new()),
"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
);
}

/// This example is from https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html#API_CreateMultipartUpload_Examples
#[test]
fn test_deserialize_initiate_multipart_upload_result() {
Expand Down
7 changes: 7 additions & 0 deletions core/services/s3/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ impl oio::MultipartWrite for S3Writer {
part_number: p.part_number,
etag: p.etag.clone(),
checksum_crc32c: p.checksum.clone(),
..Default::default()
},
ChecksumAlgorithm::Sha256 => CompleteMultipartUploadRequestPart {
part_number: p.part_number,
etag: p.etag.clone(),
checksum_sha256: p.checksum.clone(),
..Default::default()
},
ChecksumAlgorithm::Md5 => CompleteMultipartUploadRequestPart {
part_number: p.part_number,
Expand Down
Loading