Skip to content

Commit 99b9d3f

Browse files
authored
Support HostPrefix in E2.0, enable E2.0 for all services (#3564)
* 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. * Review comments * Set tokenProvider() for Bearer auth services
1 parent 5af85b1 commit 99b9d3f

File tree

39 files changed

+679
-105
lines changed

39 files changed

+679
-105
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
@@ -69,9 +69,7 @@ public TypeSpec poetSpec() {
6969
}
7070
}
7171

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

7674
if (AuthUtils.usesBearerAuth(model)) {
7775
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
});
@@ -191,9 +189,8 @@ private MethodSpec mergeServiceDefaultsMethod() {
191189
.addParameter(SdkClientConfiguration.class, "config")
192190
.addCode("return config.merge(c -> c");
193191

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

198195
if (defaultAwsAuthSignerMethod().isPresent()) {
199196
builder.addCode(".option($T.SIGNER, defaultSigner())\n", SdkAdvancedClientOption.class);
@@ -261,11 +258,9 @@ private MethodSpec finalizeServiceConfigurationMethod() {
261258

262259
List<ClassName> builtInInterceptors = new ArrayList<>();
263260

264-
if (endpointRulesSpecUtils.isEndpointRulesEnabled()) {
265-
builtInInterceptors.add(endpointRulesSpecUtils.resolverInterceptorName());
266-
builtInInterceptors.add(endpointRulesSpecUtils.authSchemesInterceptorName());
267-
builtInInterceptors.add(endpointRulesSpecUtils.requestModifierInterceptorName());
268-
}
261+
builtInInterceptors.add(endpointRulesSpecUtils.resolverInterceptorName());
262+
builtInInterceptors.add(endpointRulesSpecUtils.authSchemesInterceptorName());
263+
builtInInterceptors.add(endpointRulesSpecUtils.requestModifierInterceptorName());
269264

270265
for (String interceptor : model.getCustomizationConfig().getInterceptors()) {
271266
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
@@ -69,9 +69,7 @@ public TypeSpec poetSpec() {
6969
}
7070
}
7171

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

7674
if (AuthUtils.usesBearerAuth(model)) {
7775
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: 81 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,15 @@ private MethodSpec modifyRequestMethod() {
106115
b.beginControlFlow("try");
107116
b.addStatement("$T result = $N.resolveEndpoint(ruleParams(context, executionAttributes)).join()", Endpoint.class,
108117
providerVar);
118+
b.beginControlFlow("if (!$T.disableHostPrefixInjection(executionAttributes))",
119+
endpointRulesSpecUtils.rulesRuntimeClassName("AwsEndpointProviderUtils"));
120+
b.addStatement("$T hostPrefix = hostPrefix(executionAttributes.getAttribute($T.OPERATION_NAME), context.request())",
121+
ParameterizedTypeName.get(Optional.class, String.class), SdkExecutionAttribute.class);
122+
b.beginControlFlow("if (hostPrefix.isPresent())");
123+
b.addStatement("result = $T.addHostPrefix(result, hostPrefix.get())",
124+
endpointRulesSpecUtils.rulesRuntimeClassName("AwsEndpointProviderUtils"));
125+
b.endControlFlow();
126+
b.endControlFlow();
109127
b.addStatement("executionAttributes.putAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT, result)");
110128
b.addStatement("return context.request()");
111129
b.endControlFlow();
@@ -171,7 +189,7 @@ private MethodSpec ruleParams() {
171189
case AWS_S3_FORCE_PATH_STYLE:
172190
case AWS_S3_USE_ARN_REGION:
173191
case AWS_S3_CONTROL_USE_ARN_REGION:
174-
// end of S3 specific builtins
192+
// end of S3 specific builtins
175193
case AWS_STS_USE_GLOBAL_ENDPOINT:
176194
// V2 doesn't support this, only regional endpoints
177195
return;
@@ -361,4 +379,66 @@ private MethodSpec setClientContextParamsMethod() {
361379

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

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

Lines changed: 26 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;
@@ -56,6 +58,7 @@
5658
import software.amazon.awssdk.codegen.poet.ClassSpec;
5759
import software.amazon.awssdk.codegen.poet.PoetExtension;
5860
import software.amazon.awssdk.codegen.poet.PoetUtils;
61+
import software.amazon.awssdk.codegen.utils.AuthUtils;
5962
import software.amazon.awssdk.core.SdkSystemSetting;
6063
import software.amazon.awssdk.core.async.AsyncRequestBody;
6164
import software.amazon.awssdk.core.rules.testing.AsyncTestCase;
@@ -217,7 +220,8 @@ private MethodSpec syncTestsSourceMethod() {
217220
SyncTestCase.class,
218221
test.getDocumentation(),
219222
syncOperationCallLambda(opModel, test.getParams(), opInput.getOperationParams()),
220-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
223+
TestGeneratorUtils.createExpect(test.getExpect(), opModel, opInput.getOperationParams()),
224+
getSkipReasonBlock(test.getDocumentation()));
221225

222226
if (operationInputsIter.hasNext()) {
223227
b.addCode(",");
@@ -228,7 +232,8 @@ private MethodSpec syncTestsSourceMethod() {
228232
SyncTestCase.class,
229233
test.getDocumentation(),
230234
syncOperationCallLambda(defaultOpModel, test.getParams(), Collections.emptyMap()),
231-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
235+
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
236+
getSkipReasonBlock(test.getDocumentation()));
232237
}
233238

234239
if (testIter.hasNext()) {
@@ -248,6 +253,9 @@ private CodeBlock syncOperationCallLambda(OperationModel opModel, Map<String, Tr
248253
b.beginControlFlow("() -> ");
249254
b.addStatement("$T builder = $T.builder()", syncClientBuilder(), syncClientClass());
250255
b.addStatement("builder.credentialsProvider($T.CREDENTIALS_PROVIDER)", BaseRuleSetClientTest.class);
256+
if (AuthUtils.usesBearerAuth(model)) {
257+
b.addStatement("builder.tokenProvider($T.TOKEN_PROVIDER)", BaseRuleSetClientTest.class);
258+
}
251259
b.addStatement("builder.httpClient(getSyncHttpClient())");
252260

253261
if (params != null) {
@@ -272,6 +280,9 @@ private CodeBlock asyncOperationCallLambda(OperationModel opModel, Map<String, T
272280
b.beginControlFlow("() -> ");
273281
b.addStatement("$T builder = $T.builder()", asyncClientBuilder(), asyncClientClass());
274282
b.addStatement("builder.credentialsProvider($T.CREDENTIALS_PROVIDER)", BaseRuleSetClientTest.class);
283+
if (AuthUtils.usesBearerAuth(model)) {
284+
b.addStatement("builder.tokenProvider($T.TOKEN_PROVIDER)", BaseRuleSetClientTest.class);
285+
}
275286
b.addStatement("builder.httpClient(getAsyncHttpClient())");
276287

277288
if (params != null) {
@@ -355,7 +366,8 @@ private MethodSpec asyncTestsSourceMethod() {
355366
AsyncTestCase.class,
356367
test.getDocumentation(),
357368
asyncOperationCallLambda(opModel, test.getParams(), opInput.getOperationParams()),
358-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
369+
TestGeneratorUtils.createExpect(test.getExpect(), opModel, opInput.getOperationParams()),
370+
getSkipReasonBlock(test.getDocumentation()));
359371

360372
if (operationInputsIter.hasNext()) {
361373
b.addCode(",");
@@ -366,7 +378,8 @@ private MethodSpec asyncTestsSourceMethod() {
366378
AsyncTestCase.class,
367379
test.getDocumentation(),
368380
asyncOperationCallLambda(defaultOpModel, test.getParams(), Collections.emptyMap()),
369-
TestGeneratorUtils.createExpect(test.getExpect()), getSkipReasonBlock(test.getDocumentation()));
381+
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
382+
getSkipReasonBlock(test.getDocumentation()));
370383
}
371384

372385
if (testIter.hasNext()) {
@@ -399,8 +412,10 @@ private CodeBlock requestCreation(OperationModel opModel, Map<String, TreeNode>
399412
return b.add(".build()").build();
400413
}
401414

415+
String hostPrefix = getHostPrefixTemplate(opModel).orElse("");
416+
402417
inputShape.getMembers().forEach(m -> {
403-
if (!boundToPath(m)) {
418+
if (!boundToPath(m) && !hostPrefix.contains("{" + m.getC2jName() + "}")) {
404419
return;
405420
}
406421

@@ -445,6 +460,12 @@ private static boolean canBeEmpty(OperationModel opModel) {
445460
return false;
446461
}
447462

463+
String hostPrefix = getHostPrefixTemplate(opModel).orElse("");
464+
465+
if (hostPrefix.contains("{") && hostPrefix.contains("}")) {
466+
return false;
467+
}
468+
448469
Optional<MemberModel> pathMemberOrStreaming = members.stream()
449470
.filter(EndpointRulesClientTestSpec::boundToPath)
450471
.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)