Redesign of classic (blocking) connection management APIs

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1784138 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2017-02-23 14:32:24 +00:00
parent 7637761238
commit 564537aebc
62 changed files with 1523 additions and 2760 deletions

View File

@ -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()) {

View File

@ -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);
}

View File

@ -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<ClassicHttpRequest> 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<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
H1Config.DEFAULT, requestWriterFactory, responseParserFactory);
HttpConnectionFactory<ManagedHttpClientConnection> 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);

View File

@ -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
* <http://www.apache.org/>.
*
*/
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<HttpRoute> 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();
}
}

View File

@ -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

View File

@ -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
* <http://www.apache.org/>.
*
*/
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;
}
}

View File

@ -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<HttpRoute, ManagedHttpClientConnection> connFactory;
private final HttpConnectionFactory<ManagedHttpClientConnection> 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<ConnectionSocketFactory> getDefaultRegistry() {
return RegistryBuilder.<ConnectionSocketFactory>create()
@ -106,14 +110,12 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
}
public BasicHttpClientConnectionManager(
final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final SchemePortResolver schemePortResolver,
final DnsResolver dnsResolver) {
this(
new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver),
connFactory
);
final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> connFactory) {
final HttpConnectionFactory<ManagedHttpClientConnection> 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<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) {
final HttpConnectionFactory<ManagedHttpClientConnection> 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<ManagedHttpClientConnection> 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);
}
}
}

View File

@ -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
* <http://www.apache.org/>.
*
*/
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<HttpRoute, ManagedHttpClientConnection> poolEntry;
private volatile boolean routeComplete;
CPoolProxy(final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry) {
super();
this.poolEntry = entry;
}
PoolEntry<HttpRoute, ManagedHttpClientConnection> getPoolEntry() {
return this.poolEntry;
}
boolean isDetached() {
return this.poolEntry == null;
}
PoolEntry<HttpRoute, ManagedHttpClientConnection> detach() {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> local = this.poolEntry;
this.poolEntry = null;
return local;
}
public void markRouteComplete() {
this.routeComplete = true;
}
public boolean isRouteComplete() {
return this.routeComplete;
}
ManagedHttpClientConnection getConnection() {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> local = this.poolEntry;
if (local != null) {
final ManagedHttpClientConnection conn = local.getConnection();
if (conn != null) {
conn.close();
}
}
}
@Override
public void shutdown() throws IOException {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> local = this.poolEntry;
if (local != null) {
final ManagedHttpClientConnection conn = local.getConnection();
if (conn != null) {
conn.close();
}
}
}
@Override
public boolean isOpen() {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> 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);
}
}

View File

@ -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);

View File

@ -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<String, Object> 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());
}
}
}
}

View File

@ -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
* <http://www.apache.org/>.
*
*/
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<ClassicHttpRequest> requestWriterFactory,
final HttpMessageParserFactory<ClassicHttpResponse> 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());
}
}
}
}

View File

