From bfbc573cbddfe2c607771faf02c905b837769eb9 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Thu, 13 Dec 2012 14:22:28 +0000 Subject: [PATCH] Added methods to set SocketConfig and ConnectionConfig for PoolingHttpClientConnectionManager and HttpClientBuilder git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1421309 13f79535-47bb-0310-9956-ffa450edef68 --- .../http/impl/client/HttpClientBuilder.java | 30 +- .../java/org/apache/http/impl/conn/CPool.java | 12 +- .../conn/HttpClientConnectionManagerBase.java | 269 -------- .../PoolingHttpClientConnectionManager.java | 305 ++++++++- .../TestHttpClientConnectionManagerBase.java | 198 ------ ...estPoolingHttpClientConnectionManager.java | 578 ++++-------------- 6 files changed, 411 insertions(+), 981 deletions(-) delete mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java index b79380c6b..8c7c41ae4 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java @@ -60,8 +60,10 @@ import org.apache.http.client.protocol.RequestDefaultHeaders; import org.apache.http.client.protocol.RequestExpectContinue; import org.apache.http.client.protocol.ResponseContentEncoding; import org.apache.http.client.protocol.ResponseProcessCookies; +import org.apache.http.config.ConnectionConfig; import org.apache.http.config.Lookup; import org.apache.http.config.RegistryBuilder; +import org.apache.http.config.SocketConfig; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.SchemePortResolver; @@ -125,7 +127,7 @@ import org.apache.http.util.VersionInfo; *
  • http.nonProxyHosts
  • *
  • http.keepAlive
  • *
  • http.maxConnections
  • - *
  • http.user
  • + *
  • http.agent
  • * *

    * @@ -164,7 +166,9 @@ public class HttpClientBuilder { private CredentialsProvider credentialsProvider; private String userAgent; private Collection defaultHeaders; - private RequestConfig defaultConfig; + private SocketConfig defaultSocketConfig; + private ConnectionConfig defaultConnectionConfig; + private RequestConfig defaultRequestConfig; private boolean systemProperties; private boolean redirectHandlingDisabled; @@ -379,8 +383,18 @@ public class HttpClientBuilder { return this; } - public final HttpClientBuilder setDefaultConfig(final RequestConfig defaultConfig) { - this.defaultConfig = defaultConfig; + public final HttpClientBuilder setDefaultSocketConfig(final SocketConfig config) { + this.defaultSocketConfig = config; + return this; + } + + public final HttpClientBuilder setDefaultConnectionConfig(final ConnectionConfig config) { + this.defaultConnectionConfig = config; + return this; + } + + public final HttpClientBuilder setDefaultRequestConfig(final RequestConfig config) { + this.defaultRequestConfig = config; return this; } @@ -438,6 +452,12 @@ public class HttpClientBuilder { .register("http", PlainSocketFactory.getSocketFactory()) .register("https", sslSocketFactory) .build()); + if (defaultSocketConfig != null) { + poolingmgr.setDefaultSocketConfig(defaultSocketConfig); + } + if (defaultConnectionConfig != null) { + poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig); + } if (systemProperties) { String s = System.getProperty("http.keepAlive"); if ("true".equalsIgnoreCase(s)) { @@ -659,7 +679,7 @@ public class HttpClientBuilder { authSchemeRegistry, defaultCookieStore, defaultCredentialsProvider, - defaultConfig != null ? defaultConfig : RequestConfig.DEFAULT); + defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT); } } diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java b/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java index 2285c766d..0ef1d92bb 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java @@ -26,7 +26,6 @@ */ package org.apache.http.impl.conn; -import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -52,9 +51,10 @@ class CPool extends AbstractConnPool connFactory, final int defaultMaxPerRoute, final int maxTotal, final long timeToLive, final TimeUnit tunit) { - super(new InternalConnFactory(), defaultMaxPerRoute, maxTotal); + super(connFactory, defaultMaxPerRoute, maxTotal); this.timeToLive = timeToLive; this.tunit = tunit; } @@ -65,12 +65,4 @@ class CPool extends AbstractConnPool { - - public SocketClientConnection create(final HttpRoute route) throws IOException { - return new SocketClientConnectionImpl(8 * 1024); - } - - } - } diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java deleted file mode 100644 index ec4a8b597..000000000 --- a/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * ==================================================================== - * - * 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.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.apache.http.HttpClientConnection; -import org.apache.http.HttpHost; -import org.apache.http.annotation.ThreadSafe; -import org.apache.http.config.ConnectionConfig; -import org.apache.http.config.Lookup; -import org.apache.http.config.SocketConfig; -import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.ConnectionRequest; -import org.apache.http.conn.DnsResolver; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.conn.SchemePortResolver; -import org.apache.http.conn.SocketClientConnection; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.socket.ConnectionSocketFactory; -import org.apache.http.pool.ConnPool; -import org.apache.http.protocol.HttpContext; - -/** - * Base class for {@link HttpClientConnectionManager} implementations. - * This class primarily provides consistent implementation of - * {@link #connect(HttpClientConnection, HttpHost, InetAddress, HttpContext, HttpParams)} - * and {@link #upgrade(HttpClientConnection, HttpHost, HttpContext, HttpParams)} - * methods for all standard connection managers shipped with HttpClient. - * - * @since 4.3 - */ -@ThreadSafe -abstract class HttpClientConnectionManagerBase implements HttpClientConnectionManager { - - private final ConnPool pool; - private final HttpClientConnectionOperator connectionOperator; - private final Map socketConfigMap; - private final Map connectionConfigMap; - private volatile SocketConfig defaultSocketConfig; - private volatile ConnectionConfig defaultConnectionConfig; - - HttpClientConnectionManagerBase( - final ConnPool pool, - final Lookup socketFactoryRegistry, - final SchemePortResolver schemePortResolver, - final DnsResolver dnsResolver) { - super(); - if (pool == null) { - throw new IllegalArgumentException("Connection pool may nor be null"); - } - this.pool = pool; - this.connectionOperator = new HttpClientConnectionOperator( - socketFactoryRegistry, schemePortResolver, dnsResolver); - this.socketConfigMap = new ConcurrentHashMap(); - this.connectionConfigMap = new ConcurrentHashMap(); - this.defaultSocketConfig = SocketConfig.DEFAULT; - this.defaultConnectionConfig = ConnectionConfig.DEFAULT; - } - - @Override - protected void finalize() throws Throwable { - try { - shutdown(); - } finally { - super.finalize(); - } - } - - protected void onConnectionLeaseRequest(final HttpRoute route, final Object state) { - } - - protected void onConnectionLease(final CPoolEntry entry) { - } - - protected void onConnectionKeepAlive(final CPoolEntry entry) { - } - - protected void onConnectionRelease(final CPoolEntry entry) { - } - - public ConnectionRequest requestConnection( - final HttpRoute route, - final Object state) { - if (route == null) { - throw new IllegalArgumentException("HTTP route may not be null"); - } - onConnectionLeaseRequest(route, state); - final Future future = this.pool.lease(route, state, null); - return new ConnectionRequest() { - - public boolean cancel() { - return future.cancel(true); - } - - public HttpClientConnection get( - final long timeout, - final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { - return leaseConnection(future, timeout, tunit); - } - - }; - - } - - protected HttpClientConnection leaseConnection( - final Future future, - final long timeout, - final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { - CPoolEntry entry; - try { - entry = future.get(timeout, tunit); - if (entry == null || future.isCancelled()) { - throw new InterruptedException(); - } - if (entry.getConnection() == null) { - throw new IllegalStateException("Pool entry with no connection"); - } - onConnectionLease(entry); - return CPoolProxy.newProxy(entry); - } catch (ExecutionException ex) { - Throwable cause = ex.getCause(); - if (cause == null) { - cause = ex; - } - InterruptedException intex = new InterruptedException(); - intex.initCause(cause); - throw intex; - } catch (TimeoutException ex) { - throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); - } - } - - public void releaseConnection( - final HttpClientConnection managedConn, - final Object state, - final long keepalive, final TimeUnit tunit) { - if (managedConn == null) { - throw new IllegalArgumentException("Managed connection may not be null"); - } - synchronized (managedConn) { - CPoolEntry entry = CPoolProxy.detach(managedConn); - if (entry == null) { - return; - } - SocketClientConnection conn = entry.getConnection(); - try { - if (conn.isOpen()) { - entry.setState(state); - entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); - onConnectionKeepAlive(entry); - } - } finally { - this.pool.release(entry, conn.isOpen()); - onConnectionRelease(entry); - } - } - } - - public void connect( - final HttpClientConnection managedConn, - final HttpHost host, - final InetAddress local, - final int connectTimeout, - final HttpContext context) throws IOException { - if (managedConn == null) { - throw new IllegalArgumentException("Connection may not be null"); - } - SocketClientConnection conn; - synchronized (managedConn) { - CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); - conn = entry.getConnection(); - } - SocketConfig socketConfig = this.socketConfigMap.get(host); - if (socketConfig == null) { - socketConfig = this.defaultSocketConfig; - } - ConnectionConfig connConfig = this.connectionConfigMap.get(host); - if (connConfig == null) { - connConfig = this.defaultConnectionConfig; - } - - InetSocketAddress localAddress = local != null ? new InetSocketAddress(local, 0) : null; - this.connectionOperator.connect(conn, host, localAddress, - connectTimeout, socketConfig, context); - } - - public void upgrade( - final HttpClientConnection managedConn, - final HttpHost host, - final HttpContext context) throws IOException { - if (managedConn == null) { - throw new IllegalArgumentException("Connection may not be null"); - } - SocketClientConnection conn; - synchronized (managedConn) { - CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); - conn = entry.getConnection(); - } - this.connectionOperator.upgrade(conn, host, context); - } - - public SocketConfig getDefaultSocketConfig() { - return this.defaultSocketConfig; - } - - public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { - this.defaultSocketConfig = defaultSocketConfig != null ? defaultSocketConfig : - SocketConfig.DEFAULT; - } - - public ConnectionConfig getDefaultConnectionConfig() { - return this.defaultConnectionConfig; - } - - public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { - this.defaultConnectionConfig = defaultConnectionConfig != null ? defaultConnectionConfig : - ConnectionConfig.DEFAULT; - } - - public SocketConfig getSocketConfig(final HttpHost host) { - return this.socketConfigMap.get(host); - } - - public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { - this.socketConfigMap.put(host, socketConfig); - } - - public ConnectionConfig getConnectionConfig(final HttpHost host) { - return this.connectionConfigMap.get(host); - } - - public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { - this.connectionConfigMap.put(host, connectionConfig); - } - -} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java index 31665585f..83bf51277 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java @@ -27,22 +27,40 @@ package org.apache.http.impl.conn; import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpClientConnection; +import org.apache.http.HttpHost; import org.apache.http.annotation.ThreadSafe; +import org.apache.http.config.ConnectionConfig; +import org.apache.http.config.Lookup; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.ConnectionPoolTimeoutException; +import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.HttpConnectionFactory; import org.apache.http.conn.SchemePortResolver; +import org.apache.http.conn.SocketClientConnection; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainSocketFactory; import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.pool.ConnFactory; import org.apache.http.pool.ConnPoolControl; import org.apache.http.pool.PoolStats; +import org.apache.http.protocol.HttpContext; /** * ClientConnectionPoolManager maintains a pool of @@ -63,11 +81,13 @@ import org.apache.http.pool.PoolStats; * @since 4.3 */ @ThreadSafe -public class PoolingHttpClientConnectionManager extends HttpClientConnectionManagerBase { +public class PoolingHttpClientConnectionManager implements HttpClientConnectionManager { private final Log log = LogFactory.getLog(getClass()); + private final ConfigData configData; private final CPool pool; + private final HttpClientConnectionOperator connectionOperator; private static Registry getDefaultRegistry() { return RegistryBuilder.create() @@ -81,42 +101,51 @@ public class PoolingHttpClientConnectionManager extends HttpClientConnectionMana } public PoolingHttpClientConnectionManager(final long timeToLive, final TimeUnit tunit) { - this(getDefaultRegistry(), timeToLive, tunit); + this(getDefaultRegistry(), null, timeToLive, tunit); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry) { - this(socketFactoryRegistry, -1, TimeUnit.MILLISECONDS); + this(socketFactoryRegistry, null, -1, TimeUnit.MILLISECONDS); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, final DnsResolver dnsResolver) { - this(socketFactoryRegistry, null, dnsResolver, -1, TimeUnit.MILLISECONDS); + this(socketFactoryRegistry, null, null, dnsResolver, -1, TimeUnit.MILLISECONDS); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, + final HttpConnectionFactory connFactory, final long timeToLive, final TimeUnit tunit) { - this(new CPool(2, 20, timeToLive, tunit), socketFactoryRegistry, null, null); + this(socketFactoryRegistry, connFactory, null, null, timeToLive, tunit); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, + final HttpConnectionFactory connFactory, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver, final long timeToLive, final TimeUnit tunit) { - this(new CPool(2, 20, timeToLive, tunit), + super(); + this.configData = new ConfigData(); + this.pool = new CPool( + new InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, tunit); + this.connectionOperator = new HttpClientConnectionOperator( socketFactoryRegistry, schemePortResolver, dnsResolver); } PoolingHttpClientConnectionManager( final CPool pool, - final Registry socketFactoryRegistry, + final Lookup socketFactoryRegistry, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver) { - super(pool, socketFactoryRegistry, schemePortResolver, dnsResolver); + super(); + this.configData = new ConfigData(); this.pool = pool; + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); } @Override @@ -160,39 +189,137 @@ public class PoolingHttpClientConnectionManager extends HttpClientConnectionMana return buf.toString(); } - @Override - protected void onConnectionLeaseRequest(final HttpRoute route, final Object state) { + public ConnectionRequest 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)); } - } + final Future future = this.pool.lease(route, state, null); + return new ConnectionRequest() { - @Override - protected void onConnectionLease(final CPoolEntry entry) { - if (this.log.isDebugEnabled()) { - this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); - } - } - - @Override - protected void onConnectionKeepAlive(final CPoolEntry entry) { - if (this.log.isDebugEnabled()) { - long keepalive = entry.getExpiry(); - String s; - if (keepalive > 0) { - s = "for " + (double) keepalive / 1000 + " seconds"; - } else { - s = "indefinitely"; + public boolean cancel() { + return future.cancel(true); } - this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + + public HttpClientConnection get( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + return leaseConnection(future, timeout, tunit); + } + + }; + + } + + protected HttpClientConnection leaseConnection( + final Future future, + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + CPoolEntry entry; + try { + entry = future.get(timeout, tunit); + if (entry == null || future.isCancelled()) { + throw new InterruptedException(); + } + if (entry.getConnection() == null) { + throw new IllegalStateException("Pool entry with no connection"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + } + return CPoolProxy.newProxy(entry); + } catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause == null) { + cause = ex; + } + InterruptedException intex = new InterruptedException(); + intex.initCause(cause); + throw intex; + } catch (TimeoutException ex) { + throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); } } - @Override - protected void onConnectionRelease(final CPoolEntry entry) { - if (this.log.isDebugEnabled()) { - this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + public void releaseConnection( + final HttpClientConnection managedConn, + final Object state, + final long keepalive, final TimeUnit tunit) { + if (managedConn == null) { + throw new IllegalArgumentException("Managed connection may not be null"); } + synchronized (managedConn) { + CPoolEntry entry = CPoolProxy.detach(managedConn); + if (entry == null) { + return; + } + SocketClientConnection conn = entry.getConnection(); + try { + if (conn.isOpen()) { + entry.setState(state); + entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + String s; + if (keepalive > 0) { + s = "for " + (double) keepalive / 1000 + " seconds"; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + } + } + } finally { + this.pool.release(entry, conn.isOpen()); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + } + } + } + } + + public void connect( + final HttpClientConnection managedConn, + final HttpHost host, + final InetAddress local, + final int connectTimeout, + final HttpContext context) throws IOException { + if (managedConn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + SocketClientConnection conn; + synchronized (managedConn) { + CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + SocketConfig socketConfig = this.configData.getSocketConfig(host); + if (socketConfig == null) { + socketConfig = this.configData.getDefaultSocketConfig(); + } + if (socketConfig == null) { + socketConfig = SocketConfig.DEFAULT; + } + InetSocketAddress localAddress = local != null ? new InetSocketAddress(local, 0) : null; + this.connectionOperator.connect( + conn, host, localAddress, connectTimeout, socketConfig, context); + } + + public void upgrade( + final HttpClientConnection managedConn, + final HttpHost host, + final HttpContext context) throws IOException { + if (managedConn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + SocketClientConnection conn; + synchronized (managedConn) { + CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + this.connectionOperator.upgrade(conn, host, context); } public void shutdown() { @@ -249,4 +376,116 @@ public class PoolingHttpClientConnectionManager extends HttpClientConnectionMana return this.pool.getStats(route); } -} + public SocketConfig getDefaultSocketConfig() { + return this.configData.getDefaultSocketConfig(); + } + + public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { + this.configData.setDefaultSocketConfig(defaultSocketConfig); + } + + public ConnectionConfig getDefaultConnectionConfig() { + return this.configData.getDefaultConnectionConfig(); + } + + public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { + this.configData.setDefaultConnectionConfig(defaultConnectionConfig); + } + + public SocketConfig getSocketConfig(final HttpHost host) { + return this.configData.getSocketConfig(host); + } + + public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { + this.configData.setSocketConfig(host, socketConfig); + } + + public ConnectionConfig getConnectionConfig(final HttpHost host) { + return this.configData.getConnectionConfig(host); + } + + public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { + this.configData.setConnectionConfig(host, connectionConfig); + } + + static class ConfigData { + + private final Map socketConfigMap; + private final Map connectionConfigMap; + private volatile SocketConfig defaultSocketConfig; + private volatile ConnectionConfig defaultConnectionConfig; + + ConfigData() { + super(); + this.socketConfigMap = new ConcurrentHashMap(); + this.connectionConfigMap = new ConcurrentHashMap(); + } + + public SocketConfig getDefaultSocketConfig() { + return this.defaultSocketConfig; + } + + public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { + this.defaultSocketConfig = defaultSocketConfig; + } + + public ConnectionConfig getDefaultConnectionConfig() { + return this.defaultConnectionConfig; + } + + public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { + this.defaultConnectionConfig = defaultConnectionConfig; + } + + public SocketConfig getSocketConfig(final HttpHost host) { + return this.socketConfigMap.get(host); + } + + public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { + this.socketConfigMap.put(host, socketConfig); + } + + public ConnectionConfig getConnectionConfig(final HttpHost host) { + return this.connectionConfigMap.get(host); + } + + public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { + this.connectionConfigMap.put(host, connectionConfig); + } + + } + + static class InternalConnectionFactory implements ConnFactory { + + private final ConfigData configData; + private final HttpConnectionFactory connFactory; + + InternalConnectionFactory( + final ConfigData configData, + final HttpConnectionFactory connFactory) { + super(); + this.configData = configData != null ? configData : new ConfigData(); + this.connFactory = connFactory != null ? connFactory : + DefaultClientConnectionFactory.INSTANCE; + } + + public SocketClientConnection create(final HttpRoute route) throws IOException { + ConnectionConfig config = null; + if (route.getProxyHost() != null) { + config = this.configData.getConnectionConfig(route.getProxyHost()); + } + if (config == null) { + config = this.configData.getConnectionConfig(route.getTargetHost()); + } + if (config == null) { + config = this.configData.getDefaultConnectionConfig(); + } + if (config == null) { + config = ConnectionConfig.DEFAULT; + } + return this.connFactory.create(config); + } + + } + +} \ No newline at end of file diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java deleted file mode 100644 index 7628a503f..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * ==================================================================== - * 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.Socket; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.apache.commons.logging.LogFactory; -import org.apache.http.HttpClientConnection; -import org.apache.http.HttpHost; -import org.apache.http.config.Lookup; -import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.ConnectionRequest; -import org.apache.http.conn.DnsResolver; -import org.apache.http.conn.SchemePortResolver; -import org.apache.http.conn.SocketClientConnection; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.socket.ConnectionSocketFactory; -import org.apache.http.pool.ConnPool; -import org.apache.http.protocol.HttpContext; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestHttpClientConnectionManagerBase { - - private SocketClientConnection conn; - private Socket socket; - private ConnectionSocketFactory plainSocketFactory; - private Lookup socketFactoryRegistry; - private SchemePortResolver schemePortResolver; - private DnsResolver dnsResolver; - private Future future; - private ConnPool pool; - private HttpClientConnectionManagerBase mgr; - - @SuppressWarnings("unchecked") - @Before - public void setup() throws Exception { - conn = Mockito.mock(SocketClientConnection.class); - socket = Mockito.mock(Socket.class); - plainSocketFactory = Mockito.mock(ConnectionSocketFactory.class); - socketFactoryRegistry = Mockito.mock(Lookup.class); - schemePortResolver = Mockito.mock(SchemePortResolver.class); - dnsResolver = Mockito.mock(DnsResolver.class); - pool = Mockito.mock(ConnPool.class); - future = Mockito.mock(Future.class); - mgr = new HttpClientConnectionManagerBase( - pool, socketFactoryRegistry, schemePortResolver, dnsResolver) { - - public void closeIdleConnections(long idletime, TimeUnit tunit) { - } - - public void closeExpiredConnections() { - } - - public void shutdown() { - } - - }; - } - - @Test - public void testLeaseRelease() throws Exception { - HttpHost target = new HttpHost("localhost"); - HttpRoute route = new HttpRoute(target); - - CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, - -1, TimeUnit.MILLISECONDS); - - Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); - Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); - Mockito.when(schemePortResolver.resolve(target)).thenReturn(80); - Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); - - Mockito.when(future.isCancelled()).thenReturn(false); - Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); - Mockito.when(pool.lease(route, null, null)).thenReturn(future); - - ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertFalse(conn1.isOpen()); - Assert.assertNotSame(conn, conn1); - - mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); - - Mockito.verify(pool).release(entry, false); - } - - @Test(expected=InterruptedException.class) - public void testLeaseFutureCancelled() throws Exception { - HttpHost target = new HttpHost("localhost"); - HttpRoute route = new HttpRoute(target); - - CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, - -1, TimeUnit.MILLISECONDS); - - Mockito.when(future.isCancelled()).thenReturn(Boolean.TRUE); - Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); - Mockito.when(pool.lease(route, null, null)).thenReturn(future); - - ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - connRequest1.get(1, TimeUnit.SECONDS); - } - - @Test(expected=ConnectionPoolTimeoutException.class) - public void testLeaseFutureTimeout() throws Exception { - HttpHost target = new HttpHost("localhost"); - HttpRoute route = new HttpRoute(target); - - Mockito.when(future.isCancelled()).thenReturn(Boolean.TRUE); - Mockito.when(future.get(1, TimeUnit.SECONDS)).thenThrow(new TimeoutException()); - Mockito.when(pool.lease(route, null, null)).thenReturn(future); - - ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - connRequest1.get(1, TimeUnit.SECONDS); - } - - @Test - public void testReleaseReusable() throws Exception { - HttpHost target = new HttpHost("localhost"); - HttpRoute route = new HttpRoute(target); - - CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, - -1, TimeUnit.MILLISECONDS)); - - Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); - Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); - Mockito.when(pool.lease(route, null, null)).thenReturn(future); - Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); - - ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertTrue(conn1.isOpen()); - - mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); - - Mockito.verify(pool).release(entry, true); - Mockito.verify(entry).setState("some state"); - Mockito.verify(entry).updateExpiry(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)); - } - - @Test - public void testReleaseNonReusable() throws Exception { - HttpHost target = new HttpHost("localhost"); - HttpRoute route = new HttpRoute(target); - - CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, - -1, TimeUnit.MILLISECONDS)); - - Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); - Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); - Mockito.when(pool.lease(route, null, null)).thenReturn(future); - Mockito.when(conn.isOpen()).thenReturn(Boolean.FALSE); - - ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertFalse(conn1.isOpen()); - - mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); - - Mockito.verify(pool).release(entry, false); - Mockito.verify(entry, Mockito.never()).setState(Mockito.anyObject()); - Mockito.verify(entry, Mockito.never()).updateExpiry(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)); - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java index e299a2516..ab8f10d57 100644 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java @@ -27,15 +27,25 @@ package org.apache.http.impl.conn; +import java.net.Socket; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.apache.commons.logging.LogFactory; import org.apache.http.HttpClientConnection; import org.apache.http.HttpHost; +import org.apache.http.config.Lookup; import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.SchemePortResolver; +import org.apache.http.conn.SocketClientConnection; import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -44,500 +54,136 @@ import org.mockito.Mockito; */ public class TestPoolingHttpClientConnectionManager { - private static HttpClientConnection getConnection( - final HttpClientConnectionManager mgr, - final HttpRoute route, - long timeout, - TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException { - ConnectionRequest connRequest = mgr.requestConnection(route, null); - return connRequest.get(timeout, unit); + private SocketClientConnection conn; + private Socket socket; + private ConnectionSocketFactory plainSocketFactory; + private Lookup socketFactoryRegistry; + private SchemePortResolver schemePortResolver; + private DnsResolver dnsResolver; + private Future future; + private CPool pool; + private PoolingHttpClientConnectionManager mgr; + + @SuppressWarnings("unchecked") + @Before + public void setup() throws Exception { + conn = Mockito.mock(SocketClientConnection.class); + socket = Mockito.mock(Socket.class); + plainSocketFactory = Mockito.mock(ConnectionSocketFactory.class); + socketFactoryRegistry = Mockito.mock(Lookup.class); + schemePortResolver = Mockito.mock(SchemePortResolver.class); + dnsResolver = Mockito.mock(DnsResolver.class); + pool = Mockito.mock(CPool.class); + future = Mockito.mock(Future.class); + mgr = new PoolingHttpClientConnectionManager( + pool, socketFactoryRegistry, schemePortResolver, dnsResolver); } - private static HttpClientConnection getConnection( - final HttpClientConnectionManager mgr, - final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException { - ConnectionRequest connRequest = mgr.requestConnection(route, null); - return connRequest.get(0, null); - } - - @Test(expected=IllegalArgumentException.class) - public void testIllegalConstructor() { - new PoolingHttpClientConnectionManager(null); - } - - @Test(expected=IllegalArgumentException.class) - public void testGetConnection() - throws InterruptedException, ConnectionPoolTimeoutException { - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - - HttpHost target = new HttpHost("www.test.invalid", 80, "http"); - HttpRoute route = new HttpRoute(target, null, false); - - HttpClientConnection conn = getConnection(mgr, route); - Assert.assertNotNull(conn); - Assert.assertFalse(conn.isOpen()); - - mgr.releaseConnection(conn, null, -1, null); - - try { - getConnection(mgr, null); - } finally { - mgr.shutdown(); - } - } - - // testTimeout in 3.x TestHttpConnectionManager is redundant - // several other tests here rely on timeout behavior @Test - public void testMaxConnTotal() - throws InterruptedException, ConnectionPoolTimeoutException { + public void testLeaseRelease() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(2); - mgr.setDefaultMaxPerRoute(1); + CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS); - HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); - HttpRoute route1 = new HttpRoute(target1, null, false); - HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); - HttpRoute route2 = new HttpRoute(target2, null, false); + Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); + Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); + Mockito.when(schemePortResolver.resolve(target)).thenReturn(80); + Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); - HttpClientConnection conn1 = getConnection(mgr, route1); + Mockito.when(future.isCancelled()).thenReturn(false); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); Assert.assertNotNull(conn1); - HttpClientConnection conn2 = getConnection(mgr, route2); - Assert.assertNotNull(conn2); + Assert.assertFalse(conn1.isOpen()); + Assert.assertNotSame(conn, conn1); - try { - // this should fail quickly, connection has not been released - getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } + mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); - // release one of the connections - mgr.releaseConnection(conn2, null, -1, null); - conn2 = null; + Mockito.verify(pool).release(entry, false); + } - // there should be a connection available now - try { - getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS); - } catch (ConnectionPoolTimeoutException cptx) { - Assert.fail("connection should have been available: " + cptx); - } + @Test(expected=InterruptedException.class) + public void testLeaseFutureCancelled() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); - mgr.shutdown(); + CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS); + + Mockito.when(future.isCancelled()).thenReturn(Boolean.TRUE); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + connRequest1.get(1, TimeUnit.SECONDS); + } + + @Test(expected=ConnectionPoolTimeoutException.class) + public void testLeaseFutureTimeout() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + + Mockito.when(future.isCancelled()).thenReturn(Boolean.TRUE); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenThrow(new TimeoutException()); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + connRequest1.get(1, TimeUnit.SECONDS); } @Test - public void testMaxConnPerHost() throws Exception { + public void testReleaseReusable() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); - HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); - HttpRoute route1 = new HttpRoute(target1, null, false); - HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); - HttpRoute route2 = new HttpRoute(target2, null, false); - HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http"); - HttpRoute route3 = new HttpRoute(target3, null, false); + CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS)); - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(100); - mgr.setDefaultMaxPerRoute(1); - mgr.setMaxPerRoute(route2, 2); - mgr.setMaxPerRoute(route3, 3); + Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); - // route 3, limit 3 - HttpClientConnection conn1 = - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); Assert.assertNotNull(conn1); - HttpClientConnection conn2 = - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn2); - HttpClientConnection conn3 = - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn3); - try { - // should fail quickly, connection has not been released - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } + Assert.assertTrue(conn1.isOpen()); - // route 2, limit 2 - conn1 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - try { - // should fail quickly, connection has not been released - getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } + mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); - // route 1, should use default limit of 1 - conn1 = getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); - try { - // should fail quickly, connection has not been released - getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - // check releaseConnection with invalid arguments - try { - mgr.releaseConnection(null, null, -1, null); - Assert.fail("null connection adapter not detected"); - } catch (IllegalArgumentException iax) { - // expected - } - try { - HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); - mgr.releaseConnection(conn, null, -1, null); - Assert.fail("foreign connection adapter not detected"); - } catch (IllegalArgumentException iax) { - // expected - } - - mgr.shutdown(); + Mockito.verify(pool).release(entry, true); + Mockito.verify(entry).setState("some state"); + Mockito.verify(entry).updateExpiry(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)); } @Test - public void testReleaseConnection() throws Exception { + public void testReleaseNonReusable() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(3); - mgr.setDefaultMaxPerRoute(1); + CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS)); - HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); - HttpRoute route1 = new HttpRoute(target1, null, false); - HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); - HttpRoute route2 = new HttpRoute(target2, null, false); - HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http"); - HttpRoute route3 = new HttpRoute(target3, null, false); + Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + Mockito.when(conn.isOpen()).thenReturn(Boolean.FALSE); - // the first three allocations should pass - HttpClientConnection conn1 = - getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); - HttpClientConnection conn2 = - getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - HttpClientConnection conn3 = - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); Assert.assertNotNull(conn1); - Assert.assertNotNull(conn2); - Assert.assertNotNull(conn3); + Assert.assertFalse(conn1.isOpen()); - // obtaining another connection for either of the three should fail - // this is somehow redundant with testMaxConnPerHost - try { - getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - try { - getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - try { - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - - // now release one and check that exactly that one can be obtained then - mgr.releaseConnection(conn2, null, -1, null); - conn2 = null; - try { - getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - // this one succeeds - conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn2); - try { - getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - - mgr.shutdown(); - } - - @Test - public void testDeleteClosedConnections() throws Exception { - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - - HttpHost target = new HttpHost("www.test.invalid", 80, "http"); - HttpRoute route = new HttpRoute(target, null, false); - - HttpClientConnection conn = getConnection(mgr, route); - - Assert.assertEquals(1, mgr.getTotalStats().getLeased()); - Assert.assertEquals(1, mgr.getStats(route).getLeased()); - mgr.releaseConnection(conn, null, -1, null); - - Assert.assertEquals(0, mgr.getTotalStats().getAvailable()); - Assert.assertEquals(0, mgr.getStats(route).getAvailable()); - - mgr.shutdown(); - } - - @Test - public void testShutdown() throws Exception { - // 3.x: TestHttpConnectionManager.testShutdown - - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(1); - mgr.setDefaultMaxPerRoute(1); - - HttpHost target = new HttpHost("www.test.invalid", 80, "http"); - HttpRoute route = new HttpRoute(target, null, false); - - // get the only connection, then start an extra thread - // on shutdown, the extra thread should get an exception - - HttpClientConnection conn = - getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout - gct.start(); - Thread.sleep(100); // give extra thread time to block - - - mgr.shutdown(); - - // First release the connection. If the manager keeps working - // despite the shutdown, this will deblock the extra thread. - // The release itself should turn into a no-op, without exception. - mgr.releaseConnection(conn, null, -1, null); - - - gct.join(10000); - Assert.assertNull("thread should not have obtained connection", - gct.getConnection()); - Assert.assertNotNull("thread should have gotten an exception", - gct.getException()); - Assert.assertSame("thread got wrong exception", - InterruptedException.class, gct.getException().getClass()); - - // the manager is down, we should not be able to get a connection - try { - getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - Assert.fail("shut-down manager does not raise exception"); - } catch (IllegalStateException isx) { - // expected - } - } - - @Test - public void testInterruptThread() throws Exception { - // 3.x: TestHttpConnectionManager.testWaitingThreadInterrupted - - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(1); - - HttpHost target = new HttpHost("www.test.invalid", 80, "http"); - HttpRoute route = new HttpRoute(target, null, false); - - // get the only connection, then start an extra thread - HttpClientConnection conn = - getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout - gct.start(); - Thread.sleep(100); // give extra thread time to block - - - // interrupt the thread, it should cancel waiting with an exception - gct.interrupt(); - - - gct.join(10000); - Assert.assertNotNull("thread should have gotten an exception", - gct.getException()); - Assert.assertSame("thread got wrong exception", - InterruptedException.class, - gct.getException().getClass()); - - // make sure the manager is still working - try { - getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); - Assert.fail("should have gotten a timeout"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - - mgr.releaseConnection(conn, null, -1, null); - // this time: no exception - conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); - Assert.assertNotNull("should have gotten a connection", conn); - - mgr.shutdown(); - } - - @Test - public void testReusePreference() throws Exception { - // 3.x: TestHttpConnectionManager.testHostReusePreference - - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(1); - - HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); - HttpRoute route1 = new HttpRoute(target1, null, false); - HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); - HttpRoute route2 = new HttpRoute(target2, null, false); - - // get the only connection, then start two extra threads - HttpClientConnection conn = - getConnection(mgr, route1, 1L, TimeUnit.MILLISECONDS); - GetConnThread gct1 = new GetConnThread(mgr, route1, 1000L); - GetConnThread gct2 = new GetConnThread(mgr, route2, 1000L); - - // the second thread is started first, to distinguish the - // route-based reuse preference from first-come, first-served - gct2.start(); - Thread.sleep(100); // give the thread time to block - gct1.start(); - Thread.sleep(100); // give the thread time to block - - - // releasing the connection for route1 should deblock thread1 - // the other thread gets a timeout - mgr.releaseConnection(conn, null, -1, null); - - gct1.join(10000); - gct2.join(10000); - - Assert.assertNotNull("thread 1 should have gotten a connection", - gct1.getConnection()); - Assert.assertNull ("thread 2 should NOT have gotten a connection", - gct2.getConnection()); - - mgr.shutdown(); - } - - @Test - public void testAbortAfterRequestStarts() throws Exception { - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(1); - - HttpHost target = new HttpHost("www.test.invalid", 80, "http"); - HttpRoute route = new HttpRoute(target, null, false); - - // get the only connection, then start an extra thread - HttpClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - ConnectionRequest request = mgr.requestConnection(route, null); - GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout - gct.start(); - Thread.sleep(100); // give extra thread time to block - - request.cancel(); - - gct.join(10000); - Assert.assertNotNull("thread should have gotten an exception", - gct.getException()); - Assert.assertSame("thread got wrong exception", - InterruptedException.class, - gct.getException().getClass()); - - // make sure the manager is still working - try { - getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); - Assert.fail("should have gotten a timeout"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - - mgr.releaseConnection(conn, null, -1, null); - // this time: no exception - conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); - Assert.assertNotNull("should have gotten a connection", conn); - - mgr.shutdown(); - } - - @Test - public void testAbortBeforeRequestStarts() throws Exception { - PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); - mgr.setMaxTotal(1); - - HttpHost target = new HttpHost("www.test.invalid", 80, "http"); - HttpRoute route = new HttpRoute(target, null, false); - - // get the only connection, then start an extra thread - HttpClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - ConnectionRequest request = mgr.requestConnection(route, null); - request.cancel(); - - GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout - gct.start(); - Thread.sleep(100); // give extra thread time to block - - gct.join(10000); - Assert.assertNotNull("thread should have gotten an exception", - gct.getException()); - Assert.assertSame("thread got wrong exception", - InterruptedException.class, - gct.getException().getClass()); - - // make sure the manager is still working - try { - getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); - Assert.fail("should have gotten a timeout"); - } catch (ConnectionPoolTimeoutException e) { - // expected - } - - mgr.releaseConnection(conn, null, -1, null); - // this time: no exception - conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); - Assert.assertNotNull("should have gotten a connection", conn); - - mgr.shutdown(); - } - - public class GetConnThread extends Thread { - - private final ConnectionRequest connRequest; - private final long timeout; - - private volatile HttpClientConnection connection; - private volatile Exception exception; - - public GetConnThread( - final HttpClientConnectionManager mgr, - final HttpRoute route, long timeout) { - this(mgr.requestConnection(route, null), route, timeout); - } - - public GetConnThread( - final ConnectionRequest connRequest, - final HttpRoute route, long timeout) { - super(); - this.connRequest = connRequest; - this.timeout = timeout; - } - - @Override - public void run() { - try { - connection = connRequest.get(timeout, TimeUnit.MILLISECONDS); - } catch (Exception ex) { - exception = ex; - } - } - - public Throwable getException() { - return exception; - } - - public HttpClientConnection getConnection() { - return connection; - } + mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); + Mockito.verify(pool).release(entry, false); + Mockito.verify(entry, Mockito.never()).setState(Mockito.anyObject()); + Mockito.verify(entry, Mockito.never()).updateExpiry(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)); } }