diff --git a/pom.xml b/pom.xml
index f33beb9ef..f504c79ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,9 @@
8
8
UTF-8
+ 2.21.46
+ 0.29.24
+ 1.12.441
@@ -48,7 +51,7 @@
com.amazonaws
aws-java-sdk-bom
- 1.12.441
+ ${com.amazonaws.version}
pom
import
@@ -56,7 +59,7 @@
software.amazon.awssdk
bom
- 2.20.38
+ ${aws.java.sdk.version}
true
pom
import
@@ -68,13 +71,11 @@
software.amazon.awssdk
s3
- 2.20.38
software.amazon.awssdk
kms
- 2.20.38
true
@@ -82,7 +83,7 @@
software.amazon.awssdk.crt
aws-crt
- 0.29.24
+ ${aws.java.crt.version}
true
@@ -164,7 +165,6 @@
software.amazon.awssdk
sts
- 2.20.38
true
test
diff --git a/src/main/java/software/amazon/encryption/s3/S3AsyncEncryptionClient.java b/src/main/java/software/amazon/encryption/s3/S3AsyncEncryptionClient.java
index 2ed1ba3f5..f44c57545 100644
--- a/src/main/java/software/amazon/encryption/s3/S3AsyncEncryptionClient.java
+++ b/src/main/java/software/amazon/encryption/s3/S3AsyncEncryptionClient.java
@@ -30,6 +30,7 @@
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Request;
+import software.amazon.awssdk.services.s3.multipart.MultipartConfiguration;
import software.amazon.encryption.s3.internal.GetEncryptedObjectPipeline;
import software.amazon.encryption.s3.internal.NoRetriesAsyncRequestBody;
import software.amazon.encryption.s3.internal.PutEncryptedObjectPipeline;
@@ -71,6 +72,7 @@ public class S3AsyncEncryptionClient extends DelegatingS3AsyncClient {
private final boolean _enableDelayedAuthenticationMode;
private final boolean _enableMultipartPutObject;
private final long _bufferSize;
+ private final boolean _clientMultipartEnabled;
private S3AsyncEncryptionClient(Builder builder) {
super(builder._wrappedClient);
@@ -81,6 +83,7 @@ private S3AsyncEncryptionClient(Builder builder) {
_enableDelayedAuthenticationMode = builder._enableDelayedAuthenticationMode;
_enableMultipartPutObject = builder._enableMultipartPutObject;
_bufferSize = builder._bufferSize;
+ _clientMultipartEnabled = builder._multipartEnabled != null && builder._multipartEnabled;
}
/**
@@ -147,16 +150,19 @@ public CompletableFuture putObject(PutObjectRequest putObject
}
private CompletableFuture multipartPutObject(PutObjectRequest putObjectRequest, AsyncRequestBody requestBody) {
- S3AsyncClient crtClient;
- if (_wrappedClient instanceof S3CrtAsyncClient) {
+ S3AsyncClient mpuClient;
+ if (_wrappedClient instanceof S3CrtAsyncClient && !_clientMultipartEnabled) {
// if the wrappedClient is a CRT, use it
- crtClient = _wrappedClient;
- } else {
- // else create a default one
- crtClient = S3AsyncClient.crtCreate();
+ mpuClient = _wrappedClient;
+ } else if (_clientMultipartEnabled) {
+ mpuClient = _wrappedClient;
+ }
+ else {
+ // else create a default CRT client
+ mpuClient = S3AsyncClient.crtCreate();
}
PutEncryptedObjectPipeline pipeline = PutEncryptedObjectPipeline.builder()
- .s3AsyncClient(crtClient)
+ .s3AsyncClient(mpuClient)
.cryptoMaterialsManager(_cryptoMaterialsManager)
.secureRandom(_secureRandom)
.build();
@@ -291,8 +297,12 @@ public static class Builder implements S3AsyncClientBuilder {
private S3Configuration _serviceConfiguration = null;
private Boolean _accelerate = null;
private Boolean _disableMultiRegionAccessPoints = null;
+ private Boolean _disableS3ExpressSessionAuth = null;
private Boolean _forcePathStyle = null;
private Boolean _useArnRegion = null;
+ private Boolean _crossRegionAccessEnabled = null;
+ private Boolean _multipartEnabled = null;
+ private MultipartConfiguration _multipartConfiguration = null;
private Builder() {
}
@@ -696,6 +706,12 @@ public Builder disableMultiRegionAccessPoints(Boolean disableMultiRegionAccessPo
return this;
}
+ @Override
+ public S3AsyncClientBuilder disableS3ExpressSessionAuth(Boolean disableS3ExpressSessionAuth) {
+ _disableS3ExpressSessionAuth = disableS3ExpressSessionAuth;
+ return this;
+ }
+
/**
* Forces this client to use path-style addressing for buckets.
*
@@ -719,6 +735,24 @@ public Builder useArnRegion(Boolean useArnRegion) {
return this;
}
+ @Override
+ public Builder multipartEnabled(Boolean enabled) {
+ _multipartEnabled = enabled;
+ return this;
+ }
+
+ @Override
+ public S3AsyncClientBuilder multipartConfiguration(MultipartConfiguration multipartConfiguration) {
+ _multipartConfiguration = multipartConfiguration;
+ return this;
+ }
+
+ @Override
+ public Builder crossRegionAccessEnabled(Boolean crossRegionAccessEnabled) {
+ _crossRegionAccessEnabled = crossRegionAccessEnabled;
+ return this;
+ }
+
/**
* Validates and builds the S3AsyncEncryptionClient according
* to the configuration options passed to the Builder object.
@@ -737,6 +771,12 @@ public S3AsyncEncryptionClient build() {
_bufferSize = DEFAULT_BUFFER_SIZE_BYTES;
}
+ // The S3 Async Client has its own multipart setting,
+ // we enforce that the S3EC multipart PutObject setting is enabled as well.
+ if (_multipartEnabled != null && _multipartEnabled && !_enableMultipartPutObject) {
+ throw new S3EncryptionClientException("EnableMultipartPutObject MUST be enabled when the MultipartEnabled option is set to true.");
+ }
+
if (_wrappedClient == null) {
_wrappedClient = S3AsyncClient.builder()
.credentialsProvider(_awsCredentialsProvider)
@@ -751,8 +791,12 @@ public S3AsyncEncryptionClient build() {
.serviceConfiguration(_serviceConfiguration)
.accelerate(_accelerate)
.disableMultiRegionAccessPoints(_disableMultiRegionAccessPoints)
+ .disableS3ExpressSessionAuth(_disableS3ExpressSessionAuth)
.forcePathStyle(_forcePathStyle)
.useArnRegion(_useArnRegion)
+ .crossRegionAccessEnabled(_crossRegionAccessEnabled)
+ .multipartEnabled(_multipartEnabled)
+ .multipartConfiguration(_multipartConfiguration)
.build();
}
diff --git a/src/main/java/software/amazon/encryption/s3/S3EncryptionClient.java b/src/main/java/software/amazon/encryption/s3/S3EncryptionClient.java
index 1c3f6c27a..7f3f1f6be 100644
--- a/src/main/java/software/amazon/encryption/s3/S3EncryptionClient.java
+++ b/src/main/java/software/amazon/encryption/s3/S3EncryptionClient.java
@@ -544,8 +544,10 @@ public static class Builder implements S3BaseClientBuilder encryptionContext = new HashMap<>();
+ encryptionContext.put("user-metadata-key", "user-metadata-value-v3-to-v3");
+
+ ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
+
+ CompletableFuture futurePut = v3Client.putObject(builder -> builder
+ .bucket(BUCKET)
+ .overrideConfiguration(withAdditionalConfiguration(encryptionContext))
+ .key(objectKey), AsyncRequestBody.fromInputStream(inputStream, fileSizeLimit, singleThreadExecutor));
+ futurePut.join();
+ singleThreadExecutor.shutdown();
+
+ // Asserts
+ CompletableFuture> getFuture = v3Client.getObject(builder -> builder
+ .bucket(BUCKET)
+ .overrideConfiguration(S3EncryptionClient.withAdditionalConfiguration(encryptionContext))
+ .key(objectKey), AsyncResponseTransformer.toBlockingInputStream());
+ ResponseInputStream output = getFuture.join();
+
+ assertTrue(IOUtils.contentEquals(objectStreamForResult, output));
+
+ deleteObject(BUCKET, objectKey, v3Client);
+ v3Client.close();
+ }
}