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-eeeef1c.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 support for resolving the disable host prefix injection setting from the aws.disableHostPrefixInjection system property, the AWS_DISABLE_HOST_PREFIX_INJECTION environment variable, and the disable_host_prefix_injection profile property."
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import software.amazon.awssdk.awscore.retry.AwsRetryStrategy;
import software.amazon.awssdk.core.ClientEndpointProvider;
import software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder;
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.http.EnableDefaultSocketTimeout2026Resolver;
Expand Down Expand Up @@ -146,7 +145,6 @@ protected AwsDefaultClientBuilder() {
protected final SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) {
SdkClientConfiguration config = mergeServiceDefaults(configuration);
config = config.merge(c -> c.option(AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION, true)
.option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, false)
.option(AwsClientOption.SERVICE_SIGNING_NAME, signingName())
.option(SdkClientOption.SERVICE_NAME, serviceName())
.option(AwsClientOption.ENDPOINT_PREFIX, serviceEndpointPrefix()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static software.amazon.awssdk.awscore.client.config.AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION;
import static software.amazon.awssdk.awscore.client.config.AwsClientOption.SERVICE_SIGNING_NAME;
import static software.amazon.awssdk.awscore.client.config.AwsClientOption.SIGNING_REGION;
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION;
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.SIGNER;

import java.beans.BeanInfo;
Expand All @@ -37,6 +38,7 @@
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -53,6 +55,7 @@
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.SdkSystemSetting;
import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.SdkHttpClient;
Expand Down Expand Up @@ -99,6 +102,32 @@ public class DefaultAwsClientBuilderTest {
public void setup() {
when(defaultHttpClientBuilder.buildWithDefaults(any())).thenReturn(mock(SdkHttpClient.class));
when(defaultAsyncHttpClientFactory.buildWithDefaults(any())).thenReturn(mock(SdkAsyncHttpClient.class));
System.clearProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property());
}

@AfterEach
public void cleanup() {
System.clearProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property());
}

@Test
public void disableHostPrefixInjection_nothingSet_defaultsToFalse() {
TestClient client = testClientBuilder().region(Region.US_WEST_1).build();
assertThat(client.clientConfiguration.option(DISABLE_HOST_PREFIX_INJECTION)).isFalse();
}

@Test
public void disableHostPrefixInjection_systemPropertyTrue_resolvesTrue() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "true");
TestClient client = testClientBuilder().region(Region.US_WEST_1).build();
assertThat(client.clientConfiguration.option(DISABLE_HOST_PREFIX_INJECTION)).isTrue();
}