@ -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<HttpRoute, ManagedHttpClientConnection> {
public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory<ManagedHttpClientConnection> {
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<ClassicHttpRequest> requestWriterFactory;
private final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory;
private final ContentLengthStrategy incomingContentStrategy;
private final ContentLengthStrategy outgoingContentStrategy;
/**
* @since 4.4
*/
public ManagedHttpClientConnectionFactory(
final H1Config h1Config,
final ConnectionConfig connectionConfig,
final HttpMessageWriterFactory<ClassicHttpRequest> requestWriterFactory,
final HttpMessageParserFactory<ClassicHttpResponse> 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<ClassicHttpRequest> requestWriterFactory,
final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory) {
this(h1Config, requestWriterFactory, responseParserFactory, null, null);
this(h1Config, connectionConfig, requestWriterFactory, responseParserFactory, null, null);
}
public ManagedHttpClientConnectionFactory(
final H1Config h1Config,
final ConnectionConfig connectionConfig,
final HttpMessageParserFactory<ClassicHttpResponse> 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;
}
}

View File

@ -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.
* <p>
* {@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.
* </p>
* on a per route basis and in total. Connection limits, however, can be adjusted
* using {@link ConnPoolControl} methods.
* <p>
* 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.
* </p>
* <p>
* 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)}
* </p>
* The default timeout is set to 5000ms.
*
* @since 4.3
*/
@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
public class PoolingHttpClientConnectionManager
implements HttpClientConnectionManager, ConnPoolControl<HttpRoute>, Closeable {
implements HttpClientConnectionManager, ConnPoolControl<HttpRoute> {
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<HttpRoute, ManagedHttpClientConnection> pool;
private final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory;
private final HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
private final HttpClientConnectionOperator connectionOperator;
private final AtomicBoolean isShutDown;
private final AtomicBoolean closed;
private volatile SocketConfig defaultSocketConfig;
private volatile int validateAfterInactivity;
private static Registry<ConnectionSocketFactory> 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<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) {
final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
this(socketFactoryRegistry, connFactory, null);
}
public PoolingHttpClientConnectionManager(
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) {
final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
this(getDefaultRegistry(), connFactory, null);
}
public PoolingHttpClientConnectionManager(
final Registry<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final HttpConnectionFactory<ManagedHttpClientConnection> 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<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final HttpConnectionFactory<ManagedHttpClientConnection> connFactory,
final SchemePortResolver schemePortResolver,
final DnsResolver dnsResolver,
final ConnPoolPolicy connPoolPolicy,
final ConnPoolListener<HttpRoute> 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<HttpRoute, ManagedHttpClientConnection> connFactory,
final HttpConnectionFactory<ManagedHttpClientConnection> connFactory,
final ConnPoolPolicy connPoolPolicy,
final ConnPoolListener<HttpRoute> 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<HttpRoute, ManagedHttpClientConnection> 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<PoolEntry<HttpRoute, ManagedHttpClientConnection>> future = this.pool.lease(route, state, null);
return new ConnectionRequest() {
final Future<PoolEntry<HttpRoute, ManagedHttpClientConnection>> 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<HttpRoute, ManagedHttpClientConnection> 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<PoolEntry<HttpRoute, ManagedHttpClientConnection>> future,
final long timeout,
final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> 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<HttpHost, SocketConfig> socketConfigMap;
private final Map<HttpHost, ConnectionConfig> 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<PoolEntry<HttpRoute, ManagedHttpClientConnection>> poolEntryRef;
InternalConnectionEndpoint(
final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry) {
this.poolEntryRef = new AtomicReference<>(poolEntry);
}
public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) {
this.connectionConfigMap.put(host, connectionConfig);
PoolEntry<HttpRoute, ManagedHttpClientConnection> getPoolEntry() {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
if (poolEntry == null) {
throw new ConnectionShutdownException();
}
return poolEntry;
}
PoolEntry<HttpRoute, ManagedHttpClientConnection> getValidatedPoolEntry() {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = getPoolEntry();
final ManagedHttpClientConnection connection = poolEntry.getConnection();
Asserts.check(connection != null && connection.isOpen(), "Endpoint is not connected");
return poolEntry;
}
PoolEntry<HttpRoute, ManagedHttpClientConnection> detach() {
return poolEntryRef.getAndSet(null);
}
@Override
public void shutdown() throws IOException {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
if (poolEntry != null) {
poolEntry.discardConnection();
}
}
@Override
public boolean isConnected() {
final PoolEntry<HttpRoute, ManagedHttpClientConnection> 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);
}
}

View File

@ -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<ManagedHttpClientConnection> connectionFactory;
private LayeredConnectionSocketFactory sslSocketFactory;
private SchemePortResolver schemePortResolver;
private DnsResolver dnsResolver;
private ConnPoolPolicy connPoolPolicy;
private ConnPoolListener<HttpRoute> 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<ManagedHttpClientConnection> 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<HttpRoute> 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);
}

View File

@ -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;

View File

@ -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());

View File

@ -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

View File

@ -24,52 +24,36 @@
* <http://www.apache.org/>.
*
*/
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);
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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 &gt;= 300, the response
* body is consumed and an {@link org.apache.hc.client5.http.protocol.HttpResponseException} is thrown.
* <p>
* 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.
* </p>
*

View File

@ -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);
}
}
}

View File

@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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,

View File

@ -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;
}
}
}

View File

