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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -70,6 +71,7 @@
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
import software.amazon.awssdk.services.polly.auth.scheme.PollyAuthSchemeProvider;
import software.amazon.awssdk.services.polly.endpoints.PollyEndpointProvider;
import software.amazon.awssdk.services.polly.internal.presigner.model.transform.SynthesizeSpeechRequestMarshaller;
import software.amazon.awssdk.services.polly.model.PollyRequest;
import software.amazon.awssdk.services.polly.presigner.PollyPresigner;
Expand Down Expand Up @@ -341,20 +343,26 @@ private void applyEndpoint(SdkHttpFullRequest.Builder httpRequestBuilder) {
}

private URI resolveEndpoint() {
return AwsClientEndpointProvider.builder()
.clientEndpointOverride(endpointOverride)
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_POLLY")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlPolly")
.serviceProfileProperty("polly")
.serviceEndpointPrefix(SERVICE_NAME)
.defaultProtocol("https")
.region(region)
.profileFile(profileFile)
.profileName(profileName)
.dualstackEnabled(dualstackEnabled)
.fipsEnabled(fipsEnabled)
.build()
.clientEndpoint();
// If user configured an endpoint override or one is set via environment/profile, use it.
Optional<URI> overrideEndpoint = AwsClientEndpointProvider.builder()
.clientEndpointOverride(endpointOverride)
.serviceEndpointOverrideEnvironmentVariable(
"AWS_ENDPOINT_URL_POLLY")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlPolly")
.serviceProfileProperty("polly")
.profileFile(profileFile)
.profileName(profileName)
.resolveFromOverrides();

// Resolve endpoint using the service's Endpoints 2.0 provider. Unlike S3, the Polly presigner
// does not have an endpoint resolution interceptor chain — this endpoint becomes the final
// presigned URL host, so it must be accurate.
return overrideEndpoint.orElseGet(() -> CompletableFutureUtils.joinLikeSync(
PollyEndpointProvider.defaultProvider()
.resolveEndpoint(p -> p.region(region)
.useDualStack(dualstackEnabled)
.useFips(fipsEnabled))
).url());
}

public static class BuilderImpl implements PollyPresigner.Builder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,21 +426,25 @@ private Region resolveRegionForGetUrl(GetUrlRequest getUrlRequest) {

/**
* If endpoint is not present, construct a default endpoint using the region information.
* The endpoint resolved here is only used for initial request marshalling — it gets completely replaced
* by S3ResolveEndpointInterceptor during the getUrl flow.
*/
private ClientEndpointProvider clientEndpointProvider(URI overrideEndpoint, Region region) {
return AwsClientEndpointProvider.builder()
.clientEndpointOverride(overrideEndpoint)
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_S3")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlS3")
.serviceProfileProperty("s3")
.serviceEndpointPrefix(SERVICE_NAME)
.defaultProtocol("https")
.region(region)
.profileFile(profileFile)
.profileName(profileName)
.dualstackEnabled(s3Configuration.dualstackEnabled())
.fipsEnabled(fipsEnabled)
.build();
// First check if there's an endpoint override from the user or environment.
Optional<URI> resolvedOverride = AwsClientEndpointProvider.builder()
.clientEndpointOverride(overrideEndpoint)
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_S3")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlS3")
.serviceProfileProperty("s3")
.profileFile(profileFile)
.profileName(profileName)
.resolveFromOverrides();

// If an override is configured, use it (marked as overridden). Otherwise, use a localhost placeholder
// marked as NOT overridden — S3ResolveEndpointInterceptor will replace it with the real endpoint.
return resolvedOverride
.map(uri -> ClientEndpointProvider.create(uri, true))
.orElseGet(() -> ClientEndpointProvider.create(URI.create("https://localhost"), false));
}