@Test
public void disableHostPrefixInjection_asyncClient_systemPropertyTrue_resolvesTrue() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "true");
TestAsyncClient client = testAsyncClientBuilder().region(Region.US_WEST_1).build();
assertThat(client.clientConfiguration.option(DISABLE_HOST_PREFIX_INJECTION)).isTrue();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ public final class ProfileProperty {
*/
public static final String DISABLE_REQUEST_COMPRESSION = "disable_request_compression";

/**
* Whether the SDK should skip prepending the modeled hostPrefix to the endpoint host. The default value is false, i.e.,
* the hostPrefix is prepended.
*/
public static final String DISABLE_HOST_PREFIX_INJECTION = "disable_host_prefix_injection";

/**
* The minimum compression size in bytes, inclusive, for a request to be compressed. The default value is 10_240.
* The value must be non-negative and no greater than 10_485_760.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ public enum SdkSystemSetting implements SystemSetting {
*/
AWS_DISABLE_REQUEST_COMPRESSION("aws.disableRequestCompression", null),

/**
* Whether the SDK should skip prepending the modeled hostPrefix to the endpoint host. The default value is false, i.e.,
* the hostPrefix is prepended.
*/
AWS_DISABLE_HOST_PREFIX_INJECTION("aws.disableHostPrefixInjection", null),

/**
* Defines the minimum compression size in bytes, inclusive, for a request to be compressed. The default value is 10_240.
* The value must be non-negative and no greater than 10_485_760.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static software.amazon.awssdk.core.ClientType.ASYNC;
import static software.amazon.awssdk.core.ClientType.SYNC;
import static software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR;
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION;
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.USER_AGENT_PREFIX;
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.USER_AGENT_SUFFIX;
import static software.amazon.awssdk.core.client.config.SdkClientOption.ADDITIONAL_HTTP_HEADERS;
Expand Down Expand Up @@ -360,7 +361,8 @@ private SdkClientConfiguration finalizeConfiguration(SdkClientConfiguration conf
.option(EXECUTION_INTERCEPTORS, resolveExecutionInterceptors(config))
.lazyOption(CLIENT_USER_AGENT, this::resolveClientUserAgent)
.lazyOption(COMPRESSION_CONFIGURATION, this::resolveCompressionConfiguration)
.lazyOptionIfAbsent(IDENTITY_PROVIDERS, c -> IdentityProviders.builder().build());
.lazyOptionIfAbsent(IDENTITY_PROVIDERS, c -> IdentityProviders.builder().build())
.lazyOptionIfAbsent(DISABLE_HOST_PREFIX_INJECTION, this::resolveDisableHostPrefixInjection);
builder.computeOptionIfAbsent(HTTP_CLIENT_CONFIG_TYPE, () -> BusinessMetricFeatureId.HTTP_CLIENT_AUTO);
return builder.build();
}
Expand Down Expand Up @@ -390,6 +392,17 @@ private Boolean resolveCompressionEnabled(LazyValueSource config, CompressionCon
.orElse(true);
}

private Boolean resolveDisableHostPrefixInjection(LazyValueSource config) {
Supplier<Optional<Boolean>> profileFileConfiguration =
() -> config.get(PROFILE_FILE_SUPPLIER).get()
.profile(config.get(PROFILE_NAME))
.flatMap(p -> p.booleanProperty(ProfileProperty.DISABLE_HOST_PREFIX_INJECTION));

return OptionalUtils.firstPresent(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.getBooleanValue(),
profileFileConfiguration)
.orElse(false);
}

private Integer resolveMinCompressionThreshold(LazyValueSource config, CompressionConfiguration compressionConfig) {
Supplier<Optional<Integer>> systemSettingConfiguration =
SdkSystemSetting.AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES::getIntegerValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ public class SdkAdvancedClientOption<T> extends ClientOption<T> {
* the endpoint host that the API request is sent to.
*
* Customers can set this value to True to disable the behavior.
*
* <p>If this option is not set, the SDK resolves it from the following sources, in order:
* <ol>
* <li>The {@code aws.disableHostPrefixInjection} system property</li>
* <li>The {@code AWS_DISABLE_HOST_PREFIX_INJECTION} environment variable</li>
* <li>The {@code disable_host_prefix_injection} property in the configured profile</li>
* </ol>
* If none of them is set, the default is false.
*/
public static final SdkAdvancedClientOption<Boolean> DISABLE_HOST_PREFIX_INJECTION =
new SdkAdvancedClientOption<>(Boolean.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -72,6 +73,7 @@
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import software.amazon.awssdk.core.ClientEndpointProvider;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
Expand All @@ -89,6 +91,7 @@
import software.amazon.awssdk.metrics.MetricCollection;
import software.amazon.awssdk.metrics.MetricPublisher;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.testutils.EnvironmentVariableHelper;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.StringInputStream;

Expand All @@ -111,6 +114,9 @@ public class DefaultClientBuilderTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

@Rule
public EnvironmentVariableHelper environmentVariableHelper = new EnvironmentVariableHelper();

@Mock
private SdkHttpClient.Builder defaultHttpClientFactory;

Expand All @@ -121,6 +127,12 @@ public class DefaultClientBuilderTest {
public void setup() {
when(defaultHttpClientFactory.buildWithDefaults(any())).thenReturn(mock(SdkHttpClient.class));
when(defaultAsyncHttpClientFactory.buildWithDefaults(any())).thenReturn(mock(SdkAsyncHttpClient.class));
System.clearProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property());
}

@After
public void cleanup() {
System.clearProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property());
}

@Test
Expand Down Expand Up @@ -520,6 +532,108 @@ public void defaultProfileFileSupplier_isStaticOrHasIdentityCaching() {
assertThat(secondGet).isSameAs(firstGet);
}

@Test
public void disableHostPrefixInjection_nothingSet_defaultsToFalse() {
assertThat(disableHostPrefixInjection(null, null)).isFalse();
}

@Test
public void disableHostPrefixInjection_asyncClient_nothingSet_defaultsToFalse() {
SdkClientConfiguration config = testAsyncClientBuilder()
.overrideConfiguration(o -> o.defaultProfileFile(profileFile(""))
.defaultProfileName("p"))
.build().clientConfiguration;
assertThat(config.option(DISABLE_HOST_PREFIX_INJECTION)).isFalse();
}

@Test
public void disableHostPrefixInjection_systemPropertyTrue_resolvesTrue() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "TRUE");
assertThat(disableHostPrefixInjection(null, null)).isTrue();
}

