diff --git a/.brazil.json b/.brazil.json index 69638fc11a0a..9d7a63c68514 100644 --- a/.brazil.json +++ b/.brazil.json @@ -94,7 +94,7 @@ "http-clients": { "skipImport": true }, "metric-publishers": { "skipImport": true }, "module-path-tests": { "skipImport": true }, - "old-client-version-compatibility-test": { "skipImport": true }, + "service-client-backward-compat-test": { "skipImport": true }, "protocol-tests": { "skipImport": true }, "protocol-tests-core": { "skipImport": true }, "protocols": { "skipImport": true }, diff --git a/.changes/next-release/feature-AWSSDKforJavav2-87d236c.json b/.changes/next-release/feature-AWSSDKforJavav2-87d236c.json new file mode 100644 index 000000000000..c96886ade8c9 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-87d236c.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Removed ServiceMetadata usages from client creation and request processing code paths to improve cold start performance. Endpoint and signing region resolution now uses Endpoints 2.0 directly instead of triggering eager initialization of all service metadata classes." +} diff --git a/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java b/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java index ebd60e55afa9..0de2293aa79f 100644 --- a/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java +++ b/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java @@ -15,11 +15,16 @@ package software.amazon.awssdk.codegen.lite.maven.plugin; +import java.io.BufferedReader; import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; @@ -50,6 +55,8 @@ public class RegionGenerationMojo extends AbstractMojo { private static final String SERVICE_METADATA_BASE = "software.amazon.awssdk.regions.servicemetadata"; private static final String REGION_METADATA_BASE = "software.amazon.awssdk.regions.regionmetadata"; private static final String REGION_BASE = "software.amazon.awssdk.regions"; + private static final String SERVICE_METADATA_ALLOWLIST_PATH = + "/software/amazon/awssdk/codegen/lite/service-metadata-allowlist.txt"; @Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}") private String outputDirectory; @@ -68,21 +75,28 @@ public class RegionGenerationMojo extends AbstractMojo { @Override public void execute() throws MojoExecutionException { Path baseSourcesDirectory = Paths.get(outputDirectory).resolve("generated-sources").resolve("sdk"); - Path testsDirectory = Paths.get(outputDirectory).resolve("generated-test-sources").resolve("sdk-tests"); Partitions partitions = RegionMetadataLoader.build(endpoints); PartitionsRegionsMetadata regionPartitions = PartitionsRegionsMetadataLoader.build(partitionsJson); + Set allEndpointServices = new HashSet<>(); + partitions.getPartitions().forEach(p -> allEndpointServices.addAll(p.getServices().keySet())); + Set allowedServices = loadServiceMetadataAllowlist(); + Set effectiveServices = allowedServices.stream() + .filter(allEndpointServices::contains) + .collect(Collectors.toSet()); + generatePartitionMetadataClass(baseSourcesDirectory, regionPartitions); generateRegionClass(baseSourcesDirectory, regionPartitions); - generateServiceMetadata(baseSourcesDirectory, partitions); + generateServiceMetadata(baseSourcesDirectory, partitions, effectiveServices); generateRegions(baseSourcesDirectory, regionPartitions); generatePartitionProvider(baseSourcesDirectory, regionPartitions); generateRegionProvider(baseSourcesDirectory, regionPartitions); - generateServiceProvider(baseSourcesDirectory, partitions); + generateServiceProvider(baseSourcesDirectory, effectiveServices); generateEndpointTags(baseSourcesDirectory, partitions); project.addCompileSourceRoot(baseSourcesDirectory.toFile().getAbsolutePath()); + Path testsDirectory = Paths.get(outputDirectory).resolve("generated-test-sources").resolve("sdk-tests"); project.addTestCompileSourceRoot(testsDirectory.toFile().getAbsolutePath()); } @@ -100,15 +114,13 @@ public void generateRegionClass(Path baseSourcesDirectory, PartitionsRegionsMeta new CodeGenerator(sourcesDirectory.toString(), new RegionGenerator(partitions, REGION_BASE)).generate(); } - public void generateServiceMetadata(Path baseSourcesDirectory, Partitions partitions) { + public void generateServiceMetadata(Path baseSourcesDirectory, Partitions partitions, Set effectiveServices) { Path sourcesDirectory = baseSourcesDirectory.resolve(StringUtils.replace(SERVICE_METADATA_BASE, ".", "/")); - Set services = new HashSet<>(); - partitions.getPartitions().forEach(p -> services.addAll(p.getServices().keySet())); - - services.forEach(s -> new CodeGenerator(sourcesDirectory.toString(), new ServiceMetadataGenerator(partitions, - s, - SERVICE_METADATA_BASE, - REGION_BASE)) + effectiveServices.forEach(s -> new CodeGenerator(sourcesDirectory.toString(), + new ServiceMetadataGenerator(partitions, + s, + SERVICE_METADATA_BASE, + REGION_BASE)) .generate()); } @@ -141,11 +153,11 @@ public void generateRegionProvider(Path baseSourcesDirectory, PartitionsRegionsM .generate(); } - public void generateServiceProvider(Path baseSourcesDirectory, Partitions partitions) { + public void generateServiceProvider(Path baseSourcesDirectory, Set effectiveServices) { Path sourcesDirectory = baseSourcesDirectory.resolve(StringUtils.replace(REGION_BASE, ".", "/")); - new CodeGenerator(sourcesDirectory.toString(), new ServiceMetadataProviderGenerator(partitions, - SERVICE_METADATA_BASE, - REGION_BASE)) + new CodeGenerator(sourcesDirectory.toString(), new ServiceMetadataProviderGenerator(SERVICE_METADATA_BASE, + REGION_BASE, + effectiveServices)) .generate(); } @@ -153,4 +165,18 @@ public void generateEndpointTags(Path baseSourcesDirectory, Partitions partition Path sourcesDirectory = baseSourcesDirectory.resolve(StringUtils.replace(REGION_BASE, ".", "/")); new CodeGenerator(sourcesDirectory.toString(), new EndpointTagGenerator(partitions, REGION_BASE)).generate(); } + + private Set loadServiceMetadataAllowlist() { + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + Objects.requireNonNull(RegionGenerationMojo.class.getResourceAsStream(SERVICE_METADATA_ALLOWLIST_PATH), + "Failed to load service-metadata-allowlist.txt"), + StandardCharsets.UTF_8))) { + return reader.lines() + .map(String::trim) + .filter(line -> !line.isEmpty() && !line.startsWith("#")) + .collect(Collectors.toSet()); + } catch (Exception e) { + throw new RuntimeException("Failed to load service-metadata-allowlist.txt", e); + } + } } diff --git a/codegen-lite-maven-plugin/src/main/resources/software/amazon/awssdk/codegen/lite/service-metadata-allowlist.txt b/codegen-lite-maven-plugin/src/main/resources/software/amazon/awssdk/codegen/lite/service-metadata-allowlist.txt new file mode 100644 index 000000000000..fb9c41e243f4 --- /dev/null +++ b/codegen-lite-maven-plugin/src/main/resources/software/amazon/awssdk/codegen/lite/service-metadata-allowlist.txt @@ -0,0 +1,310 @@ +# Frozen list of service endpoint prefixes for which ServiceMetadata classes are generated. +# New services should NOT be added to this list. ServiceMetadata is a legacy endpoint resolution +# system that has been superseded by Endpoints 2.0. +access-analyzer +account +acm +acm-pca +agreement-marketplace +airflow +amplify +amplifybackend +amplifyuibuilder +aoss +api.detective +api.ecr +api.ecr-public +api.iotdeviceadvisor +api.iotwireless +api.mediatailor +api.pricing +api.sagemaker +api.tunneling.iot +apigateway +app-integrations +appconfig +appconfigdata +appflow +application-autoscaling +applicationinsights +appmesh +apprunner +appstream2 +appsync +aps +arc-zonal-shift +athena +auditmanager +autoscaling +autoscaling-plans +backup +backup-gateway +batch +bedrock +billingconductor +braket +budgets +cases +cassandra +catalog.marketplace +ce +chime +cleanrooms +cloud9 +cloudcontrolapi +clouddirectory +cloudformation +cloudfront +cloudhsm +cloudhsmv2 +cloudsearch +cloudtrail +cloudtrail-data +codeartifact +codebuild +codecatalyst +codecommit +codedeploy +codeguru-profiler +codeguru-reviewer +codepipeline +codestar-connections +codestar-notifications +cognito-identity +cognito-idp +cognito-sync +comprehend +comprehendmedical +compute-optimizer +config +connect +connect-campaigns +contact-lens +controltower +cost-optimization-hub +cur +data-ats.iot +data.iot +data.jobs.iot +data.mediastore +databrew +dataexchange +datapipeline +datasync +datazone +dax +devicefarm +devops-guru +directconnect +discovery +dlm +dms +docdb +drs +ds +dynamodb +ebs +ec2 +ecs +edge.sagemaker +eks +eks-auth +elasticache +elasticbeanstalk +elasticfilesystem +elasticloadbalancing +elasticmapreduce +email +emr-containers +emr-serverless +entitlement.marketplace +es +events +finspace +finspace-api +firehose +fms +forecast +forecastquery +frauddetector +fsx +gamelift +gameliftstreams +geo +glacier +globalaccelerator +glue +grafana +greengrass +groundstation +guardduty +health +healthlake +iam +identity-chime +identitystore +importexport +ingest.timestream +inspector +inspector2 +internetmonitor +iot +iotfleetwise +iotsecuredtunneling +iotsitewise +iotthingsgraph +iottwinmaker +iotwireless +ivs +ivschat +ivsrealtime +kafka +kafkaconnect +kendra +kendra-ranking +kinesis +kinesisanalytics +kinesisvideo +kms +lakeformation +lambda +license-manager +license-manager-linux-subscriptions +license-manager-user-subscriptions +lightsail +logs +lookoutequipment +m2 +machinelearning +macie2 +managedblockchain +managedblockchain-query +marketplacecommerceanalytics +media-pipelines-chime +mediaconnect +mediaconvert +medialive +mediapackage +mediapackage-vod +mediapackagev2 +mediastore +meetings-chime +memory-db +messaging-chime +metering.marketplace +metrics.sagemaker +mgh +mgn +migrationhub-orchestrator +migrationhub-strategy +mobileanalytics +models-v2-lex +models.lex +monitoring +mq +mturk-requester +neptune +network-firewall +networkmanager +notifications +notifications-contacts +nova-act +oam +oidc +omics +organizations +osis +outposts +participant.connect +partnercentral-channel +personalize +pi +pinpoint +pipes +polly +portal.sso +profile +proton +qbusiness +query.timestream +quicksight +ram +rbin +rds +rds-data +redshift +redshift-serverless +rekognition +resiliencehub +resource-explorer-2 +resource-groups +rolesanywhere +route53 +route53-recovery-control-config +route53domains +route53profiles +route53resolver +rum +runtime-v2-lex +runtime.lex +runtime.sagemaker +s3 +s3-control +s3-outposts +sagemaker-geospatial +savingsplans +scheduler +schemas +sdb +secretsmanager +securityhub +securitylake +serverlessrepo +servicecatalog +servicecatalog-appregistry +servicediscovery +servicequotas +shield +signer +sms-voice +snowball +sns +sqs +ssm +ssm-contacts +ssm-incidents +ssm-quicksetup +ssm-sap +sso +states +storagegateway +streams.dynamodb +sts +support +supportapp +swf +synthetics +tagging +tax +textract +thinclient +tnb +transcribe +transcribestreaming +transfer +translate +trustedadvisor +verifiedpermissions +voice-chime +voiceid +vpc-lattice +waf +waf-regional +wafv2 +wellarchitected +wisdom +workdocs +workmail +workspaces +workspaces-web +xray diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java index f169fac71f7a..ce422d3d8022 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java @@ -37,21 +37,20 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.codegen.lite.PoetClass; import software.amazon.awssdk.codegen.lite.Utils; -import software.amazon.awssdk.codegen.lite.regions.model.Partitions; import software.amazon.awssdk.utils.ImmutableMap; public class ServiceMetadataProviderGenerator implements PoetClass { - private final Partitions partitions; private final String basePackage; private final String regionBasePackage; + private final Set allowedServices; - public ServiceMetadataProviderGenerator(Partitions partitions, - String basePackage, - String regionBasePackage) { - this.partitions = partitions; + public ServiceMetadataProviderGenerator(String basePackage, + String regionBasePackage, + Set allowedServices) { this.basePackage = basePackage; this.regionBasePackage = regionBasePackage; + this.allowedServices = new HashSet<>(allowedServices); } @Override @@ -69,7 +68,7 @@ public TypeSpec poetClass() { .addModifiers(FINAL) .addField(FieldSpec.builder(mapOfServiceMetadata, "SERVICE_METADATA") .addModifiers(PRIVATE, FINAL, STATIC) - .initializer(regions(partitions)) + .initializer(regions()) .build()) .addMethod(getter()) .build(); @@ -80,20 +79,11 @@ public ClassName className() { return ClassName.get(regionBasePackage, "GeneratedServiceMetadataProvider"); } - private CodeBlock regions(Partitions partitions) { + private CodeBlock regions() { CodeBlock.Builder builder = CodeBlock.builder().add("$T.builder()", ImmutableMap.class); - Set seenServices = new HashSet<>(); - - partitions.getPartitions() - .forEach(p -> p.getServices() - .keySet() - .forEach(s -> { - if (!seenServices.contains(s)) { - builder.add(".put($S, new $T())", s, serviceMetadataClass(s)); - seenServices.add(s); - } - })); + allowedServices.stream().sorted().forEach(s -> + builder.add(".put($S, new $T())", s, serviceMetadataClass(s))); return builder.add(".build()").build(); } diff --git a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java index 02376fbc5861..7d434d9802a4 100644 --- a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java +++ b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java @@ -17,8 +17,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import static software.amazon.awssdk.codegen.lite.PoetMatchers.generatesTo; +import java.io.BufferedReader; import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Paths; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import software.amazon.awssdk.codegen.lite.regions.model.Partitions; @@ -96,9 +102,10 @@ public void serviceWithOverriddenPartitionsMetadataClass() { @Test public void serviceMetadataProviderClass() { - ServiceMetadataProviderGenerator serviceMetadataProviderGenerator = new ServiceMetadataProviderGenerator(partitions, - SERVICE_METADATA_BASE, - REGION_BASE); + Set allowedServices = loadTestAllowlist(); + ServiceMetadataProviderGenerator serviceMetadataProviderGenerator = new ServiceMetadataProviderGenerator(SERVICE_METADATA_BASE, + REGION_BASE, + allowedServices); assertThat(serviceMetadataProviderGenerator, generatesTo("service-metadata-provider.java")); } @@ -126,4 +133,17 @@ public void partitionMetadataProviderClass() { assertThat(partitionMetadataProviderGenerator, generatesTo("partition-metadata-provider.java")); } + + private Set loadTestAllowlist() { + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + Objects.requireNonNull(getClass().getResourceAsStream("/software/amazon/awssdk/codegen/lite/regions/test-service-metadata-allowlist.txt")), + StandardCharsets.UTF_8))) { + return reader.lines() + .map(String::trim) + .filter(line -> !line.isEmpty() && !line.startsWith("#")) + .collect(Collectors.toSet()); + } catch (Exception e) { + throw new RuntimeException("Failed to load test allowlist", e); + } + } } diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java index c5231d0ab17e..91441a29172f 100644 --- a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java @@ -3,407 +3,30 @@ import java.util.Map; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkPublicApi; -import software.amazon.awssdk.regions.servicemetadata.A4bServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.AccessAnalyzerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AccountServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AcmPcaServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AcmServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AirflowServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AmplifyServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AmplifybackendServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApiDetectiveServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.ApiEcrPublicServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.ApiEcrServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApiElasticInferenceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApiFleethubIotServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApiMediatailorServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApiPricingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApiSagemakerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApigatewayServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AppIntegrationsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AppflowServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApplicationAutoscalingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApplicationinsightsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AppmeshServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApprunnerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Appstream2ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AppsyncServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ApsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AthenaServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AuditmanagerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AutoscalingPlansServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.AutoscalingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.BackupServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.BatchServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.BraketServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.BudgetsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ChimeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Cloud9ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CloudcontrolapiServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ClouddirectoryServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CloudformationServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CloudfrontServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CloudhsmServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Cloudhsmv2ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CloudsearchServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CloudtrailServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodeartifactServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodebuildServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodecommitServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodedeployServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodeguruProfilerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodeguruReviewerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodepipelineServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodestarConnectionsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodestarNotificationsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CodestarServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CognitoIdentityServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CognitoIdpServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CognitoSyncServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ComprehendServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ComprehendmedicalServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ComputeOptimizerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ConfigServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ConnectServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ConnectparticipantServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ContactLensServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.CurServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DataIotServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DataJobsIotServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DataMediastoreServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DatabrewServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DataexchangeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DatapipelineServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DatasyncServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DaxServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DeeplensServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DevicefarmServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DevicesIot1clickServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DirectconnectServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DiscoveryServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DlmServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DmsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DocdbServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.DsServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.DynamodbServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EbsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Ec2ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EcsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EdgeSagemakerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EksServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ElasticacheServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ElasticbeanstalkServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ElasticfilesystemServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.ElasticloadbalancingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ElasticmapreduceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ElastictranscoderServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EmailServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EmrContainersServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.EnhancedS3ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EntitlementMarketplaceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.EventsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ExecuteApiServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.FinspaceApiServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.FinspaceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.FirehoseServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.FmsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ForecastServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ForecastqueryServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.FrauddetectorServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.FsxServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GameliftServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GlacierServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GlueServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GrafanaServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GreengrassServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GroundstationServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.GuarddutyServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.HealthServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.HealthlakeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.HoneycodeServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.IamServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IdentityChimeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IdentitystoreServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ImportexportServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.InspectorServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotanalyticsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotdeviceadvisorServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IoteventsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IoteventsdataServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotsecuredtunnelingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotsitewiseServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotthingsgraphServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IotwirelessServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.IvsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KafkaServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KafkaconnectServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KendraServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KinesisServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KinesisanalyticsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KinesisvideoServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.KmsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LakeformationServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.LambdaServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LicenseManagerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LightsailServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LogsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LookoutequipmentServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LookoutmetricsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.LookoutvisionServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MachinelearningServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Macie2ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MacieServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ManagedblockchainServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MarketplacecommerceanalyticsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MediaconnectServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MediaconvertServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MedialiveServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MediapackageServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MediapackageVodServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MediastoreServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MemorydbServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MessagingChimeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MeteringMarketplaceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MghServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MgnServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MigrationhubStrategyServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MobileanalyticsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ModelsLexServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ModelsV2LexServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MonitoringServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MqServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.MturkRequesterServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.NeptuneServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.NetworkFirewallServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.NetworkmanagerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.NimbleServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.OidcServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.OperatorServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.OpsworksCmServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.OpsworksServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.OrganizationsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.OutpostsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.PersonalizeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.PiServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.PinpointServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.PinpointSmsVoiceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.PollyServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.PortalSsoServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ProfileServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ProjectsIot1clickServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.QldbServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.QuicksightServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RamServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RdsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RdsdataserviceServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RedshiftServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RekognitionServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ResourceGroupsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RobomakerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Route53RecoveryControlConfigServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.Route53ServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Route53domainsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.Route53resolverServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RuntimeLexServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.RuntimeSagemakerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.RuntimeV2LexServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.S3ControlServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.S3OutpostsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SavingsplansServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SchemasServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SdbServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SecretsmanagerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SecurityhubServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ServerlessrepoServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ServicecatalogAppregistryServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ServicecatalogServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ServicediscoveryServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ServicequotasServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SessionQldbServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ShieldServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SignerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SmsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SmsVoicePinpointServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SnowDeviceManagementServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SnowballServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SnsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SqsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SsmIncidentsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SsmServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.StatesServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.StoragegatewayServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.StreamsDynamodbServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.StsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SupportServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SwfServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.SyntheticsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TaggingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TextractServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TimestreamQueryServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TimestreamWriteServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TranscribeServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TranscribestreamingServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TransferServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.TranslateServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.ValkyrieServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.VoiceidServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.WafRegionalServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.WafServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.WisdomServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.WorkdocsServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.WorkmailServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.WorkspacesServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.XrayServiceMetadata; import software.amazon.awssdk.utils.ImmutableMap; @Generated("software.amazon.awssdk:codegen") @SdkPublicApi public final class GeneratedServiceMetadataProvider implements ServiceMetadataProvider { - private static final Map SERVICE_METADATA = ImmutableMap. builder() - .put("a4b", new A4bServiceMetadata()).put("access-analyzer", new AccessAnalyzerServiceMetadata()) - .put("account", new AccountServiceMetadata()).put("acm", new AcmServiceMetadata()) - .put("acm-pca", new AcmPcaServiceMetadata()).put("airflow", new AirflowServiceMetadata()) - .put("amplify", new AmplifyServiceMetadata()).put("amplifybackend", new AmplifybackendServiceMetadata()) - .put("api.detective", new ApiDetectiveServiceMetadata()).put("api.ecr", new ApiEcrServiceMetadata()) - .put("api.ecr-public", new ApiEcrPublicServiceMetadata()) - .put("api.elastic-inference", new ApiElasticInferenceServiceMetadata()) - .put("api.fleethub.iot", new ApiFleethubIotServiceMetadata()) - .put("api.mediatailor", new ApiMediatailorServiceMetadata()).put("api.pricing", new ApiPricingServiceMetadata()) - .put("api.sagemaker", new ApiSagemakerServiceMetadata()).put("apigateway", new ApigatewayServiceMetadata()) - .put("app-integrations", new AppIntegrationsServiceMetadata()).put("appflow", new AppflowServiceMetadata()) - .put("application-autoscaling", new ApplicationAutoscalingServiceMetadata()) - .put("applicationinsights", new ApplicationinsightsServiceMetadata()).put("appmesh", new AppmeshServiceMetadata()) - .put("apprunner", new ApprunnerServiceMetadata()).put("appstream2", new Appstream2ServiceMetadata()) - .put("appsync", new AppsyncServiceMetadata()).put("aps", new ApsServiceMetadata()) - .put("athena", new AthenaServiceMetadata()).put("auditmanager", new AuditmanagerServiceMetadata()) - .put("autoscaling", new AutoscalingServiceMetadata()).put("autoscaling-plans", new AutoscalingPlansServiceMetadata()) - .put("backup", new BackupServiceMetadata()).put("batch", new BatchServiceMetadata()) - .put("braket", new BraketServiceMetadata()).put("budgets", new BudgetsServiceMetadata()) - .put("ce", new CeServiceMetadata()).put("chime", new ChimeServiceMetadata()) - .put("cloud9", new Cloud9ServiceMetadata()).put("cloudcontrolapi", new CloudcontrolapiServiceMetadata()) - .put("clouddirectory", new ClouddirectoryServiceMetadata()) - .put("cloudformation", new CloudformationServiceMetadata()).put("cloudfront", new CloudfrontServiceMetadata()) - .put("cloudhsm", new CloudhsmServiceMetadata()).put("cloudhsmv2", new Cloudhsmv2ServiceMetadata()) - .put("cloudsearch", new CloudsearchServiceMetadata()).put("cloudtrail", new CloudtrailServiceMetadata()) - .put("codeartifact", new CodeartifactServiceMetadata()).put("codebuild", new CodebuildServiceMetadata()) - .put("codecommit", new CodecommitServiceMetadata()).put("codedeploy", new CodedeployServiceMetadata()) - .put("codeguru-profiler", new CodeguruProfilerServiceMetadata()) - .put("codeguru-reviewer", new CodeguruReviewerServiceMetadata()) - .put("codepipeline", new CodepipelineServiceMetadata()).put("codestar", new CodestarServiceMetadata()) - .put("codestar-connections", new CodestarConnectionsServiceMetadata()) - .put("codestar-notifications", new CodestarNotificationsServiceMetadata()) - .put("cognito-identity", new CognitoIdentityServiceMetadata()).put("cognito-idp", new CognitoIdpServiceMetadata()) - .put("cognito-sync", new CognitoSyncServiceMetadata()).put("comprehend", new ComprehendServiceMetadata()) - .put("comprehendmedical", new ComprehendmedicalServiceMetadata()) - .put("compute-optimizer", new ComputeOptimizerServiceMetadata()).put("config", new ConfigServiceMetadata()) - .put("connect", new ConnectServiceMetadata()).put("connectparticipant", new ConnectparticipantServiceMetadata()) - .put("contact-lens", new ContactLensServiceMetadata()).put("cur", new CurServiceMetadata()) - .put("data.iot", new DataIotServiceMetadata()).put("data.jobs.iot", new DataJobsIotServiceMetadata()) - .put("data.mediastore", new DataMediastoreServiceMetadata()).put("databrew", new DatabrewServiceMetadata()) - .put("dataexchange", new DataexchangeServiceMetadata()).put("datapipeline", new DatapipelineServiceMetadata()) - .put("datasync", new DatasyncServiceMetadata()).put("dax", new DaxServiceMetadata()) - .put("deeplens", new DeeplensServiceMetadata()).put("devicefarm", new DevicefarmServiceMetadata()) - .put("devices.iot1click", new DevicesIot1clickServiceMetadata()) - .put("directconnect", new DirectconnectServiceMetadata()).put("discovery", new DiscoveryServiceMetadata()) - .put("dlm", new DlmServiceMetadata()).put("dms", new DmsServiceMetadata()).put("docdb", new DocdbServiceMetadata()) - .put("ds", new DsServiceMetadata()).put("dynamodb", new DynamodbServiceMetadata()) - .put("ebs", new EbsServiceMetadata()).put("ec2", new Ec2ServiceMetadata()).put("ecs", new EcsServiceMetadata()) - .put("edge.sagemaker", new EdgeSagemakerServiceMetadata()).put("eks", new EksServiceMetadata()) - .put("elasticache", new ElasticacheServiceMetadata()).put("elasticbeanstalk", new ElasticbeanstalkServiceMetadata()) - .put("elasticfilesystem", new ElasticfilesystemServiceMetadata()) - .put("elasticloadbalancing", new ElasticloadbalancingServiceMetadata()) - .put("elasticmapreduce", new ElasticmapreduceServiceMetadata()) - .put("elastictranscoder", new ElastictranscoderServiceMetadata()).put("email", new EmailServiceMetadata()) - .put("emr-containers", new EmrContainersServiceMetadata()) - .put("entitlement.marketplace", new EntitlementMarketplaceServiceMetadata()).put("es", new EsServiceMetadata()) - .put("events", new EventsServiceMetadata()).put("execute-api", new ExecuteApiServiceMetadata()) - .put("finspace", new FinspaceServiceMetadata()).put("finspace-api", new FinspaceApiServiceMetadata()) - .put("firehose", new FirehoseServiceMetadata()).put("fms", new FmsServiceMetadata()) - .put("forecast", new ForecastServiceMetadata()).put("forecastquery", new ForecastqueryServiceMetadata()) - .put("frauddetector", new FrauddetectorServiceMetadata()).put("fsx", new FsxServiceMetadata()) - .put("gamelift", new GameliftServiceMetadata()).put("glacier", new GlacierServiceMetadata()) - .put("glue", new GlueServiceMetadata()).put("grafana", new GrafanaServiceMetadata()) - .put("greengrass", new GreengrassServiceMetadata()).put("groundstation", new GroundstationServiceMetadata()) - .put("guardduty", new GuarddutyServiceMetadata()).put("health", new HealthServiceMetadata()) - .put("healthlake", new HealthlakeServiceMetadata()).put("honeycode", new HoneycodeServiceMetadata()) - .put("iam", new IamServiceMetadata()).put("identity-chime", new IdentityChimeServiceMetadata()) - .put("identitystore", new IdentitystoreServiceMetadata()).put("importexport", new ImportexportServiceMetadata()) - .put("inspector", new InspectorServiceMetadata()).put("iot", new IotServiceMetadata()) - .put("iotanalytics", new IotanalyticsServiceMetadata()) - .put("iotdeviceadvisor", new IotdeviceadvisorServiceMetadata()).put("iotevents", new IoteventsServiceMetadata()) - .put("ioteventsdata", new IoteventsdataServiceMetadata()) - .put("iotsecuredtunneling", new IotsecuredtunnelingServiceMetadata()) - .put("iotsitewise", new IotsitewiseServiceMetadata()).put("iotthingsgraph", new IotthingsgraphServiceMetadata()) - .put("iotwireless", new IotwirelessServiceMetadata()).put("ivs", new IvsServiceMetadata()) - .put("kafka", new KafkaServiceMetadata()).put("kafkaconnect", new KafkaconnectServiceMetadata()) - .put("kendra", new KendraServiceMetadata()).put("kinesis", new KinesisServiceMetadata()) - .put("kinesisanalytics", new KinesisanalyticsServiceMetadata()) - .put("kinesisvideo", new KinesisvideoServiceMetadata()).put("kms", new KmsServiceMetadata()) - .put("lakeformation", new LakeformationServiceMetadata()).put("lambda", new LambdaServiceMetadata()) - .put("license-manager", new LicenseManagerServiceMetadata()).put("lightsail", new LightsailServiceMetadata()) - .put("logs", new LogsServiceMetadata()).put("lookoutequipment", new LookoutequipmentServiceMetadata()) - .put("lookoutmetrics", new LookoutmetricsServiceMetadata()).put("lookoutvision", new LookoutvisionServiceMetadata()) - .put("machinelearning", new MachinelearningServiceMetadata()).put("macie", new MacieServiceMetadata()) - .put("macie2", new Macie2ServiceMetadata()).put("managedblockchain", new ManagedblockchainServiceMetadata()) - .put("marketplacecommerceanalytics", new MarketplacecommerceanalyticsServiceMetadata()) - .put("mediaconnect", new MediaconnectServiceMetadata()).put("mediaconvert", new MediaconvertServiceMetadata()) - .put("medialive", new MedialiveServiceMetadata()).put("mediapackage", new MediapackageServiceMetadata()) - .put("mediapackage-vod", new MediapackageVodServiceMetadata()).put("mediastore", new MediastoreServiceMetadata()) - .put("memorydb", new MemorydbServiceMetadata()).put("messaging-chime", new MessagingChimeServiceMetadata()) - .put("metering.marketplace", new MeteringMarketplaceServiceMetadata()).put("mgh", new MghServiceMetadata()) - .put("mgn", new MgnServiceMetadata()).put("migrationhub-strategy", new MigrationhubStrategyServiceMetadata()) - .put("mobileanalytics", new MobileanalyticsServiceMetadata()).put("models-v2-lex", new ModelsV2LexServiceMetadata()) - .put("models.lex", new ModelsLexServiceMetadata()).put("monitoring", new MonitoringServiceMetadata()) - .put("mq", new MqServiceMetadata()).put("mturk-requester", new MturkRequesterServiceMetadata()) - .put("neptune", new NeptuneServiceMetadata()).put("network-firewall", new NetworkFirewallServiceMetadata()) - .put("networkmanager", new NetworkmanagerServiceMetadata()).put("nimble", new NimbleServiceMetadata()) - .put("oidc", new OidcServiceMetadata()).put("opsworks", new OpsworksServiceMetadata()) - .put("opsworks-cm", new OpsworksCmServiceMetadata()).put("organizations", new OrganizationsServiceMetadata()) - .put("outposts", new OutpostsServiceMetadata()).put("personalize", new PersonalizeServiceMetadata()) - .put("pi", new PiServiceMetadata()).put("pinpoint", new PinpointServiceMetadata()) - .put("pinpoint-sms-voice", new PinpointSmsVoiceServiceMetadata()).put("polly", new PollyServiceMetadata()) - .put("portal.sso", new PortalSsoServiceMetadata()).put("profile", new ProfileServiceMetadata()) - .put("projects.iot1click", new ProjectsIot1clickServiceMetadata()).put("qldb", new QldbServiceMetadata()) - .put("quicksight", new QuicksightServiceMetadata()).put("ram", new RamServiceMetadata()) - .put("rds", new RdsServiceMetadata()).put("rdsdataservice", new RdsdataserviceServiceMetadata()) - .put("redshift", new RedshiftServiceMetadata()).put("rekognition", new RekognitionServiceMetadata()) - .put("resource-groups", new ResourceGroupsServiceMetadata()).put("robomaker", new RobomakerServiceMetadata()) - .put("route53", new Route53ServiceMetadata()) - .put("route53-recovery-control-config", new Route53RecoveryControlConfigServiceMetadata()) - .put("route53domains", new Route53domainsServiceMetadata()) - .put("route53resolver", new Route53resolverServiceMetadata()) - .put("runtime-v2-lex", new RuntimeV2LexServiceMetadata()).put("runtime.lex", new RuntimeLexServiceMetadata()) - .put("runtime.sagemaker", new RuntimeSagemakerServiceMetadata()).put("s3", new EnhancedS3ServiceMetadata()) - .put("s3-control", new S3ControlServiceMetadata()).put("s3-outposts", new S3OutpostsServiceMetadata()) - .put("savingsplans", new SavingsplansServiceMetadata()).put("schemas", new SchemasServiceMetadata()) - .put("sdb", new SdbServiceMetadata()).put("secretsmanager", new SecretsmanagerServiceMetadata()) - .put("securityhub", new SecurityhubServiceMetadata()).put("serverlessrepo", new ServerlessrepoServiceMetadata()) - .put("servicecatalog", new ServicecatalogServiceMetadata()) - .put("servicecatalog-appregistry", new ServicecatalogAppregistryServiceMetadata()) - .put("servicediscovery", new ServicediscoveryServiceMetadata()) - .put("servicequotas", new ServicequotasServiceMetadata()).put("session.qldb", new SessionQldbServiceMetadata()) - .put("shield", new ShieldServiceMetadata()).put("signer", new SignerServiceMetadata()) - .put("sms", new SmsServiceMetadata()).put("sms-voice.pinpoint", new SmsVoicePinpointServiceMetadata()) - .put("snow-device-management", new SnowDeviceManagementServiceMetadata()) - .put("snowball", new SnowballServiceMetadata()).put("sns", new SnsServiceMetadata()) - .put("sqs", new SqsServiceMetadata()).put("ssm", new SsmServiceMetadata()) - .put("ssm-incidents", new SsmIncidentsServiceMetadata()).put("states", new StatesServiceMetadata()) - .put("storagegateway", new StoragegatewayServiceMetadata()) - .put("streams.dynamodb", new StreamsDynamodbServiceMetadata()).put("sts", new StsServiceMetadata()) - .put("support", new SupportServiceMetadata()).put("swf", new SwfServiceMetadata()) - .put("synthetics", new SyntheticsServiceMetadata()).put("tagging", new TaggingServiceMetadata()) - .put("textract", new TextractServiceMetadata()).put("timestream.query", new TimestreamQueryServiceMetadata()) - .put("timestream.write", new TimestreamWriteServiceMetadata()).put("transcribe", new TranscribeServiceMetadata()) - .put("transcribestreaming", new TranscribestreamingServiceMetadata()).put("transfer", new TransferServiceMetadata()) - .put("translate", new TranslateServiceMetadata()).put("valkyrie", new ValkyrieServiceMetadata()) - .put("voiceid", new VoiceidServiceMetadata()).put("waf", new WafServiceMetadata()) - .put("waf-regional", new WafRegionalServiceMetadata()).put("wisdom", new WisdomServiceMetadata()) - .put("workdocs", new WorkdocsServiceMetadata()).put("workmail", new WorkmailServiceMetadata()) - .put("workspaces", new WorkspacesServiceMetadata()).put("xray", new XrayServiceMetadata()) - .put("operator", new OperatorServiceMetadata()).build(); + private static final Map SERVICE_METADATA = ImmutableMap.builder() + .put("access-analyzer", new AccessAnalyzerServiceMetadata()).put("api.ecr", new ApiEcrServiceMetadata()) + .put("api.ecr-public", new ApiEcrPublicServiceMetadata()).put("dynamodb", new DynamodbServiceMetadata()) + .put("elasticloadbalancing", new ElasticloadbalancingServiceMetadata()).put("iam", new IamServiceMetadata()) + .put("lambda", new LambdaServiceMetadata()).put("route53", new Route53ServiceMetadata()) + .put("runtime.sagemaker", new RuntimeSagemakerServiceMetadata()).put("s3", new EnhancedS3ServiceMetadata()) + .put("s3-outposts", new S3OutpostsServiceMetadata()).put("sts", new StsServiceMetadata()).build(); public ServiceMetadata serviceMetadata(String endpointPrefix) { return SERVICE_METADATA.get(endpointPrefix); diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/test-service-metadata-allowlist.txt b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/test-service-metadata-allowlist.txt new file mode 100644 index 000000000000..aafe0b58d2e8 --- /dev/null +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/test-service-metadata-allowlist.txt @@ -0,0 +1,13 @@ +# Test allowlist for ServiceMetadataProviderGenerator +access-analyzer +api.ecr +api.ecr-public +dynamodb +elasticloadbalancing +iam +lambda +route53 +runtime.sagemaker +s3 +s3-outposts +sts diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java index cb0d9b103f1d..f2ea2836be7a 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java @@ -30,6 +30,7 @@ import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import com.squareup.javapoet.WildcardTypeName; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -49,6 +50,7 @@ import software.amazon.awssdk.codegen.internal.Utils; import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; import software.amazon.awssdk.codegen.model.intermediate.OperationModel; +import software.amazon.awssdk.codegen.model.rules.endpoints.BuiltInParameter; import software.amazon.awssdk.codegen.model.service.ClientContextParam; import software.amazon.awssdk.codegen.poet.ClassSpec; import software.amazon.awssdk.codegen.poet.PoetExtension; @@ -86,7 +88,6 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.awssdk.utils.StringUtils; @@ -510,21 +511,107 @@ private MethodSpec finalizeServiceConfigurationMethod() { String serviceNameForSystemProperty = model.getNamingStrategy().getServiceNameForSystemProperties(); String serviceNameForProfileFile = model.getNamingStrategy().getServiceNameForProfileFile(); - builder.addCode("builder.lazyOptionIfAbsent($T.CLIENT_ENDPOINT_PROVIDER, c ->", SdkClientOption.class) - .addCode(" $T.builder()", AwsClientEndpointProvider.class) - .addCode(" .serviceEndpointOverrideEnvironmentVariable($S)", "AWS_ENDPOINT_URL_" + serviceNameForEnvVar) - .addCode(" .serviceEndpointOverrideSystemProperty($S)", "aws.endpointUrl" + serviceNameForSystemProperty) - .addCode(" .serviceProfileProperty($S)", serviceNameForProfileFile) - .addCode(" .serviceEndpointPrefix(serviceEndpointPrefix())") - .addCode(" .defaultProtocol($S)", "https") - .addCode(" .region(c.get($T.AWS_REGION))", AwsClientOption.class) - .addCode(" .profileFile(c.get($T.PROFILE_FILE_SUPPLIER))", SdkClientOption.class) - .addCode(" .profileName(c.get($T.PROFILE_NAME))", SdkClientOption.class) - .addCode(" .putAdvancedOption($T.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT,", ServiceMetadataAdvancedOption.class) - .addCode(" c.get($T.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT))", ServiceMetadataAdvancedOption.class) - .addCode(" .dualstackEnabled(c.get($T.DUALSTACK_ENDPOINT_ENABLED))", AwsClientOption.class) - .addCode(" .fipsEnabled(c.get($T.FIPS_ENDPOINT_ENABLED))", AwsClientOption.class) - .addCode(" .build());"); + builder.addCode("builder.lazyOptionIfAbsent($T.CLIENT_ENDPOINT_PROVIDER, c -> {\n", SdkClientOption.class) + .addCode(" $T<$T> overrideEndpoint = $T.builder()\n", + Optional.class, URI.class, + AwsClientEndpointProvider.class) + .addCode(" .serviceEndpointOverrideEnvironmentVariable($S)\n", "AWS_ENDPOINT_URL_" + serviceNameForEnvVar) + .addCode(" .serviceEndpointOverrideSystemProperty($S)\n", "aws.endpointUrl" + serviceNameForSystemProperty) + .addCode(" .serviceProfileProperty($S)\n", serviceNameForProfileFile) + .addCode(" .profileFile(c.get($T.PROFILE_FILE_SUPPLIER))\n", SdkClientOption.class) + .addCode(" .profileName(c.get($T.PROFILE_NAME))\n", SdkClientOption.class) + .addCode(" .resolveFromOverrides();\n") + .addCode(" if (overrideEndpoint.isPresent()) {\n") + .addCode(" return $T.create(overrideEndpoint.get(), true);\n", + ClassName.get("software.amazon.awssdk.core", "ClientEndpointProvider")) + .addCode(" }\n") + .addCode(" $T clientEndpointUri = null;\n", URI.class) + .addCode(" $T region = c.get($T.AWS_REGION);\n", + ClassName.get("software.amazon.awssdk.regions", "Region"), + AwsClientOption.class) + .addCode(" try {\n") + .addCode(" $T endpointParams = $T.builder()\n", + endpointRulesSpecUtils.parametersClassName(), endpointRulesSpecUtils.parametersClassName()); + + if (hasBuiltIn(BuiltInParameter.AWS_REGION)) { + builder.addCode(" .region(region)\n"); + } + + if (hasBuiltIn(BuiltInParameter.AWS_USE_DUAL_STACK)) { + builder.addCode(" .useDualStack(c.get($T.DUALSTACK_ENDPOINT_ENABLED))\n", AwsClientOption.class); + } + if (hasBuiltIn(BuiltInParameter.AWS_USE_FIPS)) { + builder.addCode(" .useFips(c.get($T.FIPS_ENDPOINT_ENABLED))\n", AwsClientOption.class); + } + + builder.addCode(" .build();\n") + .addCode(" $T endpoint = $T.joinLikeSync(defaultEndpointProvider().resolveEndpoint(endpointParams));\n", + ClassName.get("software.amazon.awssdk.endpoints", "Endpoint"), + ClassName.get("software.amazon.awssdk.utils", "CompletableFutureUtils")) + .addCode(" clientEndpointUri = endpoint.url();\n") + .addCode(" } catch (Exception e) {\n") + .addCode(" // Endpoint resolution failed. This is expected for services with required parameters\n") + .addCode(" // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time.\n") + .addCode(" return $T.create($T.create($S), false);\n", + ClassName.get("software.amazon.awssdk.core", "ClientEndpointProvider"), + URI.class, "https://localhost") + .addCode(" }\n") + .addCode(" if (clientEndpointUri.getHost() == null) {\n") + .addCode(" throw $T.create(\"Configured region (\" + region\n", + ClassName.get("software.amazon.awssdk.core.exception", "SdkClientException")) + .addCode(" + \") resulted in an invalid URI: \" + clientEndpointUri\n") + .addCode(" + \". This is usually caused by an invalid region configuration.\");\n") + .addCode(" }\n") + .addCode(" return $T.create(clientEndpointUri, false);\n", + ClassName.get("software.amazon.awssdk.core", "ClientEndpointProvider")) + .addCode("});\n"); + + builder.addCode("builder.lazyOptionIfAbsent($T.SIGNING_REGION, c -> {\n", AwsClientOption.class) + .addCode(" $T region = c.get($T.AWS_REGION);\n", + ClassName.get("software.amazon.awssdk.regions", "Region"), + AwsClientOption.class); + + if (hasBuiltIn(BuiltInParameter.AWS_REGION)) { + builder.addCode(" try {\n") + .addCode(" $T endpointParams = $T.builder()\n", + endpointRulesSpecUtils.parametersClassName(), endpointRulesSpecUtils.parametersClassName()) + .addCode(" .region(region)\n"); + + if (hasBuiltIn(BuiltInParameter.AWS_USE_DUAL_STACK)) { + builder.addCode(" .useDualStack(c.get($T.DUALSTACK_ENDPOINT_ENABLED))\n", AwsClientOption.class); + } + if (hasBuiltIn(BuiltInParameter.AWS_USE_FIPS)) { + builder.addCode(" .useFips(c.get($T.FIPS_ENDPOINT_ENABLED))\n", AwsClientOption.class); + } + + builder.addCode(" .build();\n") + .addCode(" $T endpoint = $T.joinLikeSync(defaultEndpointProvider().resolveEndpoint(endpointParams));\n", + ClassName.get("software.amazon.awssdk.endpoints", "Endpoint"), + ClassName.get("software.amazon.awssdk.utils", "CompletableFutureUtils")) + .addCode(" $T<$T> authSchemes = endpoint.attribute($T.AUTH_SCHEMES);\n", + List.class, + ClassName.get("software.amazon.awssdk.awscore.endpoints.authscheme", "EndpointAuthScheme"), + ClassName.get("software.amazon.awssdk.awscore.endpoints", "AwsEndpointAttribute")) + .addCode(" if (authSchemes != null && !authSchemes.isEmpty()) {\n") + .addCode(" $T firstScheme = authSchemes.get(0);\n", + ClassName.get("software.amazon.awssdk.awscore.endpoints.authscheme", "EndpointAuthScheme")) + .addCode(" if (firstScheme instanceof $T) {\n", + ClassName.get("software.amazon.awssdk.awscore.endpoints.authscheme", "SigV4AuthScheme")) + .addCode(" String signingRegion = (($T) firstScheme).signingRegion();\n", + ClassName.get("software.amazon.awssdk.awscore.endpoints.authscheme", "SigV4AuthScheme")) + .addCode(" if (signingRegion != null) {\n") + .addCode(" return $T.of(signingRegion);\n", + ClassName.get("software.amazon.awssdk.regions", "Region")) + .addCode(" }\n") + .addCode(" }\n") + .addCode(" }\n") + .addCode(" } catch (Exception e) {\n") + .addCode(" // Endpoint resolution failed. Fall back to using the client region as signing region.\n") + .addCode(" }\n"); + } + + builder.addCode(" return region;\n") + .addCode("});\n"); if (model.getMetadata().isJsonProtocol()) { builder.addStatement("builder.option($1T.ENABLE_FAST_UNMARSHALLER, true)", @@ -1047,6 +1134,11 @@ private boolean hasClientContextParams() { return clientContextParams != null && !clientContextParams.isEmpty(); } + private boolean hasBuiltIn(BuiltInParameter builtIn) { + return model.getEndpointRuleSetModel().getParameters().values().stream() + .anyMatch(p -> builtIn.equals(p.getBuiltInEnum())); + } + private boolean hasSdkClientContextParams() { return model.getCustomizationConfig() != null && model.getCustomizationConfig().getCustomClientContextParams() != null diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java index 7426688dd92d..a0b07ebd8e9d 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java @@ -622,6 +622,27 @@ private static ServiceModel getServiceModel(File file) { return ModelLoaderUtils.loadModel(ServiceModel.class, file); } + public static IntermediateModel queryServiceModelsNoRegionEndpointRules() { + File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/query/service-2.json").getFile()); + File customizationModel = new File(ClientTestModels.class.getResource("client/c2j/query/customization.config").getFile()); + File waitersModel = new File(ClientTestModels.class.getResource("client/c2j/query/waiters-2.json").getFile()); + File endpointRuleSetModel = + new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-rule-set-no-region.json").getFile()); + File endpointTestsModel = + new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-tests-no-region.json").getFile()); + + C2jModels models = C2jModels + .builder() + .serviceModel(getServiceModel(serviceModel)) + .customizationConfig(getCustomizationConfig(customizationModel)) + .waitersModel(getWaiters(waitersModel)) + .endpointRuleSetModel(getEndpointRuleSet(endpointRuleSetModel)) + .endpointTestSuiteModel(getEndpointTestSuite(endpointTestsModel)) + .build(); + + return new IntermediateModelBuilder(models).build(); + } + private static CustomizationConfig getCustomizationConfig(File file) { return ModelLoaderUtils.loadModel(CustomizationConfig.class, file); } diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java index 52b5677b2d5f..3a0f184b1462 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java @@ -23,6 +23,7 @@ import static software.amazon.awssdk.codegen.poet.ClientTestModels.opsWithSigv4a; import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModels; import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModelsEndpointAuthParamsWithAllowList; +import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModelsNoRegionEndpointRules; import static software.amazon.awssdk.codegen.poet.ClientTestModels.restJsonServiceModels; import static software.amazon.awssdk.codegen.poet.ClientTestModels.serviceWithH2; import static software.amazon.awssdk.codegen.poet.ClientTestModels.serviceWithH2UsePriorKnowledgeForH2; @@ -100,6 +101,12 @@ void baseClientBuilderClassWithH2_usePriorKnowledgeForH2() { validateBaseClientBuilderClassGeneration(serviceWithH2UsePriorKnowledgeForH2(), "test-h2-usePriorKnowledgeForH2-service-client-builder-class.java"); } + @Test + void baseClientBuilderClass_noRegionEndpointRules() { + validateBaseClientBuilderClassGeneration(queryServiceModelsNoRegionEndpointRules(), + "test-no-region-client-builder-class.java"); + } + private void validateBaseClientBuilderClassGeneration(IntermediateModel model, String expectedClassName) { validateGeneration(BaseClientBuilderClass::new, model, expectedClassName); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java index 1b7d802857d9..b6301bb1d629 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.json; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -14,14 +16,20 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -29,12 +37,14 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; +import software.amazon.awssdk.services.json.endpoints.JsonEndpointParams; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -90,20 +100,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson").serviceProfileProperty("json_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java index cdba2ca0888c..da14d53a1e07 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java @@ -1,9 +1,11 @@ package software.amazon.awssdk.services.json; +import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.MyServiceHttpConfig; import software.amazon.MyServiceRetryPolicy; @@ -16,9 +18,13 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; import software.amazon.awssdk.codegen.poet.plugins.InternalTestPlugin1; import software.amazon.awssdk.codegen.poet.plugins.InternalTestPlugin2; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; import software.amazon.awssdk.core.checksums.RequestChecksumCalculationResolver; @@ -27,9 +33,11 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; @@ -38,14 +46,16 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; import software.amazon.awssdk.services.json.endpoints.JsonClientContextParams; +import software.amazon.awssdk.services.json.endpoints.JsonEndpointParams; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -186,20 +196,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.SERVICE_CONFIGURATION, finalServiceConfig); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson").serviceProfileProperty("json_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); SdkClientConfiguration clientConfig = config; builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java index 0fe341a73bca..dd5b5f9ff3af 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.query; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -16,7 +18,11 @@ import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; import software.amazon.awssdk.awscore.endpoints.AccountIdEndpointMode; import software.amazon.awssdk.awscore.endpoints.AccountIdEndpointModeResolver; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; import software.amazon.awssdk.core.checksums.RequestChecksumCalculationResolver; @@ -25,9 +31,11 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; @@ -37,13 +45,15 @@ import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.query.auth.scheme.QueryAuthSchemeProvider; import software.amazon.awssdk.services.query.endpoints.QueryClientContextParams; +import software.amazon.awssdk.services.query.endpoints.QueryEndpointParams; import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider; import software.amazon.awssdk.services.query.internal.QueryServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -105,20 +115,60 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(AwsClientOption.ACCOUNT_ID_ENDPOINT_MODE, resolveAccountIdEndpointMode(config)); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_QUERY_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlQuery") - .serviceProfileProperty("query_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_QUERY_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlQuery") + .serviceProfileProperty("query_service").profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + QueryEndpointParams endpointParams = QueryEndpointParams.builder().region(region) + .useDualStack(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + QueryEndpointParams endpointParams = QueryEndpointParams.builder().region(region) + .useDualStack(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); SdkClientConfiguration clientConfig = config; builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); builder.lazyOption(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, c -> resolveResponseChecksumValidation(clientConfig)); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java index e5d1cf583c58..9f269bf6e33f 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.json; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -12,26 +14,34 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; +import software.amazon.awssdk.services.json.endpoints.JsonEndpointParams; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; /** * Internal base class for {@link DefaultJsonClientBuilder} and {@link DefaultJsonAsyncClientBuilder}. @@ -92,20 +102,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson").serviceProfileProperty("json_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java index 168c15a64e31..71f969a5ba7f 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.json; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -14,7 +16,11 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; import software.amazon.awssdk.core.checksums.RequestChecksumCalculationResolver; @@ -23,9 +29,11 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; @@ -34,13 +42,15 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; import software.amazon.awssdk.services.json.endpoints.JsonClientContextParams; +import software.amazon.awssdk.services.json.endpoints.JsonEndpointParams; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -109,20 +119,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.SERVICE_CONFIGURATION, finalServiceConfig); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson").serviceProfileProperty("json_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); SdkClientConfiguration clientConfig = config; builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-env-bearer-token-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-env-bearer-token-client-builder-class.java index 615f6a76d1a5..d8b7056db198 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-env-bearer-token-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-env-bearer-token-client-builder-class.java @@ -1,5 +1,6 @@ package software.amazon.awssdk.services.json; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -16,16 +17,22 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -33,13 +40,15 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; +import software.amazon.awssdk.services.json.endpoints.JsonEndpointParams; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; import software.amazon.awssdk.services.json.internal.EnvironmentTokenSystemSettings; import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -107,20 +116,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson").serviceProfileProperty("json_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + JsonEndpointParams endpointParams = JsonEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java index 2e5198dac4b6..0d4a53a08dd2 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.h2; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -12,14 +14,20 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.ProtocolNegotiation; import software.amazon.awssdk.http.SdkHttpConfigurationOption; @@ -29,13 +37,15 @@ import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.h2.auth.scheme.H2AuthSchemeProvider; +import software.amazon.awssdk.services.h2.endpoints.H2EndpointParams; import software.amazon.awssdk.services.h2.endpoints.H2EndpointProvider; import software.amazon.awssdk.services.h2.internal.H2ServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; /** * Internal base class for {@link DefaultH2ClientBuilder} and {@link DefaultH2AsyncClientBuilder}. @@ -87,20 +97,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2") - .serviceProfileProperty("h2_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2").serviceProfileProperty("h2_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + H2EndpointParams endpointParams = H2EndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + H2EndpointParams endpointParams = H2EndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java index 80d554e50d1f..8939c3ba4c34 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.h2; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -12,14 +14,20 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.SdkHttpConfigurationOption; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; @@ -28,13 +36,15 @@ import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.h2.auth.scheme.H2AuthSchemeProvider; +import software.amazon.awssdk.services.h2.endpoints.H2EndpointParams; import software.amazon.awssdk.services.h2.endpoints.H2EndpointProvider; import software.amazon.awssdk.services.h2.internal.H2ServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; /** * Internal base class for {@link DefaultH2ClientBuilder} and {@link DefaultH2AsyncClientBuilder}. @@ -86,20 +96,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2") - .serviceProfileProperty("h2_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2").serviceProfileProperty("h2_service") + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + H2EndpointParams endpointParams = H2EndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + H2EndpointParams endpointParams = H2EndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-multi-auth-sigv4a-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-multi-auth-sigv4a-client-builder-class.java index fcc5f72b4e25..f1a2db7b4795 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-multi-auth-sigv4a-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-multi-auth-sigv4a-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.database; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -12,14 +14,20 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; @@ -28,12 +36,14 @@ import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeProvider; +import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointParams; import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointProvider; import software.amazon.awssdk.services.database.internal.DatabaseServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; /** * Internal base class for {@link DefaultDatabaseClientBuilder} and {@link DefaultDatabaseAsyncClientBuilder}. @@ -86,20 +96,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") - .serviceProfileProperty("database_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") + .serviceProfileProperty("database_service").profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + DatabaseEndpointParams endpointParams = DatabaseEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + DatabaseEndpointParams endpointParams = DatabaseEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java index 5da93144892e..cdc499c42158 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.database; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -14,14 +16,20 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; @@ -30,12 +38,14 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeProvider; +import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointParams; import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointProvider; import software.amazon.awssdk.services.database.internal.DatabaseServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -96,20 +106,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") - .serviceProfileProperty("database_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") + .serviceProfileProperty("database_service").profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + DatabaseEndpointParams endpointParams = DatabaseEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + DatabaseEndpointParams endpointParams = DatabaseEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java index 05f0b72afa2b..93536d8160fb 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.database; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -12,24 +14,32 @@ import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeProvider; +import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointParams; import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointProvider; import software.amazon.awssdk.services.database.internal.DatabaseServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; /** * Internal base class for {@link DefaultDatabaseClientBuilder} and {@link DefaultDatabaseAsyncClientBuilder}. @@ -78,20 +88,56 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") - .serviceProfileProperty("database_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") + .serviceProfileProperty("database_service").profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + DatabaseEndpointParams endpointParams = DatabaseEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + DatabaseEndpointParams endpointParams = DatabaseEndpointParams.builder().region(region).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-region-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-region-client-builder-class.java new file mode 100644 index 000000000000..81951f9927ec --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-region-client-builder-class.java @@ -0,0 +1,284 @@ +package software.amazon.awssdk.services.query; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.auth.credentials.TokenUtils; +import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider; +import software.amazon.awssdk.awscore.auth.AuthSchemePreferenceResolver; +import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; +import software.amazon.awssdk.core.SdkPlugin; +import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; +import software.amazon.awssdk.core.checksums.RequestChecksumCalculationResolver; +import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; +import software.amazon.awssdk.core.checksums.ResponseChecksumValidationResolver; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +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.interceptor.ClasspathInterceptorChainFactory; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; +import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; +import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; +import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; +import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.IdentityProviders; +import software.amazon.awssdk.identity.spi.TokenIdentity; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.retries.api.RetryStrategy; +import software.amazon.awssdk.services.query.auth.scheme.QueryAuthSchemeProvider; +import software.amazon.awssdk.services.query.endpoints.QueryClientContextParams; +import software.amazon.awssdk.services.query.endpoints.QueryEndpointParams; +import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider; +import software.amazon.awssdk.services.query.internal.QueryServiceClientConfigurationBuilder; +import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; +import software.amazon.awssdk.utils.Validate; + +/** + * Internal base class for {@link DefaultQueryClientBuilder} and {@link DefaultQueryAsyncClientBuilder}. + */ +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +abstract class DefaultQueryBaseClientBuilder, C> extends AwsDefaultClientBuilder { + private final Map> additionalAuthSchemes = new HashMap<>(); + + @Override + protected final String serviceEndpointPrefix() { + return "query-service"; + } + + @Override + protected final String serviceName() { + return "Query"; + } + + @Override + protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); + } + + @Override + protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + List endpointInterceptors = new ArrayList<>(); + ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); + List interceptors = interceptorFactory + .getInterceptors("software/amazon/awssdk/services/query/execution.interceptors"); + List additionalInterceptors = new ArrayList<>(); + interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); + interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); + interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS)); + SdkClientConfiguration.Builder builder = config.toBuilder(); + builder.lazyOption(SdkClientOption.IDENTITY_PROVIDERS, c -> { + IdentityProviders.Builder result = IdentityProviders.builder(); + IdentityProvider tokenIdentityProvider = c.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER); + if (tokenIdentityProvider != null) { + result.putIdentityProvider(tokenIdentityProvider); + } + IdentityProvider credentialsIdentityProvider = c.get(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER); + if (credentialsIdentityProvider != null) { + result.putIdentityProvider(credentialsIdentityProvider); + } + return result.build(); + }); + builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); + builder.option(SdkClientOption.CLIENT_CONTEXT_PARAMS, clientContextParams.build()); + builder.lazyOptionIfAbsent( + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_QUERY_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlQuery") + .serviceProfileProperty("query_service").profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + QueryEndpointParams endpointParams = QueryEndpointParams.builder().build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent(AwsClientOption.SIGNING_REGION, c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + return region; + }); + SdkClientConfiguration clientConfig = config; + builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); + builder.lazyOption(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, c -> resolveResponseChecksumValidation(clientConfig)); + return builder.build(); + } + + @Override + protected final String signingName() { + return "query-service"; + } + + private QueryEndpointProvider defaultEndpointProvider() { + return QueryEndpointProvider.defaultProvider(); + } + + public B authSchemeProvider(QueryAuthSchemeProvider authSchemeProvider) { + clientConfiguration.option(SdkClientOption.AUTH_SCHEME_PROVIDER, authSchemeProvider); + return thisBuilder(); + } + + private QueryAuthSchemeProvider defaultAuthSchemeProvider(SdkClientConfiguration config) { + AuthSchemePreferenceResolver authSchemePreferenceProvider = AuthSchemePreferenceResolver.builder() + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)).build(); + List preferences = authSchemePreferenceProvider.resolveAuthSchemePreference(); + if (!preferences.isEmpty()) { + return QueryAuthSchemeProvider.defaultProvider(preferences); + } + return QueryAuthSchemeProvider.defaultProvider(); + } + + @Override + public B putAuthScheme(AuthScheme authScheme) { + additionalAuthSchemes.put(authScheme.schemeId(), authScheme); + return thisBuilder(); + } + + private Map> authSchemes() { + Map> schemes = new HashMap<>(3 + this.additionalAuthSchemes.size()); + AwsV4AuthScheme awsV4AuthScheme = AwsV4AuthScheme.create(); + schemes.put(awsV4AuthScheme.schemeId(), awsV4AuthScheme); + BearerAuthScheme bearerAuthScheme = BearerAuthScheme.create(); + schemes.put(bearerAuthScheme.schemeId(), bearerAuthScheme); + NoAuthAuthScheme noAuthAuthScheme = NoAuthAuthScheme.create(); + schemes.put(noAuthAuthScheme.schemeId(), noAuthAuthScheme); + schemes.putAll(this.additionalAuthSchemes); + return schemes; + } + + public B requestChecksumCalculation(RequestChecksumCalculation requestChecksumCalculation) { + clientConfiguration.option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, requestChecksumCalculation); + return thisBuilder(); + } + + public B responseChecksumValidation(ResponseChecksumValidation responseChecksumValidation) { + clientConfiguration.option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, responseChecksumValidation); + return thisBuilder(); + } + + public B booleanContextParam(Boolean booleanContextParam) { + clientContextParams.put(QueryClientContextParams.BOOLEAN_CONTEXT_PARAM, booleanContextParam); + return thisBuilder(); + } + + public B stringContextParam(String stringContextParam) { + clientContextParams.put(QueryClientContextParams.STRING_CONTEXT_PARAM, stringContextParam); + return thisBuilder(); + } + + private IdentityProvider defaultTokenProvider() { + return DefaultAwsTokenProvider.create(); + } + + @Override + protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) { + List internalPlugins = internalPlugins(config); + List externalPlugins = plugins(); + if (internalPlugins.isEmpty() && externalPlugins.isEmpty()) { + return config; + } + List plugins = CollectionUtils.mergeLists(internalPlugins, externalPlugins); + SdkClientConfiguration.Builder configuration = config.toBuilder(); + QueryServiceClientConfigurationBuilder serviceConfigBuilder = new QueryServiceClientConfigurationBuilder(configuration); + for (SdkPlugin plugin : plugins) { + plugin.configureClient(serviceConfigBuilder); + } + updateRetryStrategyClientConfiguration(configuration); + return configuration.build(); + } + + private void updateRetryStrategyClientConfiguration(SdkClientConfiguration.Builder configuration) { + ClientOverrideConfiguration.Builder builder = configuration.asOverrideConfigurationBuilder(); + RetryMode retryMode = builder.retryMode(); + if (retryMode != null) { + configuration.option(SdkClientOption.RETRY_STRATEGY, AwsRetryStrategy.forRetryMode(retryMode)); + } else { + Consumer> configurator = builder.retryStrategyConfigurator(); + if (configurator != null) { + RetryStrategy.Builder defaultBuilder = AwsRetryStrategy.defaultRetryStrategy().toBuilder(); + configurator.accept(defaultBuilder); + configuration.option(SdkClientOption.RETRY_STRATEGY, defaultBuilder.build()); + } else { + RetryStrategy retryStrategy = builder.retryStrategy(); + if (retryStrategy != null) { + configuration.option(SdkClientOption.RETRY_STRATEGY, retryStrategy); + } + } + } + configuration.option(SdkClientOption.CONFIGURED_RETRY_MODE, null); + configuration.option(SdkClientOption.CONFIGURED_RETRY_STRATEGY, null); + configuration.option(SdkClientOption.CONFIGURED_RETRY_CONFIGURATOR, null); + } + + private List internalPlugins(SdkClientConfiguration config) { + return Collections.emptyList(); + } + + private RequestChecksumCalculation resolveRequestChecksumCalculation(SdkClientConfiguration config) { + RequestChecksumCalculation configuredChecksumCalculation = config.option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION); + if (configuredChecksumCalculation == null) { + configuredChecksumCalculation = RequestChecksumCalculationResolver.create() + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .defaultChecksumCalculation(RequestChecksumCalculation.WHEN_SUPPORTED).resolve(); + } + return configuredChecksumCalculation; + } + + private ResponseChecksumValidation resolveResponseChecksumValidation(SdkClientConfiguration config) { + ResponseChecksumValidation configuredChecksumValidation = config.option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION); + if (configuredChecksumValidation == null) { + configuredChecksumValidation = ResponseChecksumValidationResolver.create() + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .defaultChecksumValidation(ResponseChecksumValidation.WHEN_SUPPORTED).resolve(); + } + return configuredChecksumValidation; + } + + protected static void validateClientOptions(SdkClientConfiguration c) { + Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), + "The 'tokenProvider' must be configured in the client builder."); + } +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java index db0dae50266e..360ef4e77c36 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java @@ -1,10 +1,12 @@ package software.amazon.awssdk.services.query; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -16,7 +18,11 @@ import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; import software.amazon.awssdk.awscore.endpoints.AccountIdEndpointMode; import software.amazon.awssdk.awscore.endpoints.AccountIdEndpointModeResolver; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; import software.amazon.awssdk.core.checksums.RequestChecksumCalculationResolver; @@ -25,9 +31,11 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 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.interceptor.ClasspathInterceptorChainFactory; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; @@ -35,13 +43,15 @@ import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.identity.spi.TokenIdentity; -import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.retries.api.RetryStrategy; import software.amazon.awssdk.services.query.auth.scheme.QueryAuthSchemeProvider; import software.amazon.awssdk.services.query.endpoints.QueryClientContextParams; +import software.amazon.awssdk.services.query.endpoints.QueryEndpointParams; import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider; import software.amazon.awssdk.services.query.internal.QueryServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Validate; /** @@ -103,20 +113,60 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(AwsClientOption.ACCOUNT_ID_ENDPOINT_MODE, resolveAccountIdEndpointMode(config)); builder.lazyOptionIfAbsent( SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_QUERY_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlQuery") - .serviceProfileProperty("query_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + c -> { + Optional overrideEndpoint = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_QUERY_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlQuery") + .serviceProfileProperty("query_service").profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)).resolveFromOverrides(); + if (overrideEndpoint.isPresent()) { + return ClientEndpointProvider.create(overrideEndpoint.get(), true); + } + URI clientEndpointUri = null; + Region region = c.get(AwsClientOption.AWS_REGION); + try { + QueryEndpointParams endpointParams = QueryEndpointParams.builder().region(region) + .useDualStack(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + clientEndpointUri = endpoint.url(); + } catch (Exception e) { + // Endpoint resolution failed. This is expected for services with required parameters + // beyond region, dualstack, and FIPS. Use a placeholder that will be replaced at request time. + return ClientEndpointProvider.create(URI.create("https://localhost"), false); + } + if (clientEndpointUri.getHost() == null) { + throw SdkClientException.create("Configured region (" + region + ") resulted in an invalid URI: " + + clientEndpointUri + ". This is usually caused by an invalid region configuration."); + } + return ClientEndpointProvider.create(clientEndpointUri, false); + }); + builder.lazyOptionIfAbsent( + AwsClientOption.SIGNING_REGION, + c -> { + Region region = c.get(AwsClientOption.AWS_REGION); + try { + QueryEndpointParams endpointParams = QueryEndpointParams.builder().region(region) + .useDualStack(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build(); + Endpoint endpoint = CompletableFutureUtils.joinLikeSync(defaultEndpointProvider().resolveEndpoint( + endpointParams)); + List authSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + if (authSchemes != null && !authSchemes.isEmpty()) { + EndpointAuthScheme firstScheme = authSchemes.get(0); + if (firstScheme instanceof SigV4AuthScheme) { + String signingRegion = ((SigV4AuthScheme) firstScheme).signingRegion(); + if (signingRegion != null) { + return Region.of(signingRegion); + } + } + } + } catch (Exception e) { + // Endpoint resolution failed. Fall back to using the client region as signing region. + } + return region; + }); SdkClientConfiguration clientConfig = config; builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); builder.lazyOption(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, c -> resolveResponseChecksumValidation(clientConfig)); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-rule-set-no-region.json b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-rule-set-no-region.json new file mode 100644 index 000000000000..14d83691d85a --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-rule-set-no-region.json @@ -0,0 +1,54 @@ +{ + "version": "1.2", + "serviceId": "query", + "parameters": { + "Endpoint": { + "type": "string", + "builtIn": "SDK::Endpoint", + "documentation": "Override the endpoint used to send this request" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "endpoint": { + "url": "{endpoint}", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "us-east-1", + "signingName": "query-service" + } + ] + } + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://query-service.us-east-1.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "us-east-1", + "signingName": "query-service" + } + ] + } + }, + "type": "endpoint" + } + ] +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-tests-no-region.json b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-tests-no-region.json new file mode 100644 index 000000000000..727e21bb016f --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-tests-no-region.json @@ -0,0 +1,14 @@ +{ + "testCases": [ + { + "documentation": "Default endpoint", + "params": {}, + "expect": { + "endpoint": { + "url": "https://query-service.us-east-1.amazonaws.com" + } + } + } + ], + "version": "1.0" +} diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java index cf74d712bc26..92036575eeb1 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java @@ -34,7 +34,6 @@ import software.amazon.awssdk.awscore.client.config.AwsAdvancedClientOption; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.defaultsmode.DefaultsMode; -import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; import software.amazon.awssdk.awscore.endpoint.DualstackEnabledProvider; import software.amazon.awssdk.awscore.endpoint.FipsEnabledProvider; import software.amazon.awssdk.awscore.eventstream.EventStreamInitialRequestInterceptor; @@ -64,7 +63,6 @@ import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.regions.ServiceMetadata; import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain; import software.amazon.awssdk.retries.api.RetryStrategy; @@ -75,6 +73,7 @@ import software.amazon.awssdk.utils.Logger; import software.amazon.awssdk.utils.Pair; import software.amazon.awssdk.utils.StringUtils; +import software.amazon.awssdk.utils.Validate; /** * An SDK-internal implementation of the methods in {@link AwsClientBuilder}, {@link AwsAsyncClientBuilder} and @@ -97,7 +96,6 @@ public abstract class AwsDefaultClientBuilder implements AwsClientBuilder { private static final Logger log = Logger.loggerFor(AwsClientBuilder.class); - private static final String DEFAULT_ENDPOINT_PROTOCOL = "https"; private static final String[] FIPS_SEARCH = {"fips-", "-fips"}; private static final String[] FIPS_REPLACE = {"", ""}; @@ -188,11 +186,10 @@ private SdkClientConfiguration finalizeAwsConfiguration(SdkClientConfiguration c this::resolveCredentialsIdentityProvider) // Set CREDENTIALS_PROVIDER, because older clients may be relying on it .lazyOptionIfAbsent(AwsClientOption.CREDENTIALS_PROVIDER, this::resolveCredentialsProvider) - .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, this::resolveClientEndpointProvider) // Set ENDPOINT and ENDPOINT_OVERRIDDEN, because older clients may be relying on it .lazyOptionIfAbsent(SdkClientOption.ENDPOINT, this::resolveEndpoint) .lazyOptionIfAbsent(SdkClientOption.ENDPOINT_OVERRIDDEN, this::resolveEndpointOverridden) - .lazyOption(AwsClientOption.SIGNING_REGION, this::resolveSigningRegion) + .lazyOptionIfAbsent(AwsClientOption.SIGNING_REGION, this::resolveSigningRegion) .lazyOption(SdkClientOption.HTTP_CLIENT_CONFIG, this::resolveHttpClientConfig) .applyMutation(this::configureRetryPolicy) .applyMutation(this::configureRetryStrategy) @@ -313,32 +310,12 @@ private AttributeMap mergeSmartHttpDefaults(LazyValueSource config, AttributeMap } /** - * Resolve the signing region from the default-applied configuration. + * Fallback signing region resolution. Returns the client region as-is. + * Generated service builders resolve the signing region from endpoint rules in finalizeServiceConfiguration, + * which takes precedence over this fallback via lazyOptionIfAbsent ordering. */ private Region resolveSigningRegion(LazyValueSource config) { - return ServiceMetadata.of(serviceEndpointPrefix()) - .signingRegion(config.get(AwsClientOption.AWS_REGION)); - } - - /** - * Specify the client endpoint provider to use for the client, if the client didn't specify one itself. - *

