HTTPCLIENT-1123: Support for pluggable DNS resolvers.

Contributed by Alin Vasile <alinachegalati at yahoo dot com>


git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1165635 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2011-09-06 12:20:26 +00:00
parent 5d554ccf26
commit ce13e3606f
6 changed files with 320 additions and 3 deletions

View File

@ -1,8 +1,18 @@
Changes since 4.1.1
Changes since 4.1.2
* [HTTPCLIENT-1123] Support for pluggable DNS resolvers.
Contributed by Alin Vasile <alinachegalati at yahoo dot com>
* [HTTPCLIENT-1120] DefaultHttpRequestRetryHandler#retryRequest should not retry aborted requests
Contributed by Alin Vasile <alinachegalati at yahoo dot com>
Release 4.1.2
-------------------
The HttpClient 4.1.2 is a bug fix release that addresses a number of non-critical issues reported
since release 4.1.1.
* [HTTPCLIENT-1100] Missing Content-Length header makes cached entry invalid
Contributed by Bart Robeyns <bart dot robeyns at gmail dot com>
@ -32,6 +42,9 @@ Changes since 4.1.1
do not correctly handle content streaming.
Contributed by James Abley <james.abley at gmail.com>
* [HTTPCLIENT-1051] Avoid reverse DNS lookups when opening SSL connections by IP address.
Contributed by Oleg Kalnichevski <olegk at apache.org>
Release 4.1.1
-------------------

View File

@ -0,0 +1,52 @@
/*
* ====================================================================
* 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
* <http://www.apache.org/>.
*
*/
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;
}

View File

@ -53,6 +53,8 @@ import org.apache.http.conn.scheme.Scheme;
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.
@ -90,6 +92,9 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
/** 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,6 +105,26 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
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() {
@ -233,6 +258,10 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
* 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
* @exception UnknownHostException if no IP address for the host could be determined.
@ -240,7 +269,11 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
* @since 4.1
*/
protected InetAddress[] resolveHostname(final String host) throws UnknownHostException {
return InetAddress.getAllByName(host);
if (dnsResolver != null) {
return dnsResolver.resolve(host);
} else {
return InetAddress.getAllByName(host);
}
}
}

View File

@ -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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.conn;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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 InMemoryDnsResolver#add(String, String) method}.
*
* Currently this class supports only IPv4 addresses.
*
*/
public class InMemoryDnsResolver implements DnsResolver {
/** Logger associated to this class. */
private final Log log = LogFactory.getLog(InMemoryDnsResolver.class);
/**
* In-memory collection that will hold the associations between a host name
* and an array of InetAddress instances.
*/
private Map<String, InetAddress[]> dnsMap;
/**
* Builds a DNS resolver that will resolve the host names against a
* collection held in-memory.
*/
public InMemoryDnsResolver() {
dnsMap = new ConcurrentHashMap<String, InetAddress[]>();
}
/**
* 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;
}
}

View File

@ -47,6 +47,7 @@ import org.apache.http.pool.ConnPoolControl;
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
@ -77,10 +78,17 @@ public class PoolingClientConnectionManager implements ClientConnectionManager,
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,6 +101,20 @@ public class PoolingClientConnectionManager implements ClientConnectionManager,
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);
}
@ -119,7 +141,7 @@ public class PoolingClientConnectionManager implements ClientConnectionManager,
* @return the connection operator to use
*/
protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
return new DefaultClientConnectionOperator(schreg);
return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
}
public SchemeRegistry getSchemeRegistry() {

View File

@ -0,0 +1,78 @@
/*
* ====================================================================
* 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
* <http://www.apache.org/>.
*
*/
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() throws Exception {
DnsResolver dnsResolver = mock(DnsResolver.class);
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);
DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
SchemeRegistryFactory.createDefault(), dnsResolver);
Assert.assertArrayEquals(firstAddress, operator.resolveHostname("somehost.example.com"));
Assert.assertArrayEquals(secondAddress, operator.resolveHostname("otherhost.example.com"));
}
@Test(expected=UnknownHostException.class)
public void testDnsResolverUnknownHost() throws Exception {
DnsResolver dnsResolver = mock(DnsResolver.class);
when(dnsResolver.resolve("unknown.example.com")).thenThrow(new UnknownHostException());
DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
SchemeRegistryFactory.createDefault(), dnsResolver);
operator.resolveHostname("unknown.example.com");
}
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) };
}
}