private URI getEndpointOverride(GetUrlRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static software.amazon.awssdk.utils.CollectionUtils.mergeLists;
import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;

import java.net.URI;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
Expand All @@ -45,6 +46,7 @@
import software.amazon.awssdk.awscore.internal.defaultsmode.DefaultsModeConfiguration;
import software.amazon.awssdk.awscore.presigner.PresignRequest;
import software.amazon.awssdk.awscore.presigner.PresignedRequest;
import software.amazon.awssdk.core.ClientEndpointProvider;
import software.amazon.awssdk.core.ClientType;
import software.amazon.awssdk.core.RequestOverrideConfiguration;
import software.amazon.awssdk.core.SdkBytes;
Expand All @@ -53,6 +55,7 @@
import software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.http.ExecutionContext;
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
Expand Down Expand Up @@ -251,28 +254,34 @@ private List<ExecutionInterceptor> initializeInterceptors() {
* Copied from {@link AwsDefaultClientBuilder}.
*/
private SdkClientConfiguration createClientConfiguration() {
AwsClientEndpointProvider endpointProvider =
AwsClientEndpointProvider.builder()
.clientEndpointOverride(endpointOverride())
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_S3")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlS3")
.serviceProfileProperty("s3")
.serviceEndpointPrefix(SERVICE_NAME)
.defaultProtocol("https")
.region(region())
.profileFile(profileFileSupplier())
.profileName(profileName())
.dualstackEnabled(serviceConfiguration.dualstackEnabled())
.fipsEnabled(fipsEnabled())
.build();

// Make sure the endpoint resolver can actually resolve an endpoint, so that we fail now instead of
// when a request is made.
endpointProvider.clientEndpoint();
// First check if there's an endpoint override from the user or environment.
Optional<URI> overrideEndpoint = AwsClientEndpointProvider.builder()
.clientEndpointOverride(endpointOverride())
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_S3")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlS3")
.serviceProfileProperty("s3")
.profileFile(profileFileSupplier())
.profileName(profileName())
.resolveFromOverrides();

// If an override is configured, use it (marked as overridden). Otherwise, use a localhost placeholder
// marked as NOT overridden — S3ResolveEndpointInterceptor will replace it with the real endpoint
// at presigning time using S3EndpointProvider with the full request context.
ClientEndpointProvider endpointProvider;
if (overrideEndpoint.isPresent()) {
endpointProvider = ClientEndpointProvider.create(overrideEndpoint.get(), true);
} else {
// Validate region at construction time to fail fast for invalid regions (e.g., US_EAST_1 with underscores).
URI testEndpoint = URI.create("https://s3." + region().id() + ".amazonaws.com");
if (testEndpoint.getHost() == null) {
throw SdkClientException.create("Configured region (" + region() + ") and tags ([]) resulted in an invalid URI: "
+ testEndpoint + ". This is usually caused by an invalid region configuration.");
}
endpointProvider = ClientEndpointProvider.create(URI.create("https://localhost"), false);
}

return SdkClientConfiguration.builder()
.option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER,
endpointProvider)
.option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, endpointProvider)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ public void invalidS3UtilitiesRegionAtClientGivesHelpfulMessage() {

assertThatThrownBy(() -> utilities.getUrl(r -> r.bucket("foo").key("bar")))
.isInstanceOf(SdkClientException.class)
.hasMessageContaining("US_EAST_1")
.hasMessageContaining("region")
.hasMessageContaining("us-east-1");
.hasMessageContaining("Invalid region");
}

@Test
Expand All @@ -43,9 +41,7 @@ public void invalidS3UtilitiesRegionAtRequestGivesHelpfulMessage() {

assertThatThrownBy(() -> utilities.getUrl(r -> r.bucket("foo").key("bar").region(Region.of("US_WEST_2"))))
.isInstanceOf(SdkClientException.class)
.hasMessageContaining("US_WEST_2")
.hasMessageContaining("region")
.hasMessageContaining("us-west-2");
.hasMessageContaining("Invalid region");
}

@Test
Expand Down Expand Up @@ -77,7 +73,8 @@ public void invalidS3ArnRegionAtRequestGivesHelpfulMessage() {
public void invalidS3PresignerRegionAtClientGivesHelpfulMessage() {
assertThatThrownBy(() -> S3Presigner.builder().region(Region.of("US_EAST_1")).build())
.isInstanceOf(SdkClientException.class)
.hasMessageContaining("Configured region (US_EAST_1) and tags ([]) resulted in an invalid URI");
.hasMessageContaining("Configured region (US_EAST_1)")
.hasMessageContaining("invalid URI");
}

@Test
Expand Down
Loading