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:
parent
5d554ccf26
commit
ce13e3606f
|
@ -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
|
||||
-------------------
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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) };
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue