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 All @@ -94,6 +96,7 @@ public final class DefaultPollyPresigner implements PollyPresigner {
private final URI endpointOverride;
private final Boolean dualstackEnabled;
private final Boolean fipsEnabled;
private final URI resolvedEndpoint;

private DefaultPollyPresigner(BuilderImpl builder) {
this.signingClock = builder.signingClock != null ? builder.signingClock
Expand Down Expand Up @@ -126,6 +129,7 @@ private DefaultPollyPresigner(BuilderImpl builder) {
.build()
.isFipsEnabled()
.orElse(false);
this.resolvedEndpoint = resolveEndpoint();
}

IdentityProvider<? extends AwsCredentialsIdentity> credentialsProvider() {
Expand Down Expand Up @@ -334,27 +338,28 @@ private void applyOverrideHeadersAndQueryParams(SdkHttpFullRequest.Builder httpR
}

private void applyEndpoint(SdkHttpFullRequest.Builder httpRequestBuilder) {
URI uri = resolveEndpoint();
httpRequestBuilder.protocol(uri.getScheme())
.host(uri.getHost())
.port(uri.getPort());
httpRequestBuilder.protocol(resolvedEndpoint.getScheme())
.host(resolvedEndpoint.getHost())
.port(resolvedEndpoint.getPort());
}

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();
Optional<URI> overrideEndpoint = AwsClientEndpointProvider.builder()
.clientEndpointOverride(endpointOverride)
.serviceEndpointOverrideEnvironmentVariable(
"AWS_ENDPOINT_URL_POLLY")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlPolly")
.serviceProfileProperty("polly")
.profileFile(profileFile)
.profileName(profileName)
.resolveFromOverrides();

return overrideEndpoint.orElseGet(() -> CompletableFutureUtils.joinLikeSync(
PollyEndpointProvider.defaultProvider()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we are not using localhost like we do in other presigners?

.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 @@ -191,6 +191,26 @@ void presign_includesRequestLevelQueryParams_included() {
assertThat(presignedSynthesizeSpeechRequest.httpRequest().rawQueryParameters().keySet()).contains("QueryParam1");
}

@Test
void presign_noEndpointOverride_usesDefaultEndpoint() {
PollyPresigner presigner = DefaultPollyPresigner.builder()
.region(Region.US_EAST_1)
.credentialsProvider(credentialsProvider)
.build();

SynthesizeSpeechPresignRequest presignRequest = SynthesizeSpeechPresignRequest.builder()
.synthesizeSpeechRequest(BASIC_SYNTHESIZE_SPEECH_REQUEST)
.signatureDuration(Duration.ofHours(3))
.build();

PresignedSynthesizeSpeechRequest presigned = presigner.presignSynthesizeSpeech(presignRequest);

URL presignedUrl = presigned.url();
assertThat(presignedUrl.getProtocol()).isEqualTo("https");
assertThat(presignedUrl.getHost()).isEqualTo("polly.us-east-1.amazonaws.com");
assertThat(presignedUrl.getPath()).isEqualTo("/v1/speech");
}

@Test
void presign_endpointOverriden() {
PollyPresigner presigner = DefaultPollyPresigner.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,22 +425,25 @@ private Region resolveRegionForGetUrl(GetUrlRequest getUrlRequest) {
}

/**
* If endpoint is not present, construct a default endpoint using the region information.
* Resolve the client endpoint provider for request marshalling. Checks client override,
* environment variables, system properties, and profile configuration. If no override is found,
* a localhost placeholder is used — the actual endpoint is resolved by S3EndpointProvider
* 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();
Optional<URI> resolvedOverride = AwsClientEndpointProvider.builder()
.clientEndpointOverride(overrideEndpoint)
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_S3")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlS3")
.serviceProfileProperty("s3")
.profileFile(profileFile)
.profileName(profileName)
.resolveFromOverrides();

return resolvedOverride
.map(uri -> ClientEndpointProvider.create(uri, true))
// Need an endpoint to marshall but this will be overwritten in modifyHttpRequest
.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,32 @@ 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();
Optional<URI> overrideEndpoint = AwsClientEndpointProvider.builder()
.clientEndpointOverride(endpointOverride())
.serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_S3")
.serviceEndpointOverrideSystemProperty("aws.endpointUrlS3")
.serviceProfileProperty("s3")
.profileFile(profileFileSupplier())
.profileName(profileName())
.resolveFromOverrides();

ClientEndpointProvider endpointProvider;
if (overrideEndpoint.isPresent()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we are not using S3EndpointProvider here?

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() + ") resulted in an invalid URI: "
+ testEndpoint + ". This is usually caused by an invalid region "
+ "configuration.");
}
// Need an endpoint to marshall but this will be overwritten in modifyHttpRequest
endpointProvider = ClientEndpointProvider.create(URI.create("https://localhost"), false);

@zoewangg zoewangg Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why are we using localhost here?

NVM, it's used as a placeholder and will be replaced anyway. Can we follow the same pattern in EC2 and add some comments https://github.com/aws/aws-sdk-java-v2/blob/master/services/ec2/src/main/java/software/amazon/awssdk/services/ec2/transform/internal/GeneratePreSignUrlInterceptor.java#L67

}

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