Skip to content

Commit be3658b

Browse files
committed
Support HostPrefix in E2.0, enable E2.0 for all services
This changes fixes the previously incorrect handling of `hostPrefix` in services, causing requests for some services to have the wrong URL. This change also undoes the suppression of rules-based endpoint generation for all services apart from EventBridge, S3, and S3 Control, which was implemented in #3520 because of the previously mentioned `hostPrefix` issue.
1 parent 70929c3 commit be3658b

File tree

38 files changed

+638
-106
lines changed

38 files changed

+638
-106
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "This changes fixes the previously incorrect handling of `hostPrefix` in services, causing requests for some services to have the wrong URL. This change also undoes the suppression of rules-based endpoint generation for all services apart from EventBridge, S3, and S3 Control, which was implemented in [#3520](https://github.com/aws/aws-sdk-java-v2/issues/3520) because of the previously mentioned `hostPrefix` issue."
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/EndpointProviderTasks.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import java.util.ArrayList;
1919
import java.util.Arrays;
2020
import java.util.Collection;
21-
import java.util.Collections;
2221
import java.util.List;
2322
import java.util.Map;
2423
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
@@ -46,10 +45,6 @@ public EndpointProviderTasks(GeneratorTaskParams dependencies) {
4645

4746
@Override
4847
protected List<GeneratorTask> createTasks() throws Exception {
49-
if (!generatorTaskParams.getModel().getCustomizationConfig().useRuleBasedEndpoints()) {
50-
return Collections.emptyList();
51-
}
52-
5348
List<GeneratorTask> tasks = new ArrayList<>();
5449
tasks.add(generateInterface());
5550
tasks.add(generateParams());

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,6 @@ public class CustomizationConfig {
213213

214214
private boolean useGlobalEndpoint;
215215

216-
/**
217-
* Whether Endpoints 2.0/rule based endpoints should be used for endpoint resolution.
218-
*/
219-
private boolean useRuleBasedEndpoints = false;
220-
221216
private List<String> interceptors = new ArrayList<>();
222217

223218
private CustomizationConfig() {
@@ -557,14 +552,6 @@ public void setSkipEndpointTests(Map<String, String> skipEndpointTests) {
557552
this.skipEndpointTests = skipEndpointTests;
558553
}
559554

560-
public boolean useRuleBasedEndpoints() {
561-
return useRuleBasedEndpoints;
562-
}
563-
564-
public void setUseRuleBasedEndpoints(boolean useRuleBasedEndpoints) {
565-
this.useRuleBasedEndpoints = useRuleBasedEndpoints;
566-
}
567-
568555
public List<String> getInterceptors() {
569556
return interceptors;
570557
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ public TypeSpec poetSpec() {
6868
}
6969
}
7070

71-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
72-
builder.addMethod(endpointProviderMethod());
73-
}
71+
builder.addMethod(endpointProviderMethod());
7472

7573
if (BearerAuthUtils.usesBearerAuth(model)) {
7674
builder.addMethod(bearerTokenProviderMethod());

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,9 @@ public TypeSpec poetSpec() {
111111
builder.addMethod(finalizeServiceConfigurationMethod());
112112
defaultAwsAuthSignerMethod().ifPresent(builder::addMethod);
113113
builder.addMethod(signingNameMethod());
114-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
115-
builder.addMethod(defaultEndpointProviderMethod());
116-
}
114+
builder.addMethod(defaultEndpointProviderMethod());
117115

118-
if (hasClientContextParams() && endpointRulesSpecUtils.isEndpointRulesEnabled()) {
116+
if (hasClientContextParams()) {
119117
model.getClientContextParams().forEach((n, m) -> {
120118
builder.addMethod(clientContextParamSetter(n, m));
121119
});
@@ -189,9 +187,8 @@ private MethodSpec mergeServiceDefaultsMethod() {
189187
.addParameter(SdkClientConfiguration.class, "config")
190188
.addCode("return config.merge(c -> c");
191189

192-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
193-
builder.addCode(".option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class);
194-
}
190+
builder.addCode(".option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class);
191+
195192

196193
if (defaultAwsAuthSignerMethod().isPresent()) {
197194
builder.addCode(".option($T.SIGNER, defaultSigner())\n", SdkAdvancedClientOption.class);
@@ -259,11 +256,9 @@ private MethodSpec finalizeServiceConfigurationMethod() {
259256

260257
List<ClassName> builtInInterceptors = new ArrayList<>();
261258

262-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
263-
builtInInterceptors.add(endpointRulesSpecUtils.resolverInterceptorName());
264-
builtInInterceptors.add(endpointRulesSpecUtils.authSchemesInterceptorName());
265-
builtInInterceptors.add(endpointRulesSpecUtils.requestModifierInterceptorName());
266-
}
259+
builtInInterceptors.add(endpointRulesSpecUtils.resolverInterceptorName());
260+
builtInInterceptors.add(endpointRulesSpecUtils.authSchemesInterceptorName());
261+
builtInInterceptors.add(endpointRulesSpecUtils.requestModifierInterceptorName());
267262

268263
for (String interceptor : model.getCustomizationConfig().getInterceptors()) {
269264
builtInInterceptors.add(ClassName.bestGuess(interceptor));

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderInterface.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
3939
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;
4040

41-
4241
public class BaseClientBuilderInterface implements ClassSpec {
4342
private final IntermediateModel model;
4443
private final String basePackage;
@@ -73,14 +72,12 @@ public TypeSpec poetSpec() {
7372
builder.addMethod(serviceConfigurationConsumerBuilderMethod());
7473
}
7574

76-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
77-
builder.addMethod(endpointProviderMethod());
75+
builder.addMethod(endpointProviderMethod());
7876

79-
if (hasClientContextParams()) {
80-
model.getClientContextParams().forEach((n, m) -> {
81-
builder.addMethod(clientContextParamSetter(n, m));
82-
});
83-
}
77+
if (hasClientContextParams()) {
78+
model.getClientContextParams().forEach((n, m) -> {
79+
builder.addMethod(clientContextParamSetter(n, m));
80+
});
8481
}
8582

8683
if (generateTokenProviderMethod()) {

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ public TypeSpec poetSpec() {
6868
}
6969
}
7070

71-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
72-
builder.addMethod(endpointProviderMethod());
73-
}
71+
builder.addMethod(endpointProviderMethod());
7472

7573
if (BearerAuthUtils.usesBearerAuth(model)) {
7674
builder.addMethod(tokenProviderMethodImpl());

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointProviderTestSpec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private MethodSpec testsCasesMethod() {
8888
b.addStatement("testCases.add(new $T($L, $L))",
8989
EndpointProviderTestCase.class,
9090
createTestCase(test),
91-
TestGeneratorUtils.createExpect(test.getExpect()));
91+
TestGeneratorUtils.createExpect(test.getExpect(), null, null));
9292
});
9393

9494
b.addStatement("return testCases");

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import com.fasterxml.jackson.jr.stree.JrsString;
2121
import com.squareup.javapoet.ClassName;
2222
import com.squareup.javapoet.MethodSpec;
23+
import com.squareup.javapoet.ParameterizedTypeName;
2324
import com.squareup.javapoet.TypeSpec;
25+
import java.util.Iterator;
2426
import java.util.Map;
2527
import java.util.Optional;
2628
import java.util.concurrent.CompletionException;
@@ -32,6 +34,8 @@
3234
import software.amazon.awssdk.codegen.model.rules.endpoints.ParameterModel;
3335
import software.amazon.awssdk.codegen.model.service.ClientContextParam;
3436
import software.amazon.awssdk.codegen.model.service.ContextParam;
37+
import software.amazon.awssdk.codegen.model.service.EndpointTrait;
38+
import software.amazon.awssdk.codegen.model.service.HostPrefixProcessor;
3539
import software.amazon.awssdk.codegen.model.service.StaticContextParam;
3640
import software.amazon.awssdk.codegen.poet.ClassSpec;
3741
import software.amazon.awssdk.codegen.poet.PoetExtension;
@@ -41,9 +45,12 @@
4145
import software.amazon.awssdk.core.interceptor.Context;
4246
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
4347
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
48+
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
4449
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
4550
import software.amazon.awssdk.endpoints.Endpoint;
4651
import software.amazon.awssdk.utils.AttributeMap;
52+
import software.amazon.awssdk.utils.HostnameValidator;
53+
import software.amazon.awssdk.utils.StringUtils;
4754

4855
public class EndpointResolverInterceptorSpec implements ClassSpec {
4956
private final IntermediateModel model;
@@ -76,6 +83,8 @@ public TypeSpec poetSpec() {
7683
b.addMethod(setClientContextParamsMethod());
7784
}
7885

86+
b.addMethod(hostPrefixMethod());
87+
7988
return b.build();
8089
}
8190

@@ -106,6 +115,13 @@ private MethodSpec modifyRequestMethod() {
106115
b.beginControlFlow("try");
107116
b.addStatement("$T result = $N.resolveEndpoint(ruleParams(context, executionAttributes)).join()", Endpoint.class,
108117
providerVar);
118+
b.addStatement("$T hostPrefix = hostPrefix(executionAttributes.getAttribute($T.OPERATION_NAME), context.request())",
119+
ParameterizedTypeName.get(Optional.class, String.class), SdkExecutionAttribute.class);
120+
b.beginControlFlow("if (hostPrefix.isPresent() && !$T.disableHostPrefixInjection(executionAttributes))",
121+
endpointRulesSpecUtils.rulesRuntimeClassName("AwsEndpointProviderUtils"));
122+
b.addStatement("result = $T.addHostPrefix(result, hostPrefix.get())",
123+
endpointRulesSpecUtils.rulesRuntimeClassName("AwsEndpointProviderUtils"));
124+
b.endControlFlow();
109125
b.addStatement("executionAttributes.putAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT, result)");
110126
b.addStatement("return context.request()");
111127
b.endControlFlow();
@@ -171,7 +187,7 @@ private MethodSpec ruleParams() {
171187
case AWS_S3_FORCE_PATH_STYLE:
172188
case AWS_S3_USE_ARN_REGION:
173189
case AWS_S3_CONTROL_USE_ARN_REGION:
174-
// end of S3 specific builtins
190+
// end of S3 specific builtins
175191
case AWS_STS_USE_GLOBAL_ENDPOINT:
176192
// V2 doesn't support this, only regional endpoints
177193
return;
@@ -361,4 +377,66 @@ private MethodSpec setClientContextParamsMethod() {
361377

362378
return b.build();
363379
}
380+
381+
382+
private MethodSpec hostPrefixMethod() {
383+
MethodSpec.Builder builder = MethodSpec.methodBuilder("hostPrefix")
384+
.returns(ParameterizedTypeName.get(Optional.class, String.class))
385+
.addParameter(String.class, "operationName")
386+
.addParameter(SdkRequest.class, "request")
387+
.addModifiers(Modifier.PRIVATE, Modifier.STATIC);
388+
389+
builder.beginControlFlow("switch (operationName)");
390+
391+
model.getOperations().forEach((name, opModel) -> {
392+
String hostPrefix = getHostPrefix(opModel);
393+
if (StringUtils.isBlank(hostPrefix)) {
394+
return;
395+
}
396+
397+
builder.beginControlFlow("case $S:", name);
398+
HostPrefixProcessor processor = new HostPrefixProcessor(hostPrefix);
399+
400+
if (processor.c2jNames().isEmpty()) {
401+
builder.addStatement("return $T.of($S)", Optional.class, processor.hostWithStringSpecifier());
402+
} else {
403+
String requestVar = opModel.getInput().getVariableName();
404+
processor.c2jNames().forEach(c2jName -> {
405+
builder.addStatement("$1T.validateHostnameCompliant(request.getValueForField($2S, $3T.class).orElse(null), "
406+
+ "$2S, $4S)",
407+
HostnameValidator.class,
408+
c2jName,
409+
String.class,
410+
requestVar);
411+
});
412+
413+
builder.addCode("return $T.of($T.format($S, ", Optional.class, String.class,
414+
processor.hostWithStringSpecifier());
415+
Iterator<String> c2jNamesIter = processor.c2jNames().listIterator();
416+
while (c2jNamesIter.hasNext()) {
417+
builder.addCode("request.getValueForField($S, $T.class).get()", c2jNamesIter.next(), String.class);
418+
if (c2jNamesIter.hasNext()) {
419+
builder.addCode(",");
420+
}
421+
}
422+
builder.addStatement("))");
423+
}
424+
builder.endControlFlow();
425+
});
426+
427+
builder.addCode("default:");
428+
builder.addStatement("return $T.empty()", Optional.class);
429+
builder.endControlFlow();
430+
431+
return builder.build();
432+
}
433+
434+
private String getHostPrefix(OperationModel opModel) {
435+
EndpointTrait endpointTrait = opModel.getEndpointTrait();
436+
if (endpointTrait == null) {
437+
return null;
438+
}
439+
440+
return endpointTrait.getHostPrefix();
441+
}
364442
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
package software.amazon.awssdk.codegen.poet.rules;
1717

18+
import static software.amazon.awssdk.codegen.poet.rules.TestGeneratorUtils.getHostPrefixTemplate;
19+
1820
import com.fasterxml.jackson.core.TreeNode;
1921
import com.fasterxml.jackson.jr.stree.JrsArray;
2022
import com.fasterxml.jackson.jr.stree.JrsObject;
@@ -217,7 +219,8 @@ private MethodSpec syncTestsSourceMethod() {
217219
SyncTestCase.class,
218220
test.getDocumentation(),
219221
syncOperationCallLambda(opModel, test.getParams(), opInput.getOperationParams()),
220-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
222+
TestGeneratorUtils.createExpect(test.getExpect(), opModel, opInput.getOperationParams()),
223+
getSkipReasonBlock(test.getDocumentation()));
221224

222225
if (operationInputsIter.hasNext()) {
223226
b.addCode(",");
@@ -228,7 +231,8 @@ private MethodSpec syncTestsSourceMethod() {
228231
SyncTestCase.class,
229232
test.getDocumentation(),
230233
syncOperationCallLambda(defaultOpModel, test.getParams(), Collections.emptyMap()),
231-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
234+
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
235+
getSkipReasonBlock(test.getDocumentation()));
232236
}
233237

234238
if (testIter.hasNext()) {
@@ -355,7 +359,8 @@ private MethodSpec asyncTestsSourceMethod() {
355359
AsyncTestCase.class,
356360
test.getDocumentation(),
357361
asyncOperationCallLambda(opModel, test.getParams(), opInput.getOperationParams()),
358-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
362+
TestGeneratorUtils.createExpect(test.getExpect(), opModel, opInput.getOperationParams()),
363+
getSkipReasonBlock(test.getDocumentation()));
359364

360365
if (operationInputsIter.hasNext()) {
361366
b.addCode(",");
@@ -366,7 +371,8 @@ private MethodSpec asyncTestsSourceMethod() {
366371
AsyncTestCase.class,
367372
test.getDocumentation(),
368373
asyncOperationCallLambda(defaultOpModel, test.getParams(), Collections.emptyMap()),
369-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
374+
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
375+
getSkipReasonBlock(test.getDocumentation()));
370376
}
371377

372378
if (testIter.hasNext()) {
@@ -399,8 +405,10 @@ private CodeBlock requestCreation(OperationModel opModel, Map<String, TreeNode>
399405
return b.add(".build()").build();
400406
}
401407

408+
String hostPrefix = getHostPrefixTemplate(opModel).orElse("");
409+
402410
inputShape.getMembers().forEach(m -> {
403-
if (!boundToPath(m)) {
411+
if (!boundToPath(m) && !hostPrefix.contains("{" + m.getC2jName() + "}")) {
404412
return;
405413
}
406414

@@ -445,6 +453,12 @@ private static boolean canBeEmpty(OperationModel opModel) {
445453
return false;
446454
}
447455

456+
String hostPrefix = getHostPrefixTemplate(opModel).orElse("");
457+
458+
if (hostPrefix.contains("{") && hostPrefix.contains("}")) {
459+
return false;
460+
}
461+
448462
Optional<MemberModel> pathMemberOrStreaming = members.stream()
449463
.filter(EndpointRulesClientTestSpec::boundToPath)
450464
.findFirst();

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesSpecUtils.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,4 @@ public boolean isS3Control() {
182182
public TypeName resolverReturnType() {
183183
return ParameterizedTypeName.get(CompletableFuture.class, Endpoint.class);
184184
}
185-
186-
public boolean isEndpointRulesEnabled() {
187-
return intermediateModel.getCustomizationConfig().useRuleBasedEndpoints();
188-
}
189185
}

0 commit comments

Comments
 (0)