diff --git a/xtask/src/publish/s3.rs b/xtask/src/publish/s3.rs index 368110c..f3d27b4 100644 --- a/xtask/src/publish/s3.rs +++ b/xtask/src/publish/s3.rs @@ -6,7 +6,7 @@ use std::{ use aws_credential_types::Credentials; use aws_sdk_s3::{ Client, - config::Region, + config::{Region, RequestChecksumCalculation}, error::SdkError, operation::{get_object::GetObjectError, put_object::PutObjectError}, primitives::ByteStream, @@ -475,15 +475,19 @@ async fn client(options: &S3Options) -> Result { .endpoint_url(options.endpoint_url.to_owned()) .credentials_provider(credentials) .force_path_style(true) + // Cloudflare R2 rejects the aws-chunked PutObject requests generated by + // the SDK's default flexible checksum policy. + .request_checksum_calculation(RequestChecksumCalculation::WhenRequired) .build(); Ok(Client::from_conf(s3_config)) } #[cfg(test)] mod tests { + use aws_sdk_s3::config::RequestChecksumCalculation; use clap::error::ErrorKind; - use super::parse_s3_targets; + use super::{S3Options, client, parse_s3_targets}; #[test] fn brew_requires_public_base_url() { @@ -493,4 +497,22 @@ mod tests { assert_eq!(error.kind(), ErrorKind::MissingRequiredArgument); assert!(error.to_string().contains("--public-base-url")); } + + #[tokio::test] + async fn s3_client_calculates_request_checksums_only_when_required() { + let options = S3Options { + endpoint_url: "https://example.invalid".to_string(), + bucket: "download".to_string(), + access_key_id: "access".to_string(), + secret_access_key: "secret".to_string(), + dry_run: false, + }; + + let client = client(&options).await.expect("client should build"); + + assert_eq!( + client.config().request_checksum_calculation(), + Some(&RequestChecksumCalculation::WhenRequired) + ); + } }