Skip to content

Commit

Permalink
Add codegen customizations to skip ALPN for existing H2 services
Browse files Browse the repository at this point in the history
  • Loading branch information
davidh44 committed Jan 14, 2025
1 parent 050d7ce commit 815a0da
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ public class CustomizationConfig {
*/
private boolean generateEndpointClientTests;

/**
* Whether to skip using ALPN for H2 service.
*/
private boolean skipAlpn;

/**
* A mapping from the skipped test's description to the reason why it's being skipped.
*/
Expand Down Expand Up @@ -746,6 +751,14 @@ public void setGenerateEndpointClientTests(boolean generateEndpointClientTests)
this.generateEndpointClientTests = generateEndpointClientTests;
}

public boolean isSkipAlpn() {
return skipAlpn;
}

public void setSkipAlpn(boolean skipAlpn) {
this.skipAlpn = skipAlpn;
}

public boolean useGlobalEndpoint() {
return useGlobalEndpoint;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.ProtocolNegotiation;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
import software.amazon.awssdk.identity.spi.IdentityProvider;
Expand Down Expand Up @@ -638,22 +639,23 @@ private MethodSpec beanStyleSetServiceConfigurationMethod() {
private void addServiceHttpConfigIfNeeded(TypeSpec.Builder builder, IntermediateModel model) {
String serviceDefaultFqcn = model.getCustomizationConfig().getServiceSpecificHttpConfig();
boolean supportsH2 = model.getMetadata().supportsH2();
boolean skipAlpn = model.getCustomizationConfig().isSkipAlpn();

if (serviceDefaultFqcn != null || supportsH2) {
builder.addMethod(serviceSpecificHttpConfigMethod(serviceDefaultFqcn, supportsH2));
builder.addMethod(serviceSpecificHttpConfigMethod(serviceDefaultFqcn, supportsH2, skipAlpn));
}
}

private MethodSpec serviceSpecificHttpConfigMethod(String serviceDefaultFqcn, boolean supportsH2) {
private MethodSpec serviceSpecificHttpConfigMethod(String serviceDefaultFqcn, boolean supportsH2, boolean skipAlpn) {
return MethodSpec.methodBuilder("serviceHttpConfig")
.addAnnotation(Override.class)
.addModifiers(PROTECTED, FINAL)
.returns(AttributeMap.class)
.addCode(serviceSpecificHttpConfigMethodBody(serviceDefaultFqcn, supportsH2))
.addCode(serviceSpecificHttpConfigMethodBody(serviceDefaultFqcn, supportsH2, skipAlpn))
.build();
}

private CodeBlock serviceSpecificHttpConfigMethodBody(String serviceDefaultFqcn, boolean supportsH2) {
private CodeBlock serviceSpecificHttpConfigMethodBody(String serviceDefaultFqcn, boolean supportsH2, boolean skipAlpn) {
CodeBlock.Builder builder = CodeBlock.builder();

if (serviceDefaultFqcn != null) {
Expand All @@ -665,10 +667,16 @@ private CodeBlock serviceSpecificHttpConfigMethodBody(String serviceDefaultFqcn,
}

if (supportsH2) {
builder.addStatement("return result.merge(AttributeMap.builder()"
+ ".put($T.PROTOCOL, $T.HTTP2)"
+ ".build())",
SdkHttpConfigurationOption.class, Protocol.class);
builder.add("return result.merge(AttributeMap.builder()"
+ ".put($T.PROTOCOL, $T.HTTP2)",
SdkHttpConfigurationOption.class, Protocol.class);

if (!skipAlpn) {
builder.add(".put($T.PROTOCOL_NEGOTIATION, $T.ALPN)",
SdkHttpConfigurationOption.class, ProtocolNegotiation.class);
}

builder.addStatement(".build())");
} else {
builder.addStatement("return result");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,36 @@ public static IntermediateModel serviceWithNoAuth() {
return new IntermediateModelBuilder(models).build();
}

public static IntermediateModel serviceWithH2() {
File serviceModel =
new File(ClientTestModels.class.getResource("client/c2j/service-with-h2/service-2.json").getFile());
File customizationModel =
new File(ClientTestModels.class.getResource("client/c2j/service-with-h2/customization.config")
.getFile());
C2jModels models = C2jModels
.builder()
.serviceModel(getServiceModel(serviceModel))
.customizationConfig(getCustomizationConfig(customizationModel))
.build();

return new IntermediateModelBuilder(models).build();
}

public static IntermediateModel serviceWithH2SkipAlpn() {
File serviceModel =
new File(ClientTestModels.class.getResource("client/c2j/service-with-h2-skipAlpn/service-2.json").getFile());
File customizationModel =
new File(ClientTestModels.class.getResource("client/c2j/service-with-h2-skipAlpn/customization.config")
.getFile());
C2jModels models = C2jModels
.builder()
.serviceModel(getServiceModel(serviceModel))
.customizationConfig(getCustomizationConfig(customizationModel))
.build();

return new IntermediateModelBuilder(models).build();
}

public static IntermediateModel serviceMiniS3() {
File serviceModel =
new File(ClientTestModels.class.getResource("client/c2j/mini-s3/service-2.json").getFile());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
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.restJsonServiceModels;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.serviceWithH2;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.serviceWithH2SkipAlpn;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.serviceWithNoAuth;
import static software.amazon.awssdk.codegen.poet.builder.BuilderClassTestUtils.validateGeneration;

Expand Down Expand Up @@ -117,6 +119,16 @@ void syncComposedDefaultClientBuilderClass_sra() {
"test-composed-sync-default-client-builder.java", true);
}

@Test
void baseClientBuilderClassWithH2() {
validateBaseClientBuilderClassGeneration(serviceWithH2(), "test-h2-service-client-builder-class.java");
}

@Test
void baseClientBuilderClassWithH2SkipAlpn() {
validateBaseClientBuilderClassGeneration(serviceWithH2SkipAlpn(), "test-h2-skipAlpn-service-client-builder-class.java");
}

private void validateBaseClientBuilderClassGeneration(IntermediateModel model, String expectedClassName) {
validateBaseClientBuilderClassGeneration(model, expectedClassName, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package software.amazon.awssdk.services.h2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import software.amazon.awssdk.annotations.Generated;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.signer.Aws4Signer;
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.SdkPlugin;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
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.core.signer.Signer;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.ProtocolNegotiation;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.identity.spi.IdentityProvider;
import software.amazon.awssdk.identity.spi.IdentityProviders;
import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.h2.endpoints.H2EndpointProvider;
import software.amazon.awssdk.services.h2.endpoints.internal.H2RequestSetEndpointInterceptor;
import software.amazon.awssdk.services.h2.endpoints.internal.H2ResolveEndpointInterceptor;
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.Validate;

/**
* Internal base class for {@link DefaultH2ClientBuilder} and {@link DefaultH2AsyncClientBuilder}.
*/
@Generated("software.amazon.awssdk:codegen")
@SdkInternalApi
abstract class DefaultH2BaseClientBuilder<B extends H2BaseClientBuilder<B, C>, C> extends AwsDefaultClientBuilder<B, C> {
@Override
protected final String serviceEndpointPrefix() {
return "h2-service";
}

@Override
protected final String serviceName() {
return "H2";
}

@Override
protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) {
return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider())
.option(SdkAdvancedClientOption.SIGNER, defaultSigner())
.option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false));
}

@Override
protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) {
List<ExecutionInterceptor> endpointInterceptors = new ArrayList<>();
endpointInterceptors.add(new H2ResolveEndpointInterceptor());
endpointInterceptors.add(new H2RequestSetEndpointInterceptor());
ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory();
List<ExecutionInterceptor> interceptors = interceptorFactory
.getInterceptors("software/amazon/awssdk/services/h2/execution.interceptors");
List<ExecutionInterceptor> 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<?> credentialsIdentityProvider = c.get(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER);
if (credentialsIdentityProvider != null) {
result.putIdentityProvider(credentialsIdentityProvider);
}
return result.build();
});
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());
return builder.build();
}

private Signer defaultSigner() {
return Aws4Signer.create();
}

@Override
protected final String signingName() {
return "h2-service";
}

private H2EndpointProvider defaultEndpointProvider() {
return H2EndpointProvider.defaultProvider();
}

@Override
protected final AttributeMap serviceHttpConfig() {
AttributeMap result = AttributeMap.empty();
return result.merge(AttributeMap.builder().put(SdkHttpConfigurationOption.PROTOCOL, Protocol.HTTP2)
.put(SdkHttpConfigurationOption.PROTOCOL_NEGOTIATION, ProtocolNegotiation.ALPN).build());
}

@Override
protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) {
List<SdkPlugin> internalPlugins = internalPlugins(config);
List<SdkPlugin> externalPlugins = plugins();
if (internalPlugins.isEmpty() && externalPlugins.isEmpty()) {
return config;
}
List<SdkPlugin> plugins = CollectionUtils.mergeLists(internalPlugins, externalPlugins);
SdkClientConfiguration.Builder configuration = config.toBuilder();
H2ServiceClientConfigurationBuilder serviceConfigBuilder = new H2ServiceClientConfigurationBuilder(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<RetryStrategy.Builder<?, ?>> 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<SdkPlugin> internalPlugins(SdkClientConfiguration config) {
return Collections.emptyList();
}

protected static void validateClientOptions(SdkClientConfiguration c) {
Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER),
"The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder.");
}
}
Loading

0 comments on commit 815a0da

Please sign in to comment.