Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-83dd6ab.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add HTTP client configuration type metadata to the User-Agent header, tracking whether the HTTP client was auto-detected from the classpath or explicitly configured by the user."
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ private AwsExecutionContextBuilder() {
AwsSignerExecutionAttribute.AWS_CREDENTIALS).orElse(null)));

putStreamingInputOutputTypesMetadata(executionAttributes, executionParams);
putHttpClientConfigTypeMetadata(executionAttributes, clientConfig);

return ExecutionContext.builder()
.interceptorChain(executionInterceptorChain)
Expand All @@ -183,53 +184,58 @@ private AwsExecutionContextBuilder() {

private static <InputT extends SdkRequest, OutputT extends SdkResponse> void putStreamingInputOutputTypesMetadata(
ExecutionAttributes executionAttributes, ClientExecutionParams<InputT, OutputT> executionParams) {
List<AdditionalMetadata> userAgentMetadata = new ArrayList<>();

if (executionParams.getRequestBody() != null) {
userAgentMetadata.add(
AdditionalMetadata
.builder()
.name("rb")
.value(ContentStreamProvider.ProviderType.shortValueFromName(
executionParams.getRequestBody().contentStreamProvider().name())
)
.build());
addUserAgentMetadata(executionAttributes, "rb",
ContentStreamProvider.ProviderType.shortValueFromName(
executionParams.getRequestBody().contentStreamProvider().name()));
}

if (executionParams.getAsyncRequestBody() != null) {
userAgentMetadata.add(
AdditionalMetadata
.builder()
.name("rb")
.value(AsyncRequestBody.BodyType.shortValueFromName(
executionParams.getAsyncRequestBody().body())
)
.build());
addUserAgentMetadata(executionAttributes, "rb",
AsyncRequestBody.BodyType.shortValueFromName(
executionParams.getAsyncRequestBody().body()));
}

if (executionParams.getResponseTransformer() != null) {
userAgentMetadata.add(
AdditionalMetadata
.builder()
.name("rt")
.value(ResponseTransformer.TransformerType.shortValueFromName(
executionParams.getResponseTransformer().name())
)
.build());
addUserAgentMetadata(executionAttributes, "rt",
ResponseTransformer.TransformerType.shortValueFromName(
executionParams.getResponseTransformer().name()));
}

if (executionParams.getAsyncResponseTransformer() != null) {
userAgentMetadata.add(
AdditionalMetadata
.builder()
.name("rt")
.value(AsyncResponseTransformer.TransformerType.shortValueFromName(
executionParams.getAsyncResponseTransformer().name())
)
.build());
addUserAgentMetadata(executionAttributes, "rt",
AsyncResponseTransformer.TransformerType.shortValueFromName(
executionParams.getAsyncResponseTransformer().name()));
}
}

private static void putHttpClientConfigTypeMetadata(ExecutionAttributes executionAttributes,
SdkClientConfiguration clientConfig) {
BusinessMetricFeatureId httpClientConfigType = clientConfig.option(SdkClientOption.HTTP_CLIENT_CONFIG_TYPE);
if (httpClientConfigType == null) {
return;
}
BusinessMetricCollection businessMetrics = executionAttributes.getAttribute(
SdkInternalExecutionAttribute.BUSINESS_METRICS);
if (businessMetrics != null) {
businessMetrics.addMetric(httpClientConfigType.value());
}
}

executionAttributes.putAttribute(SdkInternalExecutionAttribute.USER_AGENT_METADATA, userAgentMetadata);
private static void addUserAgentMetadata(ExecutionAttributes executionAttributes, String name, String value) {
List<AdditionalMetadata> metadata = executionAttributes.getAttribute(
SdkInternalExecutionAttribute.USER_AGENT_METADATA);
if (metadata == null) {
metadata = new ArrayList<>();
executionAttributes.putAttribute(SdkInternalExecutionAttribute.USER_AGENT_METADATA, metadata);
}
metadata.add(
AdditionalMetadata
.builder()
.name(name)
.value(value)
.build());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.core.useragent.AdditionalMetadata;
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
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;
Expand Down Expand Up @@ -510,6 +512,51 @@ public void invokeInterceptorsAndCreateExecutionContext_withAsyncResponseTransfo
);
}