@ -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<CookieSpecProvider> cookieSpecRegistry;
private final Lookup<AuthSchemeProvider> authSchemeRegistry;
@ -81,7 +79,6 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable {
public InternalHttpClient(
final ClientExecChain execChain,
final HttpClientConnectionManager connManager,
final HttpRoutePlanner routePlanner,
final Lookup<CookieSpecProvider> cookieSpecRegistry,
final Lookup<AuthSchemeProvider> authSchemeRegistry,
@ -91,10 +88,8 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable {
final List<Closeable> 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) {

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -110,8 +110,8 @@ class MinimalHttpClient extends CloseableHttpClient {
}
@Override
public void close() {
this.connManager.shutdown();
public void close() throws IOException {
this.connManager.close();
}
}

View File

@ -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<HttpRoute, ManagedHttpClientConnection> connFactory;
private final HttpConnectionFactory<ManagedHttpClientConnection> 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<HttpRoute, ManagedHttpClientConnection> connFactory,
final HttpConnectionFactory<ManagedHttpClientConnection> 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;

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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
* <http://www.apache.org/>.
*
*/
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.
* <p>
* 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;
}

View File

@ -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.
* </p>
* <p>
* 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.
* </p>
*
* @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()}.
* <p>
* 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.
* </p>
* 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.
* <p>
* 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
* </p>
* <p>
* All expired connections will also be closed.
* </p>
*
* @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.
* <p>
* 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.
* </p>
*/
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();
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -24,18 +24,16 @@
* <http://www.apache.org/>.
*
*/
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<T, C extends HttpConnection> {
public interface Identifiable {
C create(T route, ConnectionConfig config);
String getId();
}

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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
* <http://www.apache.org/>.
*
*/
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<Throwable> 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<Throwable> 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<Throwable> 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<Throwable> 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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());
}

View File

@ -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<HttpRoute, ManagedHttpClientConnection> connFactory;
private HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
@Mock
private Lookup<ConnectionSocketFactory> 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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<HttpRoute>any(), Mockito.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).create(
Mockito.eq(route2), Mockito.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
}
@Test(expected=IllegalArgumentException.class)
public void testLeaseInvalidArg() throws Exception {
mgr.requestConnection(null, null);
Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any());
Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<InetSocketAddress>any(),
Mockito.<HttpContext>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.<ConnectionConfig>any())).thenReturn(conn);
Mockito.when(connFactory.createConnection(Mockito.<Socket>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.<InetSocketAddress>any(),
Mockito.<HttpContext>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);
}
}

View File

@ -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
* <http://www.apache.org/>.
*
*/
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<ClassicHttpResponse> 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<ClassicHttpResponse> 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<ClassicHttpResponse> 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<ClassicHttpResponse> parser = new LenientHttpResponseParser(H1Config.DEFAULT);
parser.parse(inbuffer, new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)));
}
}

View File

@ -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.<HttpContext>any())).thenReturn(socket);

View File

@ -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.<InetSocketAddress>any(),
Mockito.<HttpContext>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.<InetSocketAddress>any(),
Mockito.<HttpContext>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);
}
}

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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

View File

