Skip to content

Commit f8a440c

Browse files
authored
adds DNS resolver configuration option for ApacheHttpClient (#2834)
1 parent 821a342 commit f8a440c

File tree

4 files changed

+122
-2
lines changed

4 files changed

+122
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "Apache HTTP Client",
3+
"contributor": "",
4+
"type": "feature",
5+
"description": "Add DNS resolver override support for Apache HTTP Client"
6+
}

http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ApacheHttpClient.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.apache.http.config.RegistryBuilder;
5252
import org.apache.http.config.SocketConfig;
5353
import org.apache.http.conn.ConnectionKeepAliveStrategy;
54+
import org.apache.http.conn.DnsResolver;
5455
import org.apache.http.conn.HttpClientConnectionManager;
5556
import org.apache.http.conn.routing.HttpRoutePlanner;
5657
import org.apache.http.conn.socket.ConnectionSocketFactory;
@@ -393,6 +394,11 @@ public interface Builder extends SdkHttpClient.Builder<ApacheHttpClient.Builder>
393394
*/
394395
Builder useIdleConnectionReaper(Boolean useConnectionReaper);
395396

397+
/**
398+
* Configuration that defines a DNS resolver. If no matches are found, the default resolver is used.
399+
*/
400+
Builder dnsResolver(DnsResolver dnsResolver);
401+
396402
/**
397403
* Configuration that defines an HTTP route planner that computes the route an HTTP request should take.
398404
* May not be used in conjunction with {@link #proxyConfiguration(ProxyConfiguration)}.
@@ -441,6 +447,7 @@ private static final class DefaultBuilder implements Builder {
441447
private Boolean expectContinueEnabled;
442448
private HttpRoutePlanner httpRoutePlanner;
443449
private CredentialsProvider credentialsProvider;
450+
private DnsResolver dnsResolver;
444451

445452
private DefaultBuilder() {
446453
}
@@ -551,6 +558,16 @@ public void setUseIdleConnectionReaper(Boolean useIdleConnectionReaper) {
551558
useIdleConnectionReaper(useIdleConnectionReaper);
552559
}
553560

561+
@Override
562+
public Builder dnsResolver(DnsResolver dnsResolver) {
563+
this.dnsResolver = dnsResolver;
564+
return this;
565+
}
566+
567+
public void setDnsResolver(DnsResolver dnsResolver) {
568+
dnsResolver(dnsResolver);
569+
}
570+
554571
@Override
555572
public Builder httpRoutePlanner(HttpRoutePlanner httpRoutePlanner) {
556573
this.httpRoutePlanner = httpRoutePlanner;
@@ -620,7 +637,7 @@ public HttpClientConnectionManager create(ApacheHttpClient.DefaultBuilder config
620637
createSocketFactoryRegistry(sslsf),
621638
null,
622639
DefaultSchemePortResolver.INSTANCE,
623-
null,
640+
configuration.dnsResolver,
624641
standardOptions.get(SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE).toMillis(),
625642
TimeUnit.MILLISECONDS);
626643

http-clients/apache-client/src/test/java/software/amazon/awssdk/http/apache/ApacheHttpClientTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717

1818
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1919

20+
import java.net.InetAddress;
2021
import java.net.URI;
22+
import java.net.UnknownHostException;
2123
import org.apache.http.client.CredentialsProvider;
24+
import org.apache.http.conn.DnsResolver;
2225
import org.apache.http.conn.routing.HttpRoutePlanner;
26+
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
2327
import org.junit.After;
2428
import org.junit.Test;
2529
import org.mockito.Mockito;
@@ -112,7 +116,6 @@ public void credentialProviderCantBeUsedWithProxyCredentials_SystemProperties()
112116
}).isInstanceOf(IllegalArgumentException.class);
113117
}
114118

115-
116119
@Test
117120
public void credentialProviderCanBeUsedWithProxy() {
118121
ProxyConfiguration proxyConfig = ProxyConfiguration.builder()
@@ -123,4 +126,23 @@ public void credentialProviderCanBeUsedWithProxy() {
123126
.credentialsProvider(Mockito.mock(CredentialsProvider.class))
124127
.build();
125128
}
129+
130+
@Test
131+
public void dnsResolverCanBeUsed() {
132+
DnsResolver dnsResolver = new SystemDefaultDnsResolver() {
133+
@Override
134+
public InetAddress[] resolve(final String host) throws UnknownHostException {
135+
if (host.equalsIgnoreCase("my.host.com")) {
136+
return new InetAddress[] { InetAddress.getByName("127.0.0.1") };
137+
} else {
138+
return super.resolve(host);
139+
}
140+
}
141+
};
142+
143+
ApacheHttpClient.builder()
144+
.dnsResolver(dnsResolver)
145+
.build()
146+
.close();
147+
}
126148
}

http-clients/apache-client/src/test/java/software/amazon/awssdk/http/apache/ApacheHttpClientWireMockTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package software.amazon.awssdk.http.apache;
1717

1818
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
19+
import static com.github.tomakehurst.wiremock.client.WireMock.any;
1920
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
2021
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
2122
import static org.mockito.Mockito.verify;
@@ -25,21 +26,30 @@
2526
import com.github.tomakehurst.wiremock.client.WireMock;
2627
import com.github.tomakehurst.wiremock.junit.WireMockRule;
2728
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
29+
import java.io.IOException;
2830
import java.net.HttpURLConnection;
31+
import java.net.InetAddress;
32+
import java.net.URI;
33+
import java.net.UnknownHostException;
2934
import org.apache.http.HttpHost;
3035
import org.apache.http.auth.AuthScope;
3136
import org.apache.http.auth.Credentials;
3237
import org.apache.http.auth.UsernamePasswordCredentials;
3338
import org.apache.http.client.CredentialsProvider;
39+
import org.apache.http.conn.DnsResolver;
3440
import org.apache.http.conn.HttpClientConnectionManager;
3541
import org.apache.http.conn.routing.HttpRoute;
42+
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
3643
import org.junit.Rule;
3744
import org.junit.Test;
3845
import org.junit.runner.RunWith;
3946
import org.mockito.Mock;
4047
import org.mockito.runners.MockitoJUnitRunner;
48+
import software.amazon.awssdk.http.HttpExecuteRequest;
4149
import software.amazon.awssdk.http.SdkHttpClient;
4250
import software.amazon.awssdk.http.SdkHttpClientTestSuite;
51+
import software.amazon.awssdk.http.SdkHttpFullRequest;
52+
import software.amazon.awssdk.http.SdkHttpMethod;
4353
import software.amazon.awssdk.http.apache.internal.ApacheHttpRequestConfig;
4454
import software.amazon.awssdk.http.apache.internal.impl.ConnectionManagerAwareHttpClient;
4555
import software.amazon.awssdk.utils.AttributeMap;
@@ -148,4 +158,69 @@ public void clear() {
148158

149159
mockProxyServer.verify(2, RequestPatternBuilder.allRequests());
150160
}
161+
162+
@Test
163+
public void overrideDnsResolver_WithDnsMatchingResolver_successful() throws Exception {
164+
overrideDnsResolver("magic.local.host");
165+
}
166+
167+
@Test(expected = UnknownHostException.class)
168+
public void overrideDnsResolver_WithUnknownHost_throwsException() throws Exception {
169+
overrideDnsResolver("sad.local.host");
170+
}
171+
172+
@Test
173+
public void overrideDnsResolver_WithLocalhost_successful() throws Exception {
174+
overrideDnsResolver("localhost");
175+
}
176+
177+
@Test
178+
public void explicitNullDnsResolver_WithLocalhost_successful() throws Exception {
179+
overrideDnsResolver("localhost", true);
180+
}
181+
182+
private void overrideDnsResolver(String hostName) throws IOException {
183+
overrideDnsResolver(hostName, false);
184+
}
185+
186+
private void overrideDnsResolver(String hostName, boolean nullifyResolver) throws IOException {
187+
188+
DnsResolver dnsResolver = new SystemDefaultDnsResolver() {
189+
@Override
190+
public InetAddress[] resolve(final String host) throws UnknownHostException {
191+
if (host.equalsIgnoreCase("magic.local.host")) {
192+
return new InetAddress[] { InetAddress.getByName("127.0.0.1") };
193+
} else {
194+
return super.resolve(host);
195+
}
196+
}
197+
};
198+
if (nullifyResolver) {
199+
dnsResolver = null;
200+
}
201+
202+
SdkHttpClient client = ApacheHttpClient.builder()
203+
.dnsResolver(dnsResolver)
204+
.buildWithDefaults(AttributeMap.builder()
205+
.put(TRUST_ALL_CERTIFICATES, Boolean.TRUE)
206+
.build());
207+
208+
mockProxyServer.resetToDefaultMappings();
209+
mockProxyServer.stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK)));
210+
211+
URI uri = URI.create("https://" + hostName + ":" + mockProxyServer.httpsPort());
212+
SdkHttpFullRequest req = SdkHttpFullRequest.builder()
213+
.uri(uri)
214+
.method(SdkHttpMethod.POST)
215+
.putHeader("Host", uri.getHost())
216+
.build();
217+
218+
client.prepareRequest(HttpExecuteRequest.builder()
219+
.request(req)
220+
.contentStreamProvider(req.contentStreamProvider().orElse(null))
221+
.build())
222+
.call();
223+
224+
mockProxyServer.verify(1, RequestPatternBuilder.allRequests());
225+
}
151226
}

0 commit comments

Comments
 (0)