@Test
public void disableHostPrefixInjection_asyncClient_systemPropertyTrue_resolvesTrue() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "true");
SdkClientConfiguration config = testAsyncClientBuilder().build().clientConfiguration;
assertThat(config.option(DISABLE_HOST_PREFIX_INJECTION)).isTrue();
}

@Test
public void disableHostPrefixInjection_envVarTrue_resolvesTrue() {
environmentVariableHelper.set(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION, "true");
assertThat(disableHostPrefixInjection(null, null)).isTrue();
}

@Test
public void disableHostPrefixInjection_profileTrue_resolvesTrue() {
assertThat(disableHostPrefixInjection(null, "[profile p]\ndisable_host_prefix_injection = True")).isTrue();
}

@Test
public void disableHostPrefixInjection_profileTrueInOtherProfile_isIgnored() {
assertThat(disableHostPrefixInjection(null, "[profile other]\ndisable_host_prefix_injection = true")).isFalse();
}

@Test
public void disableHostPrefixInjection_explicitFalse_beatsSystemProperty() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "true");
assertThat(disableHostPrefixInjection(Boolean.FALSE, null)).isFalse();
}

@Test
public void disableHostPrefixInjection_explicitTrue_beatsSystemProperty() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "false");
assertThat(disableHostPrefixInjection(Boolean.TRUE, null)).isTrue();
}

@Test
public void disableHostPrefixInjection_systemPropertyFalse_beatsEnvVar() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "false");
environmentVariableHelper.set(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION, "true");
assertThat(disableHostPrefixInjection(null, null)).isFalse();
}

@Test
public void disableHostPrefixInjection_envVarFalse_beatsProfile() {
environmentVariableHelper.set(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION, "false");
assertThat(disableHostPrefixInjection(null, "[profile p]\ndisable_host_prefix_injection = true")).isFalse();
}

@Test
public void disableHostPrefixInjection_invalidSystemProperty_throwsOnBuild() {
System.setProperty(SdkSystemSetting.AWS_DISABLE_HOST_PREFIX_INJECTION.property(), "yes");
assertThatThrownBy(() -> disableHostPrefixInjection(null, null)).isInstanceOf(IllegalStateException.class);
}

@Test
public void disableHostPrefixInjection_invalidProfileValue_throwsOnBuild() {
assertThatThrownBy(() -> disableHostPrefixInjection(null, "[profile p]\ndisable_host_prefix_injection = yes"))
.isInstanceOf(IllegalStateException.class);
}

private Boolean disableHostPrefixInjection(Boolean explicitValue, String profileContent) {
ClientOverrideConfiguration.Builder overrideConfig =
ClientOverrideConfiguration.builder()
.putAdvancedOption(SIGNER, TEST_SIGNER)
.defaultProfileFile(profileFile(profileContent == null ? "" : profileContent))
.defaultProfileName("p");
if (explicitValue != null) {
overrideConfig.putAdvancedOption(DISABLE_HOST_PREFIX_INJECTION, explicitValue);
}
return new TestClientBuilder().overrideConfiguration(overrideConfig.build())
.build()
.clientConfiguration
.option(DISABLE_HOST_PREFIX_INJECTION);
}

private static ProfileFile profileFile(String content) {
return ProfileFile.builder()
.content(new StringInputStream(content))
.type(ProfileFile.Type.CONFIGURATION)
.build();
}

private SdkDefaultClientBuilder<TestClientBuilder, TestClient> testClientBuilder() {
ClientOverrideConfiguration overrideConfig =
ClientOverrideConfiguration.builder()
Expand Down
Loading
Loading