@ -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.<HttpClientConnection>any(),
Mockito.verify(endpoint, Mockito.times(1)).shutdown();
Mockito.verify(mgr, Mockito.times(1)).release(
Mockito.<ConnectionEndpoint>any(),
Mockito.anyObject(),
Mockito.anyLong(),
Mockito.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.verify(endpoint, Mockito.times(1)).shutdown();
Mockito.verify(mgr, Mockito.times(1)).release(
Mockito.<ConnectionEndpoint>any(),
Mockito.anyObject(),
Mockito.anyLong(),
Mockito.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.verify(mgr, Mockito.times(1)).release(
Mockito.<ConnectionEndpoint>any(),
Mockito.anyObject(),
Mockito.anyLong(),
Mockito.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.verify(mgr, Mockito.times(1)).release(
Mockito.<ConnectionEndpoint>any(),
Mockito.anyObject(),
Mockito.anyLong(),
Mockito.<TimeUnit>any());

View File

@ -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();

View File

@ -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<CookieSpecProvider> cookieSpecRegistry;
private Lookup<AuthSchemeProvider> 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<HttpRequest> argcap = ArgumentCaptor.forClass(HttpRequest.class);
Mockito.when(routePlanner.determineRoute(
Mockito.eq(new HttpHost("somehost")),
argcap.capture(),
Mockito.<HttpClientContext>any())).thenReturn(route);
client.execute(httpget);
Assert.assertNotNull(argcap.getValue());
Assert.assertSame(httpget, argcap.getValue());
Mockito.verify(execChain).execute(
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any(),
@ -120,7 +110,6 @@ public class TestInternalHttpClient {
Mockito.when(routePlanner.determineRoute(
Mockito.eq(new HttpHost("somehost")),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(route);
Mockito.when(execChain.execute(
Mockito.<RoutedHttpRequest>any(),
@ -137,7 +126,6 @@ public class TestInternalHttpClient {
Mockito.when(routePlanner.determineRoute(
Mockito.eq(new HttpHost("somehost")),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>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.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>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.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(route);
final HttpClientContext context = HttpClientContext.create();

View File

@ -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.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
Mockito.when(connRequest.get(
Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(managedConn);
Mockito.anyLong(), Mockito.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<Cancellable>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@ -192,11 +190,11 @@ public class TestMainClientExec {
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@ -228,11 +225,11 @@ public class TestMainClientExec {
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@ -264,23 +260,23 @@ public class TestMainClientExec {
Mockito.<HttpClientContext>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.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<Cancellable>any());
Mockito.verify(connManager, Mockito.never()).connect(
Mockito.same(managedConn),
Mockito.<HttpRoute>any(),
Mockito.same(endpoint),
Mockito.anyInt(),
Mockito.<TimeUnit>any(),
Mockito.<HttpContext>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.<HttpClientConnection>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenAnswer(new Answer<HttpResponse>() {
@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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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<ClassicHttpRequest> 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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<HttpResponse>any(),
Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
Mockito.when(requestExecutor.execute(
Mockito.when(endpoint.execute(
Mockito.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
Mockito.when(proxyAuthStrategy.select(
@ -768,10 +756,9 @@ public class TestMainClientExec {
Mockito.<Map<String, AuthChallenge>>any(),
Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>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.<HttpResponse>any(),
Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
Mockito.when(requestExecutor.execute(
Mockito.when(endpoint.execute(
Mockito.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
Mockito.when(proxyAuthStrategy.select(
@ -811,12 +798,11 @@ public class TestMainClientExec {
Mockito.<Map<String, AuthChallenge>>any(),
Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>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);
}
}

View File

@ -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.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
Mockito.when(connRequest.get(
Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(managedConn);
Mockito.anyLong(), Mockito.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<Cancellable>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@ -156,11 +153,11 @@ public class TestMinimalClientExec {
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@ -195,23 +191,23 @@ public class TestMinimalClientExec {
Mockito.<HttpClientContext>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.<TimeUnit>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<HttpClientConnection>any(),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>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.<ClassicHttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.when(endpoint.execute(
Mockito.same(request),
Mockito.<HttpRequestExecutor>any(),
Mockito.<HttpClientContext>any())).thenReturn(response);
minimalClientExec.execute(request, context, execAware);
final ArgumentCaptor<ClassicHttpRequest> reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
Mockito.verify(requestExecutor).execute(reqCaptor.capture(), Mockito.<HttpClientConnection>any(), Mockito.<HttpClientContext>any());
Mockito.verify(endpoint).execute(reqCaptor.capture(), Mockito.<HttpRequestExecutor>any(), Mockito.<HttpClientContext>any());
Assert.assertEquals("/test", reqCaptor.getValue().getRequestUri());
}

View File

@ -121,7 +121,6 @@ public class TestRedirectExec {
Mockito.<HttpClientContext>any())).thenReturn(redirect);
Mockito.when(httpRoutePlanner.determineRoute(
Mockito.eq(target),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(route);
redirectExec.execute(request, context, execAware);
@ -173,7 +172,6 @@ public class TestRedirectExec {
Mockito.<HttpClientContext>any())).thenReturn(redirect);
Mockito.when(httpRoutePlanner.determineRoute(
Mockito.eq(target),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(route);
redirectExec.execute(request, context, execAware);
@ -207,7 +205,6 @@ public class TestRedirectExec {
Mockito.<HttpClientContext>any())).thenReturn(redirect);
Mockito.when(httpRoutePlanner.determineRoute(
Mockito.eq(target),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(route);
redirectExec.execute(request, context, execAware);
@ -253,11 +250,9 @@ public class TestRedirectExec {
Mockito.<HttpClientContext>any())).thenReturn(redirect);
Mockito.when(httpRoutePlanner.determineRoute(
Mockito.eq(target),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(target));
Mockito.when(httpRoutePlanner.determineRoute(
Mockito.eq(otherHost),
Mockito.<RoutedHttpRequest>any(),
Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(otherHost));
redirectExec.execute(request, context, execAware);

View File

@ -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();
}
}