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
|
* [HTTPCLIENT-1120] DefaultHttpRequestRetryHandler#retryRequest should not retry aborted requests
|
||||||
Contributed by Alin Vasile <alinachegalati at yahoo dot com>
|
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
|
* [HTTPCLIENT-1100] Missing Content-Length header makes cached entry invalid
|
||||||
Contributed by Bart Robeyns <bart dot robeyns at gmail dot com>
|
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.
|
do not correctly handle content streaming.
|
||||||
Contributed by James Abley <james.abley at gmail.com>
|
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
|
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.SchemeRegistry;
|
||||||
import org.apache.http.conn.scheme.SchemeSocketFactory;
|
import org.apache.http.conn.scheme.SchemeSocketFactory;
|
||||||
|
|
||||||
|
import org.apache.http.conn.DnsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry}
|
* Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry}
|
||||||
* to look up {@link SchemeSocketFactory} objects.
|
* to look up {@link SchemeSocketFactory} objects.
|
||||||
|
@ -90,6 +92,9 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
|
||||||
/** The scheme registry for looking up socket factories. */
|
/** The scheme registry for looking up socket factories. */
|
||||||
protected final SchemeRegistry schemeRegistry; // @ThreadSafe
|
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.
|
* 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");
|
throw new IllegalArgumentException("Scheme registry amy not be null");
|
||||||
}
|
}
|
||||||
this.schemeRegistry = schemes;
|
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() {
|
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
|
* Resolves the given host name to an array of corresponding IP addresses, based on the
|
||||||
* configured name service on the system.
|
* 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
|
* @param host host name to resolve
|
||||||
* @return array of IP addresses
|
* @return array of IP addresses
|
||||||
* @exception UnknownHostException if no IP address for the host could be determined.
|
* @exception UnknownHostException if no IP address for the host could be determined.
|
||||||
|
@ -240,7 +269,11 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
protected InetAddress[] resolveHostname(final String host) throws UnknownHostException {
|
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.pool.PoolStats;
|
||||||
import org.apache.http.impl.conn.DefaultClientConnectionOperator;
|
import org.apache.http.impl.conn.DefaultClientConnectionOperator;
|
||||||
import org.apache.http.impl.conn.SchemeRegistryFactory;
|
import org.apache.http.impl.conn.SchemeRegistryFactory;
|
||||||
|
import org.apache.http.conn.DnsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages a pool of {@link OperatedClientConnection client connections} and
|
* Manages a pool of {@link OperatedClientConnection client connections} and
|
||||||
|
@ -77,10 +78,17 @@ public class PoolingClientConnectionManager implements ClientConnectionManager,
|
||||||
|
|
||||||
private final ClientConnectionOperator operator;
|
private final ClientConnectionOperator operator;
|
||||||
|
|
||||||
|
/** the custom-configured DNS lookup mechanism. */
|
||||||
|
private final DnsResolver dnsResolver;
|
||||||
|
|
||||||
public PoolingClientConnectionManager(final SchemeRegistry schreg) {
|
public PoolingClientConnectionManager(final SchemeRegistry schreg) {
|
||||||
this(schreg, -1, TimeUnit.MILLISECONDS);
|
this(schreg, -1, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) {
|
||||||
|
this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver);
|
||||||
|
}
|
||||||
|
|
||||||
public PoolingClientConnectionManager() {
|
public PoolingClientConnectionManager() {
|
||||||
this(SchemeRegistryFactory.createDefault());
|
this(SchemeRegistryFactory.createDefault());
|
||||||
}
|
}
|
||||||
|
@ -93,6 +101,20 @@ public class PoolingClientConnectionManager implements ClientConnectionManager,
|
||||||
throw new IllegalArgumentException("Scheme registry may not be null");
|
throw new IllegalArgumentException("Scheme registry may not be null");
|
||||||
}
|
}
|
||||||
this.schemeRegistry = schemeRegistry;
|
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.operator = createConnectionOperator(schemeRegistry);
|
||||||
this.pool = new HttpConnPool(this.log, 2, 20, timeToLive, tunit);
|
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
|
* @return the connection operator to use
|
||||||
*/
|
*/
|
||||||
protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
|
protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
|
||||||
return new DefaultClientConnectionOperator(schreg);
|
return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SchemeRegistry getSchemeRegistry() {
|
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