@Test
public void invokeInterceptorsAndCreateExecutionContext_withDefaultHttpClient_addsAutoFeatureId() {
SdkClientConfiguration clientConfig = testClientConfiguration()
.option(SdkClientOption.HTTP_CLIENT_CONFIG_TYPE, BusinessMetricFeatureId.HTTP_CLIENT_AUTO)
.build();

ExecutionContext executionContext =
AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), clientConfig);

ExecutionAttributes executionAttributes = executionContext.executionAttributes();
BusinessMetricCollection businessMetrics =
executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
assertThat(businessMetrics.recordedMetrics()).contains("AJ");
}

@Test
public void invokeInterceptorsAndCreateExecutionContext_withExplicitHttpClientInstance_addsExplicitInstanceFeatureId() {
SdkClientConfiguration clientConfig = testClientConfiguration()
.option(SdkClientOption.HTTP_CLIENT_CONFIG_TYPE, BusinessMetricFeatureId.HTTP_CLIENT_EXPLICIT_INSTANCE)
.build();

ExecutionContext executionContext =
AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), clientConfig);

ExecutionAttributes executionAttributes = executionContext.executionAttributes();
BusinessMetricCollection businessMetrics =
executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
assertThat(businessMetrics.recordedMetrics()).contains("AK");
}

@Test
public void invokeInterceptorsAndCreateExecutionContext_withExplicitHttpClientFactory_addsExplicitFactoryFeatureId() {
SdkClientConfiguration clientConfig = testClientConfiguration()
.option(SdkClientOption.HTTP_CLIENT_CONFIG_TYPE, BusinessMetricFeatureId.HTTP_CLIENT_EXPLICIT_FACTORY)
.build();

ExecutionContext executionContext =
AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), clientConfig);

ExecutionAttributes executionAttributes = executionContext.executionAttributes();
BusinessMetricCollection businessMetrics =
executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
assertThat(businessMetrics.recordedMetrics()).contains("AL");
}