- * This is only used for older client versions. Newer clients specify this value themselves. - */ - private ClientEndpointProvider resolveClientEndpointProvider(LazyValueSource config) { - ServiceMetadataAdvancedOption useGlobalS3EndpointProperty = - ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT; - return AwsClientEndpointProvider.builder() - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol(DEFAULT_ENDPOINT_PROTOCOL) - .region(config.get(AwsClientOption.AWS_REGION)) - .profileFile(config.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(config.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(useGlobalS3EndpointProperty, - config.get(useGlobalS3EndpointProperty)) - .dualstackEnabled(config.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(config.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)) - .build(); + return config.get(AwsClientOption.AWS_REGION); } /** @@ -346,7 +323,7 @@ private ClientEndpointProvider resolveClientEndpointProvider(LazyValueSource con * information from the client endpoint provider. */ private URI resolveEndpoint(LazyValueSource config) { - return config.get(SdkClientOption.CLIENT_ENDPOINT_PROVIDER).clientEndpoint(); + return requireClientEndpointProvider(config).clientEndpoint(); } /** @@ -354,7 +331,16 @@ private URI resolveEndpoint(LazyValueSource config) { * client versions resolve this information from the client endpoint provider. */ private boolean resolveEndpointOverridden(LazyValueSource config) { - return config.get(SdkClientOption.CLIENT_ENDPOINT_PROVIDER).isEndpointOverridden(); + return requireClientEndpointProvider(config).isEndpointOverridden(); + } + + private ClientEndpointProvider requireClientEndpointProvider(LazyValueSource config) { + ClientEndpointProvider clientEndpointProvider = config.get(SdkClientOption.CLIENT_ENDPOINT_PROVIDER); + Validate.notNull(clientEndpointProvider, + "No CLIENT_ENDPOINT_PROVIDER was configured. This is typically caused by using " + + "an older service client version with a newer sdk-core. " + + "Please align all SDK dependency versions."); + return clientEndpointProvider; } /** diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProvider.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProvider.java index 0bb5119b369e..6697bbb94448 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProvider.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProvider.java @@ -95,7 +95,8 @@ public boolean isEndpointOverridden() { return clientEndpoint.getValue().isEndpointOverridden; } - private ClientEndpoint resolveClientEndpoint(Builder builder) { + // TODO: Remove once all callers are migrated to use resolveFromOverrides + private static ClientEndpoint resolveClientEndpoint(Builder builder) { return OptionalUtils.firstPresent(clientEndpointFromClientOverride(builder), () -> clientEndpointFromEnvironment(builder), () -> clientEndpointFromServiceMetadata(builder)) @@ -107,14 +108,14 @@ private static SdkClientException failToLoadEndpointException() { AwsClientEndpointProvider.class.getName() + " for more information."); } - private Optional clientEndpointFromClientOverride(Builder builder) { + private static Optional clientEndpointFromClientOverride(Builder builder) { Optional result = Optional.ofNullable(builder.clientEndpointOverride) .map(uri -> new ClientEndpoint(uri, true)); result.ifPresent(e -> log.trace(() -> "Client was configured with endpoint override: " + e.clientEndpoint)); return result; } - private Optional clientEndpointFromEnvironment(Builder builder) { + private static Optional clientEndpointFromEnvironment(Builder builder) { if (builder.serviceEndpointOverrideEnvironmentVariable == null || builder.serviceEndpointOverrideSystemProperty == null || builder.serviceProfileProperty == null) { @@ -147,19 +148,19 @@ private Optional clientEndpointFromEnvironment(Builder builder) .map(uri -> new ClientEndpoint(uri, true)); } - private Optional systemProperty(String systemProperty) { + private static Optional systemProperty(String systemProperty) { // CHECKSTYLE:OFF - We have to read system properties directly here to match the load order of the other SDKs return createUri("system property " + systemProperty, Optional.ofNullable(System.getProperty(systemProperty))); // CHECKSTYLE:ON } - private Optional environmentVariable(String environmentVariable) { + private static Optional environmentVariable(String environmentVariable) { return createUri("environment variable " + environmentVariable, SystemSettingUtils.resolveEnvironmentVariable(environmentVariable)); } - private Optional profileProperty(Builder builder, String profileProperty) { + private static Optional profileProperty(Builder builder, String profileProperty) { initializeProfileFileDefaults(builder); return createUri("profile property " + profileProperty, Optional.ofNullable(builder.profileFile.get()) @@ -167,7 +168,7 @@ private Optional profileProperty(Builder builder, String profileProperty) { .flatMap(p -> p.property(profileProperty))); } - private Optional servicesProperty(Builder builder) { + private static Optional servicesProperty(Builder builder) { Optional profileFile = Optional.ofNullable(builder.profileFile.get()); Optional servicesSectionName = profileFile .flatMap(pf -> pf.profile(builder.profileName)) @@ -181,7 +182,7 @@ private Optional servicesProperty(Builder builder) { return createUri("services section property", serviceEndpoint); } - private Optional clientEndpointFromServiceMetadata(Builder builder) { + private static Optional clientEndpointFromServiceMetadata(Builder builder) { // This value is generally overridden after endpoints 2.0. It seems to exist for backwards-compatibility // with older client versions or interceptors. @@ -258,7 +259,7 @@ private Optional clientEndpointFromServiceMetadata(Builder build return Optional.of(new ClientEndpoint(endpoint, false)); } - private Optional createUri(String source, Optional uri) { + private static Optional createUri(String source, Optional uri) { return uri.map(u -> { try { URI parsedUri = SdkUri.getInstance().newUri(uri.get()); @@ -270,7 +271,7 @@ private Optional createUri(String source, Optional uri) { }); } - private void initializeProfileFileDefaults(Builder builder) { + private static void initializeProfileFileDefaults(Builder builder) { if (builder.profileFile == null) { builder.profileFile = new Lazy<>(ProfileFile::defaultProfileFile)::getValue; } @@ -479,8 +480,28 @@ public Builder putAdvancedOption(ServiceMetadataAdvancedOption option, T return this; } + /** + * @deprecated This method triggers expensive ServiceMetadata + * initialization and is no longer used by the SDK. Service endpoint resolution is handled + * by Endpoints 2.0 at request time. + */ + @Deprecated public AwsClientEndpointProvider build() { return new AwsClientEndpointProvider(this); } + + /** + * Resolve an endpoint from overrides and environment configuration. + * Checks client override, system properties, environment variables, and profile configuration. + * Returns {@link Optional#empty()} if no override is configured, allowing callers to provide + * their own fallback. + */ + public Optional resolveFromOverrides() { + Builder copy = new Builder(this); + initializeProfileFileDefaults(copy); + return OptionalUtils.firstPresent(clientEndpointFromClientOverride(copy), + () -> clientEndpointFromEnvironment(copy)) + .map(endpoint -> endpoint.clientEndpoint); + } } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java index 8a4eab2e5673..dbb4086c06ae 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java @@ -16,20 +16,11 @@ package software.amazon.awssdk.awscore.interceptor; import java.net.UnknownHostException; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; import software.amazon.awssdk.annotations.SdkProtectedApi; -import software.amazon.awssdk.awscore.AwsExecutionAttribute; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; -import software.amazon.awssdk.regions.PartitionMetadata; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.regions.RegionMetadata; -import software.amazon.awssdk.regions.ServiceMetadata; -import software.amazon.awssdk.regions.ServicePartitionMetadata; /** * This interceptor will monitor for {@link UnknownHostException}s and provide the customer with additional information they can @@ -37,90 +28,20 @@ */ @SdkProtectedApi public final class HelpfulUnknownHostExceptionInterceptor implements ExecutionInterceptor { + private static final String ERROR_MESSAGE = + "Received an UnknownHostException when attempting to interact with a service. " + + "See cause for the exact endpoint that is failing to resolve. " + + "If this is happening on an endpoint that previously worked, " + + "there may be a network connectivity issue or your DNS cache " + + "could be storing endpoints for too long."; + @Override public Throwable modifyException(Context.FailedExecution context, ExecutionAttributes executionAttributes) { if (!hasCause(context.exception(), UnknownHostException.class)) { return context.exception(); } - StringBuilder error = new StringBuilder(); - error.append("Received an UnknownHostException when attempting to interact with a service. See cause for the " - + "exact endpoint that is failing to resolve. "); - - Optional globalRegionErrorDetails = getGlobalRegionErrorDetails(executionAttributes); - - if (globalRegionErrorDetails.isPresent()) { - error.append(globalRegionErrorDetails.get()); - } else { - error.append("If this is happening on an endpoint that previously worked, there may be a network connectivity " - + "issue or your DNS cache could be storing endpoints for too long."); - } - - return SdkClientException.builder().message(error.toString()).cause(context.exception()).build(); - } - - /** - * If the customer is interacting with a global service (one with a single endpoint/region for an entire partition), this - * will return error details that can instruct the customer on how to configure their client for success. - */ - private Optional getGlobalRegionErrorDetails(ExecutionAttributes executionAttributes) { - Region clientRegion = clientRegion(executionAttributes); - if (clientRegion.isGlobalRegion()) { - return Optional.empty(); - } - - List globalPartitionsForService = globalPartitionsForService(executionAttributes); - if (globalPartitionsForService.isEmpty()) { - return Optional.empty(); - } - - String clientPartition = Optional.ofNullable(clientRegion.metadata()) - .map(RegionMetadata::partition) - .map(PartitionMetadata::id) - .orElse(null); - - Optional globalRegionForClientRegion = - globalPartitionsForService.stream() - .filter(p -> p.partition().id().equals(clientPartition)) - .findAny() - .flatMap(ServicePartitionMetadata::globalRegion); - - if (!globalRegionForClientRegion.isPresent()) { - String globalRegionsForThisService = globalPartitionsForService.stream() - .map(ServicePartitionMetadata::globalRegion) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(Region::isGlobalRegion) - .map(Region::id) - .collect(Collectors.joining("/")); - - return Optional.of("This specific service may be a global service, in which case you should configure a global " - + "region like " + globalRegionsForThisService + " on the client."); - } - - Region globalRegion = globalRegionForClientRegion.get(); - - return Optional.of("This specific service is global in the same partition as the region configured on this client (" - + clientRegion + "). If this is the first time you're trying to talk to this service in this region, " - + "you should try configuring the global region on your client, instead: " + globalRegion); - } - - /** - * Retrieve the region configured on the client. - */ - private Region clientRegion(ExecutionAttributes executionAttributes) { - return executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION); - } - - /** - * Retrieve all global partitions for the AWS service that we're interacting with. - */ - private List globalPartitionsForService(ExecutionAttributes executionAttributes) { - return ServiceMetadata.of(executionAttributes.getAttribute(AwsExecutionAttribute.ENDPOINT_PREFIX)) - .servicePartitions() - .stream() - .filter(sp -> sp.globalRegion().isPresent()) - .collect(Collectors.toList()); + return SdkClientException.builder().message(ERROR_MESSAGE).cause(context.exception()).build(); } private boolean hasCause(Throwable thrown, Class cause) { diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java index 74d263c68f43..c76b5bb36823 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java @@ -52,6 +52,7 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy; import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.http.SdkHttpClient; @@ -161,6 +162,43 @@ public void buildWithEndpointShouldHaveCorrectEndpointAndSigningRegion() { assertThat(client.clientConfiguration.option(SERVICE_SIGNING_NAME)).isEqualTo(SIGNING_NAME); } + @Test + public void resolveSigningRegion_withGlobalRegion_resolvesCorrectlyFromEndpointRules() { + ClientOverrideConfiguration overrideConfig = + ClientOverrideConfiguration.builder() + .putAdvancedOption(SIGNER, TEST_SIGNER) + .putAdvancedOption(ENABLE_DEFAULT_REGION_DETECTION, false) + .build(); + + TestClient client = new TestClientBuilderWithEndpointRulesSigningRegion(Region.US_EAST_1) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .overrideConfiguration(overrideConfig) + .region(Region.AWS_GLOBAL) + .build(); + + // The endpoint-rules-resolved signing region (us-east-1) is used, not the client region (aws-global) + assertThat(client.clientConfiguration.option(SIGNING_REGION)).isEqualTo(Region.US_EAST_1); + } + + @Test + public void signingRegionFromEndpointRules_fallsBackToClientRegionOnFailure() { + // Simulates a generated builder where endpoint resolution fails. + // Should fall back to client region. + ClientOverrideConfiguration overrideConfig = + ClientOverrideConfiguration.builder() + .putAdvancedOption(SIGNER, TEST_SIGNER) + .putAdvancedOption(ENABLE_DEFAULT_REGION_DETECTION, false) + .build(); + + TestClient client = new TestClientBuilderWithFailingEndpointResolution() + .credentialsProvider(AnonymousCredentialsProvider.create()) + .overrideConfiguration(overrideConfig) + .region(Region.US_WEST_2) + .build(); + + assertThat(client.clientConfiguration.option(SIGNING_REGION)).isEqualTo(Region.US_WEST_2); + } + @Test public void noClientProvided_DefaultHttpClientIsManagedBySdk() { TestClient client = testClientBuilder().region(Region.US_WEST_2).build(); @@ -403,6 +441,17 @@ protected TestClient buildClient() { return new TestClient(super.syncClientConfiguration()); } + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } + @Override protected String serviceEndpointPrefix() { return ENDPOINT_PREFIX; @@ -444,6 +493,17 @@ protected TestAsyncClient buildClient() { return new TestAsyncClient(super.asyncClientConfiguration()); } + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } + @Override protected String serviceEndpointPrefix() { return ENDPOINT_PREFIX; @@ -467,4 +527,104 @@ protected AttributeMap serviceHttpConfig() { private static class TestException extends Throwable { } + + /** + * Test builder that sets SIGNING_REGION from endpoint rules in finalizeServiceConfiguration. + */ + private class TestClientBuilderWithEndpointRulesSigningRegion + extends AwsDefaultClientBuilder + implements AwsClientBuilder { + + private final Region resolvedSigningRegion; + + public TestClientBuilderWithEndpointRulesSigningRegion(Region resolvedSigningRegion) { + super(defaultHttpClientBuilder, null, autoModeDiscovery); + this.resolvedSigningRegion = resolvedSigningRegion; + } + + @Override + protected TestClient buildClient() { + return new TestClient(super.syncClientConfiguration()); + } + + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .lazyOptionIfAbsent(AwsClientOption.SIGNING_REGION, c -> resolvedSigningRegion) + .build(); + } + + @Override + protected String serviceEndpointPrefix() { + return ENDPOINT_PREFIX; + } + + @Override + protected String signingName() { + return SIGNING_NAME; + } + + @Override + protected String serviceName() { + return SERVICE_NAME; + } + + @Override + protected AttributeMap serviceHttpConfig() { + return MOCK_DEFAULTS; + } + } + + /** + * Test builder where finalizeServiceConfiguration does not set SIGNING_REGION. + */ + private class TestClientBuilderWithFailingEndpointResolution + extends AwsDefaultClientBuilder + implements AwsClientBuilder { + + public TestClientBuilderWithFailingEndpointResolution() { + super(defaultHttpClientBuilder, null, autoModeDiscovery); + } + + @Override + protected TestClient buildClient() { + return new TestClient(super.syncClientConfiguration()); + } + + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } + + @Override + protected String serviceEndpointPrefix() { + return ENDPOINT_PREFIX; + } + + @Override + protected String signingName() { + return SIGNING_NAME; + } + + @Override + protected String serviceName() { + return SERVICE_NAME; + } + + @Override + protected AttributeMap serviceHttpConfig() { + return MOCK_DEFAULTS; + } + } } diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java index 2b9ea42a5d73..37bfd4dcf901 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java @@ -37,6 +37,10 @@ import software.amazon.awssdk.awscore.internal.defaultsmode.DefaultsModeConfiguration; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.core.ClientEndpointProvider; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import java.net.URI; import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy; import software.amazon.awssdk.core.retry.RetryMode; import software.amazon.awssdk.http.SdkHttpClient; @@ -187,6 +191,17 @@ protected TestClient buildClient() { return new TestClient(super.syncClientConfiguration()); } + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } + @Override protected String serviceEndpointPrefix() { return ENDPOINT_PREFIX; @@ -220,6 +235,17 @@ protected TestAsyncClient buildClient() { return new TestAsyncClient(super.asyncClientConfiguration()); } + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } + @Override protected String serviceEndpointPrefix() { return ENDPOINT_PREFIX; diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/FipsPseudoRegionTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/FipsPseudoRegionTest.java index c20f3d924157..5cf5c5d859e7 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/FipsPseudoRegionTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/FipsPseudoRegionTest.java @@ -22,6 +22,7 @@ import static software.amazon.awssdk.awscore.client.config.AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION; import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.SIGNER; +import java.net.URI; import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -30,8 +31,10 @@ import org.junit.jupiter.params.provider.MethodSource; import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.SdkHttpConfigurationOption; @@ -122,6 +125,17 @@ protected TestClient buildClient() { return new TestClient(super.syncClientConfiguration()); } + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } + @Override protected String serviceEndpointPrefix() { return ENDPOINT_PREFIX; diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/InternalDefaultsTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/InternalDefaultsTest.java index 5c411276e923..6de908763b03 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/InternalDefaultsTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/InternalDefaultsTest.java @@ -28,6 +28,9 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import software.amazon.awssdk.core.SdkSystemSetting; +import java.net.URI; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.http.SdkHttpClient; @@ -159,5 +162,16 @@ protected TestClient buildClient() { return new TestClient(config); } + + @Override + protected SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + return config.toBuilder() + .lazyOptionIfAbsent(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, c -> { + URI endpoint = URI.create("https://" + serviceEndpointPrefix() + "." + + c.get(AwsClientOption.AWS_REGION) + ".amazonaws.com"); + return ClientEndpointProvider.create(endpoint, false); + }) + .build(); + } } } diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProviderResolveFromOverridesTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProviderResolveFromOverridesTest.java new file mode 100644 index 000000000000..8ca96cf9aefb --- /dev/null +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/endpoint/AwsClientEndpointProviderResolveFromOverridesTest.java @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.awscore.endpoint; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +class AwsClientEndpointProviderResolveFromOverridesTest { + + private static final String TEST_SYSTEM_PROPERTY = "aws.endpointUrlTestService"; + + @AfterEach + void cleanup() { + System.clearProperty(TEST_SYSTEM_PROPERTY); + } + + @Test + void resolveFromOverrides_withClientEndpointOverride_returnsOverrideUri() { + URI override = URI.create("https://custom-endpoint.example.com"); + + Optional result = AwsClientEndpointProvider.builder() + .clientEndpointOverride(override) + .resolveFromOverrides(); + + assertThat(result).isPresent(); + assertThat(result.get()).isEqualTo(override); + } + + @Test + void resolveFromOverrides_withSystemPropertyEndpoint_returnsEndpointUri() { + System.setProperty(TEST_SYSTEM_PROPERTY, "https://sys-prop-endpoint.example.com"); + + Optional result = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideSystemProperty(TEST_SYSTEM_PROPERTY) + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_TEST_SERVICE") + .serviceProfileProperty("testservice") + .resolveFromOverrides(); + + assertThat(result).isPresent(); + assertThat(result.get()).isEqualTo(URI.create("https://sys-prop-endpoint.example.com")); + } + + @Test + void resolveFromOverrides_withNoOverrideOrEnvironment_returnsEmpty() { + Optional result = AwsClientEndpointProvider.builder() + .serviceEndpointOverrideSystemProperty("aws.endpointUrlNonExistent") + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_NON_EXISTENT") + .serviceProfileProperty("nonexistent") + .resolveFromOverrides(); + + assertThat(result).isEmpty(); + } + + @Test + void resolveFromOverrides_withNoParamsConfigured_returnsEmpty() { + Optional result = AwsClientEndpointProvider.builder() + .resolveFromOverrides(); + + assertThat(result).isEmpty(); + } + +} diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java index 76dc2cc3c465..686f0253b903 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java @@ -6,14 +6,12 @@ import java.net.UnknownHostException; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import software.amazon.awssdk.awscore.AwsExecutionAttribute; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.interceptor.InterceptorContext; import software.amazon.awssdk.core.internal.interceptor.DefaultFailedExecutionContext; -import software.amazon.awssdk.regions.Region; public class HelpfulUnknownHostExceptionInterceptorTest { private static final ExecutionInterceptor INTERCEPTOR = new HelpfulUnknownHostExceptionInterceptor(); @@ -31,76 +29,26 @@ public void modifyException_supportsNestedUnknownHostExceptions() { exception = new IllegalArgumentException(exception); exception = new UnsupportedOperationException(exception); - assertThat(modifyException(exception, Region.AWS_GLOBAL)).isInstanceOf(SdkClientException.class); + assertThat(modifyException(exception)).isInstanceOf(SdkClientException.class); } @Test - public void modifyException_returnsGenericHelp_forGlobalRegions() { + public void modifyException_returnsNetworkHelp_forUnknownHostException() { UnknownHostException exception = new UnknownHostException(); - assertThat(modifyException(exception, Region.AWS_GLOBAL)) + assertThat(modifyException(exception)) .isInstanceOf(SdkClientException.class) .hasMessageContaining("network"); } @Test - public void modifyException_returnsGenericHelp_forUnknownServices() { - UnknownHostException exception = new UnknownHostException(); - assertThat(modifyException(exception, Region.US_EAST_1, "millems-hotdog-stand")) - .isInstanceOf(SdkClientException.class) - .satisfies(t -> doesNotHaveMessageContaining(t, "global")) - .hasMessageContaining("network"); - } - - @Test - public void modifyException_returnsGenericHelp_forUnknownServicesInUnknownRegions() { - UnknownHostException exception = new UnknownHostException(); - assertThat(modifyException(exception, Region.of("cn-north-99"), "millems-hotdog-stand")) - .isInstanceOf(SdkClientException.class) - .satisfies(t -> doesNotHaveMessageContaining(t, "global")) - .hasMessageContaining("network"); - } - - @Test - public void modifyException_returnsGenericHelp_forServicesRegionalizedInAllPartitions() { - UnknownHostException exception = new UnknownHostException(); - assertThat(modifyException(exception, Region.US_EAST_1, "dynamodb")) - .isInstanceOf(SdkClientException.class) - .satisfies(t -> doesNotHaveMessageContaining(t, "global")) - .hasMessageContaining("network"); - } - - @Test - public void modifyException_returnsGenericGlobalRegionHelp_forServicesGlobalInSomePartitionOtherThanTheClientPartition() { - UnknownHostException exception = new UnknownHostException(); - assertThat(modifyException(exception, Region.of("cn-north-99"), "iam")) - .isInstanceOf(SdkClientException.class) - .satisfies(t -> doesNotHaveMessageContaining(t, "network")) - .hasMessageContaining("aws-global") - .hasMessageContaining("aws-cn-global"); - } - - @Test - public void modifyException_returnsSpecificGlobalRegionHelp_forServicesGlobalInTheClientRegionPartition() { - UnknownHostException exception = new UnknownHostException(); - assertThat(modifyException(exception, Region.of("cn-north-1"), "iam")) - .isInstanceOf(SdkClientException.class) - .satisfies(t -> doesNotHaveMessageContaining(t, "aws-global")) - .hasMessageContaining("aws-cn-global"); - } - - private void doesNotHaveMessageContaining(Throwable throwable, String value) { - assertThat(throwable.getMessage()).doesNotContain(value); + public void modifyException_preservesOriginalExceptionAsCause() { + UnknownHostException exception = new UnknownHostException("iam.us-east-1.amazonaws.com"); + Throwable result = modifyException(exception); + assertThat(result).isInstanceOf(SdkClientException.class); + assertThat(result.getCause()).isSameAs(exception); } private Throwable modifyException(Throwable throwable) { - return modifyException(throwable, null); - } - - private Throwable modifyException(Throwable throwable, Region clientRegion) { - return modifyException(throwable, clientRegion, null); - } - - private Throwable modifyException(Throwable throwable, Region clientRegion, String serviceEndpointPrefix) { SdkRequest sdkRequest = Mockito.mock(SdkRequest.class); DefaultFailedExecutionContext context = @@ -109,10 +57,6 @@ private Throwable modifyException(Throwable throwable, Region clientRegion, Stri .exception(throwable) .build(); - ExecutionAttributes executionAttributes = - new ExecutionAttributes().putAttribute(AwsExecutionAttribute.AWS_REGION, clientRegion) - .putAttribute(AwsExecutionAttribute.ENDPOINT_PREFIX, serviceEndpointPrefix); - - return INTERCEPTOR.modifyException(context, executionAttributes); + return INTERCEPTOR.modifyException(context, new ExecutionAttributes()); } } \ No newline at end of file diff --git a/core/regions/src/test/java/software/amazon/awssdk/regions/PartitionServiceMetadataTest.java b/core/regions/src/test/java/software/amazon/awssdk/regions/PartitionServiceMetadataTest.java index b6b5e46e95f9..200d4c3a9a6d 100644 --- a/core/regions/src/test/java/software/amazon/awssdk/regions/PartitionServiceMetadataTest.java +++ b/core/regions/src/test/java/software/amazon/awssdk/regions/PartitionServiceMetadataTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -120,6 +121,54 @@ public void servicePartitions_HasNoGlobalEndpoint_ForRegionalServices() { validateHasGlobalEndpointInPartition(AWS_US_GOV_PARTITION_REGIONALIZED_SERVICES, "aws-us-gov", false); } + @Test + public void endpointFor_ResolvesCorrectFipsEndpoint() { + ServiceEndpointKey key = ServiceEndpointKey.builder() + .region(Region.US_EAST_1) + .tags(EndpointTag.FIPS) + .build(); + assertThat(ServiceMetadata.of("s3").endpointFor(key)) + .isEqualTo(URI.create("s3-fips.us-east-1.amazonaws.com")); + } + + @Test + public void endpointFor_ResolvesCorrectDualstackEndpoint() { + ServiceEndpointKey key = ServiceEndpointKey.builder() + .region(Region.US_WEST_2) + .tags(EndpointTag.DUALSTACK) + .build(); + assertThat(ServiceMetadata.of("s3").endpointFor(key)) + .isEqualTo(URI.create("s3.dualstack.us-west-2.amazonaws.com")); + } + + @Test + public void endpointFor_ResolvesGlobalServiceEndpoint() { + ServiceEndpointKey key = ServiceEndpointKey.builder() + .region(Region.AWS_GLOBAL) + .build(); + assertThat(ServiceMetadata.of("iam").endpointFor(key)) + .isEqualTo(URI.create("iam.amazonaws.com")); + } + + @Test + public void endpointFor_ResolvesCorrectFipsDualstackEndpoint() { + ServiceEndpointKey key = ServiceEndpointKey.builder() + .region(Region.US_WEST_2) + .tags(EndpointTag.FIPS, EndpointTag.DUALSTACK) + .build(); + assertThat(ServiceMetadata.of("s3").endpointFor(key)) + .isEqualTo(URI.create("s3-fips.dualstack.us-west-2.amazonaws.com")); + } + + @Test + public void signingRegion_ResolvesCorrectly_ForGlobalService() { + ServiceEndpointKey key = ServiceEndpointKey.builder() + .region(Region.AWS_GLOBAL) + .build(); + assertThat(ServiceMetadata.of("iam").signingRegion(key)) + .isEqualTo(Region.US_EAST_1); + } + private void validateHasGlobalEndpointInPartition(List services, String partition, boolean hasGlobalEndpoint) { services.forEach(s -> assertThat(ServiceMetadata.of(s) .servicePartitions() diff --git a/pom.xml b/pom.xml index e47309d127a3..48513fd0d672 100644 --- a/pom.xml +++ b/pom.xml @@ -88,9 +88,8 @@ test/sdk-native-image-test test/s3-benchmarks test/auth-tests - test/region-testing test/ruleset-testing-core - test/old-client-version-compatibility-test + test/service-client-backward-compat-test test/bundle-logging-bridge-binding-test test/v2-migration-tests test/bundle-shading-tests diff --git a/services/docdb/src/main/java/software/amazon/awssdk/services/docdb/internal/RdsPresignInterceptor.java b/services/docdb/src/main/java/software/amazon/awssdk/services/docdb/internal/RdsPresignInterceptor.java index cd2c0d8c4de1..650619610feb 100644 --- a/services/docdb/src/main/java/software/amazon/awssdk/services/docdb/internal/RdsPresignInterceptor.java +++ b/services/docdb/src/main/java/software/amazon/awssdk/services/docdb/internal/RdsPresignInterceptor.java @@ -24,9 +24,7 @@ import java.util.concurrent.CompletableFuture; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; import software.amazon.awssdk.core.ClientEndpointProvider; -import software.amazon.awssdk.core.Protocol; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; @@ -35,7 +33,6 @@ import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; -import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; @@ -48,6 +45,7 @@ import software.amazon.awssdk.identity.spi.Identity; import software.amazon.awssdk.protocols.query.AwsQueryProtocolFactory; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.docdb.endpoints.DocDbEndpointProvider; import software.amazon.awssdk.services.docdb.model.DocDbRequest; import software.amazon.awssdk.utils.CompletableFutureUtils; @@ -72,7 +70,6 @@ public abstract class RdsPresignInterceptor implements E .build()) .build(); - private static final String SERVICE_NAME = "rds"; private static final String PARAM_SOURCE_REGION = "SourceRegion"; private static final String PARAM_DESTINATION_REGION = "DestinationRegion"; private static final String PARAM_PRESIGNED_URL = "PreSignedUrl"; @@ -109,7 +106,7 @@ public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, SelectedAuthScheme selectedAuthScheme = resolveAuthScheme(context.request(), executionAttributes); String sourceRegion = presignableRequest.getSourceRegion(); String destinationRegion = selectedAuthScheme.authSchemeOption().signerProperty(AwsV4HttpSigner.REGION_NAME); - URI endpoint = createEndpoint(sourceRegion, SERVICE_NAME, executionAttributes); + URI endpoint = createEndpoint(sourceRegion, executionAttributes); SdkHttpFullRequest.Builder marshalledRequest = presignableRequest.marshall().toBuilder().uri(endpoint); SdkHttpFullRequest requestToPresign = @@ -223,17 +220,14 @@ private SdkHttpFullRequest toSdkHttpFullRequest(SignedRequest signedRequest) { .build(); } - private URI createEndpoint(String regionName, String serviceName, ExecutionAttributes attributes) { - return AwsClientEndpointProvider.builder() - .serviceEndpointPrefix(SERVICE_NAME) - .defaultProtocol(Protocol.HTTPS.toString()) - .region(Region.of(regionName)) - .profileFile(attributes.getAttribute(SdkExecutionAttribute.PROFILE_FILE_SUPPLIER)) - .profileName(attributes.getAttribute(SdkExecutionAttribute.PROFILE_NAME)) - .dualstackEnabled( - attributes.getAttribute(AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(attributes.getAttribute(AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED)) - .build() - .clientEndpoint(); + private URI createEndpoint(String regionName, ExecutionAttributes attributes) { + return CompletableFutureUtils.joinLikeSync( + DocDbEndpointProvider.defaultProvider() + .resolveEndpoint(p -> p.region(Region.of(regionName)) + .useDualStack(attributes.getAttribute( + AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(attributes.getAttribute( + AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED))) + ).url(); } } diff --git a/services/neptune/src/main/java/software/amazon/awssdk/services/neptune/internal/RdsPresignInterceptor.java b/services/neptune/src/main/java/software/amazon/awssdk/services/neptune/internal/RdsPresignInterceptor.java index 1ff7adf4221d..09aa57f1ec65 100644 --- a/services/neptune/src/main/java/software/amazon/awssdk/services/neptune/internal/RdsPresignInterceptor.java +++ b/services/neptune/src/main/java/software/amazon/awssdk/services/neptune/internal/RdsPresignInterceptor.java @@ -24,9 +24,7 @@ import java.util.concurrent.CompletableFuture; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; import software.amazon.awssdk.core.ClientEndpointProvider; -import software.amazon.awssdk.core.Protocol; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; @@ -35,7 +33,6 @@ import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; -import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; @@ -48,6 +45,7 @@ import software.amazon.awssdk.identity.spi.Identity; import software.amazon.awssdk.protocols.query.AwsQueryProtocolFactory; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.neptune.endpoints.NeptuneEndpointProvider; import software.amazon.awssdk.services.neptune.model.NeptuneRequest; import software.amazon.awssdk.utils.CompletableFutureUtils; @@ -72,7 +70,6 @@ public abstract class RdsPresignInterceptor implements .build()) .build(); - private static final String SERVICE_NAME = "rds"; private static final String PARAM_SOURCE_REGION = "SourceRegion"; private static final String PARAM_DESTINATION_REGION = "DestinationRegion"; private static final String PARAM_PRESIGNED_URL = "PreSignedUrl"; @@ -109,7 +106,7 @@ public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, SelectedAuthScheme selectedAuthScheme = resolveAuthScheme(context.request(), executionAttributes); String sourceRegion = presignableRequest.getSourceRegion(); String destinationRegion = selectedAuthScheme.authSchemeOption().signerProperty(AwsV4HttpSigner.REGION_NAME); - URI endpoint = createEndpoint(sourceRegion, SERVICE_NAME, executionAttributes); + URI endpoint = createEndpoint(sourceRegion, executionAttributes); SdkHttpFullRequest.Builder marshalledRequest = presignableRequest.marshall().toBuilder().uri(endpoint); SdkHttpFullRequest requestToPresign = @@ -223,17 +220,14 @@ private SdkHttpFullRequest toSdkHttpFullRequest(SignedRequest signedRequest) { .build(); } - private URI createEndpoint(String regionName, String serviceName, ExecutionAttributes attributes) { - return AwsClientEndpointProvider.builder() - .serviceEndpointPrefix(SERVICE_NAME) - .defaultProtocol(Protocol.HTTPS.toString()) - .region(Region.of(regionName)) - .profileFile(attributes.getAttribute(SdkExecutionAttribute.PROFILE_FILE_SUPPLIER)) - .profileName(attributes.getAttribute(SdkExecutionAttribute.PROFILE_NAME)) - .dualstackEnabled( - attributes.getAttribute(AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(attributes.getAttribute(AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED)) - .build() - .clientEndpoint(); + private URI createEndpoint(String regionName, ExecutionAttributes attributes) { + return CompletableFutureUtils.joinLikeSync( + NeptuneEndpointProvider.defaultProvider() + .resolveEndpoint(p -> p.region(Region.of(regionName)) + .useDualStack(attributes.getAttribute( + AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(attributes.getAttribute( + AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED))) + ).url(); } } diff --git a/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java b/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java index 0ed43bf8df63..bc42d3440d0f 100644 --- a/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java +++ b/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java @@ -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; @@ -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; @@ -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 @@ -126,6 +129,7 @@ private DefaultPollyPresigner(BuilderImpl builder) { .build() .isFipsEnabled() .orElse(false); + this.resolvedEndpoint = resolveEndpoint(); } IdentityProvider credentialsProvider() { @@ -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 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() + .resolveEndpoint(p -> p.region(region) + .useDualStack(dualstackEnabled) + .useFips(fipsEnabled)) + ).url()); } public static class BuilderImpl implements PollyPresigner.Builder { diff --git a/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java b/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java index a56acb3be2c6..c62aee14c089 100644 --- a/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java +++ b/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java @@ -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() diff --git a/services/rds/src/main/java/software/amazon/awssdk/services/rds/internal/RdsPresignInterceptor.java b/services/rds/src/main/java/software/amazon/awssdk/services/rds/internal/RdsPresignInterceptor.java index f3b9cb723277..d35815b2392d 100644 --- a/services/rds/src/main/java/software/amazon/awssdk/services/rds/internal/RdsPresignInterceptor.java +++ b/services/rds/src/main/java/software/amazon/awssdk/services/rds/internal/RdsPresignInterceptor.java @@ -24,9 +24,7 @@ import java.util.concurrent.CompletableFuture; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; import software.amazon.awssdk.core.ClientEndpointProvider; -import software.amazon.awssdk.core.Protocol; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; @@ -35,7 +33,6 @@ import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; -import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; @@ -48,6 +45,7 @@ import software.amazon.awssdk.identity.spi.Identity; import software.amazon.awssdk.protocols.query.AwsQueryProtocolFactory; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.rds.endpoints.RdsEndpointProvider; import software.amazon.awssdk.services.rds.model.RdsRequest; import software.amazon.awssdk.utils.CompletableFutureUtils; @@ -72,7 +70,6 @@ public abstract class RdsPresignInterceptor implements Exe .build()) .build(); - private static final String SERVICE_NAME = "rds"; private static final String PARAM_SOURCE_REGION = "SourceRegion"; private static final String PARAM_DESTINATION_REGION = "DestinationRegion"; private static final String PARAM_PRESIGNED_URL = "PreSignedUrl"; @@ -109,7 +106,7 @@ public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, SelectedAuthScheme selectedAuthScheme = resolveAuthScheme(context.request(), executionAttributes); String sourceRegion = presignableRequest.getSourceRegion(); String destinationRegion = selectedAuthScheme.authSchemeOption().signerProperty(AwsV4HttpSigner.REGION_NAME); - URI endpoint = createEndpoint(sourceRegion, SERVICE_NAME, executionAttributes); + URI endpoint = createEndpoint(sourceRegion, executionAttributes); SdkHttpFullRequest.Builder marshalledRequest = presignableRequest.marshall().toBuilder().uri(endpoint); SdkHttpFullRequest requestToPresign = @@ -223,17 +220,14 @@ private SdkHttpFullRequest toSdkHttpFullRequest(SignedRequest signedRequest) { .build(); } - private URI createEndpoint(String regionName, String serviceName, ExecutionAttributes attributes) { - return AwsClientEndpointProvider.builder() - .serviceEndpointPrefix(SERVICE_NAME) - .defaultProtocol(Protocol.HTTPS.toString()) - .region(Region.of(regionName)) - .profileFile(attributes.getAttribute(SdkExecutionAttribute.PROFILE_FILE_SUPPLIER)) - .profileName(attributes.getAttribute(SdkExecutionAttribute.PROFILE_NAME)) - .dualstackEnabled( - attributes.getAttribute(AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(attributes.getAttribute(AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED)) - .build() - .clientEndpoint(); + private URI createEndpoint(String regionName, ExecutionAttributes attributes) { + return CompletableFutureUtils.joinLikeSync( + RdsEndpointProvider.defaultProvider() + .resolveEndpoint(p -> p.region(Region.of(regionName)) + .useDualStack(attributes.getAttribute( + AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED)) + .useFips(attributes.getAttribute( + AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED))) + ).url(); } } diff --git a/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java b/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java index fff1e649fc40..866f4b03dc58 100644 --- a/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java +++ b/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java @@ -100,6 +100,9 @@ public void testExpectations(TestCase testCase) { if (testCase.expectedUri != null) { assertEquals(normalize(URI.create(testCase.expectedUri)), normalize(presignedUrlAsUri)); } + if (testCase.expectedPresignedUrlHost != null) { + assertEquals(testCase.expectedPresignedUrlHost, presignedUrlAsUri.getHost()); + } } else { assertFalse(rawQueryParameters.containsKey("PreSignedUrl")); } @@ -169,6 +172,24 @@ public static List testCases() { builder("StartDBInstanceAutomatedBackupsReplication Without SourceRegion Does NOT Send PresignedUrl") .clientConsumer(c -> c.startDBInstanceAutomatedBackupsReplication(r -> r.kmsKeyId(TEST_KMS_KEY_ID))) .shouldContainPreSignedUrl(false) + .build(), + + builder("CopyDbClusterSnapshot - With FIPS enabled resolves to fips endpoint") + .clientConfigure(c -> c.region(Region.US_EAST_1).fipsEnabled(true)) + .clientConsumer(c -> c.copyDBClusterSnapshot(makeTestRequestBuilder() + .sourceRegion("us-west-2") + .build())) + .shouldContainPreSignedUrl(true) + .expectedPresignedUrlHost("rds-fips.us-west-2.amazonaws.com") + .build(), + + builder("CopyDbClusterSnapshot - With dualstack enabled resolves to dualstack endpoint") + .clientConfigure(c -> c.region(Region.US_EAST_1).dualstackEnabled(true)) + .clientConsumer(c -> c.copyDBClusterSnapshot(makeTestRequestBuilder() + .sourceRegion("us-west-2") + .build())) + .shouldContainPreSignedUrl(true) + .expectedPresignedUrlHost("rds.us-west-2.api.aws") .build() ); } @@ -296,6 +317,7 @@ static class TestCase { private final String expectedDestinationRegion; private final Clock signingClockOverride; private final String expectedUri; + private final String expectedPresignedUrlHost; TestCase(TestCaseBuilder builder) { this.name = Validate.notNull(builder.name, "name"); @@ -305,6 +327,7 @@ static class TestCase { this.expectedDestinationRegion = builder.expectedDestinationRegion; this.signingClockOverride = builder.signingClockOverride; this.expectedUri = builder.expectedUri; + this.expectedPresignedUrlHost = builder.expectedPresignedUrlHost; } } @@ -316,6 +339,7 @@ static class TestCaseBuilder { private String expectedDestinationRegion; private Clock signingClockOverride; private String expectedUri; + private String expectedPresignedUrlHost; private TestCaseBuilder name(String name) { this.name = name; @@ -352,6 +376,11 @@ public TestCaseBuilder expectedUri(String expectedUri) { return this; } + public TestCaseBuilder expectedPresignedUrlHost(String expectedPresignedUrlHost) { + this.expectedPresignedUrlHost = expectedPresignedUrlHost; + return this; + } + public TestCase build() { return new TestCase(this); } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java index 98cbcd2b004d..b777fe4d37a0 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java @@ -417,22 +417,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 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 later with Endpoints 2.0 resolution + .orElseGet(() -> ClientEndpointProvider.create(URI.create("https://localhost"), false)); } private URI getEndpointOverride(GetUrlRequest request) { diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java index 16dc52a01411..639995b6c70b 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java @@ -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; @@ -261,28 +262,32 @@ private List 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 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()) { + 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 later with Endpoints 2.0 resolution + endpointProvider = ClientEndpointProvider.create(URI.create("https://localhost"), false); + } return SdkClientConfiguration.builder() - .option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - endpointProvider) + .option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER, endpointProvider) .build(); } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/InvalidRegionTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/InvalidRegionTest.java index 4eef66bf2e22..7831a78c17eb 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/InvalidRegionTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/InvalidRegionTest.java @@ -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 @@ -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 @@ -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 diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/endpoint-rule-set.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/endpoint-rule-set.json new file mode 100644 index 000000000000..c7620b774705 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/endpoint-rule-set.json @@ -0,0 +1,54 @@ +{ + "version": "1.2", + "serviceId": "NoRegionService", + "parameters": { + "Endpoint": { + "type": "string", + "builtIn": "SDK::Endpoint", + "documentation": "Override the endpoint used to send this request" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "endpoint": { + "url": "{Endpoint}", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "us-east-1", + "signingName": "noregionservice" + } + ] + } + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://noregionservice.us-east-1.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "us-east-1", + "signingName": "noregionservice" + } + ] + } + }, + "type": "endpoint" + } + ] +} diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/endpoint-tests.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/endpoint-tests.json new file mode 100644 index 000000000000..50828b80c56e --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/endpoint-tests.json @@ -0,0 +1,23 @@ +{ + "testCases": [ + { + "documentation": "Default endpoint", + "params": {}, + "expect": { + "endpoint": { + "url": "https://noregionservice.us-east-1.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "us-east-1", + "signingName": "noregionservice" + } + ] + } + } + } + } + ], + "version": "1.0" +} diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/service-2.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/service-2.json new file mode 100644 index 000000000000..98519f7ee453 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/noregionservice/service-2.json @@ -0,0 +1,50 @@ +{ + "version": "2.0", + "metadata": { + "apiVersion": "2023-01-01", + "endpointPrefix": "noregionservice", + "protocol": "json", + "serviceFullName": "No Region Service", + "serviceId": "NoRegionService", + "signatureVersion": "v4", + "signingName": "noregionservice", + "jsonVersion": "1.1", + "targetPrefix": "NoRegionService" + }, + "operations": { + "SomeOperation": { + "name": "SomeOperation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "SomeOperationRequest" + }, + "output": { + "shape": "SomeOperationResponse" + } + } + }, + "shapes": { + "SomeOperationRequest": { + "type": "structure", + "members": { + "StringMember": { + "shape": "String" + } + } + }, + "SomeOperationResponse": { + "type": "structure", + "members": { + "StringMember": { + "shape": "String" + } + } + }, + "String": { + "type": "string" + } + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/ClientEndpointResolutionTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/ClientEndpointResolutionTest.java new file mode 100644 index 000000000000..da9e0b3e34d0 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/ClientEndpointResolutionTest.java @@ -0,0 +1,111 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services.endpointproviders; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.EndpointCapturingInterceptor; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClient; + +/** + * Tests that the generated client builder resolves a default endpoint when no override is configured, + * and correctly uses endpoint overrides from client configuration, system properties, and environment sources. + */ +public class ClientEndpointResolutionTest { + + private static final String SERVICE_SYSTEM_PROPERTY = "aws.endpointUrlRestJsonEndpointProviders"; + + @AfterEach + void cleanup() { + System.clearProperty(SERVICE_SYSTEM_PROPERTY); + } + + @Test + void clientBuild_withRegionOnly_resolvesEndpointViaEndpoints2() { + EndpointCapturingInterceptor interceptor = new EndpointCapturingInterceptor(); + + RestJsonEndpointProvidersClient client = RestJsonEndpointProvidersClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .overrideConfiguration(c -> c.addExecutionInterceptor(interceptor)) + .build(); + + try { + client.operationWithNoInputOrOutput(r -> {}); + } catch (EndpointCapturingInterceptor.CaptureCompletedException e) { + // Expected + } + + assertThat(interceptor.endpoints()) + .singleElement() + .asString() + .contains("us-west-2") + .contains("amazonaws.com"); + } + + @Test + void clientBuild_withSystemPropertyOverride_usesOverrideEndpoint() { + System.setProperty(SERVICE_SYSTEM_PROPERTY, "https://custom-override.example.com"); + + EndpointCapturingInterceptor interceptor = new EndpointCapturingInterceptor(); + + RestJsonEndpointProvidersClient client = RestJsonEndpointProvidersClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .overrideConfiguration(c -> c.addExecutionInterceptor(interceptor)) + .build(); + + try { + client.operationWithNoInputOrOutput(r -> {}); + } catch (EndpointCapturingInterceptor.CaptureCompletedException e) { + // Expected + } + + assertThat(interceptor.endpoints()) + .singleElement() + .asString() + .startsWith("https://custom-override.example.com"); + } + + @Test + void clientBuild_withEndpointOverride_usesClientOverride() { + EndpointCapturingInterceptor interceptor = new EndpointCapturingInterceptor(); + + RestJsonEndpointProvidersClient client = RestJsonEndpointProvidersClient.builder() + .region(Region.US_WEST_2) + .endpointOverride(URI.create("https://my-override.example.com")) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .overrideConfiguration(c -> c.addExecutionInterceptor(interceptor)) + .build(); + + try { + client.operationWithNoInputOrOutput(r -> {}); + } catch (EndpointCapturingInterceptor.CaptureCompletedException e) { + // Expected + } + + assertThat(interceptor.endpoints()) + .singleElement() + .asString() + .startsWith("https://my-override.example.com"); + } + +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/noregion/NoRegionSigningRegionTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/noregion/NoRegionSigningRegionTest.java new file mode 100644 index 000000000000..0f5c6ce6935f --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/noregion/NoRegionSigningRegionTest.java @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services.noregion; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.regions.Region; + +/** + * Verifies that signing region is correctly resolved at request time for a service + * without AWS_REGION in its endpoint params. The endpoint rules define signingRegion: "us-east-1" + * in the auth scheme, which should be applied at request time regardless of the client region. + */ +public class NoRegionSigningRegionTest { + + @Test + void signingRegion_resolvedFromEndpointRulesAtRequestTime() { + SigningRegionCapturingInterceptor interceptor = new SigningRegionCapturingInterceptor(); + + NoRegionClient client = NoRegionClient.builder() + .region(Region.US_WEST_2) // Client configured with us-west-2 + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))) + .overrideConfiguration(c -> c.addExecutionInterceptor(interceptor)) + .endpointOverride(java.net.URI.create("https://noregionservice.us-east-1.amazonaws.com")) + .build(); + + try { + client.someOperation(r -> r.stringMember("test")); + } catch (SigningRegionCapturingInterceptor.CaptureCompletedException e) { + // Expected — we throw after capturing + } + + // The signing region should come from the endpoint rules auth scheme (us-east-1), + // not the client region (us-west-2) + assertThat(interceptor.signingRegion).isNotNull(); + assertThat(interceptor.signingRegion.id()).isEqualTo("us-east-1"); + + client.close(); + } + + private static class SigningRegionCapturingInterceptor implements ExecutionInterceptor { + private Region signingRegion; + + @Override + public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) { + signingRegion = executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION); + throw new CaptureCompletedException(); + } + + static class CaptureCompletedException extends RuntimeException { + } + } +} diff --git a/test/old-client-version-compatibility-test/src/it/java/software/amazon/awssdk/services/oldclient/S3PutGetIntegrationTest.java b/test/old-client-version-compatibility-test/src/it/java/software/amazon/awssdk/services/oldclient/S3PutGetIntegrationTest.java deleted file mode 100644 index 30f742d7a54d..000000000000 --- a/test/old-client-version-compatibility-test/src/it/java/software/amazon/awssdk/services/oldclient/S3PutGetIntegrationTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.services.oldclient; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import java.util.UUID; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; -import software.amazon.awssdk.auth.signer.AwsS3V4Signer; -import software.amazon.awssdk.core.ResponseBytes; -import software.amazon.awssdk.core.async.AsyncRequestBody; -import software.amazon.awssdk.core.async.AsyncResponseTransformer; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.core.sync.RequestBody; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3AsyncClient; -import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.S3ClientBuilder; -import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; -import software.amazon.awssdk.services.s3.model.ChecksumMode; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.testutils.service.AwsTestBase; - -public class S3PutGetIntegrationTest extends AwsTestBase { - private static final S3Client S3 = - s3ClientBuilder().build(); - private static final S3Client S3_CUSTOM_SIGNER = - s3ClientBuilder().overrideConfiguration(c -> c.putAdvancedOption(SdkAdvancedClientOption.SIGNER, - AwsS3V4Signer.create())) - .build(); - private static final S3Client S3_NO_CREDS = - s3ClientBuilder().credentialsProvider(AnonymousCredentialsProvider.create()) - .build(); - - private static final S3AsyncClient S3_ASYNC = - getS3AsyncClientBuilder().build(); - private static final S3AsyncClient S3_ASYNC_CUSTOM_SIGNER = - getS3AsyncClientBuilder().overrideConfiguration(c -> c.putAdvancedOption(SdkAdvancedClientOption.SIGNER, - AwsS3V4Signer.create())) - .build(); - private static final S3AsyncClient S3_ASYNC_NO_CREDS = - getS3AsyncClientBuilder().credentialsProvider(AnonymousCredentialsProvider.create()) - .build(); - - private static final String BUCKET = "sra-get-put-integ-" + System.currentTimeMillis(); - private static final String BODY = "foo"; - private static final String BODY_CRC32 = "jHNlIQ=="; - - private String key; - - @BeforeAll - public static void setup() { - S3.createBucket(r -> r.bucket(BUCKET)); - } - - @BeforeEach - public void setupTest() { - key = UUID.randomUUID().toString(); - } - - @AfterAll - public static void teardown() { - TestUtils.deleteBucketAndAllContents(S3, BUCKET); - } - - @Test - public void putGet() { - S3.putObject(r -> r.bucket(BUCKET).key(key), RequestBody.fromString(BODY)); - assertThat(S3.getObjectAsBytes(r -> r.bucket(BUCKET).key(key)).asUtf8String()).isEqualTo(BODY); - } - - @Test - public void putGet_async() { - S3_ASYNC.putObject(r -> r.bucket(BUCKET).key(key), AsyncRequestBody.fromString(BODY)).join(); - assertThat(S3_ASYNC.getObject(r -> r.bucket(BUCKET).key(key), - AsyncResponseTransformer.toBytes()) - .join() - .asUtf8String()).isEqualTo(BODY); - } - - @Test - public void putGet_requestLevelCreds() { - S3_NO_CREDS.putObject(r -> r.bucket(BUCKET) - .key(key) - .overrideConfiguration(c -> c.credentialsProvider(CREDENTIALS_PROVIDER_CHAIN)), - RequestBody.fromString(BODY)); - assertThat(S3_NO_CREDS.getObjectAsBytes(r -> r.bucket(BUCKET) - .key(key) - .overrideConfiguration(c -> c.credentialsProvider(CREDENTIALS_PROVIDER_CHAIN))) - .asUtf8String()).isEqualTo(BODY); - } - - @Test - public void putGet_async_requestLevelCreds() { - S3_ASYNC_NO_CREDS.putObject(r -> r.bucket(BUCKET) - .key(key) - .overrideConfiguration(c -> c.credentialsProvider(CREDENTIALS_PROVIDER_CHAIN)), - AsyncRequestBody.fromString(BODY)) - .join(); - assertThat(S3_ASYNC_NO_CREDS.getObject(r -> r.bucket(BUCKET) - .key(key) - .overrideConfiguration(c -> c.credentialsProvider(CREDENTIALS_PROVIDER_CHAIN)), - AsyncResponseTransformer.toBytes()) - .join() - .asUtf8String()).isEqualTo(BODY); - } - - @Test - public void putGet_flexibleChecksums() { - S3.putObject(r -> r.bucket(BUCKET).key(key).checksumAlgorithm(ChecksumAlgorithm.CRC32), - RequestBody.fromString(BODY)); - ResponseBytes response = - S3.getObjectAsBytes(r -> r.bucket(BUCKET).key(key).checksumMode(ChecksumMode.ENABLED)); - assertThat(response.asUtf8String()).isEqualTo(BODY); - assertThat(response.response().checksumCRC32()).isEqualTo(BODY_CRC32); - } - - @Test - public void putGet_async_flexibleChecksums() { - S3_ASYNC.putObject(r -> r.bucket(BUCKET).key(key).checksumAlgorithm(ChecksumAlgorithm.CRC32), - AsyncRequestBody.fromString(BODY)).join(); - ResponseBytes response = S3_ASYNC.getObject(r -> r.bucket(BUCKET).key(key).checksumMode(ChecksumMode.ENABLED), - AsyncResponseTransformer.toBytes()) - .join(); - assertThat(response.asUtf8String()).isEqualTo(BODY); - assertThat(response.response().checksumCRC32()).isEqualTo(BODY_CRC32); - } - - @Test - public void putGet_customSigner_flexibleChecksums() { - S3_CUSTOM_SIGNER.putObject(r -> r.bucket(BUCKET).key(key).checksumAlgorithm(ChecksumAlgorithm.CRC32), - RequestBody.fromString(BODY)); - ResponseBytes response = - S3_CUSTOM_SIGNER.getObjectAsBytes(r -> r.bucket(BUCKET).key(key).checksumMode(ChecksumMode.ENABLED)); - assertThat(response.asUtf8String()).isEqualTo(BODY); - assertThat(response.response().checksumCRC32()).isEqualTo(BODY_CRC32); - } - - @Test - public void putGet_async_customSigner_flexibleChecksums() { - S3_ASYNC_CUSTOM_SIGNER.putObject(r -> r.bucket(BUCKET).key(key).checksumAlgorithm(ChecksumAlgorithm.CRC32), - AsyncRequestBody.fromString(BODY)) - .join(); - ResponseBytes response = - S3_ASYNC_CUSTOM_SIGNER.getObject(r -> r.bucket(BUCKET).key(key).checksumMode(ChecksumMode.ENABLED), - AsyncResponseTransformer.toBytes()) - .join(); - assertThat(response.asUtf8String()).isEqualTo(BODY); - assertThat(response.response().checksumCRC32()).isEqualTo(BODY_CRC32); - } - - private static S3ClientBuilder s3ClientBuilder() { - return S3Client.builder() - .region(Region.US_WEST_2) - .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN); - } - - private static S3AsyncClientBuilder getS3AsyncClientBuilder() { - return S3AsyncClient.builder() - .region(Region.US_WEST_2) - .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN); - } -} diff --git a/test/old-client-version-compatibility-test/src/it/java/software/amazon/awssdk/services/oldclient/TestUtils.java b/test/old-client-version-compatibility-test/src/it/java/software/amazon/awssdk/services/oldclient/TestUtils.java deleted file mode 100644 index 6a1bb1e4cec7..000000000000 --- a/test/old-client-version-compatibility-test/src/it/java/software/amazon/awssdk/services/oldclient/TestUtils.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.services.oldclient; - -import java.util.Iterator; -import java.util.List; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest; -import software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse; -import software.amazon.awssdk.services.s3.model.ListObjectsRequest; -import software.amazon.awssdk.services.s3.model.ListObjectsResponse; -import software.amazon.awssdk.services.s3.model.NoSuchBucketException; -import software.amazon.awssdk.services.s3.model.S3Object; -import software.amazon.awssdk.testutils.Waiter; - -public class TestUtils { - - public static void deleteBucketAndAllContents(S3Client s3, String bucketName) { - try { - System.out.println("Deleting S3 bucket: " + bucketName); - ListObjectsResponse response = Waiter.run(() -> s3.listObjects(r -> r.bucket(bucketName))) - .ignoringException(NoSuchBucketException.class) - .orFail(); - List objectListing = response.contents(); - - if (objectListing != null) { - while (true) { - for (Iterator iterator = objectListing.iterator(); iterator.hasNext(); ) { - S3Object objectSummary = (S3Object) iterator.next(); - s3.deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(objectSummary.key()).build()); - } - - if (response.isTruncated()) { - objectListing = s3.listObjects(ListObjectsRequest.builder() - .bucket(bucketName) - .marker(response.marker()) - .build()) - .contents(); - } else { - break; - } - } - } - - - ListObjectVersionsResponse versions = s3 - .listObjectVersions(ListObjectVersionsRequest.builder().bucket(bucketName).build()); - - if (versions.deleteMarkers() != null) { - versions.deleteMarkers().forEach(v -> s3.deleteObject(DeleteObjectRequest.builder() - .versionId(v.versionId()) - .bucket(bucketName) - .key(v.key()) - .build())); - } - - if (versions.versions() != null) { - versions.versions().forEach(v -> s3.deleteObject(DeleteObjectRequest.builder() - .versionId(v.versionId()) - .bucket(bucketName) - .key(v.key()) - .build())); - } - - s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucketName).build()); - } catch (Exception e) { - System.err.println("Failed to delete bucket: " + bucketName); - e.printStackTrace(); - } - } -} diff --git a/test/old-client-version-compatibility-test/src/it/resources/log4j2.properties b/test/old-client-version-compatibility-test/src/it/resources/log4j2.properties deleted file mode 100644 index e5e68dd2faa4..000000000000 --- a/test/old-client-version-compatibility-test/src/it/resources/log4j2.properties +++ /dev/null @@ -1,38 +0,0 @@ -# -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# -# http://aws.amazon.com/apache2.0 -# -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. -# - -status = warn - -appender.console.type = Console -appender.console.name = ConsoleAppender -appender.console.layout.type = PatternLayout -appender.console.layout.pattern = %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n%throwable - -rootLogger.level = info -rootLogger.appenderRef.stdout.ref = ConsoleAppender - -# Uncomment below to enable more specific logging -# -#logger.sdk.name = software.amazon.awssdk -#logger.sdk.level = debug -# -#logger.request.name = software.amazon.awssdk.request -#logger.request.level = debug -# -#logger.apache.name = org.apache.http.wire -#logger.apache.level = debug -# -#logger.netty.name = io.netty.handler.logging -#logger.netty.level = debug diff --git a/test/region-testing/pom.xml b/test/region-testing/pom.xml deleted file mode 100644 index 8bc9d3d4571f..000000000000 --- a/test/region-testing/pom.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - aws-sdk-java-pom - software.amazon.awssdk - 2.51.1-SNAPSHOT - ../../pom.xml - - 4.0.0 - - region-testing - AWS Java SDK :: Test :: Region Testing - Test package for testing the regions module against a fake endpoints.json file. - - - - software.amazon.awssdk - bom-internal - ${project.version} - pom - import - - - - - - - software.amazon.awssdk - regions - ${awsjavasdk.version} - - - software.amazon.awssdk - utils - ${awsjavasdk.version} - - - software.amazon.awssdk - annotations - ${awsjavasdk.version} - - - org.assertj - assertj-core - test - - - org.junit.jupiter - junit-jupiter - test - - - org.junit.vintage - junit-vintage-engine - test - - - org.apache.logging.log4j - log4j-api - test - - - org.apache.logging.log4j - log4j-core - test - - - org.apache.logging.log4j - log4j-slf4j-impl - test - - - - - - - software.amazon.awssdk - codegen-lite-maven-plugin - ${awsjavasdk.version} - - - generate-sources - - generate-regions - - - ${basedir}/src/test/resources/variants-test-endpoints.json - ${basedir}/src/test/resources/variants-test-partitions.json - - - - - - - - diff --git a/test/region-testing/src/test/java/software/amazon/awssdk/regiontesting/EndpointVariantsTest.java b/test/region-testing/src/test/java/software/amazon/awssdk/regiontesting/EndpointVariantsTest.java deleted file mode 100644 index 477fc8ba26a9..000000000000 --- a/test/region-testing/src/test/java/software/amazon/awssdk/regiontesting/EndpointVariantsTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.regiontesting; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static software.amazon.awssdk.regions.EndpointTag.DUALSTACK; -import static software.amazon.awssdk.regions.EndpointTag.FIPS; - -import java.net.URI; -import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import software.amazon.awssdk.regions.EndpointTag; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.regions.ServiceEndpointKey; -import software.amazon.awssdk.regions.ServiceMetadata; -import software.amazon.awssdk.utils.Validate; - -@RunWith(Parameterized.class) -public class EndpointVariantsTest { - @Parameterized.Parameter - public TestCase testCase; - - @Test - public void resolvesCorrectEndpoint() { - if (testCase instanceof SuccessCase) { - URI endpoint = ServiceMetadata.of(testCase.service).endpointFor(testCase.endpointKey); - assertThat(endpoint).isEqualTo(((SuccessCase) testCase).endpoint); - } else { - FailureCase failureCase = Validate.isInstanceOf(FailureCase.class, testCase, "Unknown case type %s.", getClass()); - assertThatThrownBy(() -> ServiceMetadata.of(testCase.service).endpointFor(testCase.endpointKey)) - .hasMessageContaining(failureCase.exceptionMessage); - } - } - - @Parameterized.Parameters(name = "{0}") - public static Iterable testCases() { - return Arrays.asList(new SuccessCase("default-pattern-service", "us-west-2", "default-pattern-service.us-west-2.amazonaws.com"), - new SuccessCase("default-pattern-service", "us-west-2", "default-pattern-service-fips.us-west-2.amazonaws.com", FIPS), - new SuccessCase("default-pattern-service", "af-south-1", "default-pattern-service.af-south-1.amazonaws.com"), - new SuccessCase("default-pattern-service", "af-south-1", "default-pattern-service-fips.af-south-1.amazonaws.com", FIPS), - new SuccessCase("global-service", "aws-global", "global-service.amazonaws.com"), - new SuccessCase("global-service", "aws-global", "global-service-fips.amazonaws.com", FIPS), - new SuccessCase("override-variant-service", "us-west-2", "override-variant-service.us-west-2.amazonaws.com"), - new SuccessCase("override-variant-service", "us-west-2", "fips.override-variant-service.us-west-2.new.dns.suffix", FIPS), - new SuccessCase("override-variant-service", "af-south-1", "override-variant-service.af-south-1.amazonaws.com"), - new SuccessCase("override-variant-service", "af-south-1", "fips.override-variant-service.af-south-1.new.dns.suffix", FIPS), - new SuccessCase("override-variant-dns-suffix-service", "us-west-2", "override-variant-dns-suffix-service.us-west-2.amazonaws.com"), - new SuccessCase("override-variant-dns-suffix-service", "us-west-2", "override-variant-dns-suffix-service-fips.us-west-2.new.dns.suffix", FIPS), - new SuccessCase("override-variant-dns-suffix-service", "af-south-1", "override-variant-dns-suffix-service.af-south-1.amazonaws.com"), - new SuccessCase("override-variant-dns-suffix-service", "af-south-1", "override-variant-dns-suffix-service-fips.af-south-1.new.dns.suffix", FIPS), - new SuccessCase("override-variant-hostname-service", "us-west-2", "override-variant-hostname-service.us-west-2.amazonaws.com"), - new SuccessCase("override-variant-hostname-service", "us-west-2", "fips.override-variant-hostname-service.us-west-2.amazonaws.com", FIPS), - new SuccessCase("override-variant-hostname-service", "af-south-1", "override-variant-hostname-service.af-south-1.amazonaws.com"), - new SuccessCase("override-variant-hostname-service", "af-south-1", "fips.override-variant-hostname-service.af-south-1.amazonaws.com", FIPS), - new SuccessCase("override-endpoint-variant-service", "us-west-2", "override-endpoint-variant-service.us-west-2.amazonaws.com"), - new SuccessCase("override-endpoint-variant-service", "us-west-2", "fips.override-endpoint-variant-service.us-west-2.amazonaws.com", FIPS), - new SuccessCase("override-endpoint-variant-service", "af-south-1", "override-endpoint-variant-service.af-south-1.amazonaws.com"), - new SuccessCase("override-endpoint-variant-service", "af-south-1", "override-endpoint-variant-service-fips.af-south-1.amazonaws.com", FIPS), - new SuccessCase("default-pattern-service", "us-west-2", "default-pattern-service.us-west-2.amazonaws.com"), - new SuccessCase("default-pattern-service", "us-west-2", "default-pattern-service.us-west-2.api.aws", DUALSTACK), - new SuccessCase("default-pattern-service", "af-south-1", "default-pattern-service.af-south-1.amazonaws.com"), - new SuccessCase("default-pattern-service", "af-south-1", "default-pattern-service.af-south-1.api.aws", DUALSTACK), - new SuccessCase("global-service", "aws-global", "global-service.amazonaws.com"), - new SuccessCase("global-service", "aws-global", "global-service.api.aws", DUALSTACK), - new SuccessCase("override-variant-service", "us-west-2", "override-variant-service.us-west-2.amazonaws.com"), - new SuccessCase("override-variant-service", "us-west-2", "override-variant-service.dualstack.us-west-2.new.dns.suffix", DUALSTACK), - new SuccessCase("override-variant-service", "af-south-1", "override-variant-service.af-south-1.amazonaws.com"), - new SuccessCase("override-variant-service", "af-south-1", "override-variant-service.dualstack.af-south-1.new.dns.suffix", DUALSTACK), - new SuccessCase("override-variant-dns-suffix-service", "us-west-2", "override-variant-dns-suffix-service.us-west-2.amazonaws.com"), - new SuccessCase("override-variant-dns-suffix-service", "us-west-2", "override-variant-dns-suffix-service.us-west-2.new.dns.suffix", DUALSTACK), - new SuccessCase("override-variant-dns-suffix-service", "af-south-1", "override-variant-dns-suffix-service.af-south-1.amazonaws.com"), - new SuccessCase("override-variant-dns-suffix-service", "af-south-1", "override-variant-dns-suffix-service.af-south-1.new.dns.suffix", DUALSTACK), - new SuccessCase("override-variant-hostname-service", "us-west-2", "override-variant-hostname-service.us-west-2.amazonaws.com"), - new SuccessCase("override-variant-hostname-service", "us-west-2", "override-variant-hostname-service.dualstack.us-west-2.api.aws", DUALSTACK), - new SuccessCase("override-variant-hostname-service", "af-south-1", "override-variant-hostname-service.af-south-1.amazonaws.com"), - new SuccessCase("override-variant-hostname-service", "af-south-1", "override-variant-hostname-service.dualstack.af-south-1.api.aws", DUALSTACK), - new SuccessCase("override-endpoint-variant-service", "us-west-2", "override-endpoint-variant-service.us-west-2.amazonaws.com"), - new SuccessCase("override-endpoint-variant-service", "us-west-2", "override-endpoint-variant-service.dualstack.us-west-2.amazonaws.com", DUALSTACK), - new SuccessCase("override-endpoint-variant-service", "af-south-1", "override-endpoint-variant-service.af-south-1.amazonaws.com"), - new SuccessCase("override-endpoint-variant-service", "af-south-1", "override-endpoint-variant-service.af-south-1.api.aws", DUALSTACK), - new SuccessCase("multi-variant-service", "us-west-2", "multi-variant-service.us-west-2.amazonaws.com"), - new SuccessCase("multi-variant-service", "us-west-2", "multi-variant-service.dualstack.us-west-2.api.aws", DUALSTACK), - new SuccessCase("multi-variant-service", "us-west-2", "fips.multi-variant-service.us-west-2.amazonaws.com", FIPS), - new SuccessCase("multi-variant-service", "us-west-2", "fips.multi-variant-service.dualstack.us-west-2.new.dns.suffix", FIPS, DUALSTACK), - new SuccessCase("multi-variant-service", "af-south-1", "multi-variant-service.af-south-1.amazonaws.com"), - new SuccessCase("multi-variant-service", "af-south-1", "multi-variant-service.dualstack.af-south-1.api.aws", DUALSTACK), - new SuccessCase("multi-variant-service", "af-south-1", "fips.multi-variant-service.af-south-1.amazonaws.com", FIPS), - new SuccessCase("multi-variant-service", "af-south-1", "fips.multi-variant-service.dualstack.af-south-1.new.dns.suffix", FIPS, DUALSTACK), - new FailureCase("some-service", "us-iso-east-1", "No endpoint known for [dualstack] in us-iso-east-1", DUALSTACK), - new FailureCase("some-service", "us-iso-east-1", "No endpoint known for [fips] in us-iso-east-1", FIPS), - new FailureCase("some-service", "us-iso-east-1", "No endpoint known for [fips, dualstack] in us-iso-east-1", FIPS, DUALSTACK), - - // These test case outcomes deviate from the other SDKs, because we ignore "global" services when a - // non-global region is specified. We might consider changing this behavior, since the storm of - // global services becoming regionalized is dying down. - new SuccessCase("global-service", "foo", "global-service.foo.amazonaws.com"), - new SuccessCase("global-service", "foo", "global-service-fips.foo.amazonaws.com", FIPS), - new SuccessCase("global-service", "foo", "global-service.foo.amazonaws.com"), - new SuccessCase("global-service", "foo", "global-service.foo.api.aws", DUALSTACK)); - } - - public static class TestCase { - private final String service; - private final ServiceEndpointKey endpointKey; - - public TestCase(String service, String region, EndpointTag... tags) { - this.service = service; - this.endpointKey = ServiceEndpointKey.builder().region(Region.of(region)).tags(tags).build(); - } - } - - public static class SuccessCase extends TestCase { - private final URI endpoint; - - public SuccessCase(String service, String region, String endpoint, EndpointTag... tags) { - super(service, region, tags); - this.endpoint = URI.create(endpoint); - } - - @Override - public String toString() { - return "Positive Case: " + endpoint.toString(); - } - } - - private static class FailureCase extends TestCase { - private final String exceptionMessage; - - public FailureCase(String service, String region, String exceptionMessage, EndpointTag... tags) { - super(service, region, tags); - this.exceptionMessage = exceptionMessage; - } - - @Override - public String toString() { - return "Negative Case: " + exceptionMessage; - } - } -} diff --git a/test/region-testing/src/test/resources/variants-test-endpoints.json b/test/region-testing/src/test/resources/variants-test-endpoints.json deleted file mode 100644 index bf05ed7db3f4..000000000000 --- a/test/region-testing/src/test/resources/variants-test-endpoints.json +++ /dev/null @@ -1,239 +0,0 @@ -{ - "partitions" : [ - { - "defaults" : { - "hostname" : "{service}.{region}.{dnsSuffix}", - "protocols" : [ "https" ], - "signatureVersions" : [ "v4" ], - "variants" : [ - { - "dnsSuffix" : "amazonaws.com", - "hostname" : "{service}-fips.{region}.{dnsSuffix}", - "tags" : [ "fips" ] - }, - { - "dnsSuffix" : "api.aws", - "hostname" : "{service}.{region}.{dnsSuffix}", - "tags" : [ "dualstack" ] - }, - { - "dnsSuffix" : "api.aws", - "hostname" : "{service}-fips.{region}.{dnsSuffix}", - "tags" : [ "dualstack", "fips" ] - } - ] - }, - "dnsSuffix" : "amazonaws.com", - "partition" : "aws", - "regionRegex" : "^(us|eu|ap|sa|ca|me|af)\\-\\w+\\-\\d+$", - "regions" : { - "af-south-1" : { - "description" : "Africa (Cape Town)" - }, - "us-west-2" : { - "description" : "US West (Oregon)" - } - }, - "services" : { - "default-pattern-service" : { - "endpoints" : { - "af-south-1" : { }, - "us-west-2" : { - "variants" : [ - { - "hostname" : "default-pattern-service-fips.us-west-2.amazonaws.com", - "tags" : [ "fips" ] - }, - { - "hostname" : "default-pattern-service.us-west-2.api.aws", - "tags" : [ "dualstack" ] - } - ] - } - } - }, - "global-service" : { - "endpoints" : { - "aws-global" : { - "credentialScope" : { - "region" : "us-east-1" - }, - "hostname" : "global-service.amazonaws.com", - "variants" : [ - { - "hostname" : "global-service-fips.amazonaws.com", - "tags" : [ "fips" ] - }, - { - "hostname" : "global-service.api.aws", - "tags" : [ "dualstack" ] - } - ] - } - }, - "isRegionalized" : false, - "partitionEndpoint" : "aws-global" - }, - "override-variant-service" : { - "defaults" : { - "variants" : [ - { - "hostname" : "fips.{service}.{region}.{dnsSuffix}", - "dnsSuffix" : "new.dns.suffix", - "tags" : [ "fips" ] - }, - { - "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", - "dnsSuffix" : "new.dns.suffix", - "tags" : [ "dualstack" ] - } - ] - }, - "endpoints" : { - "af-south-1" : { }, - "us-west-2" : { - "variants" : [ - { - "hostname" : "fips.override-variant-service.us-west-2.new.dns.suffix", - "tags" : [ "fips" ] - }, - { - "hostname" : "override-variant-service.dualstack.us-west-2.new.dns.suffix", - "tags" : [ "dualstack" ] - } - ] - } - } - }, - "override-variant-dns-suffix-service" : { - "defaults" : { - "variants" : [ - { - "dnsSuffix" : "new.dns.suffix", - "tags" : [ "fips" ] - }, - { - "dnsSuffix" : "new.dns.suffix", - "tags" : [ "dualstack" ] - } - ] - }, - "endpoints" : { - "af-south-1" : { }, - "us-west-2" : { - "variants" : [ - { - "hostname" : "override-variant-dns-suffix-service-fips.us-west-2.new.dns.suffix", - "tags" : [ "fips" ] - }, - { - "hostname" : "override-variant-dns-suffix-service.us-west-2.new.dns.suffix", - "tags" : [ "dualstack" ] - } - ] - } - } - }, - "override-variant-hostname-service" : { - "defaults" : { - "variants" : [ - { - "hostname" : "fips.{service}.{region}.{dnsSuffix}", - "tags" : [ "fips" ] - }, - { - "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", - "tags" : [ "dualstack" ] - } - ] - }, - "endpoints" : { - "af-south-1" : { }, - "us-west-2" : { - "variants" : [ - { - "hostname" : "fips.override-variant-hostname-service.us-west-2.amazonaws.com", - "tags" : [ "fips" ] - }, - { - "hostname" : "override-variant-hostname-service.dualstack.us-west-2.api.aws", - "tags" : [ "dualstack" ] - } - ] - } - } - }, - "override-endpoint-variant-service" : { - "endpoints" : { - "af-south-1" : { }, - "us-west-2" : { - "variants" : [ - { - "hostname" : "fips.override-endpoint-variant-service.us-west-2.amazonaws.com", - "tags" : [ "fips" ] - }, - { - "hostname" : "override-endpoint-variant-service.dualstack.us-west-2.amazonaws.com", - "tags" : [ "dualstack" ] - } - ] - } - } - }, - "multi-variant-service" : { - "defaults" : { - "variants" : [ - { - "hostname" : "fips.{service}.{region}.{dnsSuffix}", - "tags" : [ "fips" ] - }, - { - "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", - "tags" : [ "dualstack" ] - }, - { - "dnsSuffix" : "new.dns.suffix", - "hostname" : "fips.{service}.dualstack.{region}.{dnsSuffix}", - "tags" : [ "fips", "dualstack" ] - } - ] - }, - "endpoints" : { - "af-south-1" : { }, - "us-west-2" : { - "variants" : [ - { - "hostname" : "fips.multi-variant-service.dualstack.us-west-2.new.dns.suffix", - "tags" : [ "fips", "dualstack" ] - } - ] - } - } - } - } - }, - { - "defaults": { - "hostname": "{service}.{region}.{dnsSuffix}", - "protocols": ["https"], - "signatureVersions": ["v4"] - }, - "dnsSuffix": "c2s.ic.gov", - "partition": "aws-iso", - "regionRegex": "^us\\-iso\\-\\w+\\-\\d+$", - "regions": { - "us-iso-east-1": { - "description": "US ISO East" - } - }, - "services": { - "some-service": { - "endpoints" : { - "us-iso-east-1": {} - } - } - } - } - ], - "version": 3 -} diff --git a/test/region-testing/src/test/resources/variants-test-partitions.json b/test/region-testing/src/test/resources/variants-test-partitions.json deleted file mode 100644 index 398403e43c1a..000000000000 --- a/test/region-testing/src/test/resources/variants-test-partitions.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "partitions": [ - { - "id": "aws", - "outputs": { - "dnsSuffix": "amazonaws.com", - "dualStackDnsSuffix": "api.aws", - "implicitGlobalRegion": "us-east-1", - "name": "aws", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$", - "regions": { - "af-south-1": { - "description": "Africa (Cape Town)" - }, - "ap-east-1": { - "description": "Asia Pacific (Hong Kong)" - }, - "ap-east-2": { - "description": "Asia Pacific (Taipei)" - }, - "ap-northeast-1": { - "description": "Asia Pacific (Tokyo)" - }, - "ap-northeast-2": { - "description": "Asia Pacific (Seoul)" - }, - "ap-northeast-3": { - "description": "Asia Pacific (Osaka)" - }, - "ap-south-1": { - "description": "Asia Pacific (Mumbai)" - }, - "ap-south-2": { - "description": "Asia Pacific (Hyderabad)" - }, - "ap-southeast-1": { - "description": "Asia Pacific (Singapore)" - }, - "ap-southeast-2": { - "description": "Asia Pacific (Sydney)" - }, - "ap-southeast-3": { - "description": "Asia Pacific (Jakarta)" - }, - "ap-southeast-4": { - "description": "Asia Pacific (Melbourne)" - }, - "ap-southeast-5": { - "description": "Asia Pacific (Malaysia)" - }, - "ap-southeast-6": { - "description": "Asia Pacific (New Zealand)" - }, - "ap-southeast-7": { - "description": "Asia Pacific (Thailand)" - }, - "aws-global": { - "description": "aws global region" - }, - "ca-central-1": { - "description": "Canada (Central)" - }, - "ca-west-1": { - "description": "Canada West (Calgary)" - }, - "eu-central-1": { - "description": "Europe (Frankfurt)" - }, - "eu-central-2": { - "description": "Europe (Zurich)" - }, - "eu-north-1": { - "description": "Europe (Stockholm)" - }, - "eu-south-1": { - "description": "Europe (Milan)" - }, - "eu-south-2": { - "description": "Europe (Spain)" - }, - "eu-west-1": { - "description": "Europe (Ireland)" - }, - "eu-west-2": { - "description": "Europe (London)" - }, - "eu-west-3": { - "description": "Europe (Paris)" - }, - "il-central-1": { - "description": "Israel (Tel Aviv)" - }, - "me-central-1": { - "description": "Middle East (UAE)" - }, - "me-south-1": { - "description": "Middle East (Bahrain)" - }, - "mx-central-1": { - "description": "Mexico (Central)" - }, - "sa-east-1": { - "description": "South America (Sao Paulo)" - }, - "us-east-1": { - "description": "US East (N. Virginia)" - }, - "us-east-2": { - "description": "US East (Ohio)" - }, - "us-west-1": { - "description": "US West (N. California)" - }, - "us-west-2": { - "description": "US West (Oregon)" - } - } - }, - { - "id": "aws-cn", - "outputs": { - "dnsSuffix": "amazonaws.com.cn", - "dualStackDnsSuffix": "api.amazonwebservices.com.cn", - "implicitGlobalRegion": "cn-northwest-1", - "name": "aws-cn", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^cn\\-\\w+\\-\\d+$", - "regions": { - "aws-cn-global": { - "description": "aws-cn global region" - }, - "cn-north-1": { - "description": "China (Beijing)" - }, - "cn-northwest-1": { - "description": "China (Ningxia)" - } - } - }, - { - "id": "aws-eusc", - "outputs": { - "dnsSuffix": "amazonaws.eu", - "dualStackDnsSuffix": "api.amazonwebservices.eu", - "implicitGlobalRegion": "eusc-de-east-1", - "name": "aws-eusc", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^eusc\\-(de)\\-\\w+\\-\\d+$", - "regions": { - "eusc-de-east-1": { - "description": "EU (Germany)" - } - } - }, - { - "id": "aws-iso", - "outputs": { - "dnsSuffix": "c2s.ic.gov", - "implicitGlobalRegion": "us-iso-east-1", - "name": "aws-iso" - }, - "regionRegex": "^us\\-iso\\-\\w+\\-\\d+$", - "regions": { - "aws-iso-global": { - "description": "aws-iso global region" - }, - "us-iso-east-1": { - "description": "US ISO East" - }, - "us-iso-west-1": { - "description": "US ISO WEST" - } - } - }, - { - "id": "aws-iso-b", - "outputs": { - "dnsSuffix": "sc2s.sgov.gov", - "dualStackDnsSuffix": "api.aws.scloud", - "implicitGlobalRegion": "us-isob-east-1", - "name": "aws-iso-b", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^us\\-isob\\-\\w+\\-\\d+$", - "regions": { - "aws-iso-b-global": { - "description": "aws-iso-b global region" - }, - "us-isob-east-1": { - "description": "US ISOB East (Ohio)" - }, - "us-isob-west-1": { - "description": "US ISOB West" - } - } - }, - { - "id": "aws-iso-e", - "outputs": { - "dnsSuffix": "cloud.adc-e.uk", - "dualStackDnsSuffix": "api.cloud-aws.adc-e.uk", - "implicitGlobalRegion": "eu-isoe-west-1", - "name": "aws-iso-e", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^eu\\-isoe\\-\\w+\\-\\d+$", - "regions": { - "aws-iso-e-global": { - "description": "aws-iso-e global region" - }, - "eu-isoe-west-1": { - "description": "EU ISOE West" - } - } - }, - { - "id": "aws-iso-f", - "outputs": { - "dnsSuffix": "csp.hci.ic.gov", - "dualStackDnsSuffix": "api.aws.hci.ic.gov", - "implicitGlobalRegion": "us-isof-south-1", - "name": "aws-iso-f", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^us\\-isof\\-\\w+\\-\\d+$", - "regions": { - "aws-iso-f-global": { - "description": "aws-iso-f global region" - }, - "us-isof-east-1": { - "description": "US ISOF EAST" - }, - "us-isof-south-1": { - "description": "US ISOF SOUTH" - } - } - }, - { - "id": "aws-us-gov", - "outputs": { - "dnsSuffix": "amazonaws.com", - "dualStackDnsSuffix": "api.aws", - "implicitGlobalRegion": "us-gov-west-1", - "name": "aws-us-gov", - "supportsDualStack": true, - "supportsFIPS": true - }, - "regionRegex": "^us\\-gov\\-\\w+\\-\\d+$", - "regions": { - "aws-us-gov-global": { - "description": "aws-us-gov global region" - }, - "us-gov-east-1": { - "description": "AWS GovCloud (US-East)" - }, - "us-gov-west-1": { - "description": "AWS GovCloud (US-West)" - } - } - } - ], - "version": "1.1" -} diff --git a/test/old-client-version-compatibility-test/pom.xml b/test/service-client-backward-compat-test/pom.xml similarity index 78% rename from test/old-client-version-compatibility-test/pom.xml rename to test/service-client-backward-compat-test/pom.xml index f90001561574..823f3e756a6d 100644 --- a/test/old-client-version-compatibility-test/pom.xml +++ b/test/service-client-backward-compat-test/pom.xml @@ -25,10 +25,11 @@ ../../pom.xml - old-client-version-compatibility-test - AWS Java SDK :: Test :: Old Client Version Compatibility Test + service-client-backward-compat-test + AWS Java SDK :: Test :: Service Client Backward Compatibility Test - Tests for old client versions with the latest "core" package versions. + Tests that verify older service client versions (>= 2.28.1) work correctly with the latest core modules. + This ensures backward compatibility for the new core + old service client version skew scenario. https://aws.amazon.com/sdkforjava @@ -79,7 +80,7 @@ ${awsjavasdk.version} - + software.amazon.awssdk aws-xml-protocol @@ -99,54 +100,29 @@ runtime - + software.amazon.awssdk s3 - 2.20.136 + 2.28.1 software.amazon.awssdk - service-test-utils - ${project.version} - - - org.junit.jupiter - junit-jupiter - test - - - com.github.tomakehurst - wiremock-jre8-standalone - test - - - org.apache.logging.log4j - log4j-api - test - - - org.apache.logging.log4j - log4j-core - test - - - org.apache.logging.log4j - log4j-slf4j-impl + test-utils test - org.mockito - mockito-core + software.amazon.awssdk + service-test-utils + ${project.version} test - org.slf4j - jcl-over-slf4j + org.junit.jupiter + junit-jupiter test - ${slf4j.version} org.assertj @@ -154,8 +130,8 @@ test - software.amazon.awssdk - test-utils + org.mockito + mockito-core test diff --git a/test/old-client-version-compatibility-test/src/test/java/InterceptorSignerAttributeTest.java b/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/InterceptorSignerAttributeTest.java similarity index 76% rename from test/old-client-version-compatibility-test/src/test/java/InterceptorSignerAttributeTest.java rename to test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/InterceptorSignerAttributeTest.java index 58b525e89528..d014700c9a0b 100644 --- a/test/old-client-version-compatibility-test/src/test/java/InterceptorSignerAttributeTest.java +++ b/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/InterceptorSignerAttributeTest.java @@ -13,12 +13,13 @@ * permissions and limitations under the License. */ +package software.amazon.awssdk.services.backwardcompat; + import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -40,18 +41,6 @@ import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; -import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner; -import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; -import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; -import software.amazon.awssdk.http.auth.spi.signer.AsyncSignRequest; -import software.amazon.awssdk.http.auth.spi.signer.AsyncSignedRequest; -import software.amazon.awssdk.http.auth.spi.signer.HttpSigner; -import software.amazon.awssdk.http.auth.spi.signer.SignRequest; -import software.amazon.awssdk.http.auth.spi.signer.SignedRequest; -import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; -import software.amazon.awssdk.identity.spi.IdentityProvider; -import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; @@ -76,10 +65,10 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes attributeModifications.accept(executionAttributes); } }, - AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, // Endpoint rules override signing name - AwsSignerExecutionAttribute.SIGNING_REGION, // Endpoint rules override signing region - AwsSignerExecutionAttribute.AWS_CREDENTIALS, // Legacy auth strategy overrides credentials - AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE); // Endpoint rules override double-url-encode + AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, + AwsSignerExecutionAttribute.SIGNING_REGION, + AwsSignerExecutionAttribute.AWS_CREDENTIALS, + AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE); } @Test @@ -91,7 +80,7 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut return context.request(); } }, - AwsSignerExecutionAttribute.AWS_CREDENTIALS); // Legacy auth strategy overrides credentials + AwsSignerExecutionAttribute.AWS_CREDENTIALS); } @Test @@ -145,24 +134,19 @@ private void test(Function, ExecutionInterceptor> MockAsyncHttpClient asyncHttpClient = new MockAsyncHttpClient()) { stub200Responses(httpClient, asyncHttpClient); - S3ClientBuilder s3Builder = createS3Builder(configBuilder, httpClient); - S3AsyncClientBuilder s3AsyncBuilder = createS3AsyncBuilder(configBuilder, asyncHttpClient); - CapturingSigner signer1 = new CapturingSigner(); - try (S3Client s3 = s3Builder.overrideConfiguration(configBuilder.putAdvancedOption(SdkAdvancedClientOption.SIGNER, - signer1) - .build()) - .build()) { + try (S3Client s3 = createS3Builder(configBuilder, httpClient) + .overrideConfiguration(configBuilder.putAdvancedOption(SdkAdvancedClientOption.SIGNER, signer1).build()) + .build()) { callS3(s3); validateLegacySignRequest(attributesToExclude, signer1); } CapturingSigner signer2 = new CapturingSigner(); - try (S3AsyncClient s3 = - s3AsyncBuilder.overrideConfiguration(configBuilder.putAdvancedOption(SdkAdvancedClientOption.SIGNER, signer2) - .build()) - .build()) { - callS3(s3); + try (S3AsyncClient s3 = createS3AsyncBuilder(configBuilder, asyncHttpClient) + .overrideConfiguration(configBuilder.putAdvancedOption(SdkAdvancedClientOption.SIGNER, signer2).build()) + .build()) { + callS3Async(s3); validateLegacySignRequest(attributesToExclude, signer2); } } @@ -171,15 +155,14 @@ private void test(Function, ExecutionInterceptor> private static void stub200Responses(MockSyncHttpClient httpClient, MockAsyncHttpClient asyncHttpClient) { HttpExecuteResponse response = HttpExecuteResponse.builder() - .response(SdkHttpResponse.builder() - .statusCode(200) - .build()) + .response(SdkHttpResponse.builder().statusCode(200).build()) .build(); httpClient.stubResponses(response); asyncHttpClient.stubResponses(response); } - private static S3ClientBuilder createS3Builder(ClientOverrideConfiguration.Builder configBuilder, MockSyncHttpClient httpClient) { + private static S3ClientBuilder createS3Builder(ClientOverrideConfiguration.Builder configBuilder, + MockSyncHttpClient httpClient) { return S3Client.builder() .region(Region.US_WEST_2) .credentialsProvider(AnonymousCredentialsProvider.create()) @@ -187,7 +170,8 @@ private static S3ClientBuilder createS3Builder(ClientOverrideConfiguration.Build .overrideConfiguration(configBuilder.build()); } - private static S3AsyncClientBuilder createS3AsyncBuilder(ClientOverrideConfiguration.Builder configBuilder, MockAsyncHttpClient asyncHttpClient) { + private static S3AsyncClientBuilder createS3AsyncBuilder(ClientOverrideConfiguration.Builder configBuilder, + MockAsyncHttpClient asyncHttpClient) { return S3AsyncClient.builder() .region(Region.US_WEST_2) .credentialsProvider(AnonymousCredentialsProvider.create()) @@ -196,18 +180,13 @@ private static S3AsyncClientBuilder createS3AsyncBuilder(ClientOverrideConfigura } private static void callS3(S3Client s3) { - s3.putObject(r -> r.bucket("foo") - .key("bar") - .checksumAlgorithm(ChecksumAlgorithm.CRC32), + s3.putObject(r -> r.bucket("foo").key("bar").checksumAlgorithm(ChecksumAlgorithm.CRC32), RequestBody.fromString("text")); } - private void callS3(S3AsyncClient s3) { - s3.putObject(r -> r.bucket("foo") - .key("bar") - .checksumAlgorithm(ChecksumAlgorithm.CRC32), - AsyncRequestBody.fromString("text")) - .join(); + private void callS3Async(S3AsyncClient s3) { + s3.putObject(r -> r.bucket("foo").key("bar").checksumAlgorithm(ChecksumAlgorithm.CRC32), + AsyncRequestBody.fromString("text")).join(); } private void validateLegacySignRequest(Set> attributesToExclude, CapturingSigner signer) { diff --git a/test/old-client-version-compatibility-test/src/test/java/S3AnonymousTest.java b/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/S3AnonymousTest.java similarity index 98% rename from test/old-client-version-compatibility-test/src/test/java/S3AnonymousTest.java rename to test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/S3AnonymousTest.java index 72e6baba808a..bab85cf6f89f 100644 --- a/test/old-client-version-compatibility-test/src/test/java/S3AnonymousTest.java +++ b/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/S3AnonymousTest.java @@ -13,6 +13,8 @@ * permissions and limitations under the License. */ +package software.amazon.awssdk.services.backwardcompat; + import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.AfterEach; diff --git a/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/S3OperationsTest.java b/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/S3OperationsTest.java new file mode 100644 index 000000000000..9b7ba2031197 --- /dev/null +++ b/test/service-client-backward-compat-test/src/test/java/software/amazon/awssdk/services/backwardcompat/S3OperationsTest.java @@ -0,0 +1,194 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services.backwardcompat; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.core.sync.ResponseTransformer; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; +import software.amazon.awssdk.services.s3.model.ChecksumMode; +import software.amazon.awssdk.testutils.service.http.MockAsyncHttpClient; +import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; + +/** + * Ensure that S3 put/get operations, flexible checksums, and request-level credential overrides work correctly. + */ +public class S3OperationsTest { + private static final StaticCredentialsProvider REQUEST_CREDENTIALS = + StaticCredentialsProvider.create(AwsBasicCredentials.create("request-akid", "request-skid")); + + private MockSyncHttpClient httpClient; + private MockAsyncHttpClient asyncHttpClient; + private S3Client s3; + private S3AsyncClient s3Async; + + @BeforeEach + public void setup() { + this.httpClient = new MockSyncHttpClient(); + this.httpClient.stubNextResponse200(); + + this.asyncHttpClient = new MockAsyncHttpClient(); + this.asyncHttpClient.stubNextResponse200(); + + this.s3 = S3Client.builder() + .region(Region.US_WEST_2) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .httpClient(httpClient) + .build(); + + this.s3Async = S3AsyncClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .httpClient(asyncHttpClient) + .build(); + } + + @AfterEach + public void teardown() { + httpClient.close(); + asyncHttpClient.close(); + s3.close(); + s3Async.close(); + } + + // Put/Get operations + + @Test + public void putObject_sync_completesSuccessfully() { + s3.putObject(r -> r.bucket("bucket").key("key"), RequestBody.fromString("foo")); + assertThat(httpClient.getLastRequest()).isNotNull(); + assertThat(httpClient.getLastRequest().method().name()).isEqualTo("PUT"); + } + + @Test + public void putObject_async_completesSuccessfully() { + s3Async.putObject(r -> r.bucket("bucket").key("key"), AsyncRequestBody.fromString("foo")).join(); + assertThat(asyncHttpClient.getLastRequest()).isNotNull(); + assertThat(asyncHttpClient.getLastRequest().method().name()).isEqualTo("PUT"); + } + + @Test + public void getObject_sync_completesSuccessfully() { + s3.getObject(r -> r.bucket("bucket").key("key"), ResponseTransformer.toBytes()); + assertThat(httpClient.getLastRequest()).isNotNull(); + assertThat(httpClient.getLastRequest().method().name()).isEqualTo("GET"); + } + + @Test + public void getObject_async_completesSuccessfully() { + s3Async.getObject(r -> r.bucket("bucket").key("key"), AsyncResponseTransformer.toBytes()).join(); + assertThat(asyncHttpClient.getLastRequest()).isNotNull(); + assertThat(asyncHttpClient.getLastRequest().method().name()).isEqualTo("GET"); + } + + // Flexible checksums + + @Test + public void putObject_withChecksumAlgorithm_sync_sendsChecksumHeader() { + s3.putObject(r -> r.bucket("bucket").key("key").checksumAlgorithm(ChecksumAlgorithm.CRC32), + RequestBody.fromString("foo")); + assertThat(httpClient.getLastRequest()).isNotNull(); + // Checksum algorithm is sent as x-amz-sdk-checksum-algorithm, actual value as trailing header + assertThat(httpClient.getLastRequest().firstMatchingHeader("x-amz-sdk-checksum-algorithm")) + .isPresent() + .hasValue("CRC32"); + assertThat(httpClient.getLastRequest().firstMatchingHeader("x-amz-trailer")) + .isPresent() + .hasValue("x-amz-checksum-crc32"); + } + + @Test + public void putObject_withChecksumAlgorithm_async_sendsChecksumHeader() { + s3Async.putObject(r -> r.bucket("bucket").key("key").checksumAlgorithm(ChecksumAlgorithm.CRC32), + AsyncRequestBody.fromString("foo")).join(); + assertThat(asyncHttpClient.getLastRequest()).isNotNull(); + assertThat(asyncHttpClient.getLastRequest().firstMatchingHeader("x-amz-sdk-checksum-algorithm")) + .isPresent() + .hasValue("CRC32"); + assertThat(asyncHttpClient.getLastRequest().firstMatchingHeader("x-amz-trailer")) + .isPresent() + .hasValue("x-amz-checksum-crc32"); + } + + @Test + public void getObject_withChecksumMode_sync_sendsChecksumHeader() { + s3.getObject(r -> r.bucket("bucket").key("key").checksumMode(ChecksumMode.ENABLED), + ResponseTransformer.toBytes()); + assertThat(httpClient.getLastRequest()).isNotNull(); + assertThat(httpClient.getLastRequest().firstMatchingHeader("x-amz-checksum-mode")) + .isPresent() + .hasValue("ENABLED"); + } + + @Test + public void getObject_withChecksumMode_async_sendsChecksumHeader() { + s3Async.getObject(r -> r.bucket("bucket").key("key").checksumMode(ChecksumMode.ENABLED), + AsyncResponseTransformer.toBytes()).join(); + assertThat(asyncHttpClient.getLastRequest()).isNotNull(); + assertThat(asyncHttpClient.getLastRequest().firstMatchingHeader("x-amz-checksum-mode")) + .isPresent() + .hasValue("ENABLED"); + } + + // Request-level credential overrides + + @Test + public void requestLevelCredentials_overrideClientCredentials_sync() { + s3.putObject(r -> r.bucket("bucket") + .key("key") + .overrideConfiguration(c -> c.credentialsProvider(REQUEST_CREDENTIALS)), + RequestBody.fromString("foo")); + // Request should have Authorization header since we overrode anonymous with real credentials + assertThat(httpClient.getLastRequest().firstMatchingHeader("Authorization")).isPresent(); + } + + @Test + public void requestLevelCredentials_overrideClientCredentials_async() { + s3Async.putObject(r -> r.bucket("bucket") + .key("key") + .overrideConfiguration(c -> c.credentialsProvider(REQUEST_CREDENTIALS)), + AsyncRequestBody.fromString("foo")) + .join(); + // Request should have Authorization header since we overrode anonymous with real credentials + assertThat(asyncHttpClient.getLastRequest().firstMatchingHeader("Authorization")).isPresent(); + } + + @Test + public void withoutRequestLevelCredentials_usesClientAnonymous_sync() { + s3.putObject(r -> r.bucket("bucket").key("key"), RequestBody.fromString("foo")); + // No override — should be anonymous (no Authorization header) + assertThat(httpClient.getLastRequest().firstMatchingHeader("Authorization")).isEmpty(); + } + + @Test + public void withoutRequestLevelCredentials_usesClientAnonymous_async() { + s3Async.putObject(r -> r.bucket("bucket").key("key"), AsyncRequestBody.fromString("foo")).join(); + // No override — should be anonymous (no Authorization header) + assertThat(asyncHttpClient.getLastRequest().firstMatchingHeader("Authorization")).isEmpty(); + } +} diff --git a/test/tests-coverage-reporting/pom.xml b/test/tests-coverage-reporting/pom.xml index 734fa4f46ce7..9d03ccbec49d 100644 --- a/test/tests-coverage-reporting/pom.xml +++ b/test/tests-coverage-reporting/pom.xml @@ -218,7 +218,7 @@ ${awsjavasdk.version} - old-client-version-compatibility-test + service-client-backward-compat-test software.amazon.awssdk ${awsjavasdk.version}