Index: main/java/org/apache/http/conn/DnsResolver.java
===================================================================
--- main/java/org/apache/http/conn/DnsResolver.java (revision 0)
+++ main/java/org/apache/http/conn/DnsResolver.java (revision 0)
@@ -0,0 +1,53 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+package org.apache.http.conn;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Users may implement this interface to override the normal DNS lookup offered
+ * by the OS.
+ */
+
+public interface DnsResolver {
+
+ /**
+ * Returns the IP address for the specified host name, or null if the given
+ * host is not recognized or the associated IP address cannot be used to
+ * build an InetAddress instance.
+ *
+ * @see InetAddress
+ *
+ * @param host
+ * The host name to be resolved by this resolver.
+ * @return The IP address associated to the given host name, or null if the
+ * host name is not known by the implementation class.
+ */
+ InetAddress[] resolve(String host) throws UnknownHostException;
+
+}
\ No newline at end of file
Index: main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
===================================================================
--- main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java (revision 1164883)
+++ main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java (working copy)
@@ -53,6 +53,8 @@
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.conn.DnsResolver;
+
/**
* Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry}
* to look up {@link SchemeSocketFactory} objects.
@@ -89,6 +91,9 @@
/** The scheme registry for looking up socket factories. */
protected final SchemeRegistry schemeRegistry; // @ThreadSafe
+
+ /** the custom-configured DNS lookup mechanism. */
+ protected final DnsResolver dnsResolver;
/**
* Creates a new client connection operator for the given scheme registry.
@@ -100,7 +105,27 @@
throw new IllegalArgumentException("Scheme registry amy not be null");
}
this.schemeRegistry = schemes;
+ this.dnsResolver = null;
}
+
+ /**
+ * Creates a new client connection operator for the given scheme registry
+ * and the given custom DNS lookup mechanism.
+ *
+ * @param schemes
+ * the scheme registry
+ * @param dnsResolver
+ * the custom DNS lookup mechanism
+ */
+ public DefaultClientConnectionOperator(final SchemeRegistry schemes,final DnsResolver dnsResolver) {
+ if (schemes == null) {
+ throw new IllegalArgumentException(
+ "Scheme registry amy not be null");
+ }
+
+ this.schemeRegistry = schemes;
+ this.dnsResolver = dnsResolver;
+ }
public OperatedClientConnection createConnection() {
return new DefaultClientConnection();
@@ -232,6 +257,10 @@
/**
* Resolves the given host name to an array of corresponding IP addresses, based on the
* configured name service on the system.
+ *
+ * If a custom DNS resolver is provided, the given host will be searched in
+ * it first. If the host is not configured, the default OS DNS-lookup
+ * mechanism is used.
*
* @param host host name to resolve
* @return array of IP addresses
@@ -240,7 +269,18 @@
* @since 4.1
*/
protected InetAddress[] resolveHostname(final String host) throws UnknownHostException {
+ if (dnsResolver != null) {
+ try{
+ return dnsResolver.resolve(host);
+ } catch(UnknownHostException e ){
+ if(log.isInfoEnabled()){
+ log.info("Unable to resolve " + e + " using custom resolver, defaulting to system default configuration");
+ }
+ }
+ }
+
return InetAddress.getAllByName(host);
+
}
}
Index: main/java/org/apache/http/impl/conn/InMemoryDnsResolverImpl.java
===================================================================
--- main/java/org/apache/http/impl/conn/InMemoryDnsResolverImpl.java (revision 0)
+++ main/java/org/apache/http/impl/conn/InMemoryDnsResolverImpl.java (revision 0)
@@ -0,0 +1,119 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.util.InetAddressUtils;
+import org.apache.http.conn.DnsResolver;
+
+/**
+ * In-memory DNS resolver implementation with entries built using the
+ * {@link InMemoryDnsResolverImpl#add(String, String) method}.
+ *
+ * Currently this class supports only IPv4 addresses.
+ *
+ */
+public class InMemoryDnsResolverImpl implements DnsResolver {
+
+ /** Logger associated to this class. */
+ private static final Log log = LogFactory.getLog(InMemoryDnsResolverImpl.class);
+
+ /**
+ * In-memory collection that will hold the associations between a host name
+ * and an array of InetAddress instances.
+ */
+ private Map dnsMap;
+
+ /**
+ * Builds a DNS resolver that will resolve the host names against a
+ * collection held in-memory.
+ */
+ public InMemoryDnsResolverImpl() {
+ dnsMap = new HashMap();
+ }
+
+ /**
+ * Associates the given IP address to the given host in this DNS overrider.
+ *
+ * @param host
+ * The host name to be associated with the given IP.
+ * @param ip
+ * IPv4 address to be resolved by this DNS overrider to the given
+ * host name.
+ *
+ * @throws IllegalArgumentException
+ * if the given IP is not a valid IPv4 address or an InetAddress
+ * instance cannot be built based on the given IPv4 address.
+ *
+ * @see InetAddress#getByAddress
+ */
+ public void add(final String host, final String ip) {
+ if (!InetAddressUtils.isIPv4Address(ip)) {
+ throw new IllegalArgumentException(ip + " must be a valid IPv4 address");
+ }
+
+ String[] ipParts = ip.split("\\.");
+
+ byte[] byteIpAddress = new byte[4];
+
+ for (int i = 0; i < 4; i++) {
+ byteIpAddress[i] = Integer.decode(ipParts[i]).byteValue();
+ }
+
+ try {
+ dnsMap.put(host, new InetAddress[] { InetAddress.getByAddress(byteIpAddress) });
+ } catch (UnknownHostException e) {
+ log.error("Unable to build InetAddress for " + ip, e);
+ throw new IllegalArgumentException(e);
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public InetAddress[] resolve(String host) throws UnknownHostException {
+ InetAddress[] resolvedAddresses = dnsMap.get(host);
+ if (log.isInfoEnabled()) {
+ log.info("Resolving " + host + " to " + Arrays.deepToString(resolvedAddresses));
+ }
+
+ if(resolvedAddresses == null){
+ throw new UnknownHostException(host + " cannot be resolved.");
+ }
+
+ return resolvedAddresses;
+ }
+
+}
\ No newline at end of file
Index: main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java
===================================================================
--- main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java (revision 1164883)
+++ main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java (working copy)
@@ -47,6 +47,7 @@
import org.apache.http.pool.PoolStats;
import org.apache.http.impl.conn.DefaultClientConnectionOperator;
import org.apache.http.impl.conn.SchemeRegistryFactory;
+import org.apache.http.conn.DnsResolver;
/**
* Manages a pool of {@link OperatedClientConnection client connections} and
@@ -76,10 +77,17 @@
private final HttpConnPool pool;
private final ClientConnectionOperator operator;
+
+ /** the custom-configured DNS lookup mechanism. */
+ private final DnsResolver dnsResolver;
public PoolingClientConnectionManager(final SchemeRegistry schreg) {
this(schreg, -1, TimeUnit.MILLISECONDS);
}
+
+ public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) {
+ this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver);
+ }
public PoolingClientConnectionManager() {
this(SchemeRegistryFactory.createDefault());
@@ -93,9 +101,23 @@
throw new IllegalArgumentException("Scheme registry may not be null");
}
this.schemeRegistry = schemeRegistry;
+ this.dnsResolver = null;
this.operator = createConnectionOperator(schemeRegistry);
this.pool = new HttpConnPool(this.log, 2, 20, timeToLive, tunit);
}
+
+ public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry,
+ final long timeToLive, final TimeUnit tunit,
+ final DnsResolver dnsResolver) {
+ super();
+ if (schemeRegistry == null) {
+ throw new IllegalArgumentException("Scheme registry may not be null");
+ }
+ this.schemeRegistry = schemeRegistry;
+ this.dnsResolver = dnsResolver;
+ this.operator = createConnectionOperator(schemeRegistry);
+ this.pool = new HttpConnPool(this.log, 2, 20, timeToLive, tunit);
+ }
@Override
protected void finalize() throws Throwable {
@@ -119,7 +141,7 @@
* @return the connection operator to use
*/
protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
- return new DefaultClientConnectionOperator(schreg);
+ return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
}
public SchemeRegistry getSchemeRegistry() {
Index: test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java
===================================================================
--- test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java (revision 0)
+++ test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java (revision 0)
@@ -0,0 +1,100 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+package org.apache.http.impl.conn;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.http.conn.DnsResolver;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestDefaultClientConnectOperator {
+
+ @Test
+ public void testCustomDnsResolver() {
+ DnsResolver dnsResolver = mock(DnsResolver.class);
+
+ try {
+ InetAddress[] firstAddress = translateIp("192.168.1.1");
+ when(dnsResolver.resolve("somehost.example.com")).thenReturn(firstAddress);
+
+ InetAddress[] secondAddress = translateIp("192.168.12.16");
+ when(dnsResolver.resolve("otherhost.example.com")).thenReturn(secondAddress);
+
+ when(dnsResolver.resolve("unknown.example.com")).thenThrow(new UnknownHostException());
+
+ DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+ SchemeRegistryFactory.createDefault(), dnsResolver);
+
+ Assert.assertArrayEquals(firstAddress, operator.resolveHostname("somehost.example.com"));
+ Assert.assertArrayEquals(secondAddress, operator.resolveHostname("otherhost.example.com"));
+
+ try {
+ operator.resolveHostname("unknown.example.com");
+ Assert.fail("unknown.example.com shouldn't be resolved");
+ } catch (UnknownHostException ex) {
+ // expected
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.fail("Unexpected exception " + e);
+ }
+
+ }
+
+ @Test
+ public void testNullDnsResolver() {
+ DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+ SchemeRegistryFactory.createDefault());
+
+ try {
+ operator.resolveHostname("unknown.example.com");
+ Assert.fail("unknown.example.com shouldn't be resolved");
+ } catch (UnknownHostException ex) {
+ // expected
+ }
+ }
+
+ private InetAddress[] translateIp(String ip) throws UnknownHostException {
+ String[] ipParts = ip.split("\\.");
+
+ byte[] byteIpAddress = new byte[4];
+ for (int i = 0; i < 4; i++) {
+ byteIpAddress[i] = Integer.decode(ipParts[i]).byteValue();
+ }
+
+ return new InetAddress[] { InetAddress.getByAddress(byteIpAddress) };
+
+ }
+
+}
\ No newline at end of file