Skip to content

Commit c283043

Browse files
scheerermillems
authored andcommitted
Allow DefaultS3Presigner.Builder to take a custom S3Configuration
1 parent 865fe38 commit c283043

File tree

4 files changed

+171
-4
lines changed

4 files changed

+171
-4
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"category": "AWS S3",
3+
"type": "feature",
4+
"description": "Allow DefaultS3Presigner.Builder to take a custom S3Configuration"
5+
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultS3Presigner.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,13 @@
9898
@SdkInternalApi
9999
public final class DefaultS3Presigner extends DefaultSdkPresigner implements S3Presigner {
100100
private static final AwsS3V4Signer DEFAULT_SIGNER = AwsS3V4Signer.create();
101+
private static final S3Configuration DEFAULT_S3_CONFIGURATION = S3Configuration.builder()
102+
.checksumValidationEnabled(false)
103+
.build();
101104
private static final String SERVICE_NAME = "s3";
102105
private static final String SIGNING_NAME = "s3";
103106

107+
private final S3Configuration serviceConfiguration;
104108
private final List<ExecutionInterceptor> clientInterceptors;
105109
private final GetObjectRequestMarshaller getObjectRequestMarshaller;
106110
private final PutObjectRequestMarshaller putObjectRequestMarshaller;
@@ -112,6 +116,8 @@ public final class DefaultS3Presigner extends DefaultSdkPresigner implements S3P
112116
private DefaultS3Presigner(Builder b) {
113117
super(b);
114118

119+
this.serviceConfiguration = b.serviceConfiguration != null ? b.serviceConfiguration : DEFAULT_S3_CONFIGURATION;
120+
115121
this.clientInterceptors = initializeInterceptors();
116122

117123
// Copied from DefaultS3Client#init
@@ -236,6 +242,10 @@ public PresignedAbortMultipartUploadRequest presignAbortMultipartUpload(AbortMul
236242
.build();
237243
}
238244

245+
protected S3Configuration serviceConfiguration() {
246+
return serviceConfiguration;
247+
}
248+
239249
/**
240250
* Generate a {@link PresignedRequest} from a {@link PresignedRequest} and {@link SdkRequest}.
241251
*/
@@ -289,9 +299,7 @@ private ExecutionContext createExecutionContext(PresignRequest presignRequest, S
289299
.putAttribute(SdkExecutionAttribute.CLIENT_TYPE, ClientType.SYNC)
290300
.putAttribute(SdkExecutionAttribute.SERVICE_NAME, SERVICE_NAME)
291301
.putAttribute(SdkExecutionAttribute.OPERATION_NAME, operationName)
292-
.putAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG, S3Configuration.builder()
293-
.checksumValidationEnabled(false)
294-
.build())
302+
.putAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG, serviceConfiguration())
295303
.putAttribute(PRESIGNER_EXPIRATION, signatureExpiration);
296304

297305
ExecutionInterceptorChain executionInterceptorChain = new ExecutionInterceptorChain(clientInterceptors);
@@ -464,9 +472,24 @@ private void initializePresignedRequest(PresignedRequest.Builder presignedReques
464472
public static final class Builder extends DefaultSdkPresigner.Builder<Builder>
465473
implements S3Presigner.Builder {
466474

475+
private S3Configuration serviceConfiguration;
476+
467477
private Builder() {
468478
}
469479

480+
/**
481+
* Allows providing a custom S3 serviceConfiguration by providing a {@link S3Configuration} object;
482+
*
483+
* Note: chunkedEncodingEnabled and checksumValidationEnabled do not apply to presigned requests.
484+
*
485+
* @param serviceConfiguration {@link S3Configuration}
486+
* @return this Builder
487+
*/
488+
public Builder serviceConfiguration(S3Configuration serviceConfiguration) {
489+
this.serviceConfiguration = serviceConfiguration;
490+
return this;
491+
}
492+
470493
@Override
471494
public S3Presigner build() {
472495
return new DefaultS3Presigner(this);

services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/S3Presigner.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import software.amazon.awssdk.http.SdkHttpClient;
3131
import software.amazon.awssdk.regions.Region;
3232
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
33+
import software.amazon.awssdk.services.s3.S3Configuration;
3334
import software.amazon.awssdk.services.s3.internal.presigner.DefaultS3Presigner;
3435
import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest;
3536
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
@@ -512,6 +513,16 @@ default PresignedAbortMultipartUploadRequest presignAbortMultipartUpload(
512513
@SdkPublicApi
513514
@NotThreadSafe
514515
interface Builder extends SdkPresigner.Builder {
516+
/**
517+
* Allows providing a custom S3 serviceConfiguration by providing a {@link S3Configuration} object;
518+
*
519+
* Note: chunkedEncodingEnabled and checksumValidationEnabled do not apply to presigned requests.
520+
*
521+
* @param serviceConfiguration {@link S3Configuration}
522+
* @return this Builder
523+
*/
524+
Builder serviceConfiguration(S3Configuration serviceConfiguration);
525+
515526
@Override
516527
Builder region(Region region);
517528

services/s3/src/test/java/software/amazon/awssdk/services/s3/S3PresignerTest.java

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import software.amazon.awssdk.auth.credentials.AwsCredentials;
3131
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
3232
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
33-
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
3433
import software.amazon.awssdk.core.signer.NoOpSigner;
3534
import software.amazon.awssdk.regions.Region;
3635
import software.amazon.awssdk.services.s3.model.RequestPayer;
@@ -41,6 +40,7 @@
4140
@RunWith(MockitoJUnitRunner.class)
4241
public class S3PresignerTest {
4342
private static final URI FAKE_URL;
43+
private static final String BUCKET = "some-bucket";
4444

4545
private S3Presigner presigner;
4646

@@ -64,8 +64,12 @@ private S3Presigner.Builder presignerBuilder() {
6464
.credentialsProvider(() -> AwsBasicCredentials.create("x", "x"));
6565
}
6666

67+
6768
private S3Presigner generateMaximal() {
6869
return S3Presigner.builder()
70+
.serviceConfiguration(S3Configuration.builder()
71+
.checksumValidationEnabled(false)
72+
.build())
6973
.credentialsProvider(() -> AwsBasicCredentials.create("x", "x"))
7074
.region(Region.US_EAST_1)
7175
.endpointOverride(FAKE_URL)
@@ -325,4 +329,128 @@ public void putObject_Sigv4PresignerHonorsSignatureDuration() {
325329
assertThat(Integer.parseInt(expires)).isCloseTo(1234, Offset.offset(2));
326330
});
327331
}
332+
333+
@Test
334+
public void getObject_S3ConfigurationCanBeOverriddenToLeverageTransferAcceleration() {
335+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
336+
.accelerateModeEnabled(true)
337+
.build())
338+
.build();
339+
340+
PresignedGetObjectRequest presignedRequest =
341+
presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
342+
.getObjectRequest(go -> go.bucket("foo34343434")
343+
.key("bar")));
344+
345+
346+
System.out.println(presignedRequest.url());
347+
348+
assertThat(presignedRequest.httpRequest().host()).contains(".s3-accelerate.");
349+
}
350+
351+
352+
@Test
353+
public void accelerateEnabled_UsesVirtualAddressingWithAccelerateEndpoint() {
354+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
355+
.accelerateModeEnabled(true)
356+
.build())
357+
.build();
358+
359+
PresignedGetObjectRequest presignedRequest =
360+
presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
361+
.getObjectRequest(go -> go.bucket(BUCKET)
362+
.key("bar")));
363+
364+
assertThat(presignedRequest.httpRequest().host()).isEqualTo(String.format("%s.s3-accelerate.amazonaws.com", BUCKET));
365+
}
366+
367+
/**
368+
* Dualstack uses regional endpoints that support virtual addressing.
369+
*/
370+
@Test
371+
public void dualstackEnabled_UsesVirtualAddressingWithDualstackEndpoint() throws Exception {
372+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
373+
.dualstackEnabled(true)
374+
.build())
375+
.build();
376+
377+
PresignedGetObjectRequest presignedRequest =
378+
presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
379+
.getObjectRequest(go -> go.bucket(BUCKET)
380+
.key("bar")));
381+
382+
assertThat(presignedRequest.httpRequest().host()).contains(String.format("%s.s3.dualstack.us-west-2.amazonaws.com", BUCKET));
383+
}
384+
385+
/**
386+
* Dualstack also supports path style endpoints just like the normal endpoints.
387+
*/
388+
@Test
389+
public void dualstackAndPathStyleEnabled_UsesPathStyleAddressingWithDualstackEndpoint() throws Exception {
390+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
391+
.dualstackEnabled(true)
392+
.pathStyleAccessEnabled(true)
393+
.build())
394+
.build();
395+
396+
PresignedGetObjectRequest presignedRequest =
397+
presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
398+
.getObjectRequest(go -> go.bucket(BUCKET)
399+
.key("bar")));
400+
401+
assertThat(presignedRequest.httpRequest().host()).isEqualTo("s3.dualstack.us-west-2.amazonaws.com");
402+
assertThat(presignedRequest.url().toString()).startsWith(String.format("https://s3.dualstack.us-west-2.amazonaws.com/%s/%s?", BUCKET, "bar"));
403+
}
404+
405+
/**
406+
* When dualstack and accelerate are both enabled there is a special, global dualstack endpoint we must use.
407+
*/
408+
@Test
409+
public void dualstackAndAccelerateEnabled_UsesDualstackAccelerateEndpoint() throws Exception {
410+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
411+
.dualstackEnabled(true)
412+
.accelerateModeEnabled(true)
413+
.build())
414+
.build();
415+
416+
PresignedGetObjectRequest presignedRequest =
417+
presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
418+
.getObjectRequest(go -> go.bucket(BUCKET)
419+
.key("bar")));
420+
421+
assertThat(presignedRequest.httpRequest().host()).isEqualTo(String.format("%s.s3-accelerate.dualstack.amazonaws.com", BUCKET));
422+
}
423+
424+
@Test
425+
public void accessPointArn_differentRegion_useArnRegionTrue() throws Exception {
426+
String customEndpoint = "https://foobar-12345678910.s3-accesspoint.us-west-2.amazonaws.com";
427+
String accessPointArn = "arn:aws:s3:us-west-2:12345678910:accesspoint:foobar";
428+
429+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
430+
.useArnRegionEnabled(true)
431+
.build())
432+
.build();
433+
434+
PresignedGetObjectRequest presignedRequest =
435+
presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
436+
.getObjectRequest(go -> go.bucket(accessPointArn)
437+
.key("bar")));
438+
439+
assertThat(presignedRequest.url().toString()).startsWith(customEndpoint);
440+
}
441+
442+
@Test
443+
public void accessPointArn_differentRegion_useArnRegionFalse_throwsIllegalArgumentException() throws Exception {
444+
String accessPointArn = "arn:aws:s3:us-east-1:12345678910:accesspoint:foobar";
445+
446+
S3Presigner presigner = presignerBuilder().serviceConfiguration(S3Configuration.builder()
447+
.useArnRegionEnabled(false)
448+
.build())
449+
.build();
450+
451+
assertThatThrownBy(() -> presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5))
452+
.getObjectRequest(go -> go.bucket(accessPointArn).key("bar"))))
453+
.isInstanceOf(IllegalArgumentException.class)
454+
.hasMessageContaining("region");
455+
}
328456
}

0 commit comments

Comments
 (0)