Skip to content

Commit 912abe0

Browse files
dbadaya1Dhananjay Badayaalextwoods
authored
Add support for request-level configuration override in AsyncClient multipart upload (#6900)
* Add support for request-level configuration override in S3 AsyncClient multipart upload When using the S3 AsyncClient for multipart uploads and copies, request-level override configuration set on the original PutObjectRequest or CopyObjectRequest was not being propagated to the sub-requests (CreateMultipartUpload, UploadPart, CompleteMultipartUpload, AbortMultipartUpload, ListParts, HeadObject, UploadPartCopy). This change ensures overrideConfiguration is forwarded from the original request to all derived multipart sub-requests in SdkPojoConversionUtils and CopyObjectHelper. * revision 2 --------- Co-authored-by: Dhananjay Badaya <badayd@amazon.com> Co-authored-by: Alex Woods <alexwoo@amazon.com>
1 parent 2619c3d commit 912abe0

5 files changed

Lines changed: 220 additions & 7 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "Amazon S3",
4+
"contributor": "dbadaya1",
5+
"description": "Fixed request-level override configuration not being propagated to sub-requests during multipart uploads and copies in the S3 AsyncClient."
6+
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/multipart/CopyObjectHelper.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ private CompletableFuture<CompleteMultipartUploadResponse> completeMultipartUplo
162162
IntStream.range(0, completedParts.length())
163163
.mapToObj(completedParts::get)
164164
.toArray(CompletedPart[]::new);
165-
CompleteMultipartUploadRequest completeMultipartUploadRequest =
166-
CompleteMultipartUploadRequest.builder()
165+
CompleteMultipartUploadRequest.Builder builder = CompleteMultipartUploadRequest.builder()
167166
.bucket(copyObjectRequest.destinationBucket())
168167
.key(copyObjectRequest.destinationKey())
169168
.uploadId(uploadId)
@@ -172,8 +171,9 @@ private CompletableFuture<CompleteMultipartUploadResponse> completeMultipartUplo
172171
.build())
173172
.sseCustomerAlgorithm(copyObjectRequest.sseCustomerAlgorithm())
174173
.sseCustomerKey(copyObjectRequest.sseCustomerKey())
175-
.sseCustomerKeyMD5(copyObjectRequest.sseCustomerKeyMD5())
176-
.build();
174+
.sseCustomerKeyMD5(copyObjectRequest.sseCustomerKeyMD5());
175+
copyObjectRequest.overrideConfiguration().ifPresent(builder::overrideConfiguration);
176+
CompleteMultipartUploadRequest completeMultipartUploadRequest = builder.build();
177177
return s3AsyncClient.completeMultipartUpload(completeMultipartUploadRequest);
178178
}
179179

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/multipart/SdkPojoConversionUtils.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Objects;
2424
import java.util.Set;
2525
import software.amazon.awssdk.annotations.SdkInternalApi;
26+
import software.amazon.awssdk.awscore.AwsRequest;
2627
import software.amazon.awssdk.core.SdkField;
2728
import software.amazon.awssdk.core.SdkPojo;
2829
import software.amazon.awssdk.core.exception.SdkClientException;
@@ -164,6 +165,7 @@ public static UploadPartRequest toUploadPartRequest(PutObjectRequest putObjectRe
164165
UploadPartRequest.Builder builder = UploadPartRequest.builder();
165166
validateRequestFields(putObjectRequest, builder.build(), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS);
166167
setSdkFields(builder, putObjectRequest, PUT_OBJECT_REQUEST_TO_UPLOAD_PART_FIELDS_TO_IGNORE);
168+
propagateOverrideConfig(builder, putObjectRequest);
167169
return builder.uploadId(uploadId).partNumber(partNumber).build();
168170
}
169171

