diff --git a/httpclient5-osgi/src/main/java/org/apache/hc/client5/http/osgi/impl/OSGiHttpRoutePlanner.java b/httpclient5-osgi/src/main/java/org/apache/hc/client5/http/osgi/impl/OSGiHttpRoutePlanner.java index b5d36b2a6..2807b29cc 100644 --- a/httpclient5-osgi/src/main/java/org/apache/hc/client5/http/osgi/impl/OSGiHttpRoutePlanner.java +++ b/httpclient5-osgi/src/main/java/org/apache/hc/client5/http/osgi/impl/OSGiHttpRoutePlanner.java @@ -34,7 +34,6 @@ import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner; import org.apache.hc.client5.http.osgi.services.ProxyConfiguration; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** @@ -53,7 +52,7 @@ final class OSGiHttpRoutePlanner extends DefaultRoutePlanner { * {@inheritDoc} */ @Override - protected HttpHost determineProxy(final HttpHost target, final HttpRequest request, final HttpContext context) throws HttpException { + protected HttpHost determineProxy(final HttpHost target, final HttpContext context) throws HttpException { HttpHost proxyHost = null; for (final ProxyConfiguration proxyConfiguration : proxyConfigurations) { if (proxyConfiguration.isEnabled()) { diff --git a/httpclient5-osgi/src/test/java/org/apache/hc/client5/http/osgi/impl/TestOSGiHttpRoutePlanner.java b/httpclient5-osgi/src/test/java/org/apache/hc/client5/http/osgi/impl/TestOSGiHttpRoutePlanner.java index 9afac4dce..57fb99aa2 100644 --- a/httpclient5-osgi/src/test/java/org/apache/hc/client5/http/osgi/impl/TestOSGiHttpRoutePlanner.java +++ b/httpclient5-osgi/src/test/java/org/apache/hc/client5/http/osgi/impl/TestOSGiHttpRoutePlanner.java @@ -51,41 +51,41 @@ public class TestOSGiHttpRoutePlanner { public void testDeterminProxy() throws Exception { OSGiHttpRoutePlanner planner = new OSGiHttpRoutePlanner(singletonList(pc1)); - HttpHost proxy = planner.determineProxy(new HttpHost("localhost", 8090), null, null); + HttpHost proxy = planner.determineProxy(new HttpHost("localhost", 8090), null); assertNull(proxy); - proxy = planner.determineProxy(new HttpHost("there", 9090), null, null); + proxy = planner.determineProxy(new HttpHost("there", 9090), null); assertNotNull(proxy); assertTrue(proxy.getHostName().equals("proxy1")); - proxy = planner.determineProxy(new HttpHost("10.2.144.23", 4554), null, null); + proxy = planner.determineProxy(new HttpHost("10.2.144.23", 4554), null); assertNotNull(proxy); assertTrue(proxy.getHostName().equals("proxy1")); final InetAddress addr = InetAddress.getByName("localhost"); - proxy = planner.determineProxy(new HttpHost(addr, 4554), null, null); + proxy = planner.determineProxy(new HttpHost(addr, 4554), null); assertNull(proxy); - proxy = planner.determineProxy(new HttpHost("hc.apache.org", 4554), null, null); + proxy = planner.determineProxy(new HttpHost("hc.apache.org", 4554), null); assertNull(proxy); // test with more than one registration of proxyConfiguration planner = new OSGiHttpRoutePlanner(asList(pc1, pc2)); - proxy = planner.determineProxy(new HttpHost("localhost", 8090), null, null); + proxy = planner.determineProxy(new HttpHost("localhost", 8090), null); assertNull(proxy); - proxy = planner.determineProxy(new HttpHost("there", 9090), null, null); + proxy = planner.determineProxy(new HttpHost("there", 9090), null); assertNotNull(proxy); assertTrue(proxy.getHostName().equals("proxy1")); // the first one - proxy = planner.determineProxy(new HttpHost(addr, 4554), null, null); + proxy = planner.determineProxy(new HttpHost(addr, 4554), null); assertNull(proxy); - proxy = planner.determineProxy(new HttpHost("hc.apache.org", 4554), null, null); + proxy = planner.determineProxy(new HttpHost("hc.apache.org", 4554), null); assertNull(proxy); - proxy = planner.determineProxy(new HttpHost("docs.oracle.com", 4554), null, null); + proxy = planner.determineProxy(new HttpHost("docs.oracle.com", 4554), null); assertNull(proxy); } @@ -93,11 +93,11 @@ public class TestOSGiHttpRoutePlanner { public void testMasking() throws Exception { final OSGiHttpRoutePlanner planner = new OSGiHttpRoutePlanner(singletonList(pc2)); - HttpHost proxy = planner.determineProxy(new HttpHost("12.34.34.2", 4554), null, null); + HttpHost proxy = planner.determineProxy(new HttpHost("12.34.34.2", 4554), null); assertNotNull(proxy); assertTrue(proxy.getHostName().equals("proxy2")); - proxy = planner.determineProxy(new HttpHost("12.34.34.8", 4554), null, null); + proxy = planner.determineProxy(new HttpHost("12.34.34.8", 4554), null); assertNotNull(proxy); } diff --git a/httpclient5/src/examples/org/apache/hc/client5/http/examples/ClientConfiguration.java b/httpclient5/src/examples/org/apache/hc/client5/http/examples/ClientConfiguration.java index 6eda01e50..ad7deb701 100644 --- a/httpclient5/src/examples/org/apache/hc/client5/http/examples/ClientConfiguration.java +++ b/httpclient5/src/examples/org/apache/hc/client5/http/examples/ClientConfiguration.java @@ -36,7 +36,6 @@ import java.util.Arrays; import javax.net.ssl.SSLContext; import org.apache.hc.client5.http.DnsResolver; -import org.apache.hc.client5.http.HttpConnectionFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.auth.CredentialsProvider; @@ -45,15 +44,13 @@ import org.apache.hc.client5.http.config.CookieSpecs; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieStore; -import org.apache.hc.client5.http.impl.io.DefaultHttpResponseParserFactory; -import org.apache.hc.client5.http.impl.io.LenientHttpResponseParser; import org.apache.hc.client5.http.impl.io.ManagedHttpClientConnectionFactory; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.sync.CloseableHttpClient; +import org.apache.hc.client5.http.impl.sync.CloseableHttpResponse; import org.apache.hc.client5.http.impl.sync.HttpClients; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; -import org.apache.hc.client5.http.impl.sync.CloseableHttpResponse; import org.apache.hc.client5.http.methods.HttpGet; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; @@ -71,6 +68,9 @@ import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.config.SocketConfig; import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; import org.apache.hc.core5.http.impl.io.DefaultHttpRequestWriterFactory; +import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParser; +import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParserFactory; +import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; @@ -107,18 +107,30 @@ public class ClientConfiguration { } }; - return new LenientHttpResponseParser(lineParser, DefaultClassicHttpResponseFactory.INSTANCE, h1Config); + return new DefaultHttpResponseParser(lineParser, DefaultClassicHttpResponseFactory.INSTANCE, h1Config); } }; HttpMessageWriterFactory requestWriterFactory = new DefaultHttpRequestWriterFactory(); + // Create HTTP/1.1 protocol configuration + H1Config h1Config = H1Config.custom() + .setMaxHeaderCount(200) + .setMaxLineLength(2000) + .build(); + // Create connection configuration + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setMalformedInputAction(CodingErrorAction.IGNORE) + .setUnmappableInputAction(CodingErrorAction.IGNORE) + .setCharset(StandardCharsets.UTF_8) + .build(); + // Use a custom connection factory to customize the process of // initialization of outgoing HTTP connections. Beside standard connection // configuration parameters HTTP connection factory can define message // parser / writer routines to be employed by individual connections. - HttpConnectionFactory connFactory = new ManagedHttpClientConnectionFactory( - H1Config.DEFAULT, requestWriterFactory, responseParserFactory); + HttpConnectionFactory connFactory = new ManagedHttpClientConnectionFactory( + h1Config, connectionConfig, requestWriterFactory, responseParserFactory); // Client HTTP connection objects when fully initialized can be bound to // an arbitrary network socket. The process of network socket initialization, @@ -161,26 +173,9 @@ public class ClientConfiguration { // Configure the connection manager to use socket configuration either // by default or for a specific host. connManager.setDefaultSocketConfig(socketConfig); - connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig); // Validate connections after 1 sec of inactivity connManager.setValidateAfterInactivity(1000); - // Create message constraints - H1Config messageConstraints = H1Config.custom() - .setMaxHeaderCount(200) - .setMaxLineLength(2000) - .build(); - // Create connection configuration - ConnectionConfig connectionConfig = ConnectionConfig.custom() - .setMalformedInputAction(CodingErrorAction.IGNORE) - .setUnmappableInputAction(CodingErrorAction.IGNORE) - .setCharset(StandardCharsets.UTF_8) - .build(); - // Configure the connection manager to use connection configuration either - // by default or for a specific host. - connManager.setDefaultConnectionConfig(connectionConfig); - connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT); - // Configure total max or per route limits for persistent connections // that can be kept in the pool or leased by the connection manager. connManager.setMaxTotal(100); diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ConnPoolSupport.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ConnPoolSupport.java new file mode 100644 index 000000000..f6ab7d1d3 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ConnPoolSupport.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * 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.hc.client5.http.impl; + +import org.apache.hc.client5.http.HttpRoute; +import org.apache.hc.client5.http.utils.Identifiable; +import org.apache.hc.core5.pool.ConnPoolControl; +import org.apache.hc.core5.pool.PoolStats; + +public final class ConnPoolSupport { + + public static String getId(final Object object) { + if (object == null) { + return null; + } + if (object instanceof Identifiable) { + return ((Identifiable) object).getId(); + } else { + return Integer.toHexString(System.identityHashCode(object)); + } + } + + public static String formatStats( + final Object object, + final HttpRoute route, + final Object state, + final ConnPoolControl connPool) { + final StringBuilder buf = new StringBuilder(); + if (object != null) { + buf.append("[id: ").append(getId(object)).append("]"); + } + buf.append("[route: ").append(route).append("]"); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + final PoolStats totals = connPool.getTotalStats(); + final PoolStats stats = connPool.getStats(route); + 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(" of ").append(totals.getMax()).append("]"); + return buf.toString(); + } + +} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ConnectionShutdownException.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ConnectionShutdownException.java similarity index 97% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ConnectionShutdownException.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/ConnectionShutdownException.java index 0c85ce01d..6b8b915bf 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ConnectionShutdownException.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ConnectionShutdownException.java @@ -25,7 +25,7 @@ * */ -package org.apache.hc.client5.http.impl.io; +package org.apache.hc.client5.http.impl; /** * Signals that the connection has been shut down or released back to the diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultThreadFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultThreadFactory.java new file mode 100644 index 000000000..dc092c464 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultThreadFactory.java @@ -0,0 +1,64 @@ +/* + * ==================================================================== + * 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.hc.client5.http.impl; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @since 5.0 + */ +public class DefaultThreadFactory implements ThreadFactory { + + private final String namePrefix; + private final ThreadGroup group; + private final AtomicLong count; + private final boolean daemon; + + public DefaultThreadFactory(final String namePrefix, final ThreadGroup group, final boolean daemon) { + this.namePrefix = namePrefix; + this.group = group; + this.count = new AtomicLong(); + this.daemon = daemon; + } + + public DefaultThreadFactory(final String namePrefix) { + this(namePrefix, null, false); + } + + public DefaultThreadFactory(final String namePrefix, final boolean daemon) { + this(namePrefix, null, daemon); + } + + @Override + public Thread newThread(final Runnable target) { + final Thread thread = new Thread(this.group, target, this.namePrefix + "-" + this.count.incrementAndGet()); + thread.setDaemon(this.daemon); + return thread; + } + +} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java index 4d18e5393..f7a27ff6d 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java @@ -27,33 +27,38 @@ package org.apache.hc.client5.http.impl.io; -import java.io.Closeable; import java.io.IOException; -import java.net.InetSocketAddress; import java.util.Date; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.DnsResolver; -import org.apache.hc.client5.http.HttpConnectionFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.impl.ConnectionShutdownException; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.io.HttpClientConnectionOperator; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.config.ConnectionConfig; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.config.SocketConfig; -import org.apache.hc.core5.http.io.HttpClientConnection; +import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; +import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; @@ -80,12 +85,12 @@ import org.apache.logging.log4j.Logger; * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE) -public class BasicHttpClientConnectionManager implements HttpClientConnectionManager, Closeable { +public class BasicHttpClientConnectionManager implements HttpClientConnectionManager { private final Logger log = LogManager.getLogger(getClass()); private final HttpClientConnectionOperator connectionOperator; - private final HttpConnectionFactory connFactory; + private final HttpConnectionFactory connFactory; private ManagedHttpClientConnection conn; private HttpRoute route; @@ -94,9 +99,8 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan private long expiry; private boolean leased; private SocketConfig socketConfig; - private ConnectionConfig connConfig; - private final AtomicBoolean isShutdown; + private final AtomicBoolean closed; private static Registry getDefaultRegistry() { return RegistryBuilder.create() @@ -106,14 +110,12 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan } public BasicHttpClientConnectionManager( - final Lookup socketFactoryRegistry, - final HttpConnectionFactory connFactory, - final SchemePortResolver schemePortResolver, - final DnsResolver dnsResolver) { - this( - new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver), - connFactory - ); + final Lookup socketFactoryRegistry, + final HttpConnectionFactory connFactory, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + this(new DefaultHttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver), connFactory); } /** @@ -121,19 +123,18 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan */ public BasicHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, - final HttpConnectionFactory connFactory) { + final HttpConnectionFactory connFactory) { super(); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator"); this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; this.expiry = Long.MAX_VALUE; this.socketConfig = SocketConfig.DEFAULT; - this.connConfig = ConnectionConfig.DEFAULT; - this.isShutdown = new AtomicBoolean(false); + this.closed = new AtomicBoolean(false); } public BasicHttpClientConnectionManager( final Lookup socketFactoryRegistry, - final HttpConnectionFactory connFactory) { + final HttpConnectionFactory connFactory) { this(socketFactoryRegistry, connFactory, null, null); } @@ -149,7 +150,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan @Override protected void finalize() throws Throwable { try { - shutdown(); + close(); } finally { // Make sure we call overridden method even if shutdown barfs super.finalize(); } @@ -157,7 +158,9 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan @Override public void close() { - shutdown(); + if (this.closed.compareAndSet(false, true)) { + shutdownConnection(); + } } HttpRoute getRoute() { @@ -176,37 +179,30 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; } - public synchronized ConnectionConfig getConnectionConfig() { - return connConfig; - } - - public synchronized void setConnectionConfig(final ConnectionConfig connConfig) { - this.connConfig = connConfig != null ? connConfig : ConnectionConfig.DEFAULT; - } - @Override - public final ConnectionRequest requestConnection( - final HttpRoute route, - final Object state) { - Args.notNull(route, "Route"); - return new ConnectionRequest() { + public LeaseRequest lease(final HttpRoute route, final Object state) { + return new LeaseRequest() { @Override - public boolean cancel() { - // Nothing to abort, since requests are immediate. - return false; + public ConnectionEndpoint get( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ExecutionException, TimeoutException { + try { + return new InternalConnectionEndpoint(route, getConnection(route, state)); + } catch (IOException ex) { + throw new ExecutionException(ex.getMessage(), ex); + } } @Override - public HttpClientConnection get(final long timeout, final TimeUnit tunit) { - return BasicHttpClientConnectionManager.this.getConnection( - route, state); + public boolean cancel() { + return false; } }; } - private void closeConnection() { + private synchronized void closeConnection() { if (this.conn != null) { this.log.debug("Closing connection"); try { @@ -220,7 +216,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan } } - private void shutdownConnection() { + private synchronized void shutdownConnection() { if (this.conn != null) { this.log.debug("Shutting down connection"); try { @@ -243,8 +239,8 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan } } - synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) { - Asserts.check(!this.isShutdown.get(), "Connection manager has been shut down"); + synchronized ManagedHttpClientConnection getConnection(final HttpRoute route, final Object state) throws IOException { + Asserts.check(!this.closed.get(), "Connection manager has been shut down"); if (this.log.isDebugEnabled()) { this.log.debug("Get connection for route " + route); } @@ -256,23 +252,32 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan this.state = state; checkExpiry(); if (this.conn == null) { - this.conn = this.connFactory.create(route, this.connConfig); + this.conn = this.connFactory.createConnection(null); } this.leased = true; return this.conn; } + private InternalConnectionEndpoint cast(final ConnectionEndpoint endpoint) { + if (endpoint instanceof InternalConnectionEndpoint) { + return (InternalConnectionEndpoint) endpoint; + } else { + throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass()); + } + } + @Override - public synchronized void releaseConnection( - final HttpClientConnection conn, + public synchronized void release( + final ConnectionEndpoint endpoint, final Object state, final long keepalive, final TimeUnit tunit) { - Args.notNull(conn, "Connection"); - Asserts.check(conn == this.conn, "Connection not obtained from this manager"); - if (this.log.isDebugEnabled()) { + Args.notNull(endpoint, "Endpoint"); + final InternalConnectionEndpoint internalEndpoint = cast(endpoint); + final ManagedHttpClientConnection conn = internalEndpoint.detach(); + if (conn != null && this.log.isDebugEnabled()) { this.log.debug("Releasing connection " + conn); } - if (this.isShutdown.get()) { + if (this.closed.get()) { return; } try { @@ -306,45 +311,47 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan @Override public void connect( - final HttpClientConnection conn, - final HttpRoute route, - final int connectTimeout, + final ConnectionEndpoint endpoint, + final long connectTimeout, + final TimeUnit timeUnit, final HttpContext context) throws IOException { - Args.notNull(conn, "Connection"); - Args.notNull(route, "HTTP route"); - Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + Args.notNull(endpoint, "Endpoint"); + + final InternalConnectionEndpoint internalEndpoint = cast(endpoint); + if (internalEndpoint.isConnected()) { + return; + } + final HttpRoute route = internalEndpoint.getRoute(); final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } - final InetSocketAddress localAddress = route.getLocalSocketAddress(); - this.connectionOperator.connect(this.conn, host, localAddress, - connectTimeout, this.socketConfig, context); + this.connectionOperator.connect( + internalEndpoint.getConnection(), + host, + route.getLocalSocketAddress(), + (int) (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS).toMillis(connectTimeout), + this.socketConfig, + context); } @Override public void upgrade( - final HttpClientConnection conn, - final HttpRoute route, + final ConnectionEndpoint endpoint, final HttpContext context) throws IOException { - Args.notNull(conn, "Connection"); + Args.notNull(endpoint, "Endpoint"); Args.notNull(route, "HTTP route"); - Asserts.check(conn == this.conn, "Connection not obtained from this manager"); - this.connectionOperator.upgrade(this.conn, route.getTargetHost(), context); + final InternalConnectionEndpoint internalEndpoint = cast(endpoint); + this.connectionOperator.upgrade( + internalEndpoint.getConnection(), + internalEndpoint.getRoute().getTargetHost(), + context); } - @Override - public void routeComplete( - final HttpClientConnection conn, - final HttpRoute route, - final HttpContext context) throws IOException { - } - - @Override public synchronized void closeExpired() { - if (this.isShutdown.get()) { + if (this.closed.get()) { return; } if (!this.leased) { @@ -352,10 +359,9 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan } } - @Override public synchronized void closeIdle(final long idletime, final TimeUnit tunit) { Args.notNull(tunit, "Time unit"); - if (this.isShutdown.get()) { + if (this.closed.get()) { return; } if (!this.leased) { @@ -370,11 +376,75 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan } } - @Override - public synchronized void shutdown() { - if (this.isShutdown.compareAndSet(false, true)) { - shutdownConnection(); + class InternalConnectionEndpoint extends ConnectionEndpoint { + + private final HttpRoute route; + private final AtomicReference connRef; + + public InternalConnectionEndpoint(final HttpRoute route, final ManagedHttpClientConnection conn) { + this.route = route; + this.connRef = new AtomicReference<>(conn); } + + HttpRoute getRoute() { + return route; + } + + ManagedHttpClientConnection getConnection() { + final ManagedHttpClientConnection conn = this.connRef.get(); + if (conn == null) { + throw new ConnectionShutdownException(); + } + return conn; + } + + ManagedHttpClientConnection getValidatedConnection() { + final ManagedHttpClientConnection conn = getConnection(); + Asserts.check(conn.isOpen(), "Endpoint is not connected"); + return conn; + } + + ManagedHttpClientConnection detach() { + return this.connRef.getAndSet(null); + } + + @Override + public boolean isConnected() { + final ManagedHttpClientConnection conn = getConnection(); + return conn != null && conn.isOpen(); + } + + @Override + public void shutdown() throws IOException { + final ManagedHttpClientConnection conn = detach(); + if (conn != null) { + conn.shutdown(); + } + } + + @Override + public void close() throws IOException { + final ManagedHttpClientConnection conn = detach(); + if (conn != null) { + conn.close(); + } + } + + @Override + public void setSocketTimeout(final int timeout) { + getValidatedConnection().setSocketTimeout(timeout); + } + + @Override + public ClassicHttpResponse execute( + final ClassicHttpRequest request, + final HttpRequestExecutor requestExecutor, + final HttpContext context) throws IOException, HttpException { + Args.notNull(request, "HTTP request"); + Args.notNull(requestExecutor, "Request executor"); + return requestExecutor.execute(request, getValidatedConnection(), context); + } + } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/CPoolProxy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/CPoolProxy.java deleted file mode 100644 index de76b606f..000000000 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/CPoolProxy.java +++ /dev/null @@ -1,283 +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.hc.client5.http.impl.io; - -import java.io.IOException; -import java.net.Socket; -import java.net.SocketAddress; - -import javax.net.ssl.SSLSession; - -import org.apache.hc.client5.http.HttpRoute; -import org.apache.hc.client5.http.io.ManagedHttpClientConnection; -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.HttpConnectionMetrics; -import org.apache.hc.core5.http.HttpException; -import org.apache.hc.core5.http.ProtocolVersion; -import org.apache.hc.core5.http.io.HttpClientConnection; -import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.pool.PoolEntry; - -/** - * @since 4.3 - */ -class CPoolProxy implements ManagedHttpClientConnection, HttpContext { - - private volatile PoolEntry poolEntry; - private volatile boolean routeComplete; - - CPoolProxy(final PoolEntry entry) { - super(); - this.poolEntry = entry; - } - - PoolEntry getPoolEntry() { - return this.poolEntry; - } - - boolean isDetached() { - return this.poolEntry == null; - } - - PoolEntry detach() { - final PoolEntry local = this.poolEntry; - this.poolEntry = null; - return local; - } - - public void markRouteComplete() { - this.routeComplete = true; - } - - public boolean isRouteComplete() { - return this.routeComplete; - } - - ManagedHttpClientConnection getConnection() { - final PoolEntry local = this.poolEntry; - if (local == null) { - return null; - } - return local.getConnection(); - } - - ManagedHttpClientConnection getValidConnection() { - final ManagedHttpClientConnection conn = getConnection(); - if (conn == null) { - throw new ConnectionShutdownException(); - } - return conn; - } - - @Override - public void close() throws IOException { - final PoolEntry local = this.poolEntry; - if (local != null) { - final ManagedHttpClientConnection conn = local.getConnection(); - if (conn != null) { - conn.close(); - } - } - } - - @Override - public void shutdown() throws IOException { - final PoolEntry local = this.poolEntry; - if (local != null) { - final ManagedHttpClientConnection conn = local.getConnection(); - if (conn != null) { - conn.close(); - } - } - } - - @Override - public boolean isOpen() { - final PoolEntry local = this.poolEntry; - if (local != null) { - final ManagedHttpClientConnection conn = local.getConnection(); - if (conn != null) { - return conn.isOpen(); - } - } - return false; - } - - @Override - public boolean isStale() throws IOException { - final HttpClientConnection conn = getConnection(); - if (conn != null) { - return conn.isStale(); - } else { - return true; - } - } - - @Override - public ProtocolVersion getProtocolVersion() { - return getValidConnection().getProtocolVersion(); - } - - @Override - public void setSocketTimeout(final int timeout) { - getValidConnection().setSocketTimeout(timeout); - } - - @Override - public int getSocketTimeout() { - return getValidConnection().getSocketTimeout(); - } - - @Override - public String getId() { - return getValidConnection().getId(); - } - - @Override - public void bind(final Socket socket) throws IOException { - getValidConnection().bind(socket); - } - - @Override - public Socket getSocket() { - return getValidConnection().getSocket(); - } - - @Override - public SSLSession getSSLSession() { - return getValidConnection().getSSLSession(); - } - - @Override - public boolean isConsistent() { - return getValidConnection().isConsistent(); - } - - @Override - public void terminateRequest(final ClassicHttpRequest request) throws HttpException, IOException { - getValidConnection().terminateRequest(request); - } - - @Override - public boolean isDataAvailable(final int timeout) throws IOException { - return getValidConnection().isDataAvailable(timeout); - } - - @Override - public void sendRequestHeader(final ClassicHttpRequest request) throws HttpException, IOException { - getValidConnection().sendRequestHeader(request); - } - - @Override - public void sendRequestEntity(final ClassicHttpRequest request) throws HttpException, IOException { - getValidConnection().sendRequestEntity(request); - } - - @Override - public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException { - return getValidConnection().receiveResponseHeader(); - } - - @Override - public void receiveResponseEntity(final ClassicHttpResponse response) throws HttpException, IOException { - getValidConnection().receiveResponseEntity(response); - } - - @Override - public void flush() throws IOException { - getValidConnection().flush(); - } - - @Override - public HttpConnectionMetrics getMetrics() { - return getValidConnection().getMetrics(); - } - - @Override - public SocketAddress getLocalAddress() { - return getValidConnection().getLocalAddress(); - } - - @Override - public SocketAddress getRemoteAddress() { - return getValidConnection().getRemoteAddress(); - } - - @Override - public Object getAttribute(final String id) { - final ManagedHttpClientConnection conn = getValidConnection(); - if (conn instanceof HttpContext) { - return ((HttpContext) conn).getAttribute(id); - } else { - return null; - } - } - - @Override - public void setAttribute(final String id, final Object obj) { - final ManagedHttpClientConnection conn = getValidConnection(); - if (conn instanceof HttpContext) { - ((HttpContext) conn).setAttribute(id, obj); - } - } - - @Override - public Object removeAttribute(final String id) { - final ManagedHttpClientConnection conn = getValidConnection(); - if (conn instanceof HttpContext) { - return ((HttpContext) conn).removeAttribute(id); - } else { - return null; - } - } - - @Override - public void setProtocolVersion(final ProtocolVersion version) { - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("CPoolProxy{"); - final ManagedHttpClientConnection conn = getConnection(); - if (conn != null) { - sb.append(conn); - } else { - sb.append("detached"); - } - sb.append('}'); - return sb.toString(); - } - - static CPoolProxy getProxy(final HttpClientConnection conn) { - if (!CPoolProxy.class.isInstance(conn)) { - throw new IllegalStateException("Unexpected connection proxy class: " + conn.getClass()); - } - return CPoolProxy.class.cast(conn); - } - -} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java index 4609eb289..5e67b3b98 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java @@ -48,6 +48,7 @@ import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.SocketConfig; @@ -191,6 +192,9 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection } final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf; Socket sock = conn.getSocket(); + if (sock == null) { + throw new ConnectionClosedException("Connection is closed"); + } final int port = this.schemePortResolver.resolve(host); sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context); conn.bind(sock); diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultManagedHttpClientConnection.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultManagedHttpClientConnection.java index de1ee5d7f..392c41874 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultManagedHttpClientConnection.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultManagedHttpClientConnection.java @@ -32,35 +32,41 @@ import java.io.InterruptedIOException; import java.net.Socket; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; +import org.apache.hc.client5.http.impl.logging.LoggingSocketHolder; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; +import org.apache.hc.client5.http.utils.Identifiable; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; -import org.apache.hc.core5.http.ProtocolVersion; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.config.H1Config; import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection; import org.apache.hc.core5.http.impl.io.SocketHolder; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; -import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.http.message.RequestLine; +import org.apache.hc.core5.http.message.StatusLine; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Default {@link ManagedHttpClientConnection} implementation. * @since 4.3 */ -public class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnection - implements ManagedHttpClientConnection, HttpContext { +public class DefaultManagedHttpClientConnection + extends DefaultBHttpClientConnection implements ManagedHttpClientConnection, Identifiable { + + private final Logger log = LogManager.getLogger(DefaultManagedHttpClientConnection.class); + private final Logger headerlog = LogManager.getLogger("org.apache.hc.client5.http.headers"); + private final Logger wirelog = LogManager.getLogger("org.apache.hc.client5.http.wire"); private final String id; - private final Map attributes; - - private volatile boolean shutdown; + private final AtomicBoolean closed; public DefaultManagedHttpClientConnection( final String id, @@ -75,7 +81,7 @@ public class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnec super(buffersize, chardecoder, charencoder, h1Config, incomingContentStrategy, outgoingContentStrategy, requestWriterFactory, responseParserFactory); this.id = id; - this.attributes = new ConcurrentHashMap<>(); + this.closed = new AtomicBoolean(); } public DefaultManagedHttpClientConnection( @@ -89,15 +95,9 @@ public class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnec return this.id; } - @Override - public void shutdown() throws IOException { - this.shutdown = true; - super.shutdown(); - } - @Override public void bind(final SocketHolder socketHolder) throws IOException { - if (this.shutdown) { + if (this.closed.get()) { final Socket socket = socketHolder.getSocket(); socket.close(); // allow this to throw... // ...but if it doesn't, explicitly throw one ourselves. @@ -123,22 +123,58 @@ public class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnec } @Override - public void setProtocolVersion(final ProtocolVersion version) { + public void close() throws IOException { + if (this.closed.compareAndSet(false, true)) { + if (this.log.isDebugEnabled()) { + this.log.debug(this.id + ": Close connection"); + } + super.close(); + } } @Override - public Object getAttribute(final String id) { - return this.attributes.get(id); + public void setSocketTimeout(final int timeout) { + if (this.log.isDebugEnabled()) { + this.log.debug(this.id + ": set socket timeout to " + timeout); + } + super.setSocketTimeout(timeout); } @Override - public void setAttribute(final String id, final Object obj) { - this.attributes.put(id, obj); + public void shutdown() throws IOException { + if (this.closed.compareAndSet(false, true)) { + if (this.log.isDebugEnabled()) { + this.log.debug(this.id + ": Shutdown connection"); + } + super.shutdown(); + } } @Override - public Object removeAttribute(final String id) { - return this.attributes.remove(id); + public void bind(final Socket socket) throws IOException { + super.bind(this.wirelog.isDebugEnabled() ? new LoggingSocketHolder(socket, this.id, this.wirelog) : new SocketHolder(socket)); + } + + @Override + protected void onResponseReceived(final ClassicHttpResponse response) { + if (response != null && this.headerlog.isDebugEnabled()) { + this.headerlog.debug(this.id + " << " + new StatusLine(response)); + final Header[] headers = response.getAllHeaders(); + for (final Header header : headers) { + this.headerlog.debug(this.id + " << " + header.toString()); + } + } + } + + @Override + protected void onRequestSubmitted(final ClassicHttpRequest request) { + if (request != null && this.headerlog.isDebugEnabled()) { + this.headerlog.debug(this.id + " >> " + new RequestLine(request)); + final Header[] headers = request.getAllHeaders(); + for (final Header header : headers) { + this.headerlog.debug(this.id + " >> " + header.toString()); + } + } } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingManagedHttpClientConnection.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingManagedHttpClientConnection.java deleted file mode 100644 index f5333edd4..000000000 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingManagedHttpClientConnection.java +++ /dev/null @@ -1,127 +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.hc.client5.http.impl.io; - -import java.io.IOException; -import java.net.Socket; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; - -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ContentLengthStrategy; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.config.H1Config; -import org.apache.hc.core5.http.impl.io.SocketHolder; -import org.apache.hc.core5.http.io.HttpMessageParserFactory; -import org.apache.hc.core5.http.io.HttpMessageWriterFactory; -import org.apache.hc.core5.http.message.RequestLine; -import org.apache.hc.core5.http.message.StatusLine; -import org.apache.logging.log4j.Logger; - -class LoggingManagedHttpClientConnection extends DefaultManagedHttpClientConnection { - - private final Logger log; - private final Logger headerlog; - private final Wire wire; - - public LoggingManagedHttpClientConnection( - final String id, - final Logger log, - final Logger headerlog, - final Logger wirelog, - final int buffersize, - final CharsetDecoder chardecoder, - final CharsetEncoder charencoder, - final H1Config h1Config, - final ContentLengthStrategy incomingContentStrategy, - final ContentLengthStrategy outgoingContentStrategy, - final HttpMessageWriterFactory requestWriterFactory, - final HttpMessageParserFactory responseParserFactory) { - super(id, buffersize, chardecoder, charencoder, h1Config, incomingContentStrategy, outgoingContentStrategy, - requestWriterFactory, responseParserFactory); - this.log = log; - this.headerlog = headerlog; - this.wire = new Wire(wirelog, id); - } - - @Override - public void close() throws IOException { - - if (super.isOpen()) { - if (this.log.isDebugEnabled()) { - this.log.debug(getId() + ": Close connection"); - } - super.close(); - } - } - - @Override - public void setSocketTimeout(final int timeout) { - if (this.log.isDebugEnabled()) { - this.log.debug(getId() + ": set socket timeout to " + timeout); - } - super.setSocketTimeout(timeout); - } - - @Override - public void shutdown() throws IOException { - if (this.log.isDebugEnabled()) { - this.log.debug(getId() + ": Shutdown connection"); - } - super.shutdown(); - } - - @Override - public void bind(final Socket socket) throws IOException { - super.bind(this.wire.enabled() ? new LoggingSocketHolder(socket, this.wire) : new SocketHolder(socket)); - } - - @Override - protected void onResponseReceived(final ClassicHttpResponse response) { - if (response != null && this.headerlog.isDebugEnabled()) { - this.headerlog.debug(getId() + " << " + new StatusLine(response)); - final Header[] headers = response.getAllHeaders(); - for (final Header header : headers) { - this.headerlog.debug(getId() + " << " + header.toString()); - } - } - } - - @Override - protected void onRequestSubmitted(final ClassicHttpRequest request) { - if (request != null && this.headerlog.isDebugEnabled()) { - this.headerlog.debug(getId() + " >> " + new RequestLine(request)); - final Header[] headers = request.getAllHeaders(); - for (final Header header : headers) { - this.headerlog.debug(getId() + " >> " + header.toString()); - } - } - } - -} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ManagedHttpClientConnectionFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ManagedHttpClientConnectionFactory.java index 6bf6dc34d..ea05d4f4f 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ManagedHttpClientConnectionFactory.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/ManagedHttpClientConnectionFactory.java @@ -27,14 +27,14 @@ package org.apache.hc.client5.http.impl.io; +import java.io.IOException; +import java.net.Socket; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import java.util.concurrent.atomic.AtomicLong; -import org.apache.hc.client5.http.HttpConnectionFactory; -import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; @@ -45,43 +45,38 @@ import org.apache.hc.core5.http.config.ConnectionConfig; import org.apache.hc.core5.http.config.H1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.io.DefaultHttpRequestWriterFactory; +import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; /** * Factory for {@link ManagedHttpClientConnection} instances. * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) -public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory { +public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory { private static final AtomicLong COUNTER = new AtomicLong(); public static final ManagedHttpClientConnectionFactory INSTANCE = new ManagedHttpClientConnectionFactory(); - private final Logger log = LogManager.getLogger(DefaultManagedHttpClientConnection.class); - private final Logger headerlog = LogManager.getLogger("org.apache.hc.client5.http.headers"); - private final Logger wirelog = LogManager.getLogger("org.apache.hc.client5.http.wire"); - private final H1Config h1Config; + private final ConnectionConfig connectionConfig; private final HttpMessageWriterFactory requestWriterFactory; private final HttpMessageParserFactory responseParserFactory; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; - /** - * @since 4.4 - */ public ManagedHttpClientConnectionFactory( final H1Config h1Config, + final ConnectionConfig connectionConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy) { super(); this.h1Config = h1Config != null ? h1Config : H1Config.DEFAULT; + this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; this.requestWriterFactory = requestWriterFactory != null ? requestWriterFactory : DefaultHttpRequestWriterFactory.INSTANCE; this.responseParserFactory = responseParserFactory != null ? responseParserFactory : @@ -94,31 +89,32 @@ public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory public ManagedHttpClientConnectionFactory( final H1Config h1Config, + final ConnectionConfig connectionConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { - this(h1Config, requestWriterFactory, responseParserFactory, null, null); + this(h1Config, connectionConfig, requestWriterFactory, responseParserFactory, null, null); } public ManagedHttpClientConnectionFactory( final H1Config h1Config, + final ConnectionConfig connectionConfig, final HttpMessageParserFactory responseParserFactory) { - this(h1Config, null, responseParserFactory); + this(h1Config, connectionConfig, null, responseParserFactory); } public ManagedHttpClientConnectionFactory() { - this(null, null); + this(null, null, null); } @Override - public ManagedHttpClientConnection create(final HttpRoute route, final ConnectionConfig config) { - final ConnectionConfig cconfig = config != null ? config : ConnectionConfig.DEFAULT; + public ManagedHttpClientConnection createConnection(final Socket socket) throws IOException { CharsetDecoder chardecoder = null; CharsetEncoder charencoder = null; - final Charset charset = cconfig.getCharset(); - final CodingErrorAction malformedInputAction = cconfig.getMalformedInputAction() != null ? - cconfig.getMalformedInputAction() : CodingErrorAction.REPORT; - final CodingErrorAction unmappableInputAction = cconfig.getUnmappableInputAction() != null ? - cconfig.getUnmappableInputAction() : CodingErrorAction.REPORT; + final Charset charset = this.connectionConfig.getCharset(); + final CodingErrorAction malformedInputAction = this.connectionConfig.getMalformedInputAction() != null ? + this.connectionConfig.getMalformedInputAction() : CodingErrorAction.REPORT; + final CodingErrorAction unmappableInputAction = this.connectionConfig.getUnmappableInputAction() != null ? + this.connectionConfig.getUnmappableInputAction() : CodingErrorAction.REPORT; if (charset != null) { chardecoder = charset.newDecoder(); chardecoder.onMalformedInput(malformedInputAction); @@ -128,12 +124,9 @@ public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory charencoder.onUnmappableCharacter(unmappableInputAction); } final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement()); - return new LoggingManagedHttpClientConnection( + final DefaultManagedHttpClientConnection conn = new DefaultManagedHttpClientConnection( id, - log, - headerlog, - wirelog, - cconfig.getBufferSize(), + connectionConfig.getBufferSize(), chardecoder, charencoder, h1Config, @@ -141,6 +134,10 @@ public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory outgoingContentStrategy, requestWriterFactory, responseParserFactory); + if (socket != null) { + conn.bind(socket); + } + return conn; } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java index 99e189d3b..f374edf95 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java @@ -26,9 +26,7 @@ */ package org.apache.hc.client5.http.impl.io; -import java.io.Closeable; import java.io.IOException; -import java.net.InetSocketAddress; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -37,15 +35,17 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; import org.apache.hc.client5.http.DnsResolver; -import org.apache.hc.client5.http.HttpConnectionFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.impl.ConnPoolSupport; +import org.apache.hc.client5.http.impl.ConnectionShutdownException; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.io.HttpClientConnectionOperator; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; @@ -53,70 +53,68 @@ import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Callback; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.config.ConnectionConfig; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.config.SocketConfig; +import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpClientConnection; +import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.pool.ConnPoolControl; +import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.ConnPoolPolicy; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.util.Args; +import org.apache.hc.core5.util.Asserts; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * {@code ClientConnectionPoolManager} maintains a pool of - * {@link HttpClientConnection}s and is able to service connection requests + * {@link ManagedHttpClientConnection}s 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. + * a connection from the pool rather than creating a new connection. *

* {@code ClientConnectionPoolManager} 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 {@link ConnPoolControl} methods. - *

+ * on a per route basis and in total. Connection limits, however, can be adjusted + * using {@link ConnPoolControl} methods. *

* Total time to live (TTL) set at construction time defines maximum life span * of persistent connections regardless of their expiration setting. No persistent * connection will be re-used past its TTL value. - *

*

* The handling of stale connections was changed in version 4.4. * Previously, the code would check every connection by default before re-using it. * The code now only checks the connection if the elapsed time since * the last use of the connection exceeds the timeout that has been set. - * The default timeout is set to 5000ms - see - * {@link #PoolingHttpClientConnectionManager(HttpClientConnectionOperator, HttpConnectionFactory, long, TimeUnit)} - *

+ * The default timeout is set to 5000ms. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class PoolingHttpClientConnectionManager - implements HttpClientConnectionManager, ConnPoolControl, Closeable { + implements HttpClientConnectionManager, ConnPoolControl { private final Logger log = LogManager.getLogger(getClass()); public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 25; public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5; - private final ConfigData configData; private final StrictConnPool pool; - private final HttpConnectionFactory connFactory; + private final HttpConnectionFactory connFactory; private final HttpClientConnectionOperator connectionOperator; - private final AtomicBoolean isShutDown; + private final AtomicBoolean closed; + private volatile SocketConfig defaultSocketConfig; private volatile int validateAfterInactivity; private static Registry getDefaultRegistry() { @@ -130,8 +128,9 @@ public class PoolingHttpClientConnectionManager this(getDefaultRegistry()); } - public PoolingHttpClientConnectionManager(final long timeToLive, final TimeUnit tunit) { - this(getDefaultRegistry(), null, null ,null, timeToLive, tunit); + public PoolingHttpClientConnectionManager( + final long timeToLive, final TimeUnit tunit) { + this(getDefaultRegistry(), null, null ,null, ConnPoolPolicy.LIFO, null, timeToLive, tunit); } public PoolingHttpClientConnectionManager( @@ -147,49 +146,46 @@ public class PoolingHttpClientConnectionManager public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, - final HttpConnectionFactory connFactory) { + final HttpConnectionFactory connFactory) { this(socketFactoryRegistry, connFactory, null); } public PoolingHttpClientConnectionManager( - final HttpConnectionFactory connFactory) { + final HttpConnectionFactory connFactory) { this(getDefaultRegistry(), connFactory, null); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, - final HttpConnectionFactory connFactory, + final HttpConnectionFactory connFactory, final DnsResolver dnsResolver) { - this(socketFactoryRegistry, connFactory, null, dnsResolver, -1, TimeUnit.MILLISECONDS); + this(socketFactoryRegistry, connFactory, null, dnsResolver, ConnPoolPolicy.LIFO, null, -1, TimeUnit.MILLISECONDS); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, - final HttpConnectionFactory connFactory, + final HttpConnectionFactory connFactory, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver, + final ConnPoolPolicy connPoolPolicy, + final ConnPoolListener connPoolListener, final long timeToLive, final TimeUnit tunit) { - this( - new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver), - connFactory, - timeToLive, tunit - ); + this(new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver), + connFactory, connPoolPolicy, connPoolListener, timeToLive, tunit); } - /** - * @since 4.4 - */ public PoolingHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, - final HttpConnectionFactory connFactory, + final HttpConnectionFactory connFactory, + final ConnPoolPolicy connPoolPolicy, + final ConnPoolListener connPoolListener, final long timeToLive, final TimeUnit tunit) { super(); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator"); this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; - this.configData = new ConfigData(); this.pool = new StrictConnPool<>( - DEFAULT_MAX_CONNECTIONS_PER_ROUTE, DEFAULT_MAX_TOTAL_CONNECTIONS, timeToLive, tunit, ConnPoolPolicy.LIFO, null); - this.isShutDown = new AtomicBoolean(false); + DEFAULT_MAX_CONNECTIONS_PER_ROUTE, DEFAULT_MAX_TOTAL_CONNECTIONS, timeToLive, tunit, connPoolPolicy, connPoolListener); + this.closed = new AtomicBoolean(false); } /** @@ -204,15 +200,14 @@ public class PoolingHttpClientConnectionManager this.connectionOperator = new DefaultHttpClientConnectionOperator( socketFactoryRegistry, schemePortResolver, dnsResolver); this.connFactory = ManagedHttpClientConnectionFactory.INSTANCE; - this.configData = new ConfigData(); this.pool = pool; - this.isShutDown = new AtomicBoolean(false); + this.closed = new AtomicBoolean(false); } @Override protected void finalize() throws Throwable { try { - shutdown(); + close(); } finally { super.finalize(); } @@ -220,227 +215,176 @@ public class PoolingHttpClientConnectionManager @Override public void close() { - shutdown(); - } - - private String format(final HttpRoute route, final Object state) { - final StringBuilder buf = new StringBuilder(); - buf.append("[route: ").append(route).append("]"); - if (state != null) { - buf.append("[state: ").append(state).append("]"); + if (this.closed.compareAndSet(false, true)) { + this.log.debug("Connection manager is shutting down"); + this.pool.shutdown(); + this.log.debug("Connection manager shut down"); } - return buf.toString(); } - private String formatStats(final HttpRoute route) { - final StringBuilder buf = new StringBuilder(); - final PoolStats totals = this.pool.getTotalStats(); - final 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(" of ").append(stats.getMax()).append("; "); - buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); - buf.append(" of ").append(totals.getMax()).append("]"); - return buf.toString(); - } - - private String format(final PoolEntry entry) { - final StringBuilder buf = new StringBuilder(); - final ManagedHttpClientConnection conn = entry.getConnection(); - buf.append("[id: ").append(conn != null ? conn.getId() : "unknown").append("]"); - buf.append("[route: ").append(entry.getRoute()).append("]"); - final Object state = entry.getState(); - if (state != null) { - buf.append("[state: ").append(state).append("]"); + private InternalConnectionEndpoint cast(final ConnectionEndpoint endpoint) { + if (endpoint instanceof InternalConnectionEndpoint) { + return (InternalConnectionEndpoint) endpoint; + } else { + throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass()); } - return buf.toString(); } @Override - public ConnectionRequest requestConnection( + public LeaseRequest lease( final HttpRoute route, final Object state) { Args.notNull(route, "HTTP route"); if (this.log.isDebugEnabled()) { - this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + this.log.debug("Connection request: " + ConnPoolSupport.formatStats(null, route, state, this.pool)); } - final Future> future = this.pool.lease(route, state, null); - return new ConnectionRequest() { + final Future> leaseFuture = this.pool.lease(route, state, null); + return new LeaseRequest() { + + private volatile ConnectionEndpoint endpoint; @Override - public boolean cancel() { - return future.cancel(true); + public synchronized ConnectionEndpoint get( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ExecutionException, TimeoutException { + if (this.endpoint != null) { + return this.endpoint; + } + final PoolEntry poolEntry; + try { + poolEntry = leaseFuture.get(timeout, tunit); + if (poolEntry == null || leaseFuture.isCancelled()) { + throw new InterruptedException(); + } + } catch (final TimeoutException ex) { + leaseFuture.cancel(true); + throw ex; + } + try { + if (validateAfterInactivity > 0) { + final ManagedHttpClientConnection conn = poolEntry.getConnection(); + if (conn != null + && poolEntry.getUpdated() + validateAfterInactivity <= System.currentTimeMillis()) { + boolean stale; + try { + stale = conn.isStale(); + } catch (IOException ignore) { + stale = true; + } + if (stale) { + if (log.isDebugEnabled()) { + log.debug("Connection " + ConnPoolSupport.getId(conn) + " is stale"); + } + poolEntry.discardConnection(); + } + } + } + if (!poolEntry.hasConnection()) { + poolEntry.assignConnection(connFactory.createConnection(null)); + } + if (log.isDebugEnabled()) { + log.debug("Connection leased: " + ConnPoolSupport.formatStats( + poolEntry.getConnection(), route, state, pool)); + } + if (leaseFuture.isCancelled()) { + pool.release(poolEntry, false); + } else { + this.endpoint = new InternalConnectionEndpoint(poolEntry); + } + return this.endpoint; + } catch (Exception ex) { + pool.release(poolEntry, false); + throw new ExecutionException(ex.getMessage(), ex); + } } @Override - public HttpClientConnection get( - final long timeout, - final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { - return leaseConnection(future, timeout, tunit); + public boolean cancel() { + return leaseFuture.cancel(true); } }; } - protected HttpClientConnection leaseConnection( - final Future> future, - final long timeout, - final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { - final PoolEntry entry; - try { - entry = future.get(timeout, tunit); - if (entry == null || future.isCancelled()) { - throw new InterruptedException(); - } - } catch (final TimeoutException ex) { - future.cancel(true); - throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); - } - if (this.validateAfterInactivity > 0) { - final ManagedHttpClientConnection connection = entry.getConnection(); - if (connection != null - && entry.getUpdated() + this.validateAfterInactivity <= System.currentTimeMillis()) { - boolean stale; - try { - stale = connection.isStale(); - } catch (IOException ignore) { - stale = true; - } - if (stale) { - entry.discardConnection(); - } - } - } - final HttpRoute route = entry.getRoute(); - final CPoolProxy poolProxy = new CPoolProxy(entry); - if (entry.hasConnection()) { - poolProxy.markRouteComplete(); - } else { - 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; - } - entry.assignConnection(this.connFactory.create(route, config)); - } - if (this.log.isDebugEnabled()) { - this.log.debug("Connection leased: " + format(entry) + formatStats(route)); - } - return poolProxy; - } - @Override - public void releaseConnection( - final HttpClientConnection managedConn, + public void release( + final ConnectionEndpoint endpoint, final Object state, final long keepAlive, final TimeUnit timeUnit) { - Args.notNull(managedConn, "Managed connection"); - synchronized (managedConn) { - final CPoolProxy poolProxy = CPoolProxy.getProxy(managedConn); - if (poolProxy.isDetached()) { - return; - } - final PoolEntry entry = poolProxy.detach(); - try { - final ManagedHttpClientConnection conn = entry.getConnection(); - if (conn.isOpen()) { - final TimeUnit effectiveUnit = timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS; - entry.updateConnection(keepAlive, effectiveUnit, state); - if (this.log.isDebugEnabled()) { - final String s; - if (keepAlive > 0) { - s = "for " + (double) effectiveUnit.toMillis(keepAlive) / 1000 + " seconds"; - } else { - s = "indefinitely"; - } - this.log.debug("Connection " + format(entry) + " can be kept alive " + s); - } - } - } finally { - final ManagedHttpClientConnection conn = entry.getConnection(); - this.pool.release(entry, conn.isOpen() && poolProxy.isRouteComplete()); + Args.notNull(endpoint, "Managed endpoint"); + final PoolEntry entry = cast(endpoint).detach(); + if (entry == null) { + return; + } + final ManagedHttpClientConnection conn = entry.getConnection(); + boolean reusable = conn != null && conn.isOpen(); + try { + if (reusable) { + final TimeUnit effectiveUnit = timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS; + entry.updateConnection(keepAlive, effectiveUnit, state); if (this.log.isDebugEnabled()) { - this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + final String s; + if (keepAlive > 0) { + s = "for " + (double) effectiveUnit.toMillis(keepAlive) / 1000 + " seconds"; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + ConnPoolSupport.getId(conn) + " can be kept alive " + s); } } + } catch (RuntimeException ex) { + reusable = false; + throw ex; + } finally { + this.pool.release(entry, reusable); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + ConnPoolSupport.formatStats( + conn, entry.getRoute(), entry.getState(), pool)); + } } } @Override public void connect( - final HttpClientConnection managedConn, - final HttpRoute route, - final int connectTimeout, + final ConnectionEndpoint endpoint, + final long connectTimeout, + final TimeUnit timeUnit, final HttpContext context) throws IOException { - Args.notNull(managedConn, "Managed Connection"); - Args.notNull(route, "HTTP route"); - final ManagedHttpClientConnection conn; - synchronized (managedConn) { - final CPoolProxy poolProxy = CPoolProxy.getProxy(managedConn); - conn = poolProxy.getConnection(); + Args.notNull(endpoint, "Managed endpoint"); + final InternalConnectionEndpoint internalEndpoint = cast(endpoint); + if (internalEndpoint.isConnected()) { + return; } + final PoolEntry poolEntry = internalEndpoint.getPoolEntry(); + if (!poolEntry.hasConnection()) { + poolEntry.assignConnection(connFactory.createConnection(null)); + } + final HttpRoute route = poolEntry.getRoute(); final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } - final InetSocketAddress localAddress = route.getLocalSocketAddress(); - SocketConfig socketConfig = this.configData.getSocketConfig(host); - if (socketConfig == null) { - socketConfig = this.configData.getDefaultSocketConfig(); - } - if (socketConfig == null) { - socketConfig = SocketConfig.DEFAULT; - } this.connectionOperator.connect( - conn, host, localAddress, connectTimeout, socketConfig, context); + poolEntry.getConnection(), + host, + route.getLocalSocketAddress(), + (int) (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS).toMillis(connectTimeout), + this.defaultSocketConfig != null ? this.defaultSocketConfig : SocketConfig.DEFAULT, + context); } @Override public void upgrade( - final HttpClientConnection managedConn, - final HttpRoute route, + final ConnectionEndpoint endpoint, final HttpContext context) throws IOException { - Args.notNull(managedConn, "Managed Connection"); - Args.notNull(route, "HTTP route"); - final ManagedHttpClientConnection conn; - synchronized (managedConn) { - final CPoolProxy poolProxy = CPoolProxy.getProxy(managedConn); - conn = poolProxy.getConnection(); - } - this.connectionOperator.upgrade(conn, route.getTargetHost(), context); - } - - @Override - public void routeComplete( - final HttpClientConnection managedConn, - final HttpRoute route, - final HttpContext context) throws IOException { - Args.notNull(managedConn, "Managed Connection"); - Args.notNull(route, "HTTP route"); - synchronized (managedConn) { - final CPoolProxy poolProxy = CPoolProxy.getProxy(managedConn); - poolProxy.markRouteComplete(); - } - } - - @Override - public void shutdown() { - if (this.isShutDown.compareAndSet(false, true)) { - this.log.debug("Connection manager is shutting down"); - this.pool.shutdown(); - this.log.debug("Connection manager shut down"); - } + Args.notNull(endpoint, "Managed endpoint"); + final InternalConnectionEndpoint internalEndpoint = cast(endpoint); + final PoolEntry poolEntry = internalEndpoint.getValidatedPoolEntry(); + final HttpRoute route = poolEntry.getRoute(); + this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), context); } @Override @@ -513,35 +457,11 @@ public class PoolingHttpClientConnectionManager } public SocketConfig getDefaultSocketConfig() { - return this.configData.getDefaultSocketConfig(); + return this.defaultSocketConfig; } 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); + this.defaultSocketConfig = defaultSocketConfig; } /** @@ -555,12 +475,11 @@ public class PoolingHttpClientConnectionManager /** * Defines period of inactivity in milliseconds after which persistent connections must - * be re-validated prior to being {@link #leaseConnection(java.util.concurrent.Future, - * long, java.util.concurrent.TimeUnit) leased} to the consumer. Non-positive value passed - * to this method disables connection validation. This check helps detect connections - * that have become stale (half-closed) while kept inactive in the pool. + * be re-validated prior to being {@link #lease(HttpRoute, Object)} leased} to the consumer. + * Non-positive value passed to this method disables connection validation. This check helps + * detect connections that have become stale (half-closed) while kept inactive in the pool. * - * @see #leaseConnection(java.util.concurrent.Future, long, java.util.concurrent.TimeUnit) + * @see #lease(HttpRoute, Object) * * @since 4.4 */ @@ -571,14 +490,11 @@ public class PoolingHttpClientConnectionManager 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() { @@ -589,14 +505,6 @@ public class PoolingHttpClientConnectionManager 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); } @@ -605,12 +513,76 @@ public class PoolingHttpClientConnectionManager this.socketConfigMap.put(host, socketConfig); } - public ConnectionConfig getConnectionConfig(final HttpHost host) { - return this.connectionConfigMap.get(host); + } + + class InternalConnectionEndpoint extends ConnectionEndpoint { + + private final AtomicReference> poolEntryRef; + + InternalConnectionEndpoint( + final PoolEntry poolEntry) { + this.poolEntryRef = new AtomicReference<>(poolEntry); } - public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { - this.connectionConfigMap.put(host, connectionConfig); + PoolEntry getPoolEntry() { + final PoolEntry poolEntry = poolEntryRef.get(); + if (poolEntry == null) { + throw new ConnectionShutdownException(); + } + return poolEntry; + } + + PoolEntry getValidatedPoolEntry() { + final PoolEntry poolEntry = getPoolEntry(); + final ManagedHttpClientConnection connection = poolEntry.getConnection(); + Asserts.check(connection != null && connection.isOpen(), "Endpoint is not connected"); + return poolEntry; + } + + PoolEntry detach() { + return poolEntryRef.getAndSet(null); + } + + @Override + public void shutdown() throws IOException { + final PoolEntry poolEntry = poolEntryRef.get(); + if (poolEntry != null) { + final HttpClientConnection connection = poolEntry.getConnection(); + poolEntry.discardConnection(); + if (connection != null) { + connection.shutdown(); + } + } + } + + @Override + public void close() throws IOException { + final PoolEntry poolEntry = poolEntryRef.get(); + if (poolEntry != null) { + poolEntry.discardConnection(); + } + } + + @Override + public boolean isConnected() { + final PoolEntry poolEntry = getPoolEntry(); + final ManagedHttpClientConnection connection = poolEntry.getConnection(); + return connection != null && connection.isOpen(); + } + + @Override + public void setSocketTimeout(final int timeout) { + getValidatedPoolEntry().getConnection().setSocketTimeout(timeout); + } + + @Override + public ClassicHttpResponse execute( + final ClassicHttpRequest request, + final HttpRequestExecutor requestExecutor, + final HttpContext context) throws IOException, HttpException { + Args.notNull(request, "HTTP request"); + Args.notNull(requestExecutor, "Request executor"); + return requestExecutor.execute(request, getValidatedPoolEntry().getConnection(), context); } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java index f38305e23..1f2667a5a 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java @@ -30,14 +30,18 @@ package org.apache.hc.client5.http.impl.io; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.DnsResolver; +import org.apache.hc.client5.http.HttpRoute; +import org.apache.hc.client5.http.SchemePortResolver; +import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; -import org.apache.hc.core5.http.config.ConnectionConfig; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.config.SocketConfig; -import org.apache.hc.core5.util.TextUtils; +import org.apache.hc.core5.http.io.HttpConnectionFactory; +import org.apache.hc.core5.pool.ConnPoolListener; +import org.apache.hc.core5.pool.ConnPoolPolicy; /** * Builder for {@link PoolingHttpClientConnectionManager} instances. @@ -67,11 +71,13 @@ import org.apache.hc.core5.util.TextUtils; */ public class PoolingHttpClientConnectionManagerBuilder { + private HttpConnectionFactory connectionFactory; private LayeredConnectionSocketFactory sslSocketFactory; + private SchemePortResolver schemePortResolver; private DnsResolver dnsResolver; - + private ConnPoolPolicy connPoolPolicy; + private ConnPoolListener connPoolListener; private SocketConfig defaultSocketConfig; - private ConnectionConfig defaultConnectionConfig; private boolean systemProperties; @@ -91,7 +97,16 @@ public class PoolingHttpClientConnectionManagerBuilder { } /** - * Assigns {@link LayeredConnectionSocketFactory} instance for SSL connections. + * Assigns {@link HttpConnectionFactory} instance. + */ + public final PoolingHttpClientConnectionManagerBuilder setConnectionFactory( + final HttpConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + return this; + } + + /** + * Assigns {@link LayeredConnectionSocketFactory} instance. */ public final PoolingHttpClientConnectionManagerBuilder setSSLSocketFactory( final LayeredConnectionSocketFactory sslSocketFactory) { @@ -99,6 +114,38 @@ public class PoolingHttpClientConnectionManagerBuilder { return this; } + /** + * Assigns {@link DnsResolver} instance. + */ + public final PoolingHttpClientConnectionManagerBuilder setDnsResolver(final DnsResolver dnsResolver) { + this.dnsResolver = dnsResolver; + return this; + } + + /** + * Assigns {@link SchemePortResolver} instance. + */ + public final PoolingHttpClientConnectionManagerBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) { + this.schemePortResolver = schemePortResolver; + return this; + } + + /** + * Assigns {@link ConnPoolPolicy} value. + */ + public final PoolingHttpClientConnectionManagerBuilder setConnPoolPolicy(final ConnPoolPolicy connPoolPolicy) { + this.connPoolPolicy = connPoolPolicy; + return this; + } + + /** + * Assigns {@link ConnPoolListener} instance. + */ + public final PoolingHttpClientConnectionManagerBuilder setConnPoolListener(final ConnPoolListener connPoolListener) { + this.connPoolListener = connPoolListener; + return this; + } + /** * Assigns maximum total connection value. */ @@ -123,18 +170,8 @@ public class PoolingHttpClientConnectionManagerBuilder { return this; } - /** - * Assigns default {@link ConnectionConfig}. - */ - public final PoolingHttpClientConnectionManagerBuilder setDefaultConnectionConfig(final ConnectionConfig config) { - this.defaultConnectionConfig = config; - return this; - } - /** * Sets maximum time to live for persistent connections - * - * @since 4.4 */ public final PoolingHttpClientConnectionManagerBuilder setConnectionTimeToLive(final long connTimeToLive, final TimeUnit connTimeToLiveTimeUnit) { this.connTimeToLive = connTimeToLive; @@ -153,14 +190,6 @@ public class PoolingHttpClientConnectionManagerBuilder { return this; } - /** - * Assigns {@link DnsResolver} instance. - */ - public final PoolingHttpClientConnectionManagerBuilder setDnsResolver(final DnsResolver dnsResolver) { - this.dnsResolver = dnsResolver; - return this; - } - /** * Use system properties when creating and configuring default * implementations. @@ -170,13 +199,6 @@ public class PoolingHttpClientConnectionManagerBuilder { return this; } - private static String[] split(final String s) { - if (TextUtils.isBlank(s)) { - return null; - } - return s.split(" *, *"); - } - public PoolingHttpClientConnectionManager build() { @SuppressWarnings("resource") final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager( @@ -187,18 +209,17 @@ public class PoolingHttpClientConnectionManagerBuilder { SSLConnectionSocketFactory.getSystemSocketFactory() : SSLConnectionSocketFactory.getSocketFactory())) .build(), - null, - null, + connectionFactory, + schemePortResolver, dnsResolver, + connPoolPolicy, + connPoolListener, connTimeToLive, connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS); poolingmgr.setValidateAfterInactivity(this.validateAfterInactivity); if (defaultSocketConfig != null) { poolingmgr.setDefaultSocketConfig(defaultSocketConfig); } - if (defaultConnectionConfig != null) { - poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig); - } if (maxConnTotal > 0) { poolingmgr.setMaxTotal(maxConnTotal); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingInputStream.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingInputStream.java similarity index 97% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingInputStream.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingInputStream.java index ab6587827..018fae64e 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingInputStream.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingInputStream.java @@ -25,16 +25,11 @@ * */ -package org.apache.hc.client5.http.impl.io; +package org.apache.hc.client5.http.impl.logging; import java.io.IOException; import java.io.InputStream; -/** - * Internal class. - * - * @since 4.3 - */ class LoggingInputStream extends InputStream { private final InputStream in; diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingOutputStream.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingOutputStream.java similarity index 97% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingOutputStream.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingOutputStream.java index 6f1655cec..ec82d1fd7 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingOutputStream.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingOutputStream.java @@ -25,7 +25,7 @@ * */ -package org.apache.hc.client5.http.impl.io; +package org.apache.hc.client5.http.impl.logging; import java.io.IOException; import java.io.OutputStream; @@ -49,6 +49,7 @@ class LoggingOutputStream extends OutputStream { @Override public void write(final int b) throws IOException { try { + out.write(b); wire.output(b); } catch (final IOException ex) { wire.output("[write] I/O error: " + ex.getMessage()); diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingSocketHolder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingSocketHolder.java similarity index 86% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingSocketHolder.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingSocketHolder.java index b62b1a049..ddde402f8 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/LoggingSocketHolder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingSocketHolder.java @@ -25,7 +25,7 @@ * */ -package org.apache.hc.client5.http.impl.io; +package org.apache.hc.client5.http.impl.logging; import java.io.IOException; import java.io.InputStream; @@ -33,14 +33,15 @@ import java.io.OutputStream; import java.net.Socket; import org.apache.hc.core5.http.impl.io.SocketHolder; +import org.apache.logging.log4j.Logger; -class LoggingSocketHolder extends SocketHolder { +public class LoggingSocketHolder extends SocketHolder { private final Wire wire; - LoggingSocketHolder(final Socket socket, final Wire wire) { + public LoggingSocketHolder(final Socket socket, final String id, final Logger log) { super(socket); - this.wire = wire; + this.wire = new Wire(log, id); } @Override diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/Wire.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/Wire.java similarity index 57% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/Wire.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/Wire.java index e03cc2c32..a3abb5e3a 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/Wire.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/Wire.java @@ -24,52 +24,36 @@ * . * */ -package org.apache.hc.client5.http.impl.io; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; +package org.apache.hc.client5.http.impl.logging; + +import java.nio.ByteBuffer; -import org.apache.hc.core5.annotation.Contract; -import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.logging.log4j.Logger; -/** - * Logs data to the wire LOG. - * - * @since 4.0 - */ -@Contract(threading = ThreadingBehavior.IMMUTABLE) class Wire { private final Logger log; private final String id; - /** - * @since 4.3 - */ - public Wire(final Logger log, final String id) { + Wire(final Logger log, final String id) { + super(); this.log = log; this.id = id; } - public Wire(final Logger log) { - this(log, ""); - } - - private void wire(final String header, final InputStream instream) - throws IOException { + private void wire(final String header, final byte[] b, final int pos, final int off) { final StringBuilder buffer = new StringBuilder(); - int ch; - while ((ch = instream.read()) != -1) { + for (int i = 0; i < off; i++) { + final int ch = b[pos + i]; if (ch == 13) { buffer.append("[\\r]"); } else if (ch == 10) { buffer.append("[\\n]\""); buffer.insert(0, "\""); buffer.insert(0, header); - log.debug(id + " " + buffer.toString()); + this.log.debug(this.id + " " + buffer.toString()); buffer.setLength(0); } else if ((ch < 32) || (ch > 127)) { buffer.append("[0x"); @@ -83,70 +67,73 @@ class Wire { buffer.append('\"'); buffer.insert(0, '\"'); buffer.insert(0, header); - log.debug(id + " " + buffer.toString()); + this.log.debug(this.id + " " + buffer.toString()); } } - public boolean enabled() { - return log.isDebugEnabled(); + public boolean isEnabled() { + return this.log.isDebugEnabled(); } - public void output(final InputStream outstream) - throws IOException { - Args.notNull(outstream, "Output"); - wire(">> ", outstream); - } - - public void input(final InputStream instream) - throws IOException { - Args.notNull(instream, "Input"); - wire("<< ", instream); - } - - public void output(final byte[] b, final int off, final int len) - throws IOException { + public void output(final byte[] b, final int pos, final int off) { Args.notNull(b, "Output"); - wire(">> ", new ByteArrayInputStream(b, off, len)); + wire(">> ", b, pos, off); } - public void input(final byte[] b, final int off, final int len) - throws IOException { + public void input(final byte[] b, final int pos, final int off) { Args.notNull(b, "Input"); - wire("<< ", new ByteArrayInputStream(b, off, len)); + wire("<< ", b, pos, off); } - public void output(final byte[] b) - throws IOException { + public void output(final byte[] b) { Args.notNull(b, "Output"); - wire(">> ", new ByteArrayInputStream(b)); + output(b, 0, b.length); } - public void input(final byte[] b) - throws IOException { + public void input(final byte[] b) { Args.notNull(b, "Input"); - wire("<< ", new ByteArrayInputStream(b)); + input(b, 0, b.length); } - public void output(final int b) - throws IOException { + public void output(final int b) { output(new byte[] {(byte) b}); } - public void input(final int b) - throws IOException { + public void input(final int b) { input(new byte[] {(byte) b}); } - public void output(final String s) - throws IOException { + public void output(final String s) { Args.notNull(s, "Output"); output(s.getBytes()); } - public void input(final String s) - throws IOException { + public void input(final String s) { Args.notNull(s, "Input"); input(s.getBytes()); } + + public void output(final ByteBuffer b) { + Args.notNull(b, "Output"); + if (b.hasArray()) { + output(b.array(), b.arrayOffset() + b.position(), b.remaining()); + } else { + final byte[] tmp = new byte[b.remaining()]; + b.get(tmp); + output(tmp); + } + } + + public void input(final ByteBuffer b) { + Args.notNull(b, "Input"); + if (b.hasArray()) { + input(b.array(), b.arrayOffset() + b.position(), b.remaining()); + } else { + final byte[] tmp = new byte[b.remaining()]; + b.get(tmp); + input(tmp); + } + } + } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultProxyRoutePlanner.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultProxyRoutePlanner.java index 0dd09e55b..2a104b7ba 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultProxyRoutePlanner.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultProxyRoutePlanner.java @@ -32,7 +32,6 @@ import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; @@ -59,7 +58,6 @@ public class DefaultProxyRoutePlanner extends DefaultRoutePlanner { @Override protected HttpHost determineProxy( final HttpHost target, - final HttpRequest request, final HttpContext context) throws HttpException { return proxy; } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultRoutePlanner.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultRoutePlanner.java index 3b917e1d6..2505bbed5 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultRoutePlanner.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DefaultRoutePlanner.java @@ -43,7 +43,7 @@ import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.util.Args; +import org.apache.hc.core5.net.URIAuthority; /** * Default implementation of an {@link HttpRoutePlanner}. It will not make use of @@ -63,11 +63,7 @@ public class DefaultRoutePlanner implements HttpRoutePlanner { } @Override - public HttpRoute determineRoute( - final HttpHost host, - final HttpRequest request, - final HttpContext context) throws HttpException { - Args.notNull(request, "Request"); + public HttpRoute determineRoute(final HttpHost host, final HttpContext context) throws HttpException { if (host == null) { throw new ProtocolException("Target host is not specified"); } @@ -76,7 +72,7 @@ public class DefaultRoutePlanner implements HttpRoutePlanner { final InetAddress local = config.getLocalAddress(); HttpHost proxy = config.getProxy(); if (proxy == null) { - proxy = determineProxy(host, request, context); + proxy = determineProxy(host, context); } final HttpHost target; @@ -100,6 +96,20 @@ public class DefaultRoutePlanner implements HttpRoutePlanner { } } + @Override + public HttpHost determineTargetHost(final HttpRequest request, final HttpContext context) throws HttpException { + final URIAuthority authority = request.getAuthority(); + if (authority != null) { + final String scheme = request.getScheme(); + if (scheme == null) { + throw new ProtocolException("Protocol scheme is not specified"); + } + return new HttpHost(authority, scheme); + } else { + return null; + } + } + /** * This implementation returns null. * @@ -107,7 +117,6 @@ public class DefaultRoutePlanner implements HttpRoutePlanner { */ protected HttpHost determineProxy( final HttpHost target, - final HttpRequest request, final HttpContext context) throws HttpException { return null; } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/SystemDefaultRoutePlanner.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/SystemDefaultRoutePlanner.java index 6b02b7fad..1793b34a5 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/SystemDefaultRoutePlanner.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/SystemDefaultRoutePlanner.java @@ -39,7 +39,6 @@ import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** @@ -73,10 +72,7 @@ public class SystemDefaultRoutePlanner extends DefaultRoutePlanner { } @Override - protected HttpHost determineProxy( - final HttpHost target, - final HttpRequest request, - final HttpContext context) throws HttpException { + protected HttpHost determineProxy(final HttpHost target, final HttpContext context) throws HttpException { final URI targetURI; try { targetURI = new URI(target.toURI()); diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/BasicResponseHandler.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/BasicResponseHandler.java similarity index 91% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/BasicResponseHandler.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/BasicResponseHandler.java index c2ca96804..34f6e2cab 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/BasicResponseHandler.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/BasicResponseHandler.java @@ -25,11 +25,10 @@ * */ -package org.apache.hc.client5.http.impl; +package org.apache.hc.client5.http.impl.sync; import java.io.IOException; -import org.apache.hc.client5.http.impl.sync.AbstractResponseHandler; import org.apache.hc.client5.http.protocol.ClientProtocolException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; @@ -39,13 +38,13 @@ import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.io.entity.EntityUtils; /** - * A {@link org.apache.hc.client5.http.sync.ResponseHandler} that returns the response body as a String + * A {@link org.apache.hc.core5.http.io.ResponseHandler} that returns the response body as a String * for successful (2xx) responses. If the response code was >= 300, the response * body is consumed and an {@link org.apache.hc.client5.http.protocol.HttpResponseException} is thrown. *

* If this is used with * {@link org.apache.hc.client5.http.sync.HttpClient#execute( - * org.apache.hc.core5.http.ClassicHttpRequest, org.apache.hc.client5.http.sync.ResponseHandler)}, + * org.apache.hc.core5.http.ClassicHttpRequest, org.apache.hc.core5.http.io.ResponseHandler)}, * HttpClient may handle redirects (3xx responses) internally. *

* diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ConnectionHolder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/EndpointHolder.java similarity index 77% rename from httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ConnectionHolder.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/EndpointHolder.java index 5ed70db43..d610d3b27 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ConnectionHolder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/EndpointHolder.java @@ -32,12 +32,11 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.hc.client5.http.io.ConnectionReleaseTrigger; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.Cancellable; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.logging.log4j.Logger; /** @@ -46,26 +45,25 @@ import org.apache.logging.log4j.Logger; * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) -class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeable { +class EndpointHolder implements Cancellable, Closeable { private final Logger log; private final HttpClientConnectionManager manager; - private final HttpClientConnection managedConn; + private final ConnectionEndpoint endpoint; private final AtomicBoolean released; private volatile boolean reusable; private volatile Object state; private volatile long validDuration; - private volatile TimeUnit tunit; - public ConnectionHolder( + public EndpointHolder( final Logger log, final HttpClientConnectionManager manager, - final HttpClientConnection managedConn) { + final ConnectionEndpoint endpoint) { super(); this.log = log; this.manager = manager; - this.managedConn = managedConn; + this.endpoint = endpoint; this.released = new AtomicBoolean(false); } @@ -86,54 +84,46 @@ class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeab } public void setValidFor(final long duration, final TimeUnit tunit) { - synchronized (this.managedConn) { - this.validDuration = duration; - this.tunit = tunit; - } + this.validDuration = (tunit != null ? tunit : TimeUnit.MILLISECONDS).toMillis(duration); } private void releaseConnection(final boolean reusable) { if (this.released.compareAndSet(false, true)) { - synchronized (this.managedConn) { + synchronized (this.endpoint) { if (reusable) { - this.manager.releaseConnection(this.managedConn, - this.state, this.validDuration, this.tunit); + this.manager.release(this.endpoint, this.state, this.validDuration, TimeUnit.MILLISECONDS); } else { try { - this.managedConn.close(); + this.endpoint.close(); log.debug("Connection discarded"); } catch (final IOException ex) { if (this.log.isDebugEnabled()) { this.log.debug(ex.getMessage(), ex); } } finally { - this.manager.releaseConnection( - this.managedConn, null, 0, TimeUnit.MILLISECONDS); + this.manager.release(this.endpoint, null, 0, TimeUnit.MILLISECONDS); } } } } } - @Override public void releaseConnection() { releaseConnection(this.reusable); } - @Override public void abortConnection() { if (this.released.compareAndSet(false, true)) { - synchronized (this.managedConn) { + synchronized (this.endpoint) { try { - this.managedConn.shutdown(); + this.endpoint.shutdown(); log.debug("Connection discarded"); } catch (final IOException ex) { if (this.log.isDebugEnabled()) { this.log.debug(ex.getMessage(), ex); } } finally { - this.manager.releaseConnection( - this.managedConn, null, 0, TimeUnit.MILLISECONDS); + this.manager.release(this.endpoint, null, 0, TimeUnit.MILLISECONDS); } } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/HttpClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/HttpClientBuilder.java index 924381b49..9b5bffe4a 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/HttpClientBuilder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/HttpClientBuilder.java @@ -99,7 +99,7 @@ import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; -import org.apache.hc.core5.util.TextUtils; +import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.util.VersionInfo; /** @@ -547,14 +547,11 @@ public class HttpClientBuilder { * One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order * to stop and release the background thread. *

- * Please note this method has no effect if the instance of HttpClient is configuted to + * Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. - *

- * Please note this method may not be used when the instance of HttpClient is created - * inside an EJB container. * * @see #setConnectionManagerShared(boolean) - * @see HttpClientConnectionManager#closeExpired() + * @see ConnPoolControl#closeExpired() * * @since 4.4 */ @@ -570,14 +567,11 @@ public class HttpClientBuilder { * One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order * to stop and release the background thread. *

- * Please note this method has no effect if the instance of HttpClient is configuted to + * Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. - *

- * Please note this method may not be used when the instance of HttpClient is created - * inside an EJB container. * * @see #setConnectionManagerShared(boolean) - * @see HttpClientConnectionManager#closeExpired() + * @see ConnPoolControl#closeIdle(long, TimeUnit) * * @param maxIdleTime maximum time persistent connections can stay idle while kept alive * in the connection pool. Connections whose inactivity period exceeds this value will @@ -652,13 +646,6 @@ public class HttpClientBuilder { closeables.add(closeable); } - private static String[] split(final String s) { - if (TextUtils.isBlank(s)) { - return null; - } - return s.split(" *, *"); - } - public CloseableHttpClient build() { // Create main request executor // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version @@ -873,34 +860,26 @@ public class HttpClientBuilder { if (closeablesCopy == null) { closeablesCopy = new ArrayList<>(1); } - final HttpClientConnectionManager cm = connManagerCopy; - if (evictExpiredConnections || evictIdleConnections) { - final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm, - maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS); - closeablesCopy.add(new Closeable() { + if (connManagerCopy instanceof ConnPoolControl) { + final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl) connManagerCopy, + maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS); + closeablesCopy.add(new Closeable() { - @Override - public void close() throws IOException { - connectionEvictor.shutdown(); - } + @Override + public void close() throws IOException { + connectionEvictor.shutdown(); + } - }); - connectionEvictor.start(); - } - closeablesCopy.add(new Closeable() { - - @Override - public void close() throws IOException { - cm.shutdown(); + }); + connectionEvictor.start(); } - - }); + } + closeablesCopy.add(connManagerCopy); } return new InternalHttpClient( execChain, - connManagerCopy, routePlannerCopy, cookieSpecRegistryCopy, authSchemeRegistryCopy, diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/IdleConnectionEvictor.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/IdleConnectionEvictor.java index 4f3bd5b89..63e9b0c95 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/IdleConnectionEvictor.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/IdleConnectionEvictor.java @@ -30,7 +30,8 @@ package org.apache.hc.client5.http.impl.sync; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.impl.DefaultThreadFactory; +import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.util.Args; /** @@ -41,7 +42,6 @@ import org.apache.hc.core5.util.Args; */ public final class IdleConnectionEvictor { - private final HttpClientConnectionManager connectionManager; private final ThreadFactory threadFactory; private final Thread thread; private final long sleepTimeMs; @@ -50,12 +50,12 @@ public final class IdleConnectionEvictor { private volatile Exception exception; public IdleConnectionEvictor( - final HttpClientConnectionManager connectionManager, + final ConnPoolControl connectionManager, final ThreadFactory threadFactory, final long sleepTime, final TimeUnit sleepTimeUnit, final long maxIdleTime, final TimeUnit maxIdleTimeUnit) { - this.connectionManager = Args.notNull(connectionManager, "Connection manager"); - this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory(); + Args.notNull(connectionManager, "Connection manager"); + this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory("idle-connection-evictor", true); this.sleepTimeMs = sleepTimeUnit != null ? sleepTimeUnit.toMillis(sleepTime) : sleepTime; this.maxIdleTimeMs = maxIdleTimeUnit != null ? maxIdleTimeUnit.toMillis(maxIdleTime) : maxIdleTime; this.thread = this.threadFactory.newThread(new Runnable() { @@ -78,14 +78,14 @@ public final class IdleConnectionEvictor { } public IdleConnectionEvictor( - final HttpClientConnectionManager connectionManager, + final ConnPoolControl connectionManager, final long sleepTime, final TimeUnit sleepTimeUnit, final long maxIdleTime, final TimeUnit maxIdleTimeUnit) { this(connectionManager, null, sleepTime, sleepTimeUnit, maxIdleTime, maxIdleTimeUnit); } public IdleConnectionEvictor( - final HttpClientConnectionManager connectionManager, + final ConnPoolControl connectionManager, final long maxIdleTime, final TimeUnit maxIdleTimeUnit) { this(connectionManager, null, maxIdleTime > 0 ? maxIdleTime : 5, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS, @@ -108,16 +108,4 @@ public final class IdleConnectionEvictor { thread.join((tunit != null ? tunit : TimeUnit.MILLISECONDS).toMillis(time)); } - static class DefaultThreadFactory implements ThreadFactory { - - @Override - public Thread newThread(final Runnable r) { - final Thread t = new Thread(r, "Connection evictor"); - t.setDaemon(true); - return t; - } - - } - - } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/InternalHttpClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/InternalHttpClient.java index a2b437deb..42c5be16b 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/InternalHttpClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/InternalHttpClient.java @@ -37,7 +37,6 @@ import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecProvider; import org.apache.hc.client5.http.cookie.CookieStore; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.methods.Configurable; import org.apache.hc.client5.http.methods.HttpExecutionAware; import org.apache.hc.client5.http.methods.RoutedHttpRequest; @@ -70,7 +69,6 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { private final Logger log = LogManager.getLogger(getClass()); private final ClientExecChain execChain; - private final HttpClientConnectionManager connManager; private final HttpRoutePlanner routePlanner; private final Lookup cookieSpecRegistry; private final Lookup authSchemeRegistry; @@ -81,7 +79,6 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { public InternalHttpClient( final ClientExecChain execChain, - final HttpClientConnectionManager connManager, final HttpRoutePlanner routePlanner, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, @@ -91,10 +88,8 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { final List closeables) { super(); Args.notNull(execChain, "HTTP client exec chain"); - Args.notNull(connManager, "HTTP connection manager"); Args.notNull(routePlanner, "HTTP route planner"); this.execChain = execChain; - this.connManager = connManager; this.routePlanner = routePlanner; this.cookieSpecRegistry = cookieSpecRegistry; this.authSchemeRegistry = authSchemeRegistry; @@ -105,10 +100,11 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { } private HttpRoute determineRoute( - final HttpHost target, + final HttpHost host, final HttpRequest request, final HttpContext context) throws HttpException { - return this.routePlanner.determineRoute(target, request, context); + final HttpHost target = host != null ? host : this.routePlanner.determineTargetHost(request, context); + return this.routePlanner.determineRoute(target, context); } private void setupContext(final HttpClientContext context) { diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MainClientExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MainClientExec.java index b2bef708e..d4a4f60ae 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MainClientExec.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MainClientExec.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRoute; @@ -39,10 +40,11 @@ import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.auth.HttpAuthenticator; -import org.apache.hc.client5.http.impl.io.ConnectionShutdownException; +import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.impl.routing.BasicRouteDirector; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.methods.HttpExecutionAware; import org.apache.hc.client5.http.methods.RoutedHttpRequest; import org.apache.hc.client5.http.protocol.AuthenticationStrategy; @@ -54,6 +56,7 @@ import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; @@ -64,7 +67,6 @@ import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.entity.BufferedHttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; @@ -162,22 +164,24 @@ public class MainClientExec implements ClientExecChain { Object userToken = context.getUserToken(); - final ConnectionRequest connRequest = connManager.requestConnection(route, userToken); + final LeaseRequest leaseRequest = connManager.lease(route, userToken); if (execAware != null) { if (execAware.isAborted()) { - connRequest.cancel(); + leaseRequest.cancel(); throw new RequestAbortedException("Request aborted"); } else { - execAware.setCancellable(connRequest); + execAware.setCancellable(leaseRequest); } } final RequestConfig config = context.getRequestConfig(); - final HttpClientConnection managedConn; + final ConnectionEndpoint endpoint; try { final int timeout = config.getConnectionRequestTimeout(); - managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); + endpoint = leaseRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); + } catch(final TimeoutException ex) { + throw new ConnectionRequestTimeoutException(ex.getMessage()); } catch(final InterruptedException interrupted) { Thread.currentThread().interrupt(); throw new RequestAbortedException("Request aborted", interrupted); @@ -189,12 +193,12 @@ public class MainClientExec implements ClientExecChain { throw new RequestAbortedException("Request execution failed", cause); } - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn); + context.setAttribute(HttpCoreContext.HTTP_CONNECTION, endpoint); - final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn); + final EndpointHolder endpointHolder = new EndpointHolder(this.log, this.connManager, endpoint); try { if (execAware != null) { - execAware.setCancellable(connHolder); + execAware.setCancellable(endpointHolder); } final AuthExchange targetAuthExchange = context.getAuthExchange(route.getTargetHost()); @@ -213,10 +217,12 @@ public class MainClientExec implements ClientExecChain { throw new RequestAbortedException("Request aborted"); } - if (!managedConn.isOpen()) { + if (!endpoint.isConnected()) { + endpointHolder.markNonReusable(); this.log.debug("Opening connection " + route); try { - establishRoute(managedConn, route, request, context); + establishRoute(endpoint, route, request, context); + endpointHolder.markReusable(); } catch (final TunnelRefusedException ex) { if (this.log.isDebugEnabled()) { this.log.debug(ex.getMessage()); @@ -227,7 +233,7 @@ public class MainClientExec implements ClientExecChain { } final int timeout = config.getSocketTimeout(); if (timeout >= 0) { - managedConn.setSocketTimeout(timeout); + endpoint.setSocketTimeout(timeout); } if (execAware != null && execAware.isAborted()) { @@ -253,7 +259,7 @@ public class MainClientExec implements ClientExecChain { route.getProxyHost(), ChallengeType.PROXY, request, proxyAuthExchange, context); } - response = requestExecutor.execute(request, managedConn, context); + response = endpoint.execute(request, requestExecutor, context); // The connection is in or can be brought to a re-usable state. if (reuseStrategy.keepAlive(request, response, context)) { @@ -268,10 +274,10 @@ public class MainClientExec implements ClientExecChain { } this.log.debug("Connection can be kept alive " + s); } - connHolder.setValidFor(duration, TimeUnit.MILLISECONDS); - connHolder.markReusable(); + endpointHolder.setValidFor(duration, TimeUnit.MILLISECONDS); + endpointHolder.markReusable(); } else { - connHolder.markNonReusable(); + endpointHolder.markNonReusable(); } if (request.getMethod().equalsIgnoreCase("TRACE")) { @@ -283,10 +289,10 @@ public class MainClientExec implements ClientExecChain { targetAuthExchange, proxyAuthExchange, route, request, response, context)) { // Make sure the response body is fully consumed, if present final HttpEntity entity = response.getEntity(); - if (connHolder.isReusable()) { + if (endpointHolder.isReusable()) { EntityUtils.consume(entity); } else { - managedConn.close(); + endpoint.close(); if (proxyAuthExchange.getState() == AuthExchange.State.SUCCESS && proxyAuthExchange.getAuthScheme() != null && proxyAuthExchange.getAuthScheme().isConnectionBased()) { @@ -318,18 +324,18 @@ public class MainClientExec implements ClientExecChain { context.setAttribute(HttpClientContext.USER_TOKEN, userToken); } if (userToken != null) { - connHolder.setState(userToken); + endpointHolder.setState(userToken); } // check for entity, release connection if possible final HttpEntity entity = response.getEntity(); if (entity == null || !entity.isStreaming()) { // connection not needed and (assumed to be) in re-usable state - connHolder.releaseConnection(); + endpointHolder.releaseConnection(); return new CloseableHttpResponse(response, null); } else { - ResponseEntityProxy.enchance(response, connHolder); - return new CloseableHttpResponse(response, connHolder); + ResponseEntityProxy.enchance(response, endpointHolder); + return new CloseableHttpResponse(response, endpointHolder); } } catch (final ConnectionShutdownException ex) { final InterruptedIOException ioex = new InterruptedIOException( @@ -337,7 +343,7 @@ public class MainClientExec implements ClientExecChain { ioex.initCause(ex); throw ioex; } catch (final HttpException | RuntimeException | IOException ex) { - connHolder.abortConnection(); + endpointHolder.abortConnection(); throw ex; } } @@ -346,7 +352,7 @@ public class MainClientExec implements ClientExecChain { * Establishes the target route. */ void establishRoute( - final HttpClientConnection managedConn, + final ConnectionEndpoint endpoint, final HttpRoute route, final HttpRequest request, final HttpClientContext context) throws HttpException, IOException { @@ -362,23 +368,23 @@ public class MainClientExec implements ClientExecChain { case HttpRouteDirector.CONNECT_TARGET: this.connManager.connect( - managedConn, - route, + endpoint, timeout > 0 ? timeout : 0, + TimeUnit.MILLISECONDS, context); tracker.connectTarget(route.isSecure()); break; case HttpRouteDirector.CONNECT_PROXY: this.connManager.connect( - managedConn, - route, + endpoint, timeout > 0 ? timeout : 0, + TimeUnit.MILLISECONDS, context); final HttpHost proxy = route.getProxyHost(); tracker.connectProxy(proxy, false); break; case HttpRouteDirector.TUNNEL_TARGET: { - final boolean secure = createTunnelToTarget(managedConn, route, request, context); + final boolean secure = createTunnelToTarget(endpoint, route, request, context); this.log.debug("Tunnel to target created."); tracker.tunnelTarget(secure); } break; @@ -395,7 +401,7 @@ public class MainClientExec implements ClientExecChain { } break; case HttpRouteDirector.LAYER_PROTOCOL: - this.connManager.upgrade(managedConn, route, context); + this.connManager.upgrade(endpoint, context); tracker.layerProtocol(route.isSecure()); break; @@ -403,7 +409,6 @@ public class MainClientExec implements ClientExecChain { throw new HttpException("Unable to establish route: " + "planned = " + route + "; current = " + fact); case HttpRouteDirector.COMPLETE: - this.connManager.routeComplete(managedConn, route, context); break; default: throw new IllegalStateException("Unknown step indicator " @@ -422,7 +427,7 @@ public class MainClientExec implements ClientExecChain { * information about the tunnel, that is left to the caller. */ private boolean createTunnelToTarget( - final HttpClientConnection managedConn, + final ConnectionEndpoint endpoint, final HttpRoute route, final HttpRequest request, final HttpClientContext context) throws HttpException, IOException { @@ -442,18 +447,18 @@ public class MainClientExec implements ClientExecChain { this.requestExecutor.preProcess(connect, this.proxyHttpProcessor, context); while (response == null) { - if (!managedConn.isOpen()) { + if (!endpoint.isConnected()) { this.connManager.connect( - managedConn, - route, + endpoint, timeout > 0 ? timeout : 0, + TimeUnit.MILLISECONDS, context); } connect.removeHeaders(HttpHeaders.PROXY_AUTHORIZATION); this.authenticator.addAuthResponse(proxy, ChallengeType.PROXY, connect, proxyAuthExchange, context); - response = this.requestExecutor.execute(connect, managedConn, context); + response = endpoint.execute(connect, this.requestExecutor, context); final int status = response.getCode(); if (status < HttpStatus.SC_SUCCESS) { @@ -472,7 +477,7 @@ public class MainClientExec implements ClientExecChain { final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } else { - managedConn.close(); + endpoint.close(); } response = null; } @@ -489,7 +494,7 @@ public class MainClientExec implements ClientExecChain { response.setEntity(new BufferedHttpEntity(entity)); } - managedConn.close(); + endpoint.close(); throw new TunnelRefusedException("CONNECT refused by proxy: " + new StatusLine(response), response); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalClientExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalClientExec.java index 05f485f02..004e0ad92 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalClientExec.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalClientExec.java @@ -31,13 +31,16 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.config.RequestConfig; -import org.apache.hc.client5.http.impl.io.ConnectionShutdownException; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy; +import org.apache.hc.client5.http.impl.ConnectionShutdownException; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.methods.HttpExecutionAware; import org.apache.hc.client5.http.methods.RoutedHttpRequest; import org.apache.hc.client5.http.protocol.HttpClientContext; @@ -45,11 +48,12 @@ import org.apache.hc.client5.http.protocol.RequestClientConnControl; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; @@ -86,20 +90,16 @@ public class MinimalClientExec implements ClientExecChain { final HttpClientConnectionManager connManager, final ConnectionReuseStrategy reuseStrategy, final ConnectionKeepAliveStrategy keepAliveStrategy) { - Args.notNull(requestExecutor, "HTTP request executor"); - Args.notNull(connManager, "Client connection manager"); - Args.notNull(reuseStrategy, "Connection reuse strategy"); - Args.notNull(keepAliveStrategy, "Connection keep alive strategy"); + this.requestExecutor = Args.notNull(requestExecutor, "Request executor"); + this.connManager = Args.notNull(connManager, "Connection manager"); + this.reuseStrategy = reuseStrategy != null ? reuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; + this.keepAliveStrategy = keepAliveStrategy != null ? keepAliveStrategy : DefaultConnectionKeepAliveStrategy.INSTANCE; this.httpProcessor = new DefaultHttpProcessor( new RequestContent(), new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent(VersionInfo.getSoftwareInfo( "Apache-HttpClient", "org.apache.hc.client5", getClass()))); - this.requestExecutor = requestExecutor; - this.connManager = connManager; - this.reuseStrategy = reuseStrategy; - this.keepAliveStrategy = keepAliveStrategy; } @Override @@ -111,7 +111,7 @@ public class MinimalClientExec implements ClientExecChain { Args.notNull(context, "HTTP context"); final HttpRoute route = request.getRoute(); - final ConnectionRequest connRequest = connManager.requestConnection(route, null); + final LeaseRequest connRequest = connManager.lease(route, null); if (execAware != null) { if (execAware.isAborted()) { connRequest.cancel(); @@ -122,10 +122,12 @@ public class MinimalClientExec implements ClientExecChain { final RequestConfig config = context.getRequestConfig(); - final HttpClientConnection managedConn; + final ConnectionEndpoint endpoint; try { final int timeout = config.getConnectionRequestTimeout(); - managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); + endpoint = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); + } catch(final TimeoutException ex) { + throw new ConnectionRequestTimeoutException(ex.getMessage()); } catch(final InterruptedException interrupted) { Thread.currentThread().interrupt(); throw new RequestAbortedException("Request aborted", interrupted); @@ -137,57 +139,54 @@ public class MinimalClientExec implements ClientExecChain { throw new RequestAbortedException("Request execution failed", cause); } - final ConnectionHolder connHolder = new ConnectionHolder(log, connManager, managedConn); + final EndpointHolder endpointHolder = new EndpointHolder(log, connManager, endpoint); try { if (execAware != null) { if (execAware.isAborted()) { - connHolder.close(); + endpointHolder.close(); throw new RequestAbortedException("Request aborted"); } - execAware.setCancellable(connHolder); + execAware.setCancellable(endpointHolder); } - - if (!managedConn.isOpen()) { + if (!endpoint.isConnected()) { final int timeout = config.getConnectTimeout(); this.connManager.connect( - managedConn, - route, - timeout > 0 ? timeout : 0, - context); - this.connManager.routeComplete(managedConn, route, context); + endpoint, + timeout > 0 ? timeout : 0, + TimeUnit.MILLISECONDS, + context); } final int timeout = config.getSocketTimeout(); if (timeout >= 0) { - managedConn.setSocketTimeout(timeout); + endpoint.setSocketTimeout(timeout); } context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); httpProcessor.process(request, request.getEntity(), context); - final ClassicHttpResponse response = requestExecutor.execute(request, managedConn, context); + final ClassicHttpResponse response = endpoint.execute(request, requestExecutor, context); httpProcessor.process(response, response.getEntity(), context); // The connection is in or can be brought to a re-usable state. if (reuseStrategy.keepAlive(request, response, context)) { // Set the idle duration of this connection final long duration = keepAliveStrategy.getKeepAliveDuration(response, context); - connHolder.setValidFor(duration, TimeUnit.MILLISECONDS); - connHolder.markReusable(); + endpointHolder.setValidFor(duration, TimeUnit.MILLISECONDS); + endpointHolder.markReusable(); } else { - connHolder.markNonReusable(); + endpointHolder.markNonReusable(); } // check for entity, release connection if possible final HttpEntity entity = response.getEntity(); if (entity == null || !entity.isStreaming()) { // connection not needed and (assumed to be) in re-usable state - connHolder.releaseConnection(); + endpointHolder.releaseConnection(); return new CloseableHttpResponse(response, null); } else { - ResponseEntityProxy.enchance(response, connHolder); - return new CloseableHttpResponse(response, connHolder); + ResponseEntityProxy.enchance(response, endpointHolder); + return new CloseableHttpResponse(response, endpointHolder); } } catch (final ConnectionShutdownException ex) { final InterruptedIOException ioex = new InterruptedIOException( @@ -195,7 +194,7 @@ public class MinimalClientExec implements ClientExecChain { ioex.initCause(ex); throw ioex; } catch (final HttpException | RuntimeException | IOException ex) { - connHolder.abortConnection(); + endpointHolder.abortConnection(); throw ex; } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalHttpClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalHttpClient.java index 37c976f88..53b21e808 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalHttpClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/MinimalHttpClient.java @@ -110,8 +110,8 @@ class MinimalHttpClient extends CloseableHttpClient { } @Override - public void close() { - this.connManager.shutdown(); + public void close() throws IOException { + this.connManager.close(); } } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProxyClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProxyClient.java index c01dd0170..41e62cebc 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProxyClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProxyClient.java @@ -30,7 +30,6 @@ package org.apache.hc.client5.http.impl.sync; import java.io.IOException; import java.net.Socket; -import org.apache.hc.client5.http.HttpConnectionFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; @@ -66,6 +65,7 @@ import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; +import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.entity.BufferedHttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; @@ -83,7 +83,7 @@ import org.apache.hc.core5.util.Args; */ public class ProxyClient { - private final HttpConnectionFactory connFactory; + private final HttpConnectionFactory connFactory; private final ConnectionConfig connectionConfig; private final RequestConfig requestConfig; private final HttpProcessor httpProcessor; @@ -98,7 +98,7 @@ public class ProxyClient { * @since 4.3 */ public ProxyClient( - final HttpConnectionFactory connFactory, + final HttpConnectionFactory connFactory, final ConnectionConfig connectionConfig, final RequestConfig requestConfig) { super(); @@ -148,8 +148,7 @@ public class ProxyClient { this.requestConfig.getLocalAddress(), proxy, false, TunnelType.TUNNELLED, LayerType.PLAIN); - final ManagedHttpClientConnection conn = this.connFactory.create( - route, this.connectionConfig); + final ManagedHttpClientConnection conn = this.connFactory.createConnection(null); final HttpContext context = new BasicHttpContext(); ClassicHttpResponse response; diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/RedirectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/RedirectExec.java index b64f60e6b..30d58bcbd 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/RedirectExec.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/RedirectExec.java @@ -146,7 +146,7 @@ public class RedirectExec implements ClientExecChain { } } - currentRoute = this.routePlanner.determineRoute(newTarget, redirect, context); + currentRoute = this.routePlanner.determineRoute(newTarget, context); if (this.log.isDebugEnabled()) { this.log.debug("Redirecting to '" + redirectUri + "' via " + currentRoute); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ResponseEntityProxy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ResponseEntityProxy.java index c4cb714a5..7b5fc8af9 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ResponseEntityProxy.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ResponseEntityProxy.java @@ -45,35 +45,35 @@ import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; */ class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher { - private final ConnectionHolder connHolder; + private final EndpointHolder endpointHolder; - public static void enchance(final ClassicHttpResponse response, final ConnectionHolder connHolder) { + public static void enchance(final ClassicHttpResponse response, final EndpointHolder connHolder) { final HttpEntity entity = response.getEntity(); if (entity != null && entity.isStreaming() && connHolder != null) { response.setEntity(new ResponseEntityProxy(entity, connHolder)); } } - ResponseEntityProxy(final HttpEntity entity, final ConnectionHolder connHolder) { + ResponseEntityProxy(final HttpEntity entity, final EndpointHolder endpointHolder) { super(entity); - this.connHolder = connHolder; + this.endpointHolder = endpointHolder; } private void cleanup() throws IOException { - if (this.connHolder != null) { - this.connHolder.close(); + if (this.endpointHolder != null) { + this.endpointHolder.close(); } } private void abortConnection() { - if (this.connHolder != null) { - this.connHolder.abortConnection(); + if (this.endpointHolder != null) { + this.endpointHolder.abortConnection(); } } public void releaseConnection() { - if (this.connHolder != null) { - this.connHolder.releaseConnection(); + if (this.endpointHolder != null) { + this.endpointHolder.releaseConnection(); } } @@ -123,7 +123,7 @@ class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher @Override public boolean streamClosed(final InputStream wrapped) throws IOException { try { - final boolean open = connHolder != null && !connHolder.isReleased(); + final boolean open = endpointHolder != null && !endpointHolder.isReleased(); // this assumes that closing the stream will // consume the remainder of the response body: try { diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ConnectionPoolTimeoutException.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java similarity index 54% rename from httpclient5/src/main/java/org/apache/hc/client5/http/ConnectionPoolTimeoutException.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java index 586b9c3fa..92ff255ca 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/ConnectionPoolTimeoutException.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java @@ -25,33 +25,36 @@ * */ -package org.apache.hc.client5.http; +package org.apache.hc.client5.http.io; + +import java.io.Closeable; +import java.io.IOException; + +import org.apache.hc.core5.annotation.Contract; +import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; +import org.apache.hc.core5.http.protocol.HttpContext; /** - * A timeout while waiting for an available connection - * from a connection manager. + * Client connection endpoint that can be used to execute message exchanges. * - * - * @since 4.0 + * @since 5.0 */ -public class ConnectionPoolTimeoutException extends ConnectTimeoutException { +@Contract(threading = ThreadingBehavior.SAFE) +public abstract class ConnectionEndpoint implements Closeable { - private static final long serialVersionUID = -7898874842020245128L; + public abstract ClassicHttpResponse execute( + ClassicHttpRequest request, + HttpRequestExecutor executor, + HttpContext context) throws IOException, HttpException; - /** - * Creates a ConnectTimeoutException with a {@code null} detail message. - */ - public ConnectionPoolTimeoutException() { - super(); - } + public abstract boolean isConnected(); - /** - * Creates a ConnectTimeoutException with the specified detail message. - * - * @param message The exception detail message - */ - public ConnectionPoolTimeoutException(final String message) { - super(message); - } + public abstract void setSocketTimeout(int timeout); + + public abstract void shutdown() throws IOException; } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionReleaseTrigger.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionReleaseTrigger.java deleted file mode 100644 index c2df1dbe3..000000000 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionReleaseTrigger.java +++ /dev/null @@ -1,70 +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.hc.client5.http.io; - -import java.io.IOException; - -/** - * Interface for releasing a connection. This can be implemented by various - * "trigger" objects which are associated with a leased connection, for example - * a {@link org.apache.hc.core5.http.io.EofSensorInputStream} - * or the {@link ManagedHttpClientConnection} itself. - *

- * The methods in this interface can safely be called multiple times. - * The first invocation releases the connection, subsequent calls - * are ignored. - * - * @since 4.0 - */ -public interface ConnectionReleaseTrigger { - - /** - * Releases the connection with the option of keep-alive. This is a - * "graceful" release and may cause IO operations for consuming the - * remainder of a response entity. Use - * {@link #abortConnection abortConnection} for a hard release. The - * connection may be reused as specified by the duration. - * - * @throws IOException - * in case of an IO problem. The connection will be released - * anyway. - */ - void releaseConnection() - throws IOException; - - /** - * Releases the connection without the option of keep-alive. - * This is a "hard" release that implies a shutdown of the connection. - * Use {@link #releaseConnection()} for a graceful release. - * - * @throws IOException in case of an IO problem. - * The connection will be released anyway. - */ - void abortConnection() - throws IOException; - -} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionManager.java index 2f07966c6..afc3d9d96 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionManager.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionManager.java @@ -26,11 +26,11 @@ */ package org.apache.hc.client5.http.io; +import java.io.Closeable; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.HttpRoute; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.protocol.HttpContext; /** @@ -40,137 +40,77 @@ import org.apache.hc.core5.http.protocol.HttpContext; * HTTP connections, manage persistent connections and synchronize access to * persistent connections making sure that only one thread of execution can * have access to a connection at a time. - *

*

* Implementations of this interface must be thread-safe. Access to shared * data must be synchronized as methods of this interface may be executed * from multiple threads. - *

* * @since 4.3 */ -public interface HttpClientConnectionManager { +public interface HttpClientConnectionManager extends Closeable { /** - * Returns a new {@link ConnectionRequest}, from which a - * {@link HttpClientConnection} can be obtained or the request can be - * aborted. + * Returns a {@link LeaseRequest} object which can be used to obtain + * a {@link ConnectionEndpoint} to cancel the request by calling + * {@link LeaseRequest#cancel()}. *

- * Please note that newly allocated connections can be returned - * in the closed state. The consumer of that connection is responsible - * for fully establishing the route the to the connection target - * by calling {@link #connect(HttpClientConnection, HttpRoute, int, HttpContext)} connect} in order to connect - * directly to the target or to the first proxy hop, optionally calling - * {@link #upgrade(HttpClientConnection, HttpRoute, HttpContext)} upgrade} method to upgrade - * the connection after having executed {@code CONNECT} method to - * all intermediate proxy hops and and finally calling {@link #routeComplete(HttpClientConnection, HttpRoute, - * HttpContext)} routeComplete} to mark the route - * as fully completed. - *

+ * Please note that newly allocated endpoints can be leased + * {@link ConnectionEndpoint#isConnected() disconnected}. The consumer of the endpoint + * is responsible for fully establishing the route to the endpoint target + * by calling {@link #connect(ConnectionEndpoint, long, TimeUnit, HttpContext)} + * in order to connect directly to the target or to the first proxy hop, + * and optionally calling {@link #upgrade(ConnectionEndpoint, HttpContext)} method + * to upgrade the underlying transport to Transport Layer Security after having + * executed a {@code CONNECT} method to all intermediate proxy hops. * * @param route HTTP route of the requested connection. * @param state expected state of the connection or {@code null} * if the connection is not expected to carry any state. */ - ConnectionRequest requestConnection(HttpRoute route, Object state); + LeaseRequest lease(HttpRoute route, Object state); /** - * Releases the connection back to the manager making it potentially + * Releases the endpoint back to the manager making it potentially * re-usable by other consumers. Optionally, the maximum period * of how long the manager should keep the connection alive can be * defined using {@code validDuration} and {@code timeUnit} * parameters. * - * @param conn the managed connection to release. + * @param endpoint the managed endpoint. + * @param newState the new connection state of {@code null} if state-less. * @param validDuration the duration of time this connection is valid for reuse. * @param timeUnit the time unit. * * @see #closeExpired() */ - void releaseConnection( - HttpClientConnection conn, Object newState, long validDuration, TimeUnit timeUnit); + void release(ConnectionEndpoint endpoint, Object newState, long validDuration, TimeUnit timeUnit); /** - * Connects the underlying connection socket to the connection target in case + * Connects the endpoint to the initial hop (connection target in case * of a direct route or to the first proxy hop in case of a route via a proxy - * (or multiple proxies). + * or multiple proxies). * - * @param conn the managed connection. - * @param route the route of the connection. - * @param connectTimeout connect timeout in milliseconds. + * @param endpoint the managed endpoint. + * @param connectTimeout connect timeout. + * @param timeUnit the time unit. * @param context the actual HTTP context. * @throws IOException */ void connect( - HttpClientConnection conn, - HttpRoute route, - int connectTimeout, + ConnectionEndpoint endpoint, + long connectTimeout, + TimeUnit timeUnit, HttpContext context) throws IOException; /** - * Upgrades the underlying connection socket to TLS/SSL (or another layering - * protocol) after having executed {@code CONNECT} method to all - * intermediate proxy hops + * Upgrades the endpoint's underlying transport to Transport Layer Security. * - * @param conn the managed connection. - * @param route the route of the connection. + * @param endpoint the managed endpoint. * @param context the actual HTTP context. * @throws IOException */ void upgrade( - HttpClientConnection conn, - HttpRoute route, + ConnectionEndpoint endpoint, HttpContext context) throws IOException; - /** - * Marks the connection as fully established with all its intermediate - * hops completed. - * - * @param conn the managed connection. - * @param route the route of the connection. - * @param context the actual HTTP context. - * @throws IOException - */ - void routeComplete( - HttpClientConnection conn, - HttpRoute route, - HttpContext context) throws IOException; - - /** - * Closes idle connections in the pool. - *

- * Open connections in the pool that have not been used for the - * timespan given by the argument will be closed. - * Currently allocated connections are not subject to this method. - * Times will be checked with milliseconds precision - *

- *

- * All expired connections will also be closed. - *

- * - * @param idletime the idle time of connections to be closed - * @param tunit the unit for the {@code idletime} - * - * @see #closeExpired() - */ - void closeIdle(long idletime, TimeUnit tunit); - - /** - * Closes all expired connections in the pool. - *

- * Open connections in the pool that have not been used for - * the timespan defined when the connection was released will be closed. - * Currently allocated connections are not subject to this method. - * Times will be checked with milliseconds precision. - *

- */ - void closeExpired(); - - /** - * Shuts down this connection manager and releases allocated resources. - * This includes closing all connections, whether they are currently - * used or not. - */ - void shutdown(); - } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionRequest.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/LeaseRequest.java similarity index 81% rename from httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionRequest.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/io/LeaseRequest.java index f3126fc5d..80e0fc824 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionRequest.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/io/LeaseRequest.java @@ -29,21 +29,20 @@ package org.apache.hc.client5.http.io; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; import org.apache.hc.core5.concurrent.Cancellable; -import org.apache.hc.core5.http.io.HttpClientConnection; /** - * Represents a request for a {@link HttpClientConnection} whose life cycle + * Represents a request for a {@link ConnectionEndpoint} whose life cycle * is managed by a connection manager. * - * @since 4.3 + * @since 5.0 */ -public interface ConnectionRequest extends Cancellable { +public interface LeaseRequest extends Cancellable { /** - * Obtains a connection within a given time. + * Returns {@link ConnectionEndpoint} within a given time. * This method will block until a connection becomes available, * the timeout expires, or the connection manager is shut down. * Timeouts are handled with millisecond precision. @@ -59,12 +58,12 @@ public interface ConnectionRequest extends Cancellable { * @return a connection that can be used to communicate * along the given route * - * @throws ConnectionPoolTimeoutException + * @throws TimeoutException * in case of a timeout * @throws InterruptedException * if the calling thread is interrupted while waiting */ - HttpClientConnection get(long timeout, TimeUnit tunit) - throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException; + ConnectionEndpoint get(long timeout, TimeUnit tunit) + throws InterruptedException, ExecutionException, TimeoutException; -} +} \ No newline at end of file diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ManagedHttpClientConnection.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ManagedHttpClientConnection.java index 49bc25774..af23aefc2 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ManagedHttpClientConnection.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ManagedHttpClientConnection.java @@ -44,12 +44,6 @@ import org.apache.hc.core5.http.io.HttpClientConnection; */ public interface ManagedHttpClientConnection extends HttpClientConnection { - /** - * Returns connection ID which is expected to be unique - * for the life span of the connection manager. - */ - String getId(); - /** * Binds this connection to the given socket. The connection * is considered open if it is bound and the underlying socket diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/routing/HttpRoutePlanner.java b/httpclient5/src/main/java/org/apache/hc/client5/http/routing/HttpRoutePlanner.java index 5504d8433..8dade561b 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/routing/HttpRoutePlanner.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/routing/HttpRoutePlanner.java @@ -48,13 +48,9 @@ import org.apache.hc.core5.http.protocol.HttpContext; public interface HttpRoutePlanner { /** - * Determines the route for a request. + * Determines the route for the given host. * * @param target the target host for the request. - * Implementations may accept {@code null} - * if they can still determine a route, for example - * to a default target or by inspecting the request. - * @param request the request to execute * @param context the context to use for the subsequent execution. * Implementations may accept {@code null}. * @@ -62,8 +58,19 @@ public interface HttpRoutePlanner { * * @throws HttpException in case of a problem */ - HttpRoute determineRoute(HttpHost target, - HttpRequest request, - HttpContext context) throws HttpException; + HttpRoute determineRoute(HttpHost target, HttpContext context) throws HttpException; + + /** + * Determines the target host for the given request. + * + * @param request the request to be executed + * @param context the context to use for the subsequent execution. + * Implementations may accept {@code null}. + * + * @return the route that the request should take + * + * @throws HttpException in case of a problem + */ + HttpHost determineTargetHost(HttpRequest request, HttpContext context) throws HttpException; } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/HttpConnectionFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/utils/Identifiable.java similarity index 79% rename from httpclient5/src/main/java/org/apache/hc/client5/http/HttpConnectionFactory.java rename to httpclient5/src/main/java/org/apache/hc/client5/http/utils/Identifiable.java index 7f6486f52..0aaadccfb 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/HttpConnectionFactory.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/utils/Identifiable.java @@ -24,18 +24,16 @@ * . * */ -package org.apache.hc.client5.http; -import org.apache.hc.core5.http.HttpConnection; -import org.apache.hc.core5.http.config.ConnectionConfig; +package org.apache.hc.client5.http.utils; /** - * Generic {@link HttpConnection} factory. + * Object with a unique identifier. * - * @since 4.3 + * @since 5.0 */ -public interface HttpConnectionFactory { +public interface Identifiable { - C create(T route, ConnectionConfig config); + String getId(); } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/TestExceptions.java b/httpclient5/src/test/java/org/apache/hc/client5/http/TestExceptions.java index e3ae1925c..0acef236d 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/TestExceptions.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/TestExceptions.java @@ -115,15 +115,4 @@ public class TestExceptions { Assert.assertEquals("Connect to localhost [/1.2.3.4, /5.6.7.8] refused", ctx.getMessage()); } - @Test - public void testConnectionPoolTimeoutException() { - final String msg = "sample exception message"; - ConnectionPoolTimeoutException cptx = new ConnectionPoolTimeoutException(msg); - Assert.assertFalse(!cptx.toString().contains(msg)); - Assert.assertSame(msg, cptx.getMessage()); - - cptx = new ConnectionPoolTimeoutException(); - Assert.assertNull(cptx.getMessage()); - } - } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestAbstractResponseHandler.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestAbstractResponseHandler.java index 507ce1464..cb12818dd 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestAbstractResponseHandler.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestAbstractResponseHandler.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import org.apache.hc.client5.http.impl.sync.AbstractResponseHandler; +import org.apache.hc.client5.http.impl.sync.BasicResponseHandler; import org.apache.hc.client5.http.protocol.HttpResponseException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestBasicResponseHandler.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestBasicResponseHandler.java index 035987831..db8c1b7fb 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestBasicResponseHandler.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestBasicResponseHandler.java @@ -29,6 +29,7 @@ package org.apache.hc.client5.http.impl; import java.io.InputStream; +import org.apache.hc.client5.http.impl.sync.BasicResponseHandler; import org.apache.hc.client5.http.protocol.HttpResponseException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestAbortHandling.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestAbortHandling.java deleted file mode 100644 index c73e2014f..000000000 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestAbortHandling.java +++ /dev/null @@ -1,533 +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.hc.client5.http.impl.integration; - -import java.io.IOException; -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; -import org.apache.hc.client5.http.HttpRoute; -import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; -import org.apache.hc.client5.http.impl.sync.CloseableHttpClient; -import org.apache.hc.client5.http.impl.sync.HttpClients; -import org.apache.hc.client5.http.io.ConnectionRequest; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; -import org.apache.hc.client5.http.localserver.LocalServerTestBase; -import org.apache.hc.client5.http.methods.HttpGet; -import org.apache.hc.client5.http.protocol.HttpClientContext; -import org.apache.hc.core5.concurrent.Cancellable; -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.HttpException; -import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpRequest; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.HttpClientConnection; -import org.apache.hc.core5.http.io.HttpRequestHandler; -import org.apache.hc.core5.http.io.UriHttpRequestHandlerMapper; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicHeader; -import org.apache.hc.core5.http.protocol.BasicHttpContext; -import org.apache.hc.core5.http.protocol.HttpContext; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -/** - * Tests for Abort handling. - */ -@SuppressWarnings("static-access") // test code -public class TestAbortHandling extends LocalServerTestBase { - - @Test - public void testAbortRetry_HTTPCLIENT_1120() throws Exception { - final CountDownLatch wait = new CountDownLatch(1); - - this.serverBootstrap.registerHandler("*", new HttpRequestHandler() { - @Override - public void handle( - final ClassicHttpRequest request, - final ClassicHttpResponse response, - final HttpContext context) throws HttpException, IOException { - try { - wait.countDown(); // trigger abort - Thread.sleep(2000); // allow time for abort to happen - response.setCode(HttpStatus.SC_OK); - final StringEntity entity = new StringEntity("Whatever"); - response.setEntity(entity); - } catch (final Exception e) { - response.setCode(HttpStatus.SC_REQUEST_TIMEOUT); - } - } - }); - - final HttpHost target = start(); - final HttpGet httpget = new HttpGet("/"); - - final Thread t = new Thread() { - @Override - public void run(){ - try { - wait.await(); - } catch (final InterruptedException e) { - } - httpget.abort(); - } - }; - - t.start(); - - final HttpClientContext context = HttpClientContext.create(); - try { - this.httpclient.execute(target, httpget, context); - } catch (final IllegalStateException | IOException e) { - } - - final HttpRequest reqWrapper = context.getRequest(); - Assert.assertNotNull("Request should exist",reqWrapper); - } - - @Test - public void testAbortInAllocate() throws Exception { - final CountDownLatch connLatch = new CountDownLatch(1); - final CountDownLatch awaitLatch = new CountDownLatch(1); - final ConMan conMan = new ConMan(connLatch, awaitLatch); - final AtomicReference throwableRef = new AtomicReference<>(); - final CountDownLatch getLatch = new CountDownLatch(1); - this.clientBuilder.setConnectionManager(conMan); - final HttpContext context = new BasicHttpContext(); - final HttpGet httpget = new HttpGet("http://www.example.com/a"); - - start(); - - new Thread(new Runnable() { - @Override - public void run() { - try { - httpclient.execute(httpget, context); - } catch(final Throwable t) { - throwableRef.set(t); - } finally { - getLatch.countDown(); - } - } - }).start(); - - Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS)); - - httpget.abort(); - - Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS)); - Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(), - throwableRef.get() instanceof IOException); - Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(), - throwableRef.get().getCause() instanceof InterruptedException); - } - - /** - * Tests that an abort called after the connection has been retrieved - * but before a release trigger is set does still abort the request. - */ - @Test - public void testAbortAfterAllocateBeforeRequest() throws Exception { - this.serverBootstrap.registerHandler("*", new BasicService()); - - final CountDownLatch releaseLatch = new CountDownLatch(1); - final AtomicReference throwableRef = new AtomicReference<>(); - final CountDownLatch getLatch = new CountDownLatch(1); - final HttpContext context = new BasicHttpContext(); - final HttpGet httpget = new CustomGet("a", releaseLatch); - - final HttpHost target = start(); - - new Thread(new Runnable() { - @Override - public void run() { - try { - httpclient.execute(target, httpget, context); - } catch(final Throwable t) { - throwableRef.set(t); - } finally { - getLatch.countDown(); - } - } - }).start(); - - Thread.sleep(100); // Give it a little time to proceed to release... - - httpget.abort(); - - releaseLatch.countDown(); - - Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS)); - Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(), - throwableRef.get() instanceof IOException); - } - - /** - * Tests that an abort called completely before execute - * still aborts the request. - */ - @Test - public void testAbortBeforeExecute() throws Exception { - this.serverBootstrap.registerHandler("*", new BasicService()); - - final AtomicReference throwableRef = new AtomicReference<>(); - final CountDownLatch getLatch = new CountDownLatch(1); - final CountDownLatch startLatch = new CountDownLatch(1); - final HttpContext context = new BasicHttpContext(); - final HttpGet httpget = new HttpGet("a"); - - final HttpHost target = start(); - - new Thread(new Runnable() { - @Override - public void run() { - try { - try { - if(!startLatch.await(1, TimeUnit.SECONDS)) { - throw new RuntimeException("Took too long to start!"); - } - } catch(final InterruptedException interrupted) { - throw new RuntimeException("Never started!", interrupted); - } - httpclient.execute(target, httpget, context); - } catch(final Throwable t) { - throwableRef.set(t); - } finally { - getLatch.countDown(); - } - } - }).start(); - - httpget.abort(); - startLatch.countDown(); - - Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS)); - Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(), - throwableRef.get() instanceof IOException); - } - - /** - * Tests that an abort called after a redirect has found a new host - * still aborts in the correct place (while trying to get the new - * host's route, not while doing the subsequent request). - */ - @Test - public void testAbortAfterRedirectedRoute() throws Exception { - final UriHttpRequestHandlerMapper reqistry = new UriHttpRequestHandlerMapper(); - this.serverBootstrap.setHandlerMapper(reqistry); - - final CountDownLatch connLatch = new CountDownLatch(1); - final CountDownLatch awaitLatch = new CountDownLatch(1); - final ConnMan4 conMan = new ConnMan4(connLatch, awaitLatch); - final AtomicReference throwableRef = new AtomicReference<>(); - final CountDownLatch getLatch = new CountDownLatch(1); - this.clientBuilder.setConnectionManager(conMan); - final HttpContext context = new BasicHttpContext(); - final HttpGet httpget = new HttpGet("a"); - - final HttpHost target = start(); - reqistry.register("*", new BasicRedirectService(target.getPort())); - - new Thread(new Runnable() { - @Override - public void run() { - try { - final HttpHost host = new HttpHost("127.0.0.1", target.getPort()); - httpclient.execute(host, httpget, context); - } catch(final Throwable t) { - throwableRef.set(t); - } finally { - getLatch.countDown(); - } - } - }).start(); - - Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS)); - - httpget.abort(); - - Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS)); - Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(), - throwableRef.get() instanceof IOException); - Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(), - throwableRef.get().getCause() instanceof InterruptedException); - } - - - /** - * Tests that if a socket fails to connect, the allocated connection is - * properly released back to the connection manager. - */ - @Test - public void testSocketConnectFailureReleasesConnection() throws Exception { - final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); - final ConnectionRequest connrequest = Mockito.mock(ConnectionRequest.class); - Mockito.when(connrequest.get( - Mockito.anyInt(), Mockito.any(TimeUnit.class))).thenReturn(conn); - final HttpClientConnectionManager connmgr = Mockito.mock(HttpClientConnectionManager.class); - Mockito.doThrow(new ConnectException()).when(connmgr).connect( - Mockito.any(HttpClientConnection.class), - Mockito.any(HttpRoute.class), - Mockito.anyInt(), - Mockito.any(HttpContext.class)); - - Mockito.when(connmgr.requestConnection( - Mockito.any(HttpRoute.class), Mockito.any())).thenReturn(connrequest); - - final CloseableHttpClient client = HttpClients.custom().setConnectionManager(connmgr).build(); - final HttpContext context = new BasicHttpContext(); - final HttpGet httpget = new HttpGet("http://www.example.com/a"); - - try { - client.execute(httpget, context); - Assert.fail("expected IOException"); - } catch(final IOException expected) {} - - Mockito.verify(connmgr).releaseConnection(conn, null, 0, TimeUnit.MILLISECONDS); - } - - private static class BasicService implements HttpRequestHandler { - @Override - public void handle( - final ClassicHttpRequest request, - final ClassicHttpResponse response, - final HttpContext context) throws HttpException, IOException { - response.setCode(200); - response.setEntity(new StringEntity("Hello World")); - } - } - - private static class BasicRedirectService implements HttpRequestHandler { - private final int statuscode = HttpStatus.SC_SEE_OTHER; - private final int port; - - public BasicRedirectService(final int port) { - this.port = port; - } - - @Override - public void handle( - final ClassicHttpRequest request, - final ClassicHttpResponse response, - final HttpContext context) throws HttpException, IOException { - response.setCode(this.statuscode); - response.addHeader(new BasicHeader("Location", "http://localhost:" - + this.port + "/newlocation/")); - response.addHeader(new BasicHeader("Connection", "close")); - } - } - - private static class ConnMan4 extends PoolingHttpClientConnectionManager { - private final CountDownLatch connLatch; - private final CountDownLatch awaitLatch; - - public ConnMan4(final CountDownLatch connLatch, final CountDownLatch awaitLatch) { - super(); - this.connLatch = connLatch; - this.awaitLatch = awaitLatch; - } - - @Override - public ConnectionRequest requestConnection(final HttpRoute route, final Object state) { - // If this is the redirect route, stub the return value - // so-as to pretend the host is waiting on a slot... - if(route.getTargetHost().getHostName().equals("localhost")) { - final Thread currentThread = Thread.currentThread(); - - return new ConnectionRequest() { - - @Override - public boolean cancel() { - currentThread.interrupt(); - return true; - } - - @Override - public HttpClientConnection get( - final long timeout, - final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { - connLatch.countDown(); // notify waiter that we're getting a connection - - // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way. - if(!awaitLatch.await(timeout > 0 ? timeout : Integer.MAX_VALUE, tunit)) { - throw new ConnectionPoolTimeoutException(); - } - - return Mockito.mock(HttpClientConnection.class); - } - }; - } else { - return super.requestConnection(route, state); - } - } - } - - - static class ConMan implements HttpClientConnectionManager { - private final CountDownLatch connLatch; - private final CountDownLatch awaitLatch; - - public ConMan(final CountDownLatch connLatch, final CountDownLatch awaitLatch) { - this.connLatch = connLatch; - this.awaitLatch = awaitLatch; - } - - @Override - public void closeIdle(final long idletime, final TimeUnit tunit) { - throw new UnsupportedOperationException("just a mockup"); - } - - @Override - public void closeExpired() { - throw new UnsupportedOperationException("just a mockup"); - } - - public HttpClientConnection getConnection(final HttpRoute route, - final long timeout, final TimeUnit tunit) { - throw new UnsupportedOperationException("just a mockup"); - } - - @Override - public ConnectionRequest requestConnection( - final HttpRoute route, - final Object state) { - - final Thread currentThread = Thread.currentThread(); - - return new ConnectionRequest() { - - @Override - public boolean cancel() { - currentThread.interrupt(); - return true; - } - - @Override - public HttpClientConnection get( - final long timeout, - final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { - connLatch.countDown(); // notify waiter that we're getting a connection - - // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way. - if(!awaitLatch.await(timeout > 0 ? timeout : Integer.MAX_VALUE, tunit)) { - throw new ConnectionPoolTimeoutException(); - } - - return Mockito.mock(HttpClientConnection.class); - } - - }; - } - - @Override - public void shutdown() { - } - - public void close() { - } - - @Override - public void releaseConnection( - final HttpClientConnection conn, - final Object newState, - final long validDuration, final TimeUnit timeUnit) { - throw new UnsupportedOperationException("just a mockup"); - } - - @Override - public void connect( - final HttpClientConnection conn, - final HttpRoute route, - final int connectTimeout, - final HttpContext context) throws IOException { - throw new UnsupportedOperationException("just a mockup"); - } - - @Override - public void upgrade( - final HttpClientConnection conn, - final HttpRoute route, - final HttpContext context) throws IOException { - throw new UnsupportedOperationException("just a mockup"); - } - - @Override - public void routeComplete( - final HttpClientConnection conn, - final HttpRoute route, - final HttpContext context) throws IOException { - throw new UnsupportedOperationException("just a mockup"); - } - - public void connect( - final HttpClientConnection conn, - final HttpHost host, - final InetSocketAddress localAddress, - final int connectTimeout, - final HttpContext context) { - throw new UnsupportedOperationException("just a mockup"); - } - - public void upgrade( - final HttpClientConnection conn, - final HttpHost host, - final HttpContext context) { - throw new UnsupportedOperationException("just a mockup"); - } - } - - private static class CustomGet extends HttpGet { - private static final long serialVersionUID = 1L; - private final CountDownLatch releaseTriggerLatch; - - public CustomGet(final String uri, final CountDownLatch releaseTriggerLatch) { - super(uri); - this.releaseTriggerLatch = releaseTriggerLatch; - } - - @Override - public void setCancellable(final Cancellable cancellable) { - try { - if(!releaseTriggerLatch.await(1, TimeUnit.SECONDS)) { - throw new RuntimeException("Waited too long..."); - } - } catch(final InterruptedException ie) { - throw new RuntimeException(ie); - } - - super.setCancellable(cancellable); - } - - } - -} diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionAutoRelease.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionAutoRelease.java index 327c8c79a..56d5726d4 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionAutoRelease.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionAutoRelease.java @@ -31,10 +31,11 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; import org.apache.hc.client5.http.HttpRoute; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.io.ConnectionEndpoint; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.localserver.LocalServerTestBase; import org.apache.hc.client5.http.methods.HttpGet; import org.apache.hc.core5.http.ClassicHttpRequest; @@ -44,13 +45,11 @@ import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.MalformedChunkCodingException; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.BasicHttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; -import org.apache.hc.core5.pool.PoolStats; import org.junit.Assert; import org.junit.Test; @@ -62,19 +61,18 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { this.connManager.setMaxTotal(1); // Zero connections in the pool - PoolStats stats = this.connManager.getTotalStats(); - Assert.assertEquals(0, stats.getAvailable()); + Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); final HttpHost target = start(); // Get some random data final HttpGet httpget = new HttpGet("/random/20000"); final ClassicHttpResponse response = this.httpclient.execute(target, httpget); - ConnectionRequest connreq = this.connManager.requestConnection(new HttpRoute(target), null); + final LeaseRequest connreq1 = this.connManager.lease(new HttpRoute(target), null); try { - connreq.get(250, TimeUnit.MILLISECONDS); + connreq1.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException expected) { + } catch (final TimeoutException expected) { } final HttpEntity e = response.getEntity(); @@ -82,14 +80,13 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { EntityUtils.consume(e); // Expect one connection in the pool - stats = this.connManager.getTotalStats(); - Assert.assertEquals(1, stats.getAvailable()); + Assert.assertEquals(1, this.connManager.getTotalStats().getAvailable()); // Make sure one connection is available - connreq = this.connManager.requestConnection(new HttpRoute(target), null); - final HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); + final LeaseRequest connreq2 = this.connManager.lease(new HttpRoute(target), null); + final ConnectionEndpoint endpoint = connreq2.get(250, TimeUnit.MILLISECONDS); - this.connManager.releaseConnection(conn, null, -1, null); + this.connManager.release(endpoint, null, -1, null); } @Test @@ -98,19 +95,18 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { this.connManager.setMaxTotal(1); // Zero connections in the pool - PoolStats stats = this.connManager.getTotalStats(); - Assert.assertEquals(0, stats.getAvailable()); + Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); final HttpHost target = start(); // Get some random data final HttpGet httpget = new HttpGet("/random/20000"); final ClassicHttpResponse response = this.httpclient.execute(target, httpget); - ConnectionRequest connreq = this.connManager.requestConnection(new HttpRoute(target), null); + final LeaseRequest connreq1 = this.connManager.lease(new HttpRoute(target), null); try { - connreq.get(250, TimeUnit.MILLISECONDS); + connreq1.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException expected) { + } catch (final TimeoutException expected) { } final HttpEntity e = response.getEntity(); @@ -119,14 +115,13 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { e.writeTo(outsteam); // Expect one connection in the pool - stats = this.connManager.getTotalStats(); - Assert.assertEquals(1, stats.getAvailable()); + Assert.assertEquals(1, this.connManager.getTotalStats().getAvailable()); // Make sure one connection is available - connreq = this.connManager.requestConnection(new HttpRoute(target), null); - final HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); + final LeaseRequest connreq2 = this.connManager.lease(new HttpRoute(target), null); + final ConnectionEndpoint endpoint = connreq2.get(250, TimeUnit.MILLISECONDS); - this.connManager.releaseConnection(conn, null, -1, null); + this.connManager.release(endpoint, null, -1, null); } @Test @@ -135,8 +130,7 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { this.connManager.setMaxTotal(1); // Zero connections in the pool - final PoolStats stats = this.connManager.getTotalStats(); - Assert.assertEquals(0, stats.getAvailable()); + Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); final HttpHost target = start(); @@ -144,11 +138,11 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { final HttpGet httpget = new HttpGet("/random/20000"); final ClassicHttpResponse response = this.httpclient.execute(target, httpget); - ConnectionRequest connreq = this.connManager.requestConnection(new HttpRoute(target), null); + final LeaseRequest connreq1 = this.connManager.lease(new HttpRoute(target), null); try { - connreq.get(250, TimeUnit.MILLISECONDS); + connreq1.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException expected) { + } catch (final TimeoutException expected) { } final HttpEntity e = response.getEntity(); @@ -159,10 +153,10 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); // Make sure one connection is available - connreq = this.connManager.requestConnection(new HttpRoute(target), null); - final HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); + final LeaseRequest connreq2 = this.connManager.lease(new HttpRoute(target), null); + final ConnectionEndpoint endpoint = connreq2.get(250, TimeUnit.MILLISECONDS); - this.connManager.releaseConnection(conn, null, -1, null); + this.connManager.release(endpoint, null, -1, null); } @Test @@ -212,11 +206,11 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { final HttpGet httpget = new HttpGet("/dropdead"); final ClassicHttpResponse response = this.httpclient.execute(target, httpget); - ConnectionRequest connreq = this.connManager.requestConnection(new HttpRoute(target), null); + final LeaseRequest connreq1 = this.connManager.lease(new HttpRoute(target), null); try { - connreq.get(250, TimeUnit.MILLISECONDS); + connreq1.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException expected) { + } catch (final TimeoutException expected) { } final HttpEntity e = response.getEntity(); @@ -233,10 +227,10 @@ public class TestConnectionAutoRelease extends LocalServerTestBase { Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); // Make sure one connection is available - connreq = this.connManager.requestConnection(new HttpRoute(target), null); - final HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); + final LeaseRequest connreq2 = this.connManager.lease(new HttpRoute(target), null); + final ConnectionEndpoint endpoint = connreq2.get(250, TimeUnit.MILLISECONDS); - this.connManager.releaseConnection(conn, null, -1, null); + this.connManager.release(endpoint, null, -1, null); } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionManagement.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionManagement.java index 233134bd7..08451c53a 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionManagement.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestConnectionManagement.java @@ -28,27 +28,23 @@ package org.apache.hc.client5.http.impl.integration; import java.util.Collections; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; -import org.apache.hc.client5.http.io.ConnectionRequest; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.io.ConnectionEndpoint; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.localserver.LocalServerTestBase; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; -import org.apache.hc.core5.http.io.HttpClientConnection; -import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestConnControl; import org.apache.hc.core5.http.protocol.RequestContent; @@ -62,22 +58,6 @@ import org.junit.Test; */ public class TestConnectionManagement extends LocalServerTestBase { - private static HttpClientConnection getConnection( - final HttpClientConnectionManager mgr, - final HttpRoute route, - final long timeout, - final TimeUnit unit) throws ConnectionPoolTimeoutException, ExecutionException, InterruptedException { - final ConnectionRequest connRequest = mgr.requestConnection(route, null); - return connRequest.get(timeout, unit); - } - - private static HttpClientConnection getConnection( - final HttpClientConnectionManager mgr, - final HttpRoute route) throws ConnectionPoolTimeoutException, ExecutionException, InterruptedException { - final ConnectionRequest connRequest = mgr.requestConnection(route, null); - return connRequest.get(0, TimeUnit.MILLISECONDS); - } - /** * Tests releasing and re-using a connection after a response is read. */ @@ -91,79 +71,60 @@ public class TestConnectionManagement extends LocalServerTestBase { final int rsplen = 8; final String uri = "/random/" + rsplen; - final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", uri); + final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", target, uri); final HttpContext context = new BasicHttpContext(); - HttpClientConnection conn = getConnection(this.connManager, route); - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); + final LeaseRequest leaseRequest1 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint1 = leaseRequest1.get(0, TimeUnit.MILLISECONDS); - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); + this.connManager.connect(endpoint1, 0, TimeUnit.MILLISECONDS, context); final HttpProcessor httpProcessor = new DefaultHttpProcessor( new RequestTargetHost(), new RequestContent(), new RequestConnControl()); final HttpRequestExecutor exec = new HttpRequestExecutor(); exec.preProcess(request, httpProcessor, context); - ClassicHttpResponse response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in first response", - HttpStatus.SC_OK, - response.getCode()); - byte[] data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of first response entity", - rsplen, data.length); - // ignore data, but it must be read + try (final ClassicHttpResponse response1 = endpoint1.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response1.getCode()); + } // check that there is no auto-release by default try { // this should fail quickly, connection has not been released - getConnection(this.connManager, route, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException e) { + final LeaseRequest leaseRequest2 = this.connManager.lease(route, null); + leaseRequest2.get(10, TimeUnit.MILLISECONDS); + Assert.fail("TimeoutException expected"); + } catch (final TimeoutException ex) { // expected } - conn.close(); - this.connManager.releaseConnection(conn, null, -1, null); - conn = getConnection(this.connManager, route); - Assert.assertFalse("connection should have been closed", conn.isOpen()); + endpoint1.close(); + this.connManager.release(endpoint1, null, -1, null); + final LeaseRequest leaseRequest2 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint2 = leaseRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertFalse(endpoint2.isConnected()); - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); + this.connManager.connect(endpoint2, 0, TimeUnit.MILLISECONDS, context); - // repeat the communication, no need to prepare the request again - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in second response", - HttpStatus.SC_OK, - response.getCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of second response entity", - rsplen, data.length); - // ignore data, but it must be read + try (final ClassicHttpResponse response2 = endpoint2.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response2.getCode()); + } // release connection after marking it for re-use // expect the next connection obtained to be open - this.connManager.releaseConnection(conn, null, -1, null); - conn = getConnection(this.connManager, route); - Assert.assertTrue("connection should have been open", conn.isOpen()); + this.connManager.release(endpoint2, null, -1, null); + + final LeaseRequest leaseRequest3 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint3 = leaseRequest3.get(0, TimeUnit.MILLISECONDS); + Assert.assertTrue(endpoint3.isConnected()); // repeat the communication, no need to prepare the request again - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); + try (final ClassicHttpResponse response3 = endpoint3.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response3.getCode()); + } - Assert.assertEquals("wrong status in third response", - HttpStatus.SC_OK, - response.getCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of third response entity", - rsplen, data.length); - // ignore data, but it must be read - - this.connManager.releaseConnection(conn, null, -1, null); - this.connManager.shutdown(); + this.connManager.release(endpoint3, null, -1, null); + this.connManager.close(); } /** @@ -179,96 +140,71 @@ public class TestConnectionManagement extends LocalServerTestBase { final int rsplen = 8; final String uri = "/random/" + rsplen; - final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", uri); + final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", target, uri); final HttpContext context = new BasicHttpContext(); - HttpClientConnection conn = getConnection(this.connManager, route); - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); - - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); + final LeaseRequest leaseRequest1 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint1 = leaseRequest1.get(0, TimeUnit.MILLISECONDS); + this.connManager.connect(endpoint1, 0, TimeUnit.MILLISECONDS, context); final HttpProcessor httpProcessor = new DefaultHttpProcessor( new RequestTargetHost(), new RequestContent(), new RequestConnControl()); final HttpRequestExecutor exec = new HttpRequestExecutor(); exec.preProcess(request, httpProcessor, context); - ClassicHttpResponse response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in first response", - HttpStatus.SC_OK, - response.getCode()); - byte[] data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of first response entity", - rsplen, data.length); - // ignore data, but it must be read + try (final ClassicHttpResponse response1 = endpoint1.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response1.getCode()); + } // check that there is no auto-release by default try { // this should fail quickly, connection has not been released - getConnection(this.connManager, route, 10L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException e) { + final LeaseRequest leaseRequest2 = this.connManager.lease(route, null); + leaseRequest2.get(10, TimeUnit.MILLISECONDS); + Assert.fail("TimeoutException expected"); + } catch (final TimeoutException ex) { // expected } - conn.close(); - this.connManager.releaseConnection(conn, null, 100, TimeUnit.MILLISECONDS); - conn = getConnection(this.connManager, route); - Assert.assertFalse("connection should have been closed", conn.isOpen()); + endpoint1.close(); + this.connManager.release(endpoint1, null, 100, TimeUnit.MILLISECONDS); + + final LeaseRequest leaseRequest2 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint2 = leaseRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertFalse(endpoint2.isConnected()); + + this.connManager.connect(endpoint2, 0, TimeUnit.MILLISECONDS, context); + + try (final ClassicHttpResponse response2 = endpoint2.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response2.getCode()); + } + + this.connManager.release(endpoint2, null, 100, TimeUnit.MILLISECONDS); + + final LeaseRequest leaseRequest3 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint3 = leaseRequest3.get(0, TimeUnit.MILLISECONDS); + Assert.assertTrue(endpoint3.isConnected()); // repeat the communication, no need to prepare the request again - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); + try (final ClassicHttpResponse response3 = endpoint3.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response3.getCode()); + } - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in second response", - HttpStatus.SC_OK, - response.getCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of second response entity", - rsplen, data.length); - // ignore data, but it must be read - - this.connManager.releaseConnection(conn, null, 100, TimeUnit.MILLISECONDS); - conn = getConnection(this.connManager, route); - Assert.assertTrue("connection should have been open", conn.isOpen()); - - // repeat the communication, no need to prepare the request again - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in third response", - HttpStatus.SC_OK, - response.getCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of third response entity", - rsplen, data.length); - // ignore data, but it must be read - - this.connManager.releaseConnection(conn, null, 100, TimeUnit.MILLISECONDS); + this.connManager.release(endpoint3, null, 100, TimeUnit.MILLISECONDS); Thread.sleep(150); - conn = getConnection(this.connManager, route); - Assert.assertTrue("connection should have been closed", !conn.isOpen()); + + final LeaseRequest leaseRequest4 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint4 = leaseRequest4.get(0, TimeUnit.MILLISECONDS); + Assert.assertFalse(endpoint4.isConnected()); // repeat the communication, no need to prepare the request again - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); + this.connManager.connect(endpoint4, 0, TimeUnit.MILLISECONDS, context); - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); + try (final ClassicHttpResponse response4 = endpoint4.execute(request, exec, context)) { + Assert.assertEquals(HttpStatus.SC_OK, response4.getCode()); + } - Assert.assertEquals("wrong status in third response", - HttpStatus.SC_OK, - response.getCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of fourth response entity", - rsplen, data.length); - // ignore data, but it must be read - - this.connManager.shutdown(); + this.connManager.close(); } @Test @@ -280,15 +216,15 @@ public class TestConnectionManagement extends LocalServerTestBase { final HttpRoute route = new HttpRoute(target, null, false); final HttpContext context = new BasicHttpContext(); - final HttpClientConnection conn = getConnection(this.connManager, route); - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); + final LeaseRequest leaseRequest1 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint1 = leaseRequest1.get(0, TimeUnit.MILLISECONDS); + this.connManager.connect(endpoint1, 0, TimeUnit.MILLISECONDS, context); Assert.assertEquals(Collections.singleton(route), this.connManager.getRoutes()); Assert.assertEquals(1, this.connManager.getTotalStats().getLeased()); Assert.assertEquals(1, this.connManager.getStats(route).getLeased()); - this.connManager.releaseConnection(conn, null, 100, TimeUnit.MILLISECONDS); + this.connManager.release(endpoint1, null, 100, TimeUnit.MILLISECONDS); // Released, still active. Assert.assertEquals(Collections.singleton(route), this.connManager.getRoutes()); @@ -311,7 +247,7 @@ public class TestConnectionManagement extends LocalServerTestBase { Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); Assert.assertEquals(0, this.connManager.getStats(route).getAvailable()); - this.connManager.shutdown(); + this.connManager.close(); } @Test @@ -327,15 +263,15 @@ public class TestConnectionManagement extends LocalServerTestBase { final HttpRoute route = new HttpRoute(target, null, false); final HttpContext context = new BasicHttpContext(); - final HttpClientConnection conn = getConnection(this.connManager, route); - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); + final LeaseRequest leaseRequest1 = this.connManager.lease(route, null); + final ConnectionEndpoint endpoint1 = leaseRequest1.get(0, TimeUnit.MILLISECONDS); + this.connManager.connect(endpoint1, 0, TimeUnit.MILLISECONDS, context); Assert.assertEquals(Collections.singleton(route), this.connManager.getRoutes()); Assert.assertEquals(1, this.connManager.getTotalStats().getLeased()); Assert.assertEquals(1, this.connManager.getStats(route).getLeased()); // Release, let remain idle for forever - this.connManager.releaseConnection(conn, null, -1, TimeUnit.MILLISECONDS); + this.connManager.release(endpoint1, null, 0, TimeUnit.MILLISECONDS); // Released, still active. Assert.assertEquals(Collections.singleton(route), this.connManager.getRoutes()); @@ -358,64 +294,7 @@ public class TestConnectionManagement extends LocalServerTestBase { Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable()); Assert.assertEquals(0, this.connManager.getStats(route).getAvailable()); - this.connManager.shutdown(); - } - - /** - * Tests releasing connection from #abort method called from the - * main execution thread while there is no blocking I/O operation. - */ - @Test - public void testReleaseConnectionOnAbort() throws Exception { - - this.connManager.setMaxTotal(1); - - final HttpHost target = start(); - final HttpRoute route = new HttpRoute(target, null, false); - final int rsplen = 8; - final String uri = "/random/" + rsplen; - final HttpContext context = new BasicHttpContext(); - - final ClassicHttpRequest request = - new BasicClassicHttpRequest("GET", uri); - - HttpClientConnection conn = getConnection(this.connManager, route); - this.connManager.connect(conn, route, 0, context); - this.connManager.routeComplete(conn, route, context); - - context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); - - final HttpProcessor httpProcessor = new DefaultHttpProcessor( - new RequestTargetHost(), new RequestContent(), new RequestConnControl()); - - final HttpRequestExecutor exec = new HttpRequestExecutor(); - exec.preProcess(request, httpProcessor, context); - final ClassicHttpResponse response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in first response", - HttpStatus.SC_OK, - response.getCode()); - - // check that there are no connections available - try { - // this should fail quickly, connection has not been released - getConnection(this.connManager, route, 100L, TimeUnit.MILLISECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (final ConnectionPoolTimeoutException e) { - // expected - } - - // abort the connection - Assert.assertTrue(conn instanceof HttpClientConnection); - conn.shutdown(); - this.connManager.releaseConnection(conn, null, -1, null); - - // the connection is expected to be released back to the manager - conn = getConnection(this.connManager, route, 5L, TimeUnit.SECONDS); - Assert.assertFalse("connection should have been closed", conn.isOpen()); - - this.connManager.releaseConnection(conn, null, -1, null); - this.connManager.shutdown(); + this.connManager.close(); } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestContentCodings.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestContentCodings.java index e3b4c695a..5003a5384 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestContentCodings.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestContentCodings.java @@ -41,7 +41,7 @@ import java.util.concurrent.Executors; import java.util.zip.Deflater; import java.util.zip.GZIPOutputStream; -import org.apache.hc.client5.http.impl.BasicResponseHandler; +import org.apache.hc.client5.http.impl.sync.BasicResponseHandler; import org.apache.hc.client5.http.impl.sync.CloseableHttpClient; import org.apache.hc.client5.http.localserver.LocalServerTestBase; import org.apache.hc.client5.http.methods.HttpGet; diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestStatefulConnManagement.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestStatefulConnManagement.java index e78c91fb9..84d3358f0 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestStatefulConnManagement.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/integration/TestStatefulConnManagement.java @@ -36,10 +36,10 @@ import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.UserTokenHandler; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; @@ -119,12 +119,10 @@ public class TestStatefulConnManagement extends LocalServerTestBase { } for (final HttpContext context : contexts) { - final String uid = (String) context.getAttribute("user"); - - for (int r = 0; r < requestCount; r++) { - final String state = (String) context.getAttribute("r" + r); - Assert.assertNotNull(state); - Assert.assertEquals(uid, state); + final String state0 = (String) context.getAttribute("r0"); + Assert.assertNotNull(state0); + for (int r = 1; r < requestCount; r++) { + Assert.assertEquals(state0, (String) context.getAttribute("r" + r)); } } @@ -176,13 +174,8 @@ public class TestStatefulConnManagement extends LocalServerTestBase { this.context); this.count++; - final HttpClientConnection conn = this.context.getConnection(HttpClientConnection.class); - final HttpContext connContext = (HttpContext) conn; - String connuid = (String) connContext.getAttribute("user"); - if (connuid == null) { - connContext.setAttribute("user", this.uid); - connuid = this.uid; - } + final HttpConnection conn = this.context.getConnection(); + final String connuid = Integer.toHexString(System.identityHashCode(conn)); this.context.setAttribute("r" + r, connuid); EntityUtils.consume(response.getEntity()); } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java index cbe00253f..d9cbbbd9d 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java @@ -33,19 +33,18 @@ import java.net.Socket; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.DnsResolver; -import org.apache.hc.client5.http.HttpConnectionFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.io.ConnectionEndpoint; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.config.ConnectionConfig; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.SocketConfig; -import org.apache.hc.core5.http.io.HttpClientConnection; +import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.Assert; import org.junit.Before; @@ -60,7 +59,7 @@ public class TestBasicHttpClientConnectionManager { @Mock private ManagedHttpClientConnection conn; @Mock - private HttpConnectionFactory connFactory; + private HttpConnectionFactory connFactory; @Mock private Lookup socketFactoryRegistry; @Mock @@ -88,26 +87,24 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); - Assert.assertFalse(conn1.isOpen()); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); + Assert.assertFalse(endpoint1.isConnected()); - mgr.releaseConnection(conn1, null, 100, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 100, TimeUnit.MILLISECONDS); Assert.assertNull(mgr.getRoute()); Assert.assertNull(mgr.getState()); - final ConnectionRequest connRequest2 = mgr.requestConnection(route, null); - final HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + final LeaseRequest connRequest2 = mgr.lease(route, null); + final ConnectionEndpoint conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); - Assert.assertFalse(conn2.isOpen()); + Assert.assertFalse(conn2.isConnected()); - Mockito.verify(connFactory, Mockito.times(2)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any()); } @Test @@ -115,29 +112,27 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create(Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); - mgr.releaseConnection(conn1, null, 10000, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 10000, TimeUnit.MILLISECONDS); Assert.assertEquals(route, mgr.getRoute()); Assert.assertEquals(null, mgr.getState()); - final ConnectionRequest connRequest2 = mgr.requestConnection(route, null); - final HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + final LeaseRequest connRequest2 = mgr.lease(route, null); + final ConnectionEndpoint conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); - Assert.assertTrue(conn2.isOpen()); + Assert.assertTrue(conn2.isConnected()); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); } @Test @@ -145,30 +140,27 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, "some state"); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, "some state"); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); - mgr.releaseConnection(conn1, "some other state", 10000, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, "some other state", 10000, TimeUnit.MILLISECONDS); Assert.assertEquals(route, mgr.getRoute()); Assert.assertEquals("some other state", mgr.getState()); - final ConnectionRequest connRequest2 = mgr.requestConnection(route, "some other state"); - final HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + final LeaseRequest connRequest2 = mgr.lease(route, "some other state"); + final ConnectionEndpoint conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); - Assert.assertTrue(conn2.isOpen()); + Assert.assertTrue(conn2.isConnected()); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); } @Test @@ -176,35 +168,30 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target1 = new HttpHost("somehost", 80); final HttpRoute route1 = new HttpRoute(target1); - Mockito.when(connFactory.create( - Mockito.any(), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route1, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route1, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route1), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); - mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 0, TimeUnit.MILLISECONDS); Assert.assertEquals(route1, mgr.getRoute()); Assert.assertEquals(null, mgr.getState()); final HttpHost target2 = new HttpHost("otherhost", 80); final HttpRoute route2 = new HttpRoute(target2); - final ConnectionRequest connRequest2 = mgr.requestConnection(route2, null); - final HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + final LeaseRequest connRequest2 = mgr.lease(route2, null); + final ConnectionEndpoint conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); - Assert.assertFalse(conn2.isOpen()); + Assert.assertFalse(conn2.isConnected()); Mockito.verify(conn).close(); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route1), Mockito.any()); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route2), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any()); } @Test @@ -212,49 +199,41 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); - mgr.releaseConnection(conn1, null, 10, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 10, TimeUnit.MILLISECONDS); Assert.assertEquals(route, mgr.getRoute()); Assert.assertEquals(null, mgr.getState()); Thread.sleep(50); - final ConnectionRequest connRequest2 = mgr.requestConnection(route, null); - final HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + final LeaseRequest connRequest2 = mgr.lease(route, null); + final ConnectionEndpoint conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); - Assert.assertFalse(conn2.isOpen()); + Assert.assertFalse(conn2.isConnected()); Mockito.verify(conn).close(); - Mockito.verify(connFactory, Mockito.times(2)).create( - Mockito.eq(route), Mockito.any()); - } - - @Test(expected=IllegalArgumentException.class) - public void testLeaseInvalidArg() throws Exception { - mgr.requestConnection(null, null); + Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any()); } @Test(expected=IllegalArgumentException.class) public void testReleaseInvalidArg() throws Exception { - mgr.releaseConnection(null, null, 0, TimeUnit.MILLISECONDS); + mgr.release(null, null, 0, TimeUnit.MILLISECONDS); } @Test(expected=IllegalStateException.class) public void testReleaseAnotherConnection() throws Exception { - final HttpClientConnection wrongCon = Mockito.mock(HttpClientConnection.class); - mgr.releaseConnection(wrongCon, null, 0, TimeUnit.MILLISECONDS); + final ConnectionEndpoint wrongCon = Mockito.mock(ConnectionEndpoint.class); + mgr.release(wrongCon, null, 0, TimeUnit.MILLISECONDS); } @Test @@ -262,26 +241,24 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); - mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 0, TimeUnit.MILLISECONDS); - mgr.shutdown(); + mgr.close(); Mockito.verify(conn, Mockito.times(1)).shutdown(); try { - final ConnectionRequest connRequest2 = mgr.requestConnection(route, null); + final LeaseRequest connRequest2 = mgr.lease(route, null); connRequest2.get(0, TimeUnit.MILLISECONDS); Assert.fail("IllegalStateException expected"); } catch (final IllegalStateException ex) { @@ -290,7 +267,7 @@ public class TestBasicHttpClientConnectionManager { // Should have no effect mgr.closeExpired(); mgr.closeIdle(0L, TimeUnit.MILLISECONDS); - mgr.shutdown(); + mgr.close(); Mockito.verify(conn, Mockito.times(1)).shutdown(); } @@ -300,19 +277,17 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); - mgr.releaseConnection(conn1, null, 10, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 10, TimeUnit.MILLISECONDS); Assert.assertEquals(route, mgr.getRoute()); Assert.assertEquals(null, mgr.getState()); @@ -329,19 +304,17 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); - Mockito.verify(connFactory, Mockito.times(1)).create( - Mockito.eq(route), Mockito.any()); + Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); - mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 0, TimeUnit.MILLISECONDS); Assert.assertEquals(route, mgr.getRoute()); Assert.assertEquals(null, mgr.getState()); @@ -358,13 +331,12 @@ public class TestBasicHttpClientConnectionManager { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); - mgr.releaseConnection(conn1, null, 100, TimeUnit.MILLISECONDS); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); + mgr.release(endpoint1, null, 100, TimeUnit.MILLISECONDS); mgr.getConnection(route, null); mgr.getConnection(route, null); @@ -377,12 +349,11 @@ public class TestBasicHttpClientConnectionManager { final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final HttpRoute route = new HttpRoute(target, local, true); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); @@ -401,7 +372,7 @@ public class TestBasicHttpClientConnectionManager { Mockito.any(), Mockito.any())).thenReturn(socket); - mgr.connect(conn1, route, 123, context); + mgr.connect(endpoint1, 123, TimeUnit.MILLISECONDS, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); @@ -409,8 +380,6 @@ public class TestBasicHttpClientConnectionManager { Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(123, socket, target, new InetSocketAddress(remote, 8443), new InetSocketAddress(local, 0), context); - - mgr.routeComplete(conn1, route, context); } @Test @@ -421,12 +390,11 @@ public class TestBasicHttpClientConnectionManager { final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final HttpRoute route = new HttpRoute(target, local, proxy, true); - Mockito.when(connFactory.create( - Mockito.eq(route), Mockito.any())).thenReturn(conn); + Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(endpoint1); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); @@ -447,7 +415,7 @@ public class TestBasicHttpClientConnectionManager { Mockito.any(), Mockito.any())).thenReturn(socket); - mgr.connect(conn1, route, 123, context); + mgr.connect(endpoint1, 123, TimeUnit.MILLISECONDS, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy); @@ -458,13 +426,11 @@ public class TestBasicHttpClientConnectionManager { Mockito.when(conn.getSocket()).thenReturn(socket); - mgr.upgrade(conn1, route, context); + mgr.upgrade(endpoint1, context); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); Mockito.verify(sslSocketFactory, Mockito.times(1)).createLayeredSocket( socket, "somehost", 8443, context); - - mgr.routeComplete(conn1, route, context); } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestDefaultHttpResponseParser.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestDefaultHttpResponseParser.java deleted file mode 100644 index 37cfae32c..000000000 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestDefaultHttpResponseParser.java +++ /dev/null @@ -1,115 +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.hc.client5.http.impl.io; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; - -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpResponse; -import org.apache.hc.core5.http.HttpVersion; -import org.apache.hc.core5.http.MessageConstraintException; -import org.apache.hc.core5.http.NoHttpResponseException; -import org.apache.hc.core5.http.config.H1Config; -import org.apache.hc.core5.http.impl.io.SessionInputBufferImpl; -import org.apache.hc.core5.http.io.HttpMessageParser; -import org.apache.hc.core5.http.io.SessionInputBuffer; -import org.junit.Assert; -import org.junit.Test; - -/** - * Tests for {@link LenientHttpResponseParser}. - */ -public class TestDefaultHttpResponseParser { - - @Test - public void testResponseParsingWithSomeGarbage() throws Exception { - final String s = - "garbage\r\n" + - "garbage\r\n" + - "more garbage\r\n" + - "HTTP/1.1 200 OK\r\n" + - "header1: value1\r\n" + - "header2: value2\r\n" + - "\r\n"; - - final SessionInputBuffer inbuffer = new SessionInputBufferImpl(1024); - final H1Config h1Config = H1Config.custom().setMaxEmptyLineCount(Integer.MAX_VALUE).build(); - final HttpMessageParser parser = new LenientHttpResponseParser(h1Config); - - final HttpResponse response = parser.parse(inbuffer, - new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII))); - Assert.assertNotNull(response); - Assert.assertEquals(HttpVersion.HTTP_1_1, response.getVersion()); - Assert.assertEquals(200, response.getCode()); - - final Header[] headers = response.getAllHeaders(); - Assert.assertNotNull(headers); - Assert.assertEquals(2, headers.length); - Assert.assertEquals("header1", headers[0].getName()); - Assert.assertEquals("header2", headers[1].getName()); - } - - @Test(expected=MessageConstraintException.class) - public void testResponseParsingWithTooMuchGarbage() throws Exception { - final String s = - "garbage\r\n" + - "garbage\r\n" + - "more garbage\r\n" + - "HTTP/1.1 200 OK\r\n" + - "header1: value1\r\n" + - "header2: value2\r\n" + - "\r\n"; - - final SessionInputBuffer inbuffer = new SessionInputBufferImpl(1024); - final H1Config h1Config = H1Config.custom().setMaxEmptyLineCount(2).build(); - final HttpMessageParser parser = new LenientHttpResponseParser(h1Config); - parser.parse(inbuffer, new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII))); - } - - @Test(expected=NoHttpResponseException.class) - public void testResponseParsingNoResponse() throws Exception { - final SessionInputBuffer inbuffer = new SessionInputBufferImpl(1024); - final HttpMessageParser parser = new LenientHttpResponseParser(H1Config.DEFAULT); - parser.parse(inbuffer, new ByteArrayInputStream(new byte[]{})); - } - - @Test(expected=NoHttpResponseException.class) - public void testResponseParsingOnlyGarbage() throws Exception { - final String s = - "garbage\r\n" + - "garbage\r\n" + - "more garbage\r\n" + - "a lot more garbage\r\n"; - final SessionInputBuffer inbuffer = new SessionInputBufferImpl(1024); - final HttpMessageParser parser = new LenientHttpResponseParser(H1Config.DEFAULT); - parser.parse(inbuffer, new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII))); - } - -} diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java index 8facb43e2..eea669beb 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java @@ -243,6 +243,8 @@ public class TestHttpClientConnectionOperator { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("somehost", -1, "https"); + Mockito.when(conn.isOpen()).thenReturn(true); + Mockito.when(conn.getSocket()).thenReturn(socket); Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(443); Mockito.when(sslSocketFactory.createSocket(Mockito.any())).thenReturn(socket); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java index 9e6802ac1..8c0a98820 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java @@ -34,20 +34,18 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.io.ConnectionEndpoint; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.config.ConnectionConfig; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.SocketConfig; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.StrictConnPool; @@ -109,14 +107,12 @@ public class TestPoolingHttpClientConnectionManager { Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease(route, null, null)).thenReturn(future); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertNotSame(conn, conn1); - final CPoolProxy poolProxy = CPoolProxy.getProxy(conn1); - poolProxy.markRouteComplete(); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(endpoint1); + Assert.assertNotSame(conn, endpoint1); - mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 0, TimeUnit.MILLISECONDS); Mockito.verify(pool).release(entry, true); } @@ -138,12 +134,12 @@ public class TestPoolingHttpClientConnectionManager { Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease(route, null, null)).thenReturn(future); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertNotSame(conn, conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(endpoint1); + Assert.assertNotSame(conn, endpoint1); - mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, null, 0, TimeUnit.MILLISECONDS); Mockito.verify(pool).release(entry, false); } @@ -160,11 +156,11 @@ public class TestPoolingHttpClientConnectionManager { Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease(route, null, null)).thenReturn(future); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + final LeaseRequest connRequest1 = mgr.lease(route, null); connRequest1.get(1, TimeUnit.SECONDS); } - @Test(expected=ConnectionPoolTimeoutException.class) + @Test(expected=TimeoutException.class) public void testLeaseFutureTimeout() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); @@ -173,7 +169,7 @@ public class TestPoolingHttpClientConnectionManager { Mockito.when(future.get(1, TimeUnit.SECONDS)).thenThrow(new TimeoutException()); Mockito.when(pool.lease(route, null, null)).thenReturn(future); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + final LeaseRequest connRequest1 = mgr.lease(route, null); connRequest1.get(1, TimeUnit.SECONDS); } @@ -190,14 +186,12 @@ public class TestPoolingHttpClientConnectionManager { Mockito.when(pool.lease(route, null, null)).thenReturn(future); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertTrue(conn1.isOpen()); - final CPoolProxy poolProxy = CPoolProxy.getProxy(conn1); - poolProxy.markRouteComplete(); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(endpoint1); + Assert.assertTrue(endpoint1.isConnected()); - mgr.releaseConnection(conn1, "some state", 10, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, "some state", 10, TimeUnit.MILLISECONDS); Mockito.verify(pool).release(entry, true); Assert.assertEquals("some state", entry.getState()); @@ -216,14 +210,12 @@ public class TestPoolingHttpClientConnectionManager { Mockito.when(pool.lease(route, null, null)).thenReturn(future); Mockito.when(conn.isOpen()).thenReturn(Boolean.FALSE); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - Assert.assertFalse(conn1.isOpen()); - final CPoolProxy poolProxy = CPoolProxy.getProxy(conn1); - poolProxy.markRouteComplete(); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(endpoint1); + Assert.assertFalse(endpoint1.isConnected()); - mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); + mgr.release(endpoint1, "some state", 0, TimeUnit.MILLISECONDS); Mockito.verify(pool).release(entry, false); Assert.assertEquals(null, entry.getState()); @@ -240,16 +232,14 @@ public class TestPoolingHttpClientConnectionManager { entry.assignConnection(conn); Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); - Mockito.when(conn.isOpen()).thenReturn(true); + Mockito.when(conn.isOpen()).thenReturn(false); Mockito.when(future.isCancelled()).thenReturn(false); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease(route, null, null)).thenReturn(future); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); - final CPoolProxy poolProxy = CPoolProxy.getProxy(conn1); - poolProxy.markRouteComplete(); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(endpoint1); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); @@ -268,7 +258,7 @@ public class TestPoolingHttpClientConnectionManager { Mockito.any(), Mockito.any())).thenReturn(socket); - mgr.connect(conn1, route, 123, context); + mgr.connect(endpoint1, 123, TimeUnit.MILLISECONDS, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); @@ -276,8 +266,6 @@ public class TestPoolingHttpClientConnectionManager { Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(123, socket, target, new InetSocketAddress(remote, 8443), new InetSocketAddress(local, 0), context); - - mgr.routeComplete(conn1, route, context); } @Test @@ -292,24 +280,22 @@ public class TestPoolingHttpClientConnectionManager { entry.assignConnection(conn); Mockito.when(future.isCancelled()).thenReturn(Boolean.FALSE); - Mockito.when(conn.isOpen()).thenReturn(true); + Mockito.when(conn.isOpen()).thenReturn(false); Mockito.when(future.isCancelled()).thenReturn(false); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease(route, null, null)).thenReturn(future); - final ConnectionRequest connRequest1 = mgr.requestConnection(route, null); - final HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); - Assert.assertNotNull(conn1); + final LeaseRequest connRequest1 = mgr.lease(route, null); + final ConnectionEndpoint endpoint1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(endpoint1); final ConnectionSocketFactory plainsf = Mockito.mock(ConnectionSocketFactory.class); final LayeredConnectionSocketFactory sslsf = Mockito.mock(LayeredConnectionSocketFactory.class); final Socket mockSock = Mockito.mock(Socket.class); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); - final ConnectionConfig cconfig = ConnectionConfig.custom().build(); mgr.setDefaultSocketConfig(sconfig); - mgr.setDefaultConnectionConfig(cconfig); Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote}); Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080); @@ -325,7 +311,7 @@ public class TestPoolingHttpClientConnectionManager { Mockito.any(), Mockito.any())).thenReturn(mockSock); - mgr.connect(conn1, route, 123, context); + mgr.connect(endpoint1, 123, TimeUnit.MILLISECONDS, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy); @@ -334,15 +320,14 @@ public class TestPoolingHttpClientConnectionManager { new InetSocketAddress(remote, 8080), new InetSocketAddress(local, 0), context); + Mockito.when(conn.isOpen()).thenReturn(true); Mockito.when(conn.getSocket()).thenReturn(mockSock); - mgr.upgrade(conn1, route, context); + mgr.upgrade(endpoint1, context); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); Mockito.verify(sslsf, Mockito.times(1)).createLayeredSocket( mockSock, "somehost", 8443, context); - - mgr.routeComplete(conn1, route, context); } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultProxyRoutePlanner.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultProxyRoutePlanner.java index 14c7ee1c5..164440e46 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultProxyRoutePlanner.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultProxyRoutePlanner.java @@ -61,10 +61,9 @@ public class TestDefaultProxyRoutePlanner { @Test public void testDefaultProxyDirect() throws Exception { final HttpHost target = new HttpHost("somehost", 80, "http"); - final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(target, route.getTargetHost()); Assert.assertEquals(defaultProxy, route.getProxyHost()); @@ -80,7 +79,7 @@ public class TestDefaultProxyRoutePlanner { final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom().setProxy(proxy).build()); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(target, route.getTargetHost()); Assert.assertEquals(proxy, route.getProxyHost()); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultRoutePlanner.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultRoutePlanner.java index d52e2bd44..0ca9166c3 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultRoutePlanner.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestDefaultRoutePlanner.java @@ -27,6 +27,8 @@ package org.apache.hc.client5.http.impl.routing; +import java.net.URI; + import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.RequestConfig; @@ -37,6 +39,8 @@ import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.net.URIAuthority; +import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -60,10 +64,9 @@ public class TestDefaultRoutePlanner { @Test public void testDirect() throws Exception { final HttpHost target = new HttpHost("somehost", 80, "http"); - final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(target, route.getTargetHost()); Assert.assertEquals(1, route.getHopCount()); @@ -75,10 +78,9 @@ public class TestDefaultRoutePlanner { public void testDirectDefaultPort() throws Exception { final HttpHost target = new HttpHost("somehost", -1, "https"); Mockito.when(schemePortResolver.resolve(target)).thenReturn(443); - final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(new HttpHost("somehost", 443, "https"), route.getTargetHost()); Assert.assertEquals(1, route.getHopCount()); @@ -89,11 +91,10 @@ public class TestDefaultRoutePlanner { public void testViaProxy() throws Exception { final HttpHost target = new HttpHost("somehost", 80, "http"); final HttpHost proxy = new HttpHost("proxy", 8080); - final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom().setProxy(proxy).build()); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(target, route.getTargetHost()); Assert.assertEquals(proxy, route.getProxyHost()); @@ -104,10 +105,28 @@ public class TestDefaultRoutePlanner { @Test(expected= ProtocolException.class) public void testNullTarget() throws Exception { - final HttpRequest request = new BasicHttpRequest("GET", "/"); - final HttpContext context = new BasicHttpContext(); - routePlanner.determineRoute(null, request, context); + routePlanner.determineRoute(null, context); + } + + @Test + public void testDetermineHost() throws Exception { + final HttpContext context = new BasicHttpContext(); + final HttpRequest request1 = new BasicHttpRequest("GET", "/"); + final HttpHost host1 = routePlanner.determineTargetHost(request1, context); + Assert.assertThat(host1, CoreMatchers.nullValue()); + + final HttpRequest request2 = new BasicHttpRequest("GET", new URI("https://somehost:8443/")); + final HttpHost host2 = routePlanner.determineTargetHost(request2, context); + Assert.assertThat(host2, CoreMatchers.equalTo(new HttpHost("somehost", 8443, "https"))); + } + + @Test(expected = ProtocolException.class) + public void testDetermineHostMissingScheme() throws Exception { + final HttpContext context = new BasicHttpContext(); + final HttpRequest request1 = new BasicHttpRequest("GET", "/"); + request1.setAuthority(new URIAuthority("host")); + routePlanner.determineTargetHost(request1, context); } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestSystemDefaultRoutePlanner.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestSystemDefaultRoutePlanner.java index 7d6ef151f..14d118e1c 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestSystemDefaultRoutePlanner.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/TestSystemDefaultRoutePlanner.java @@ -38,8 +38,6 @@ import java.util.List; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpRequest; -import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.Assert; @@ -68,10 +66,9 @@ public class TestSystemDefaultRoutePlanner { @Test public void testDirect() throws Exception { final HttpHost target = new HttpHost("somehost", 80, "http"); - final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(target, route.getTargetHost()); Assert.assertEquals(1, route.getHopCount()); @@ -83,10 +80,9 @@ public class TestSystemDefaultRoutePlanner { public void testDirectDefaultPort() throws Exception { final HttpHost target = new HttpHost("somehost", -1, "https"); Mockito.when(schemePortResolver.resolve(target)).thenReturn(443); - final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(new HttpHost("somehost", 443, "https"), route.getTargetHost()); Assert.assertEquals(1, route.getHopCount()); @@ -109,11 +105,9 @@ public class TestSystemDefaultRoutePlanner { Mockito.when(proxySelector.select(new URI("http://somehost:80"))).thenReturn(proxies); final HttpHost target = new HttpHost("somehost", 80, "http"); - final HttpRequest request = - new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); - final HttpRoute route = routePlanner.determineRoute(target, request, context); + final HttpRoute route = routePlanner.determineRoute(target, context); Assert.assertEquals(target, route.getTargetHost()); Assert.assertEquals(2, route.getHopCount()); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestDefaultBackoffStrategy.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestDefaultBackoffStrategy.java index 7a6fff712..6e314656f 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestDefaultBackoffStrategy.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestDefaultBackoffStrategy.java @@ -32,8 +32,7 @@ import static org.junit.Assert.assertTrue; import java.net.ConnectException; import java.net.SocketTimeoutException; -import org.apache.hc.client5.http.ConnectionPoolTimeoutException; -import org.apache.hc.client5.http.sync.ConnectionBackoffStrategy; +import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; @@ -50,11 +49,6 @@ public class TestDefaultBackoffStrategy { impl = new DefaultBackoffStrategy(); } - @Test - public void isABackoffStrategy() { - assertTrue(impl instanceof ConnectionBackoffStrategy); - } - @Test public void backsOffForSocketTimeouts() { assertTrue(impl.shouldBackoff(new SocketTimeoutException())); @@ -67,7 +61,7 @@ public class TestDefaultBackoffStrategy { @Test public void doesNotBackOffForConnectionManagerTimeout() { - assertFalse(impl.shouldBackoff(new ConnectionPoolTimeoutException())); + assertFalse(impl.shouldBackoff(new ConnectionRequestTimeoutException())); } @Test diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestConnectionHolder.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestEndpointHolder.java similarity index 66% rename from httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestConnectionHolder.java rename to httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestEndpointHolder.java index 4694b6b7e..9542df151 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestConnectionHolder.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestEndpointHolder.java @@ -29,28 +29,32 @@ package org.apache.hc.client5.http.impl.sync; import java.io.IOException; import java.util.concurrent.TimeUnit; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.logging.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; @SuppressWarnings({"static-access"}) // test code -public class TestConnectionHolder { +public class TestEndpointHolder { + @Mock private Logger log; + @Mock private HttpClientConnectionManager mgr; - private HttpClientConnection conn; - private ConnectionHolder connHolder; + @Mock + private ConnectionEndpoint endpoint; + + private EndpointHolder connHolder; @Before public void setup() { - log = Mockito.mock(Logger.class); - mgr = Mockito.mock(HttpClientConnectionManager.class); - conn = Mockito.mock(HttpClientConnection.class); - connHolder = new ConnectionHolder(log, mgr, conn); + MockitoAnnotations.initMocks(this); + connHolder = new EndpointHolder(log, mgr, endpoint); } @Test @@ -59,14 +63,14 @@ public class TestConnectionHolder { Assert.assertTrue(connHolder.isReleased()); - Mockito.verify(conn).shutdown(); - Mockito.verify(mgr).releaseConnection(conn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint).shutdown(); + Mockito.verify(mgr).release(endpoint, null, 0, TimeUnit.MILLISECONDS); connHolder.abortConnection(); - Mockito.verify(conn, Mockito.times(1)).shutdown(); - Mockito.verify(mgr, Mockito.times(1)).releaseConnection( - Mockito.any(), + Mockito.verify(endpoint, Mockito.times(1)).shutdown(); + Mockito.verify(mgr, Mockito.times(1)).release( + Mockito.any(), Mockito.anyObject(), Mockito.anyLong(), Mockito.any()); @@ -74,14 +78,14 @@ public class TestConnectionHolder { @Test public void testAbortConnectionIOError() throws Exception { - Mockito.doThrow(new IOException()).when(conn).shutdown(); + Mockito.doThrow(new IOException()).when(endpoint).shutdown(); connHolder.abortConnection(); Assert.assertTrue(connHolder.isReleased()); - Mockito.verify(conn).shutdown(); - Mockito.verify(mgr).releaseConnection(conn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint).shutdown(); + Mockito.verify(mgr).release(endpoint, null, 0, TimeUnit.MILLISECONDS); } @Test @@ -90,14 +94,14 @@ public class TestConnectionHolder { Assert.assertTrue(connHolder.isReleased()); - Mockito.verify(conn).shutdown(); - Mockito.verify(mgr).releaseConnection(conn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint).shutdown(); + Mockito.verify(mgr).release(endpoint, null, 0, TimeUnit.MILLISECONDS); Assert.assertFalse(connHolder.cancel()); - Mockito.verify(conn, Mockito.times(1)).shutdown(); - Mockito.verify(mgr, Mockito.times(1)).releaseConnection( - Mockito.any(), + Mockito.verify(endpoint, Mockito.times(1)).shutdown(); + Mockito.verify(mgr, Mockito.times(1)).release( + Mockito.any(), Mockito.anyObject(), Mockito.anyLong(), Mockito.any()); @@ -113,13 +117,13 @@ public class TestConnectionHolder { Assert.assertTrue(connHolder.isReleased()); - Mockito.verify(conn, Mockito.never()).close(); - Mockito.verify(mgr).releaseConnection(conn, "some state", 100, TimeUnit.SECONDS); + Mockito.verify(endpoint, Mockito.never()).close(); + Mockito.verify(mgr).release(endpoint, "some state", 100000, TimeUnit.MILLISECONDS); connHolder.releaseConnection(); - Mockito.verify(mgr, Mockito.times(1)).releaseConnection( - Mockito.any(), + Mockito.verify(mgr, Mockito.times(1)).release( + Mockito.any(), Mockito.anyObject(), Mockito.anyLong(), Mockito.any()); @@ -135,13 +139,13 @@ public class TestConnectionHolder { Assert.assertTrue(connHolder.isReleased()); - Mockito.verify(conn, Mockito.times(1)).close(); - Mockito.verify(mgr).releaseConnection(conn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint, Mockito.times(1)).close(); + Mockito.verify(mgr).release(endpoint, null, 0, TimeUnit.MILLISECONDS); connHolder.releaseConnection(); - Mockito.verify(mgr, Mockito.times(1)).releaseConnection( - Mockito.any(), + Mockito.verify(mgr, Mockito.times(1)).release( + Mockito.any(), Mockito.anyObject(), Mockito.anyLong(), Mockito.any()); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestIdleConnectionEvictor.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestIdleConnectionEvictor.java index 5341080ea..40a652f7c 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestIdleConnectionEvictor.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestIdleConnectionEvictor.java @@ -29,7 +29,7 @@ package org.apache.hc.client5.http.impl.sync; import java.util.concurrent.TimeUnit; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.core5.pool.ConnPoolControl; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; @@ -41,7 +41,7 @@ public class TestIdleConnectionEvictor { @Test public void testEvictExpiredAndIdle() throws Exception { - final HttpClientConnectionManager cm = Mockito.mock(HttpClientConnectionManager.class); + final ConnPoolControl cm = Mockito.mock(ConnPoolControl.class); final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm, 500, TimeUnit.MILLISECONDS, 3, TimeUnit.SECONDS); connectionEvictor.start(); @@ -60,7 +60,7 @@ public class TestIdleConnectionEvictor { @Test public void testEvictExpiredOnly() throws Exception { - final HttpClientConnectionManager cm = Mockito.mock(HttpClientConnectionManager.class); + final ConnPoolControl cm = Mockito.mock(ConnPoolControl.class); final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm, 500, TimeUnit.MILLISECONDS, 0, TimeUnit.SECONDS); connectionEvictor.start(); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java index fbdb01508..c80b2510c 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java @@ -36,7 +36,6 @@ import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecProvider; import org.apache.hc.client5.http.cookie.CookieStore; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.methods.HttpGet; import org.apache.hc.client5.http.methods.RoutedHttpRequest; import org.apache.hc.client5.http.protocol.ClientProtocolException; @@ -44,12 +43,10 @@ import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.config.Lookup; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentCaptor; import org.mockito.Mockito; /** @@ -59,7 +56,6 @@ import org.mockito.Mockito; public class TestInternalHttpClient { private ClientExecChain execChain; - private HttpClientConnectionManager connManager; private HttpRoutePlanner routePlanner; private Lookup cookieSpecRegistry; private Lookup authSchemeRegistry; @@ -75,7 +71,6 @@ public class TestInternalHttpClient { @Before public void setup() throws Exception { execChain = Mockito.mock(ClientExecChain.class); - connManager = Mockito.mock(HttpClientConnectionManager.class); routePlanner = Mockito.mock(HttpRoutePlanner.class); cookieSpecRegistry = Mockito.mock(Lookup.class); authSchemeRegistry = Mockito.mock(Lookup.class); @@ -85,7 +80,7 @@ public class TestInternalHttpClient { closeable1 = Mockito.mock(Closeable.class); closeable2 = Mockito.mock(Closeable.class); - client = new InternalHttpClient(execChain, connManager, routePlanner, + client = new InternalHttpClient(execChain, routePlanner, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, defaultConfig, Arrays.asList(closeable1, closeable2)); @@ -96,17 +91,12 @@ public class TestInternalHttpClient { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); - final ArgumentCaptor argcap = ArgumentCaptor.forClass(HttpRequest.class); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), - argcap.capture(), Mockito.any())).thenReturn(route); client.execute(httpget); - Assert.assertNotNull(argcap.getValue()); - Assert.assertSame(httpget, argcap.getValue()); - Mockito.verify(execChain).execute( Mockito.any(), Mockito.any(), @@ -120,7 +110,6 @@ public class TestInternalHttpClient { Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), - Mockito.any(), Mockito.any())).thenReturn(route); Mockito.when(execChain.execute( Mockito.any(), @@ -137,7 +126,6 @@ public class TestInternalHttpClient { Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), - Mockito.any(), Mockito.any())).thenReturn(route); final HttpClientContext context = HttpClientContext.create(); @@ -157,7 +145,6 @@ public class TestInternalHttpClient { Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), - Mockito.any(), Mockito.any())).thenReturn(route); final RequestConfig config = RequestConfig.custom().build(); @@ -176,7 +163,6 @@ public class TestInternalHttpClient { Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), - Mockito.any(), Mockito.any())).thenReturn(route); final HttpClientContext context = HttpClientContext.create(); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java index 8de4441d1..e543dce52 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java @@ -49,9 +49,10 @@ import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.NTLMScheme; -import org.apache.hc.client5.http.impl.io.ConnectionShutdownException; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.impl.ConnectionShutdownException; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.methods.HttpExecutionAware; import org.apache.hc.client5.http.methods.HttpGet; import org.apache.hc.client5.http.methods.HttpPost; @@ -109,9 +110,9 @@ public class TestMainClientExec { @Mock private HttpExecutionAware execAware; @Mock - private ConnectionRequest connRequest; + private LeaseRequest connRequest; @Mock - private HttpClientConnection managedConn; + private ConnectionEndpoint endpoint; private MainClientExec mainClientExec; private HttpHost target; @@ -125,10 +126,10 @@ public class TestMainClientExec { target = new HttpHost("foo", 80); proxy = new HttpHost("bar", 8888); - Mockito.when(connManager.requestConnection( + Mockito.when(connManager.lease( Mockito.any(), Mockito.any())).thenReturn(connRequest); Mockito.when(connRequest.get( - Mockito.anyLong(), Mockito.any())).thenReturn(managedConn); + Mockito.anyLong(), Mockito.any())).thenReturn(endpoint); } @Test @@ -143,24 +144,22 @@ public class TestMainClientExec { context.setRequestConfig(config); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest); Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.any()); - Mockito.verify(connManager).connect(managedConn, route, 123, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); - Mockito.verify(managedConn).setSocketTimeout(234); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(managedConn, Mockito.times(1)).close(); - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(connManager).connect(endpoint, 123, TimeUnit.MILLISECONDS, context); + Mockito.verify(endpoint).setSocketTimeout(234); + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(endpoint, Mockito.times(1)).close(); + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); - Assert.assertSame(managedConn, context.getConnection()); Assert.assertNull(context.getUserToken()); Assert.assertNotNull(finalResponse); Assert.assertTrue(finalResponse instanceof CloseableHttpResponse); @@ -177,11 +176,10 @@ public class TestMainClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -192,11 +190,11 @@ public class TestMainClientExec { Mockito.any())).thenReturn(678L); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS); - Mockito.verify(managedConn, Mockito.never()).close(); + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(connManager).release(endpoint, null, 678L, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint, Mockito.never()).close(); Assert.assertNotNull(finalResponse); Assert.assertTrue(finalResponse instanceof CloseableHttpResponse); @@ -213,11 +211,10 @@ public class TestMainClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -228,11 +225,11 @@ public class TestMainClientExec { Mockito.any())).thenReturn("this and that"); mainClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(connManager).releaseConnection(managedConn, "this and that", 0, TimeUnit.MILLISECONDS); - Mockito.verify(managedConn, Mockito.never()).close(); + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(connManager).release(endpoint, "this and that", 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint, Mockito.never()).close(); Assert.assertEquals("this and that", context.getUserToken()); } @@ -252,11 +249,10 @@ public class TestMainClientExec { .setStream(new ByteArrayInputStream(new byte[]{})) .build()); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -264,23 +260,23 @@ public class TestMainClientExec { Mockito.any())).thenReturn(Boolean.FALSE); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(connManager, Mockito.never()).releaseConnection( - Mockito.same(managedConn), + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(connManager, Mockito.never()).release( + Mockito.same(endpoint), Mockito.any(), Mockito.anyInt(), Mockito.any()); - Mockito.verify(managedConn, Mockito.never()).close(); + Mockito.verify(endpoint, Mockito.never()).close(); Assert.assertNotNull(finalResponse); Assert.assertTrue(finalResponse instanceof CloseableHttpResponse); finalResponse.close(); - Mockito.verify(connManager, Mockito.times(1)).releaseConnection( - managedConn, null, 0, TimeUnit.MILLISECONDS); - Mockito.verify(managedConn, Mockito.times(1)).close(); + Mockito.verify(connManager, Mockito.times(1)).release( + endpoint, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint, Mockito.times(1)).close(); } @Test @@ -291,14 +287,14 @@ public class TestMainClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); context.setRequestConfig(config); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(true); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(true); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); mainClientExec.execute(request, context, execAware); - Mockito.verify(managedConn).setSocketTimeout(3000); + Mockito.verify(endpoint).setSocketTimeout(3000); } @Test @@ -307,14 +303,14 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); mainClientExec.execute(request, context, execAware); - Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt()); + Mockito.verify(endpoint, Mockito.never()).setSocketTimeout(Mockito.anyInt()); } @Test(expected=RequestAbortedException.class) @@ -323,7 +319,7 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE); Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE); try { mainClientExec.execute(request, context, execAware); @@ -343,7 +339,7 @@ public class TestMainClientExec { context.setRequestConfig(config); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE); Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.TRUE); try { mainClientExec.execute(request, context, execAware); @@ -351,9 +347,9 @@ public class TestMainClientExec { Mockito.verify(connRequest, Mockito.times(1)).get(345, TimeUnit.MILLISECONDS); Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.any()); Mockito.verify(connManager, Mockito.never()).connect( - Mockito.same(managedConn), - Mockito.any(), + Mockito.same(endpoint), Mockito.anyInt(), + Mockito.any(), Mockito.any()); throw ex; } @@ -370,13 +366,13 @@ public class TestMainClientExec { context.setRequestConfig(config); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE); Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE); try { mainClientExec.execute(request, context, execAware); } catch (final IOException ex) { Mockito.verify(connRequest, Mockito.times(1)).get(345, TimeUnit.MILLISECONDS); - Mockito.verify(connManager, Mockito.times(1)).connect(managedConn, route, 567, context); + Mockito.verify(connManager, Mockito.times(1)).connect(endpoint, 567, TimeUnit.MILLISECONDS, context); Mockito.verify(requestExecutor, Mockito.never()).execute( Mockito.same(request), Mockito.any(), @@ -417,11 +413,10 @@ public class TestMainClientExec { credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray())); context.setCredentialsProvider(credentialsProvider); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response1, response2); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -434,7 +429,7 @@ public class TestMainClientExec { final ClassicHttpResponse finalResponse = mainClientExec.execute( request, context, execAware); - Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context); + Mockito.verify(endpoint, Mockito.times(2)).execute(request, requestExecutor, context); Mockito.verify(instream1).close(); Mockito.verify(instream2, Mockito.never()).close(); @@ -469,11 +464,10 @@ public class TestMainClientExec { credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray())); context.setCredentialsProvider(credentialsProvider); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response1, response2); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -487,8 +481,8 @@ public class TestMainClientExec { final ClassicHttpResponse finalResponse = mainClientExec.execute( request, context, execAware); - Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context); - Mockito.verify(managedConn).close(); + Mockito.verify(endpoint, Mockito.times(2)).execute(request, requestExecutor, context); + Mockito.verify(endpoint).close(); Mockito.verify(instream2, Mockito.never()).close(); Assert.assertNotNull(finalResponse); @@ -518,11 +512,10 @@ public class TestMainClientExec { credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray())); context.setCredentialsProvider(credentialsProvider); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenAnswer(new Answer() { @Override @@ -553,9 +546,9 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new ConnectionShutdownException()); mainClientExec.execute(request, context, execAware); @@ -567,15 +560,15 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new RuntimeException("Ka-boom")); try { mainClientExec.execute(request, context, execAware); } catch (final Exception ex) { - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); throw ex; } @@ -587,15 +580,15 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new HttpException("Ka-boom")); try { mainClientExec.execute(request, context, execAware); } catch (final Exception ex) { - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); throw ex; } @@ -607,15 +600,15 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new IOException("Ka-boom")); try { mainClientExec.execute(request, context, execAware); } catch (final Exception ex) { - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); throw ex; } @@ -631,12 +624,11 @@ public class TestMainClientExec { context.setRequestConfig(config); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); - Mockito.verify(connManager).connect(managedConn, route, 567, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(connManager).connect(endpoint, 567, TimeUnit.MILLISECONDS, context); } @Test @@ -649,12 +641,11 @@ public class TestMainClientExec { context.setRequestConfig(config); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); - Mockito.verify(connManager).connect(managedConn, route, 567, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(connManager).connect(endpoint, 567, TimeUnit.MILLISECONDS, context); } @Test @@ -668,20 +659,19 @@ public class TestMainClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.any(), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); - Mockito.verify(connManager).connect(managedConn, route, 321, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(connManager).connect(endpoint, 321, TimeUnit.MILLISECONDS, context); final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class); - Mockito.verify(requestExecutor).execute( + Mockito.verify(endpoint).execute( reqCaptor.capture(), - Mockito.same(managedConn), + Mockito.same(requestExecutor), Mockito.same(context)); final HttpRequest connect = reqCaptor.getValue(); Assert.assertNotNull(connect); @@ -697,13 +687,13 @@ public class TestMainClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(101, "Lost"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.any(), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); } @Test(expected = HttpException.class) @@ -714,20 +704,18 @@ public class TestMainClientExec { final ClassicHttpResponse response = new BasicClassicHttpResponse(500, "Boom"); response.setEntity(new StringEntity("Ka-boom")); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.any(), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); try { - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); } catch (final TunnelRefusedException ex) { final ClassicHttpResponse r = ex.getResponse(); Assert.assertEquals("Ka-boom", EntityUtils.toString(r.getEntity())); - - Mockito.verify(managedConn).close(); - + Mockito.verify(endpoint).close(); throw ex; } } @@ -753,14 +741,14 @@ public class TestMainClientExec { credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("user", "pass".toCharArray())); context.setCredentialsProvider(credentialsProvider); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.any(), Mockito.any())).thenReturn(Boolean.TRUE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.execute( Mockito.any(), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response1, response2); Mockito.when(proxyAuthStrategy.select( @@ -768,10 +756,9 @@ public class TestMainClientExec { Mockito.>any(), Mockito.any())).thenReturn(Collections.singletonList(new BasicScheme())); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); - Mockito.verify(connManager).connect(managedConn, route, 567, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(connManager).connect(endpoint, 567, TimeUnit.MILLISECONDS, context); Mockito.verify(instream1).close(); } @@ -796,14 +783,14 @@ public class TestMainClientExec { credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("user", "pass".toCharArray())); context.setCredentialsProvider(credentialsProvider); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.any(), Mockito.any())).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.execute( Mockito.any(), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response1, response2); Mockito.when(proxyAuthStrategy.select( @@ -811,12 +798,11 @@ public class TestMainClientExec { Mockito.>any(), Mockito.any())).thenReturn(Collections.singletonList(new BasicScheme())); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); - Mockito.verify(connManager).connect(managedConn, route, 567, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(connManager).connect(endpoint, 567, TimeUnit.MILLISECONDS, context); Mockito.verify(instream1, Mockito.never()).close(); - Mockito.verify(managedConn).close(); + Mockito.verify(endpoint).close(); } @Test(expected = HttpException.class) @@ -828,9 +814,9 @@ public class TestMainClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); - mainClientExec.establishRoute(managedConn, route, request, context); + mainClientExec.establishRoute(endpoint, route, request, context); } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java index 81b6b5c55..5b2529900 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java @@ -36,9 +36,10 @@ import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.EntityBuilder; -import org.apache.hc.client5.http.impl.io.ConnectionShutdownException; -import org.apache.hc.client5.http.io.ConnectionRequest; +import org.apache.hc.client5.http.impl.ConnectionShutdownException; +import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.methods.HttpExecutionAware; import org.apache.hc.client5.http.methods.HttpGet; import org.apache.hc.client5.http.methods.RoutedHttpRequest; @@ -50,7 +51,6 @@ import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; -import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.Assert; import org.junit.Before; @@ -74,9 +74,9 @@ public class TestMinimalClientExec { @Mock private HttpExecutionAware execAware; @Mock - private ConnectionRequest connRequest; + private LeaseRequest connRequest; @Mock - private HttpClientConnection managedConn; + private ConnectionEndpoint endpoint; private MinimalClientExec minimalClientExec; private HttpHost target; @@ -88,10 +88,10 @@ public class TestMinimalClientExec { requestExecutor, connManager, reuseStrategy, keepAliveStrategy); target = new HttpHost("foo", 80); - Mockito.when(connManager.requestConnection( + Mockito.when(connManager.lease( Mockito.any(), Mockito.any())).thenReturn(connRequest); Mockito.when(connRequest.get( - Mockito.anyLong(), Mockito.any())).thenReturn(managedConn); + Mockito.anyLong(), Mockito.any())).thenReturn(endpoint); } @Test @@ -106,24 +106,22 @@ public class TestMinimalClientExec { context.setRequestConfig(config); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest); Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.any()); - Mockito.verify(connManager).connect(managedConn, route, 123, context); - Mockito.verify(connManager).routeComplete(managedConn, route, context); - Mockito.verify(managedConn).setSocketTimeout(234); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(managedConn, Mockito.times(1)).close(); - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(connManager).connect(endpoint, 123, TimeUnit.MILLISECONDS, context); + Mockito.verify(endpoint).setSocketTimeout(234); + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(endpoint, Mockito.times(1)).close(); + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); - Assert.assertSame(managedConn, context.getConnection()); Assert.assertNotNull(finalResponse); Assert.assertTrue(finalResponse instanceof CloseableHttpResponse); } @@ -141,11 +139,10 @@ public class TestMinimalClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -156,11 +153,11 @@ public class TestMinimalClientExec { Mockito.any())).thenReturn(678L); final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS); - Mockito.verify(managedConn, Mockito.never()).close(); + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(connManager).release(endpoint, null, 678L, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint, Mockito.never()).close(); Assert.assertNotNull(finalResponse); Assert.assertTrue(finalResponse instanceof CloseableHttpResponse); @@ -183,11 +180,10 @@ public class TestMinimalClientExec { .setStream(new ByteArrayInputStream(new byte[]{})) .build()); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -195,23 +191,23 @@ public class TestMinimalClientExec { Mockito.any())).thenReturn(Boolean.FALSE); final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, context, execAware); - Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connManager).lease(route, null); Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); - Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); - Mockito.verify(connManager, Mockito.never()).releaseConnection( - Mockito.same(managedConn), + Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context); + Mockito.verify(connManager, Mockito.never()).release( + Mockito.same(endpoint), Mockito.any(), Mockito.anyInt(), Mockito.any()); - Mockito.verify(managedConn, Mockito.never()).close(); + Mockito.verify(endpoint, Mockito.never()).close(); Assert.assertNotNull(finalResponse); Assert.assertTrue(finalResponse instanceof CloseableHttpResponse); finalResponse.close(); - Mockito.verify(connManager, Mockito.times(1)).releaseConnection( - managedConn, null, 0, TimeUnit.MILLISECONDS); - Mockito.verify(managedConn, Mockito.times(1)).close(); + Mockito.verify(connManager, Mockito.times(1)).release( + endpoint, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(endpoint, Mockito.times(1)).close(); } @Test @@ -222,14 +218,14 @@ public class TestMinimalClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); context.setRequestConfig(config); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(true); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(true); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); minimalClientExec.execute(request, context, execAware); - Mockito.verify(managedConn).setSocketTimeout(3000); + Mockito.verify(endpoint).setSocketTimeout(3000); } @Test @@ -238,14 +234,14 @@ public class TestMinimalClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(requestExecutor.execute( + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE); + Mockito.when(endpoint.execute( Mockito.same(request), - Mockito.any(), + Mockito.any(), Mockito.any())).thenReturn(response); minimalClientExec.execute(request, context, execAware); - Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt()); + Mockito.verify(endpoint, Mockito.never()).setSocketTimeout(Mockito.anyInt()); } @Test(expected=RequestAbortedException.class) @@ -254,7 +250,7 @@ public class TestMinimalClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE); Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE); try { minimalClientExec.execute(request, context, execAware); @@ -281,9 +277,9 @@ public class TestMinimalClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new ConnectionShutdownException()); minimalClientExec.execute(request, context, execAware); @@ -295,16 +291,15 @@ public class TestMinimalClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new RuntimeException("Ka-boom")); try { minimalClientExec.execute(request, context, execAware); } catch (final Exception ex) { - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); - + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); throw ex; } } @@ -315,16 +310,15 @@ public class TestMinimalClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new HttpException("Ka-boom")); try { minimalClientExec.execute(request, context, execAware); } catch (final Exception ex) { - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); - + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); throw ex; } } @@ -335,16 +329,15 @@ public class TestMinimalClientExec { final HttpClientContext context = new HttpClientContext(); final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new IOException("Ka-boom")); try { minimalClientExec.execute(request, context, execAware); } catch (final Exception ex) { - Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); - + Mockito.verify(connManager).release(endpoint, null, 0, TimeUnit.MILLISECONDS); throw ex; } } @@ -356,15 +349,15 @@ public class TestMinimalClientExec { final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); - Mockito.when(requestExecutor.execute( - Mockito.any(), - Mockito.any(), + Mockito.when(endpoint.execute( + Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); minimalClientExec.execute(request, context, execAware); final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class); - Mockito.verify(requestExecutor).execute(reqCaptor.capture(), Mockito.any(), Mockito.any()); + Mockito.verify(endpoint).execute(reqCaptor.capture(), Mockito.any(), Mockito.any()); Assert.assertEquals("/test", reqCaptor.getValue().getRequestUri()); } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestRedirectExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestRedirectExec.java index 4ee65b567..541d8fe06 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestRedirectExec.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestRedirectExec.java @@ -121,7 +121,6 @@ public class TestRedirectExec { Mockito.any())).thenReturn(redirect); Mockito.when(httpRoutePlanner.determineRoute( Mockito.eq(target), - Mockito.any(), Mockito.any())).thenReturn(route); redirectExec.execute(request, context, execAware); @@ -173,7 +172,6 @@ public class TestRedirectExec { Mockito.any())).thenReturn(redirect); Mockito.when(httpRoutePlanner.determineRoute( Mockito.eq(target), - Mockito.any(), Mockito.any())).thenReturn(route); redirectExec.execute(request, context, execAware); @@ -207,7 +205,6 @@ public class TestRedirectExec { Mockito.any())).thenReturn(redirect); Mockito.when(httpRoutePlanner.determineRoute( Mockito.eq(target), - Mockito.any(), Mockito.any())).thenReturn(route); redirectExec.execute(request, context, execAware); @@ -253,11 +250,9 @@ public class TestRedirectExec { Mockito.any())).thenReturn(redirect); Mockito.when(httpRoutePlanner.determineRoute( Mockito.eq(target), - Mockito.any(), Mockito.any())).thenReturn(new HttpRoute(target)); Mockito.when(httpRoutePlanner.determineRoute( Mockito.eq(otherHost), - Mockito.any(), Mockito.any())).thenReturn(new HttpRoute(otherHost)); redirectExec.execute(request, context, execAware); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestResponseEntityWrapper.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestResponseEntityWrapper.java index 83a738b66..27afaae54 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestResponseEntityWrapper.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestResponseEntityWrapper.java @@ -43,7 +43,7 @@ public class TestResponseEntityWrapper { private InputStream instream; private HttpEntity entity; - private ConnectionHolder connHolder; + private EndpointHolder endpointHolder; private ResponseEntityProxy wrapper; @Before @@ -51,83 +51,83 @@ public class TestResponseEntityWrapper { instream = Mockito.mock(InputStream.class); entity = Mockito.mock(HttpEntity.class); Mockito.when(entity.getContent()).thenReturn(instream); - connHolder = Mockito.mock(ConnectionHolder.class); - wrapper = new ResponseEntityProxy(entity, connHolder); + endpointHolder = Mockito.mock(EndpointHolder.class); + wrapper = new ResponseEntityProxy(entity, endpointHolder); } @Test public void testReusableEntityStreamClosed() throws Exception { Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); EntityUtils.consume(wrapper); Mockito.verify(instream, Mockito.times(1)).close(); - Mockito.verify(connHolder).releaseConnection(); + Mockito.verify(endpointHolder).releaseConnection(); } @Test public void testReusableEntityStreamClosedIOError() throws Exception { Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); Mockito.doThrow(new IOException()).when(instream).close(); try { EntityUtils.consume(wrapper); Assert.fail("IOException expected"); } catch (final IOException ex) { } - Mockito.verify(connHolder).abortConnection(); + Mockito.verify(endpointHolder).abortConnection(); } @Test public void testEntityStreamClosedIOErrorAlreadyReleased() throws Exception { Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); - Mockito.when(connHolder.isReleased()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReleased()).thenReturn(true); Mockito.doThrow(new SocketException()).when(instream).close(); EntityUtils.consume(wrapper); - Mockito.verify(connHolder).close(); + Mockito.verify(endpointHolder).close(); } @Test public void testReusableEntityWriteTo() throws Exception { final OutputStream outstream = Mockito.mock(OutputStream.class); Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); wrapper.writeTo(outstream); - Mockito.verify(connHolder).releaseConnection(); + Mockito.verify(endpointHolder).releaseConnection(); } @Test public void testReusableEntityWriteToIOError() throws Exception { final OutputStream outstream = Mockito.mock(OutputStream.class); Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); Mockito.doThrow(new IOException()).when(entity).writeTo(outstream); try { wrapper.writeTo(outstream); Assert.fail("IOException expected"); } catch (final IOException ex) { } - Mockito.verify(connHolder, Mockito.never()).releaseConnection(); - Mockito.verify(connHolder).abortConnection(); + Mockito.verify(endpointHolder, Mockito.never()).releaseConnection(); + Mockito.verify(endpointHolder).abortConnection(); } @Test public void testReusableEntityEndOfStream() throws Exception { Mockito.when(instream.read()).thenReturn(-1); Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); final InputStream content = wrapper.getContent(); Assert.assertEquals(-1, content.read()); Mockito.verify(instream).close(); - Mockito.verify(connHolder).releaseConnection(); + Mockito.verify(endpointHolder).releaseConnection(); } @Test public void testReusableEntityEndOfStreamIOError() throws Exception { Mockito.when(instream.read()).thenReturn(-1); Mockito.when(entity.isStreaming()).thenReturn(true); - Mockito.when(connHolder.isReusable()).thenReturn(true); + Mockito.when(endpointHolder.isReusable()).thenReturn(true); Mockito.doThrow(new IOException()).when(instream).close(); final InputStream content = wrapper.getContent(); try { @@ -135,7 +135,7 @@ public class TestResponseEntityWrapper { Assert.fail("IOException expected"); } catch (final IOException ex) { } - Mockito.verify(connHolder).abortConnection(); + Mockito.verify(endpointHolder).abortConnection(); } }