Index: src/examples/org/apache/http/examples/client/CustomDnsResolveClient.java =================================================================== --- src/examples/org/apache/http/examples/client/CustomDnsResolveClient.java (revision 0) +++ src/examples/org/apache/http/examples/client/CustomDnsResolveClient.java (revision 0) @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * 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.examples.client; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.InMemoryDnsResolverImpl; +import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.SchemeRegistryFactory; +import org.apache.http.util.EntityUtils; + +public class CustomDnsResolveClient { + + public static void main(String[] args) throws Exception { + InMemoryDnsResolverImpl dnsOverrider = new InMemoryDnsResolverImpl(); + dnsOverrider.add("www.google.ro", "74.125.39.105"); + dnsOverrider.add("somehost.example.org", "74.125.39.105"); + + DefaultHttpClient httpclient = new DefaultHttpClient( + new PoolingClientConnectionManager( + SchemeRegistryFactory.createDefault(), dnsOverrider)); + + HttpGet httpget = new HttpGet("http://somehost.example.org/"); + HttpResponse response = httpclient.execute(httpget); + HttpEntity entity = response.getEntity(); + + System.out.println("----------------------------------------"); + System.out.println(response.getStatusLine()); + if (entity != null) { + System.out.println("Response content length: " + + entity.getContentLength()); + } + System.out.println(EntityUtils.toString(entity)); + } + +} Index: src/examples/org/apache/http/examples/conn/CustomDnsResolverConnectDirect.java =================================================================== --- src/examples/org/apache/http/examples/conn/CustomDnsResolverConnectDirect.java (revision 0) +++ src/examples/org/apache/http/examples/conn/CustomDnsResolverConnectDirect.java (revision 0) @@ -0,0 +1,114 @@ +/* + * ==================================================================== + * 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.examples.conn; + +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.conn.ClientConnectionOperator; +import org.apache.http.conn.OperatedClientConnection; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.impl.conn.DefaultClientConnectionOperator; +import org.apache.http.impl.conn.InMemoryDnsResolverImpl; +import org.apache.http.message.BasicHttpRequest; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.apache.http.params.SyncBasicHttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.HttpContext; + +/** + * How to open a direct connection using {@link ClientConnectionOperator + * ClientConnectionOperator} configured with an {@link InMemoryDnsResolverImpl} + * DNS resolver. + * + */ +public class CustomDnsResolverConnectDirect { + + public static void main(String[] args) throws Exception { + // this host will be resolved to a Google public IP + // using an InMemory DNS Resolver + HttpHost target = new HttpHost("somehost.example.org", 80, "http"); + + // some general setup + // Register the "http" protocol scheme, it is required + // by the default operator to look up socket factories. + SchemeRegistry supportedSchemes = new SchemeRegistry(); + supportedSchemes.register(new Scheme("http", 80, PlainSocketFactory + .getSocketFactory())); + + // Prepare parameters. + // Since this example doesn't use the full core framework, + // only few parameters are actually required. + HttpParams params = new SyncBasicHttpParams(); + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); + HttpProtocolParams.setUseExpectContinue(params, false); + + // prepare an in-memory DNS overrider + InMemoryDnsResolverImpl dnsOverrider = new InMemoryDnsResolverImpl(); + dnsOverrider.add("www.google.ro", "74.125.39.105"); + dnsOverrider.add("somehost.example.org", "74.125.39.105"); + + // one operator can be used for many connections + ClientConnectionOperator scop = new DefaultClientConnectionOperator( + supportedSchemes, dnsOverrider); + + HttpRequest req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + req.addHeader("Host", target.getHostName()); + + HttpContext ctx = new BasicHttpContext(); + + OperatedClientConnection conn = scop.createConnection(); + try { + System.out.println("opening connection to " + target); + scop.openConnection(conn, target, null, ctx, params); + System.out.println("sending request"); + conn.sendRequestHeader(req); + // there is no request entity + conn.flush(); + + System.out.println("receiving response header"); + HttpResponse rsp = conn.receiveResponseHeader(); + + System.out.println("----------------------------------------"); + System.out.println(rsp.getStatusLine()); + Header[] headers = rsp.getAllHeaders(); + for (int i = 0; i < headers.length; i++) { + System.out.println(headers[i]); + } + System.out.println("----------------------------------------"); + } finally { + System.out.println("closing connection"); + conn.close(); + } + } + +} Index: src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java =================================================================== --- src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java (revision 1164883) +++ src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java (working copy) @@ -54,64 +54,87 @@ import org.apache.http.conn.scheme.SchemeSocketFactory; /** - * Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry} - * to look up {@link SchemeSocketFactory} objects. + * Default implementation of a {@link ClientConnectionOperator}. It uses a + * {@link SchemeRegistry} to look up {@link SchemeSocketFactory} objects. *

- * This connection operator is multihome network aware and will attempt to retry failed connects - * against all known IP addresses sequentially until the connect is successful or all known - * addresses fail to respond. Please note the same - * {@link org.apache.http.params.CoreConnectionPNames#CONNECTION_TIMEOUT} value will be used - * for each connection attempt, so in the worst case the total elapsed time before timeout - * can be CONNECTION_TIMEOUT * n where n is the number of IP addresses - * of the given host. One can disable multihome support by overriding - * the {@link #resolveHostname(String)} method and returning only one IP address for the given - * host name. + * This connection operator is multihome network aware and will attempt to retry + * failed connects against all known IP addresses sequentially until the connect + * is successful or all known addresses fail to respond. Please note the same + * {@link org.apache.http.params.CoreConnectionPNames#CONNECTION_TIMEOUT} value + * will be used for each connection attempt, so in the worst case the total + * elapsed time before timeout can be CONNECTION_TIMEOUT * n where + * n is the number of IP addresses of the given host. One can + * disable multihome support by overriding the {@link #resolveHostname(String)} + * method and returning only one IP address for the given host name. *

- * The following parameters can be used to customize the behavior of this - * class: + * The following parameters can be used to customize the behavior of this class: *

- * + * * @since 4.0 */ @ThreadSafe -public class DefaultClientConnectionOperator implements ClientConnectionOperator { +public class DefaultClientConnectionOperator implements + ClientConnectionOperator { private final Log log = LogFactory.getLog(getClass()); /** 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. - * - * @param schemes the scheme registry + * + * @param schemes + * the scheme registry */ public DefaultClientConnectionOperator(final SchemeRegistry schemes) { if (schemes == null) { - throw new IllegalArgumentException("Scheme registry amy not be null"); + 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(); } - public void openConnection( - final OperatedClientConnection conn, - final HttpHost target, - final InetAddress local, - final HttpContext context, - final HttpParams params) throws IOException { + public void openConnection(final OperatedClientConnection conn, + final HttpHost target, final InetAddress local, + final HttpContext context, final HttpParams params) + throws IOException { if (conn == null) { throw new IllegalArgumentException("Connection may not be null"); } @@ -137,7 +160,8 @@ Socket sock = sf.createSocket(params); conn.opening(sock, target); - InetSocketAddress remoteAddress = new HttpInetSocketAddress(target, address, port); + InetSocketAddress remoteAddress = new HttpInetSocketAddress(target, + address, port); InetSocketAddress localAddress = null; if (local != null) { localAddress = new InetSocketAddress(local, 0); @@ -146,7 +170,8 @@ this.log.debug("Connecting to " + remoteAddress); } try { - Socket connsock = sf.connectSocket(sock, remoteAddress, localAddress, params); + Socket connsock = sf.connectSocket(sock, remoteAddress, + localAddress, params); if (sock != connsock) { sock = connsock; conn.opening(sock, target); @@ -164,16 +189,14 @@ } } if (this.log.isDebugEnabled()) { - this.log.debug("Connect to " + remoteAddress + " timed out. " + - "Connection will be retried using another IP address"); + this.log.debug("Connect to " + remoteAddress + " timed out. " + + "Connection will be retried using another IP address"); } } } - public void updateSecureConnection( - final OperatedClientConnection conn, - final HttpHost target, - final HttpContext context, + public void updateSecureConnection(final OperatedClientConnection conn, + final HttpHost target, final HttpContext context, final HttpParams params) throws IOException { if (conn == null) { throw new IllegalArgumentException("Connection may not be null"); @@ -190,16 +213,16 @@ final Scheme schm = schemeRegistry.getScheme(target.getSchemeName()); if (!(schm.getSchemeSocketFactory() instanceof LayeredSchemeSocketFactory)) { - throw new IllegalArgumentException - ("Target scheme (" + schm.getName() + - ") must have layered socket factory."); + throw new IllegalArgumentException("Target scheme (" + + schm.getName() + ") must have layered socket factory."); } - LayeredSchemeSocketFactory lsf = (LayeredSchemeSocketFactory) schm.getSchemeSocketFactory(); + LayeredSchemeSocketFactory lsf = (LayeredSchemeSocketFactory) schm + .getSchemeSocketFactory(); Socket sock; try { - sock = lsf.createLayeredSocket( - conn.getSocket(), target.getHostName(), target.getPort(), true); + sock = lsf.createLayeredSocket(conn.getSocket(), + target.getHostName(), target.getPort(), true); } catch (ConnectException ex) { throw new HttpHostConnectException(target, ex); } @@ -209,16 +232,18 @@ /** * Performs standard initializations on a newly created socket. - * - * @param sock the socket to prepare - * @param context the context for the connection - * @param params the parameters from which to prepare the socket - * - * @throws IOException in case of an IO problem + * + * @param sock + * the socket to prepare + * @param context + * the context for the connection + * @param params + * the parameters from which to prepare the socket + * + * @throws IOException + * in case of an IO problem */ - protected void prepareSocket( - final Socket sock, - final HttpContext context, + protected void prepareSocket(final Socket sock, final HttpContext context, final HttpParams params) throws IOException { sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params)); sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params)); @@ -230,18 +255,31 @@ } /** - * Resolves the given host name to an array of corresponding IP addresses, based on the - * configured name service on the system. - * - * @param host host name to resolve + * 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. - * + * @exception UnknownHostException + * if no IP address for the host could be determined. + * * @since 4.1 */ - protected InetAddress[] resolveHostname(final String host) throws UnknownHostException { + protected InetAddress[] resolveHostname(final String host) + throws UnknownHostException { + if (dnsResolver != null) { + InetAddress[] overridenAddress = dnsResolver.resolve(host); + if (overridenAddress != null) { + return overridenAddress; + } + } + return InetAddress.getAllByName(host); } } - Index: src/main/java/org/apache/http/impl/conn/DnsResolver.java =================================================================== --- src/main/java/org/apache/http/impl/conn/DnsResolver.java (revision 0) +++ src/main/java/org/apache/http/impl/conn/DnsResolver.java (revision 0) @@ -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 + * . + * + */ +package org.apache.http.impl.conn; + +import java.net.InetAddress; + +/** + * 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); + +} Index: src/main/java/org/apache/http/impl/conn/InMemoryDnsResolverImpl.java =================================================================== --- src/main/java/org/apache/http/impl/conn/InMemoryDnsResolverImpl.java (revision 0) +++ src/main/java/org/apache/http/impl/conn/InMemoryDnsResolverImpl.java (revision 0) @@ -0,0 +1,118 @@ +/* + * ==================================================================== + * 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; + +/** + * 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) { + InetAddress[] resolvedAddresses = dnsMap.get(host); + if (log.isInfoEnabled()) { + log.info("Resolving " + host + " to " + + Arrays.deepToString(resolvedAddresses)); + } + return resolvedAddresses; + } + +} Index: src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java =================================================================== --- src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java (revision 1164883) +++ src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java (working copy) @@ -49,25 +49,25 @@ import org.apache.http.impl.conn.SchemeRegistryFactory; /** - * Manages a pool of {@link OperatedClientConnection client connections} and - * is able to service connection requests from multiple execution threads. + * Manages a pool of {@link OperatedClientConnection client connections} and is + * able to service connection requests from multiple execution threads. * Connections are pooled on a per route basis. A request for a route which - * already the manager has persistent connections for available in the pool - * will be services by leasing a connection from the pool rather than - * creating a brand new connection. + * already the manager has persistent connections for available in the pool will + * be services by leasing a connection from the pool rather than creating a + * brand new connection. *

- * PoolingConnectionManager maintains a maximum limit of connection on - * a per route basis and in total. Per default this implementation will - * create no more than than 2 concurrent connections per given route - * and no more 20 connections in total. For many real-world applications - * these limits may prove too constraining, especially if they use HTTP - * as a transport protocol for their services. Connection limits, however, - * can be adjusted using HTTP parameters. - * + * PoolingConnectionManager maintains a maximum limit of connection on a per + * route basis and in total. Per default this implementation will create no more + * than than 2 concurrent connections per given route and no more 20 connections + * in total. For many real-world applications these limits may prove too + * constraining, especially if they use HTTP as a transport protocol for their + * services. Connection limits, however, can be adjusted using HTTP parameters. + * * @since 4.2 */ @ThreadSafe -public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl { +public class PoolingClientConnectionManager implements ClientConnectionManager, + ConnPoolControl { private final Log log = LogFactory.getLog(getClass()); @@ -76,27 +76,50 @@ 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()); } - public PoolingClientConnectionManager( - final SchemeRegistry schemeRegistry, + public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry, final long timeToLive, final TimeUnit tunit) { super(); if (schemeRegistry == null) { - throw new IllegalArgumentException("Scheme registry may not be null"); + 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 { try { @@ -107,19 +130,20 @@ } /** - * Hook for creating the connection operator. - * It is called by the constructor. - * Derived classes can override this method to change the - * instantiation of the operator. - * The default implementation here instantiates - * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. - * - * @param schreg the scheme registry. - * - * @return the connection operator to use + * Hook for creating the connection operator. It is called by the + * constructor. Derived classes can override this method to change the + * instantiation of the operator. The default implementation here + * instantiates {@link DefaultClientConnectionOperator + * DefaultClientConnectionOperator}. + * + * @param schreg + * the scheme registry. + * + * @return the connection operator to use */ - protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) { - return new DefaultClientConnectionOperator(schreg); + protected ClientConnectionOperator createConnectionOperator( + SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg, this.dnsResolver); } public SchemeRegistry getSchemeRegistry() { @@ -139,10 +163,13 @@ StringBuilder buf = new StringBuilder(); PoolStats totals = this.pool.getTotalStats(); PoolStats stats = this.pool.getStats(route); - buf.append("[total kept alive: ").append(totals.getAvailable()).append("; "); - buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable()); + buf.append("[total kept alive: ").append(totals.getAvailable()) + .append("; "); + buf.append("route allocated: ").append( + stats.getLeased() + stats.getAvailable()); buf.append(" of ").append(stats.getMax()).append("; "); - buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); + buf.append("total allocated: ").append( + totals.getLeased() + totals.getAvailable()); buf.append(" of ").append(totals.getMax()).append("]"); return buf.toString(); } @@ -158,14 +185,14 @@ return buf.toString(); } - public ClientConnectionRequest requestConnection( - final HttpRoute route, + public ClientConnectionRequest requestConnection(final HttpRoute route, final Object state) { if (route == null) { throw new IllegalArgumentException("HTTP route may not be null"); } if (this.log.isDebugEnabled()) { - this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + this.log.debug("Connection request: " + format(route, state) + + formatStats(route)); } final Future future = this.pool.lease(route, state); @@ -175,9 +202,9 @@ future.cancel(true); } - public ManagedClientConnection getConnection( - final long timeout, - final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + public ManagedClientConnection getConnection(final long timeout, + final TimeUnit tunit) throws InterruptedException, + ConnectionPoolTimeoutException { return leaseConnection(future, timeout, tunit); } @@ -185,10 +212,9 @@ } - ManagedClientConnection leaseConnection( - final Future future, - final long timeout, - final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + ManagedClientConnection leaseConnection(final Future future, + final long timeout, final TimeUnit tunit) + throws InterruptedException, ConnectionPoolTimeoutException { HttpPoolEntry entry; try { entry = future.get(timeout, tunit); @@ -199,7 +225,8 @@ throw new IllegalStateException("Pool entry with no connection"); } if (this.log.isDebugEnabled()) { - this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + this.log.debug("Connection leased: " + format(entry) + + formatStats(entry.getRoute())); } return new ManagedClientConnectionImpl(this, this.operator, entry); } catch (ExecutionException ex) { @@ -207,25 +234,27 @@ if (cause == null) { cause = ex; } - this.log.error("Unexpected exception leasing connection from pool", cause); + this.log.error("Unexpected exception leasing connection from pool", + cause); // Should never happen throw new InterruptedException(); } catch (TimeoutException ex) { - throw new ConnectionPoolTimeoutException("Timeout waiting for connection"); + throw new ConnectionPoolTimeoutException( + "Timeout waiting for connection"); } } - public void releaseConnection( - final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) { + public void releaseConnection(final ManagedClientConnection conn, + final long keepalive, final TimeUnit tunit) { if (!(conn instanceof ManagedClientConnectionImpl)) { - throw new IllegalArgumentException - ("Connection class mismatch, " + - "connection not obtained from this manager."); + throw new IllegalArgumentException("Connection class mismatch, " + + "connection not obtained from this manager."); } ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn; if (managedConn.getManager() != this) { - throw new IllegalStateException("Connection not obtained from this manager."); + throw new IllegalStateException( + "Connection not obtained from this manager."); } synchronized (managedConn) { @@ -239,11 +268,14 @@ managedConn.shutdown(); } catch (IOException iox) { if (this.log.isDebugEnabled()) { - this.log.debug("I/O exception shutting down released connection", iox); + this.log.debug( + "I/O exception shutting down released connection", + iox); } } } - entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + entry.updateExpiry(keepalive, tunit != null ? tunit + : TimeUnit.MILLISECONDS); if (this.log.isDebugEnabled()) { String s; if (keepalive > 0) { @@ -251,13 +283,15 @@ } else { s = "indefinitely"; } - this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + this.log.debug("Connection " + format(entry) + + " can be kept alive " + s); } } finally { this.pool.release(entry, managedConn.isMarkedReusable()); } if (this.log.isDebugEnabled()) { - this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + this.log.debug("Connection released: " + format(entry) + + formatStats(entry.getRoute())); } } } @@ -272,13 +306,15 @@ try { shutdown(2000); } catch (IOException ex) { - this.log.error("I/O exception while shutting down connection pool", ex); + this.log.error("I/O exception while shutting down connection pool", + ex); } } public void closeIdleConnections(long idleTimeout, TimeUnit tunit) { if (log.isDebugEnabled()) { - log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + log.debug("Closing connections idle longer than " + idleTimeout + + " " + tunit); } pool.closeIdle(idleTimeout, tunit); } @@ -309,4 +345,3 @@ } } - Index: src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java =================================================================== --- src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java (revision 0) +++ src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java (revision 0) @@ -0,0 +1,102 @@ +/* + * ==================================================================== + * 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.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); + + DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator( + SchemeRegistryFactory.createDefault(), dnsResolver); + + Assert.assertArrayEquals(firstAddress, + operator.resolveHostname("somehost.example.com")); + Assert.assertArrayEquals(secondAddress, + operator.resolveHostname("otherhost.example.com")); + + try { + Assert.assertNull(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 { + Assert.assertNull(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) }; + + } + +}