@@ -175,6 +177,7 @@ public static CompleteMultipartUploadRequest toCompleteMultipartUploadRequest(Pu
175177
setSdkFields(builder, putObjectRequest);
176178

177179
builder.mpuObjectSize(contentLength);
180+
propagateOverrideConfig(builder, putObjectRequest);
178181

179182
if (S3ChecksumUtils.checksumValueSpecified(putObjectRequest)) {
180183
builder.checksumType(ChecksumType.FULL_OBJECT);
@@ -188,6 +191,7 @@ public static CreateMultipartUploadRequest toCreateMultipartUploadRequest(PutObj
188191
CreateMultipartUploadRequest.Builder builder = CreateMultipartUploadRequest.builder();
189192
validateRequestFields(putObjectRequest, builder.build(), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS);
190193
setSdkFields(builder, putObjectRequest);
194+
propagateOverrideConfig(builder, putObjectRequest);
191195

192196
if (S3ChecksumUtils.checksumValueSpecified(putObjectRequest)) {
193197
builder.checksumType(ChecksumType.FULL_OBJECT);
@@ -200,7 +204,7 @@ public static HeadObjectRequest toHeadObjectRequest(CopyObjectRequest copyObject
200204

201205
// We can't set SdkFields directly because the fields in CopyObjectRequest do not match 100% with the ones in
202206
// HeadObjectRequest
203-
return HeadObjectRequest.builder()
207+
HeadObjectRequest.Builder builder = HeadObjectRequest.builder()
204208
.bucket(copyObjectRequest.sourceBucket())
205209
.key(copyObjectRequest.sourceKey())
206210
.versionId(copyObjectRequest.sourceVersionId())
@@ -211,8 +215,9 @@ public static HeadObjectRequest toHeadObjectRequest(CopyObjectRequest copyObject
211215
.expectedBucketOwner(copyObjectRequest.expectedSourceBucketOwner())
212216
.sseCustomerAlgorithm(copyObjectRequest.copySourceSSECustomerAlgorithm())
213217
.sseCustomerKey(copyObjectRequest.copySourceSSECustomerKey())
214-
.sseCustomerKeyMD5(copyObjectRequest.copySourceSSECustomerKeyMD5())
215-
.build();
218+
.sseCustomerKeyMD5(copyObjectRequest.copySourceSSECustomerKeyMD5());
219+
propagateOverrideConfig(builder, copyObjectRequest);
220+
return builder.build();
216221
}
217222

218223
public static CompletedPart toCompletedPart(CopyPartResult copyPartResult, int partNumber) {
@@ -238,6 +243,7 @@ public static ListPartsRequest toListPartsRequest(String uploadId, PutObjectRequ
238243
ListPartsRequest.Builder builder = ListPartsRequest.builder();
239244
validateRequestFields(putObjectRequest, builder.build(), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS);
240245
setSdkFields(builder, putObjectRequest);
246+
propagateOverrideConfig(builder, putObjectRequest);
241247
return builder.uploadId(uploadId).build();
242248
}
243249

@@ -247,6 +253,7 @@ public static CreateMultipartUploadRequest toCreateMultipartUploadRequest(CopyOb
247253
setSdkFields(builder, copyObjectRequest);
248254
builder.bucket(copyObjectRequest.destinationBucket());
249255
builder.key(copyObjectRequest.destinationKey());
256+
propagateOverrideConfig(builder, copyObjectRequest);
250257
return builder.build();
251258
}
252259

@@ -275,13 +282,15 @@ public static AbortMultipartUploadRequest.Builder toAbortMultipartUploadRequest(
275282
setSdkFields(builder, copyObjectRequest);
276283
builder.bucket(copyObjectRequest.destinationBucket());
277284
builder.key(copyObjectRequest.destinationKey());
285+
propagateOverrideConfig(builder, copyObjectRequest);
278286
return builder;
279287
}
280288

281289
public static AbortMultipartUploadRequest.Builder toAbortMultipartUploadRequest(PutObjectRequest putObjectRequest) {
282290
AbortMultipartUploadRequest.Builder builder = AbortMultipartUploadRequest.builder();
283291
validateRequestFields(putObjectRequest, builder.build(), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS);
284292
setSdkFields(builder, putObjectRequest);
293+
propagateOverrideConfig(builder, putObjectRequest);
285294
return builder;
286295
}
287296

@@ -296,6 +305,7 @@ public static UploadPartCopyRequest toUploadPartCopyRequest(CopyObjectRequest co
296305
.uploadId(uploadId)
297306
.bucket(copyObjectRequest.destinationBucket())
298307
.key(copyObjectRequest.destinationKey())
308+
.overrideConfiguration(copyObjectRequest.overrideConfiguration().orElse(null))
299309
.build();
300310
}
301311

@@ -311,6 +321,10 @@ public static PutObjectResponse toPutObjectResponse(CompleteMultipartUploadRespo
311321
return builder.build();
312322
}
313323

324+
private static void propagateOverrideConfig(AwsRequest.Builder builder, AwsRequest source) {
325+
source.overrideConfiguration().ifPresent(builder::overrideConfiguration);
326+
}
327+
314328
private static Map<String, Object> retrieveSdkFields(SdkPojo sourceObject, List<SdkField<?>> sdkFields) {
315329
return sdkFields.stream().collect(
316330
HashMap::new,

services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/CopyObjectHelperTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.mockito.Mockito;
3232
import org.mockito.invocation.InvocationOnMock;
3333
import org.mockito.stubbing.Answer;
34+
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
3435
import software.amazon.awssdk.core.exception.SdkClientException;
3536
import software.amazon.awssdk.services.s3.S3AsyncClient;
3637
import software.amazon.awssdk.services.s3.internal.multipart.CopyObjectHelper;
@@ -281,6 +282,31 @@ void multiPartCopy_contentSizeExceeds10000Parts_shouldAdjustPartSize() {
281282
}
282283

283284

285+
@Test
286+
public void multiPartCopy_overrideConfigSetInOriginalRequest_includedInCompleteMultipart() {
287+
AwsRequestOverrideConfiguration overrideConfig = AwsRequestOverrideConfiguration.builder()
288+
.putHeader("x-custom", "value")
289+
.build();
290+
CopyObjectRequest copyRequest = copyObjectRequest().copy(r -> r.overrideConfiguration(overrideConfig));
291+
292+
stubSuccessfulHeadObjectCall(3 * PART_SIZE_BYTES);
293+
stubSuccessfulCreateMulipartCall();
294+
stubSuccessfulUploadPartCopyCalls();
295+
stubSuccessfulCompleteMultipartCall();
296+
297+
copyHelper.copyObject(copyRequest).join();
298+
299+
ArgumentCaptor<CompleteMultipartUploadRequest> completeMultipartCaptor =
300+
ArgumentCaptor.forClass(CompleteMultipartUploadRequest.class);
301+
302+
verify(s3AsyncClient).completeMultipartUpload(completeMultipartCaptor.capture());
303+
304+
CompleteMultipartUploadRequest completeRequest = completeMultipartCaptor.getValue();
305+
306+
assertThat(completeRequest.overrideConfiguration()).isPresent();
307+
assertThat(completeRequest.overrideConfiguration().get()).isEqualTo(overrideConfig);
308+
}
309+
284310
@Test
285311
public void multiPartCopy_sseCHeadersSetInOriginalRequest_includedInCompleteMultipart() {
286312
String customerAlgorithm = "algorithm";

0 commit comments

Comments
 (0)