private ClientExecutionParams<SdkRequest, SdkResponse> clientExecutionParams() {
return clientExecutionParams(sdkRequest);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import static software.amazon.awssdk.core.client.config.SdkClientOption.DEFAULT_RETRY_MODE;
import static software.amazon.awssdk.core.client.config.SdkClientOption.EXECUTION_INTERCEPTORS;
import static software.amazon.awssdk.core.client.config.SdkClientOption.HTTP_CLIENT_CONFIG;
import static software.amazon.awssdk.core.client.config.SdkClientOption.HTTP_CLIENT_CONFIG_TYPE;
import static software.amazon.awssdk.core.client.config.SdkClientOption.IDENTITY_PROVIDERS;
import static software.amazon.awssdk.core.client.config.SdkClientOption.INTERNAL_USER_AGENT;
import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHERS;
Expand Down Expand Up @@ -96,6 +97,7 @@
import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder;
import software.amazon.awssdk.core.internal.useragent.UserAgentConstant;
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.core.util.SystemUserAgent;
import software.amazon.awssdk.http.ExecutableHttpRequest;
import software.amazon.awssdk.http.HttpExecuteRequest;
Expand Down Expand Up @@ -310,34 +312,57 @@ protected SdkClientConfiguration finalizeChildConfiguration(SdkClientConfigurati
*/
private SdkClientConfiguration finalizeSyncConfiguration(SdkClientConfiguration config) {
return config.toBuilder()
.option(HTTP_CLIENT_CONFIG_TYPE, resolveSyncHttpClientConfigType(config))
.lazyOption(SdkClientOption.SYNC_HTTP_CLIENT, c -> resolveSyncHttpClient(c, config))
.option(SdkClientOption.CLIENT_TYPE, SYNC)
.build();
}

private BusinessMetricFeatureId resolveSyncHttpClientConfigType(SdkClientConfiguration config) {
if (config.option(CONFIGURED_SYNC_HTTP_CLIENT) != null) {
return BusinessMetricFeatureId.HTTP_CLIENT_EXPLICIT_INSTANCE;
}
if (config.option(CONFIGURED_SYNC_HTTP_CLIENT_BUILDER) != null) {
return BusinessMetricFeatureId.HTTP_CLIENT_EXPLICIT_FACTORY;
}
return BusinessMetricFeatureId.HTTP_CLIENT_AUTO;
}

/**
* Finalize async-specific configuration from the default-applied configuration.
*/
private SdkClientConfiguration finalizeAsyncConfiguration(SdkClientConfiguration config) {
return config.toBuilder()
.lazyOptionIfAbsent(FUTURE_COMPLETION_EXECUTOR, this::resolveAsyncFutureCompletionExecutor)
.option(HTTP_CLIENT_CONFIG_TYPE, resolveAsyncHttpClientConfigType(config))
.lazyOption(ASYNC_HTTP_CLIENT, c -> resolveAsyncHttpClient(c, config))
.option(SdkClientOption.CLIENT_TYPE, ASYNC)
.build();
}

private BusinessMetricFeatureId resolveAsyncHttpClientConfigType(SdkClientConfiguration config) {
if (config.option(CONFIGURED_ASYNC_HTTP_CLIENT) != null) {
return BusinessMetricFeatureId.HTTP_CLIENT_EXPLICIT_INSTANCE;
}
if (config.option(CONFIGURED_ASYNC_HTTP_CLIENT_BUILDER) != null) {
return BusinessMetricFeatureId.HTTP_CLIENT_EXPLICIT_FACTORY;
}
return BusinessMetricFeatureId.HTTP_CLIENT_AUTO;
}

/**
* Finalize global configuration from the default-applied configuration.
*/
private SdkClientConfiguration finalizeConfiguration(SdkClientConfiguration config) {
return config.toBuilder()
SdkClientConfiguration.Builder builder = config.toBuilder()
.lazyOption(SCHEDULED_EXECUTOR_SERVICE, this::resolveScheduledExecutorService)
.lazyOptionIfAbsent(RETRY_STRATEGY, this::resolveRetryStrategy)
.option(EXECUTION_INTERCEPTORS, resolveExecutionInterceptors(config))
.lazyOption(CLIENT_USER_AGENT, this::resolveClientUserAgent)
.lazyOption(COMPRESSION_CONFIGURATION, this::resolveCompressionConfiguration)
.lazyOptionIfAbsent(IDENTITY_PROVIDERS, c -> IdentityProviders.builder().build())
.build();
.lazyOptionIfAbsent(IDENTITY_PROVIDERS, c -> IdentityProviders.builder().build());
builder.computeOptionIfAbsent(HTTP_CLIENT_CONFIG_TYPE, () -> BusinessMetricFeatureId.HTTP_CLIENT_AUTO);
return builder.build();
}

private CompressionConfiguration resolveCompressionConfiguration(LazyValueSource config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.endpoints.EndpointProvider;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
Expand Down Expand Up @@ -196,6 +197,21 @@ public final class SdkClientOption<T> extends ClientOption<T> {
public static final SdkClientOption<SdkHttpClient.Builder<?>> CONFIGURED_SYNC_HTTP_CLIENT_BUILDER =
new SdkClientOption<>(new UnsafeValueType(SdkAsyncHttpClient.Builder.class));

/**
* The HTTP client configuration type indicating how the HTTP client was selected.
* <p>
* Possible values:
* <ul>
* <li>{@link BusinessMetricFeatureId#HTTP_CLIENT_AUTO} - HTTP client was auto-detected from the classpath</li>
* <li>{@link BusinessMetricFeatureId#HTTP_CLIENT_EXPLICIT_INSTANCE} - HTTP client was explicitly provided
* via {@code httpClient()}</li>
* <li>{@link BusinessMetricFeatureId#HTTP_CLIENT_EXPLICIT_FACTORY} - HTTP client factory was explicitly provided
* via {@code httpClientBuilder()}</li>
* </ul>
*/
public static final SdkClientOption<BusinessMetricFeatureId> HTTP_CLIENT_CONFIG_TYPE =
new SdkClientOption<>(BusinessMetricFeatureId.class);

/**
* Configuration that should be used to build the {@link #SYNC_HTTP_CLIENT} or {@link #ASYNC_HTTP_CLIENT}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public enum BusinessMetricFeatureId {
FLEXIBLE_CHECKSUMS_REQ_XXHASH3("AG"),
FLEXIBLE_CHECKSUMS_REQ_XXHASH64("AH"),
FLEXIBLE_CHECKSUMS_REQ_XXHASH128("AI"),
HTTP_CLIENT_AUTO("AJ"),
HTTP_CLIENT_EXPLICIT_INSTANCE("AK"),
HTTP_CLIENT_EXPLICIT_FACTORY("AL"),
DDB_MAPPER("d"),
BEARER_SERVICE_ENV_VARS("3"),
CREDENTIALS_CODE("e"),
Expand Down
Loading
Loading