You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2024/01/29 13:15:22 UTC
(httpcomponents-client) branch master updated (9d225a651 -> 3235f009d)
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
from 9d225a651 Realigned the behavior of TLS upgrade in the classic and async connection operators
new 9833c3572 Connection endpoints to provide TLS details and protocol version
new 7cf469c5c Connect executors to update the execution context with the protocol and TLS details upon successful connect
new 1111c62dd Classic ExecRuntime to support information responses (1xx)
new 3235f009d HTTPCLIENT-751: Support for RFC 2817 (Upgrading to TLS Within HTTP/1.1)
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../testing/sync/TestClientRequestExecution.java | 12 +-
.../org/apache/hc/client5/http/EndpointInfo.java | 37 ++++--
.../hc/client5/http/async/AsyncExecRuntime.java | 8 ++
.../hc/client5/http/classic/ExecRuntime.java | 27 +++++
.../hc/client5/http/config/RequestConfig.java | 40 ++++++-
.../client5/http/impl/ProtocolSwitchStrategy.java | 75 ++++++++++++
.../client5/http/impl/async/AsyncConnectExec.java | 33 ++++--
.../http/impl/async/HttpAsyncClientBuilder.java | 4 +-
.../http/impl/async/HttpAsyncMainClientExec.java | 36 +++++-
.../impl/async/InternalH2AsyncExecRuntime.java | 14 +++
.../impl/async/InternalHttpAsyncExecRuntime.java | 6 +
.../hc/client5/http/impl/classic/ConnectExec.java | 7 ++
.../http/impl/classic/HttpClientBuilder.java | 4 +-
.../http/impl/classic/InternalExecRuntime.java | 19 ++-
.../client5/http/impl/classic/MainClientExec.java | 28 ++++-
.../impl/io/BasicHttpClientConnectionManager.java | 13 ++
.../io/DefaultHttpClientConnectionOperator.java | 10 +-
.../io/PoolingHttpClientConnectionManager.java | 24 +++-
.../nio/DefaultAsyncClientConnectionOperator.java | 9 +-
.../nio/PoolingAsyncClientConnectionManager.java | 46 ++++---
.../hc/client5/http/io/ConnectionEndpoint.java | 10 ++
.../client5/http/nio/AsyncConnectionEndpoint.java | 10 ++
.../http/protocol/RequestExpectContinue.java | 4 +-
...tClientConnControl.java => RequestUpgrade.java} | 57 ++++-----
.../http/impl/TestProtocolSwitchStrategy.java | 98 +++++++++++++++
.../http/impl/classic/TestMainClientExec.java | 23 ++--
.../io/TestBasicHttpClientConnectionManager.java | 14 +--
.../impl/io/TestHttpClientConnectionOperator.java | 10 +-
.../io/TestPoolingHttpClientConnectionManager.java | 14 +--
.../client5/http/protocol/TestRequestUpgrade.java | 132 +++++++++++++++++++++
30 files changed, 707 insertions(+), 117 deletions(-)
copy httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheMatch.java => httpclient5/src/main/java/org/apache/hc/client5/http/EndpointInfo.java (63%)
create mode 100644 httpclient5/src/main/java/org/apache/hc/client5/http/impl/ProtocolSwitchStrategy.java
copy httpclient5/src/main/java/org/apache/hc/client5/http/protocol/{RequestClientConnControl.java => RequestUpgrade.java} (58%)
create mode 100644 httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestProtocolSwitchStrategy.java
create mode 100644 httpclient5/src/test/java/org/apache/hc/client5/http/protocol/TestRequestUpgrade.java
(httpcomponents-client) 04/04: HTTPCLIENT-751: Support for RFC 2817 (Upgrading to TLS Within HTTP/1.1)
Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
commit 3235f009d5673536be5c668df409ac3f83808d89
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Thu Jan 25 15:10:01 2024 +0100
HTTPCLIENT-751: Support for RFC 2817 (Upgrading to TLS Within HTTP/1.1)
---
.../hc/client5/http/config/RequestConfig.java | 40 ++++++-
.../client5/http/impl/ProtocolSwitchStrategy.java | 75 ++++++++++++
.../http/impl/async/HttpAsyncClientBuilder.java | 4 +-
.../http/impl/async/HttpAsyncMainClientExec.java | 36 +++++-
.../http/impl/classic/HttpClientBuilder.java | 4 +-
.../client5/http/impl/classic/MainClientExec.java | 28 ++++-
.../io/DefaultHttpClientConnectionOperator.java | 10 +-
.../io/PoolingHttpClientConnectionManager.java | 7 +-
.../nio/DefaultAsyncClientConnectionOperator.java | 9 +-
.../nio/PoolingAsyncClientConnectionManager.java | 32 ++---
.../http/protocol/RequestExpectContinue.java | 4 +-
...uestExpectContinue.java => RequestUpgrade.java} | 51 ++++----
.../http/impl/TestProtocolSwitchStrategy.java | 98 +++++++++++++++
.../http/impl/classic/TestMainClientExec.java | 13 +-
.../io/TestBasicHttpClientConnectionManager.java | 14 +--
.../impl/io/TestHttpClientConnectionOperator.java | 10 +-
.../io/TestPoolingHttpClientConnectionManager.java | 14 +--
.../client5/http/protocol/TestRequestUpgrade.java | 132 +++++++++++++++++++++
18 files changed, 496 insertions(+), 85 deletions(-)
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java b/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java
index 1028dc9df..d747c5b6d 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java
@@ -62,13 +62,14 @@ public class RequestConfig implements Cloneable {
private final TimeValue connectionKeepAlive;
private final boolean contentCompressionEnabled;
private final boolean hardCancellationEnabled;
+ private final boolean protocolUpgradeEnabled;
/**
* Intended for CDI compatibility
*/
protected RequestConfig() {
this(false, null, null, false, false, 0, false, null, null,
- DEFAULT_CONNECTION_REQUEST_TIMEOUT, null, null, DEFAULT_CONN_KEEP_ALIVE, false, false);
+ DEFAULT_CONNECTION_REQUEST_TIMEOUT, null, null, DEFAULT_CONN_KEEP_ALIVE, false, false, false);
}
RequestConfig(
@@ -86,7 +87,8 @@ public class RequestConfig implements Cloneable {
final Timeout responseTimeout,
final TimeValue connectionKeepAlive,
final boolean contentCompressionEnabled,
- final boolean hardCancellationEnabled) {
+ final boolean hardCancellationEnabled,
+ final boolean protocolUpgradeEnabled) {
super();
this.expectContinueEnabled = expectContinueEnabled;
this.proxy = proxy;
@@ -103,6 +105,7 @@ public class RequestConfig implements Cloneable {
this.connectionKeepAlive = connectionKeepAlive;
this.contentCompressionEnabled = contentCompressionEnabled;
this.hardCancellationEnabled = hardCancellationEnabled;
+ this.protocolUpgradeEnabled = protocolUpgradeEnabled;
}
/**
@@ -217,6 +220,13 @@ public class RequestConfig implements Cloneable {
return hardCancellationEnabled;
}
+ /**
+ * @see Builder#setProtocolUpgradeEnabled(boolean) (boolean)
+ */
+ public boolean isProtocolUpgradeEnabled() {
+ return protocolUpgradeEnabled;
+ }
+
@Override
protected RequestConfig clone() throws CloneNotSupportedException {
return (RequestConfig) super.clone();
@@ -241,6 +251,7 @@ public class RequestConfig implements Cloneable {
builder.append(", connectionKeepAlive=").append(connectionKeepAlive);
builder.append(", contentCompressionEnabled=").append(contentCompressionEnabled);
builder.append(", hardCancellationEnabled=").append(hardCancellationEnabled);
+ builder.append(", protocolUpgradeEnabled=").append(protocolUpgradeEnabled);
builder.append("]");
return builder.toString();
}
@@ -265,7 +276,8 @@ public class RequestConfig implements Cloneable {
.setResponseTimeout(config.getResponseTimeout())
.setConnectionKeepAlive(config.getConnectionKeepAlive())
.setContentCompressionEnabled(config.isContentCompressionEnabled())
- .setHardCancellationEnabled(config.isHardCancellationEnabled());
+ .setHardCancellationEnabled(config.isHardCancellationEnabled())
+ .setProtocolUpgradeEnabled(config.isProtocolUpgradeEnabled());
}
public static class Builder {
@@ -285,6 +297,7 @@ public class RequestConfig implements Cloneable {
private TimeValue connectionKeepAlive;
private boolean contentCompressionEnabled;
private boolean hardCancellationEnabled;
+ private boolean protocolUpgradeEnabled;
Builder() {
super();
@@ -294,6 +307,7 @@ public class RequestConfig implements Cloneable {
this.connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
this.contentCompressionEnabled = true;
this.hardCancellationEnabled = true;
+ this.protocolUpgradeEnabled = true;
}
/**
@@ -570,6 +584,23 @@ public class RequestConfig implements Cloneable {
return this;
}
+ /**
+ * Determines whether the client server should automatically attempt to upgrade
+ * to a safer or a newer version of the protocol, whenever possible.
+ * <p>
+ * Presently supported: HTTP/1.1 TLS upgrade
+ * </p>
+ * <p>
+ * Default: {@code true}
+ * </p>
+ *
+ * @since 5.4
+ */
+ public Builder setProtocolUpgradeEnabled(final boolean protocolUpgradeEnabled) {
+ this.protocolUpgradeEnabled = protocolUpgradeEnabled;
+ return this;
+ }
+
public RequestConfig build() {
return new RequestConfig(
expectContinueEnabled,
@@ -586,7 +617,8 @@ public class RequestConfig implements Cloneable {
responseTimeout,
connectionKeepAlive != null ? connectionKeepAlive : DEFAULT_CONN_KEEP_ALIVE,
contentCompressionEnabled,
- hardCancellationEnabled);
+ hardCancellationEnabled,
+ protocolUpgradeEnabled);
}
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ProtocolSwitchStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ProtocolSwitchStrategy.java
new file mode 100644
index 000000000..eed4bcc57
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ProtocolSwitchStrategy.java
@@ -0,0 +1,75 @@
+/*
+ * ====================================================================
+ * 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.Iterator;
+
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpMessage;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.message.MessageSupport;
+import org.apache.hc.core5.http.ssl.TLS;
+
+/**
+ * Protocol switch handler.
+ *
+ * @since 5.4
+ */
+@Internal
+public final class ProtocolSwitchStrategy {
+
+ enum ProtocolSwitch { FAILURE, TLS }
+
+ public ProtocolVersion switchProtocol(final HttpMessage response) throws ProtocolException {
+ final Iterator<String> it = MessageSupport.iterateTokens(response, HttpHeaders.UPGRADE);
+
+ ProtocolVersion tlsUpgrade = null;
+ while (it.hasNext()) {
+ final String token = it.next();
+ if (token.startsWith("TLS")) {
+ // TODO: Improve handling of HTTP protocol token once HttpVersion has a #parse method
+ try {
+ tlsUpgrade = token.length() == 3 ? TLS.V_1_2.getVersion() : TLS.parse(token.replace("TLS/", "TLSv"));
+ } catch (final ParseException ex) {
+ throw new ProtocolException("Invalid protocol: " + token);
+ }
+ } else if (token.equals("HTTP/1.1")) {
+ // TODO: Improve handling of HTTP protocol token once HttpVersion has a #parse method
+ } else {
+ throw new ProtocolException("Unsupported protocol: " + token);
+ }
+ }
+ if (tlsUpgrade == null) {
+ throw new ProtocolException("Invalid protocol switch response");
+ }
+ return tlsUpgrade;
+ }
+
+}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
index 5fad7a84a..f06a234b8 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
@@ -76,6 +76,7 @@ import org.apache.hc.client5.http.protocol.RedirectStrategy;
import org.apache.hc.client5.http.protocol.RequestAddCookies;
import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
import org.apache.hc.client5.http.protocol.RequestExpectContinue;
+import org.apache.hc.client5.http.protocol.RequestUpgrade;
import org.apache.hc.client5.http.protocol.RequestValidateTrace;
import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
@@ -843,7 +844,8 @@ public class HttpAsyncClientBuilder {
new H2RequestContent(),
new H2RequestConnControl(),
new RequestUserAgent(userAgentCopy),
- new RequestExpectContinue());
+ new RequestExpectContinue(),
+ new RequestUpgrade());
if (!cookieManagementDisabled) {
b.add(RequestAddCookies.INSTANCE);
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncMainClientExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncMainClientExec.java
index 86eff6164..953d7c6d9 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncMainClientExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncMainClientExec.java
@@ -40,17 +40,21 @@ import org.apache.hc.client5.http.async.AsyncExecCallback;
import org.apache.hc.client5.http.async.AsyncExecChain;
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
import org.apache.hc.client5.http.async.AsyncExecRuntime;
+import org.apache.hc.client5.http.impl.ProtocolSwitchStrategy;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.CancellableDependency;
+import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.message.RequestLine;
import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
import org.apache.hc.core5.http.nio.AsyncDataConsumer;
@@ -82,6 +86,7 @@ class HttpAsyncMainClientExec implements AsyncExecChainHandler {
private final HttpProcessor httpProcessor;
private final ConnectionKeepAliveStrategy keepAliveStrategy;
private final UserTokenHandler userTokenHandler;
+ private final ProtocolSwitchStrategy protocolSwitchStrategy;
HttpAsyncMainClientExec(final HttpProcessor httpProcessor,
final ConnectionKeepAliveStrategy keepAliveStrategy,
@@ -89,6 +94,7 @@ class HttpAsyncMainClientExec implements AsyncExecChainHandler {
this.httpProcessor = Args.notNull(httpProcessor, "HTTP protocol processor");
this.keepAliveStrategy = keepAliveStrategy;
this.userTokenHandler = userTokenHandler;
+ this.protocolSwitchStrategy = new ProtocolSwitchStrategy();
}
@Override
@@ -195,7 +201,35 @@ class HttpAsyncMainClientExec implements AsyncExecChainHandler {
public void consumeInformation(
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
- asyncExecCallback.handleInformationResponse(response);
+ if (response.getCode() == HttpStatus.SC_SWITCHING_PROTOCOLS) {
+ final ProtocolVersion upgradeProtocol = protocolSwitchStrategy.switchProtocol(response);
+ if (upgradeProtocol == null || !upgradeProtocol.getProtocol().equals("TLS")) {
+ throw new ProtocolException("Failure switching protocols");
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Switching to {}", upgradeProtocol);
+ }
+ execRuntime.upgradeTls(clientContext, new FutureCallback<AsyncExecRuntime>() {
+
+ @Override
+ public void completed(final AsyncExecRuntime result) {
+ LOG.debug("Successfully switched to {}", upgradeProtocol);
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ asyncExecCallback.failed(ex);
+ }
+
+ @Override
+ public void cancelled() {
+ asyncExecCallback.failed(new InterruptedIOException());
+ }
+
+ });
+ } else {
+ asyncExecCallback.handleInformationResponse(response);
+ }
}
@Override
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
index 3634ec86d..5c7ec95d1 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
@@ -80,6 +80,7 @@ import org.apache.hc.client5.http.protocol.RequestAddCookies;
import org.apache.hc.client5.http.protocol.RequestClientConnControl;
import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
import org.apache.hc.client5.http.protocol.RequestExpectContinue;
+import org.apache.hc.client5.http.protocol.RequestUpgrade;
import org.apache.hc.client5.http.protocol.RequestValidateTrace;
import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
@@ -824,7 +825,8 @@ public class HttpClientBuilder {
new RequestContent(),
new RequestClientConnControl(),
new RequestUserAgent(userAgentCopy),
- new RequestExpectContinue());
+ new RequestExpectContinue(),
+ new RequestUpgrade());
if (!cookieManagementDisabled) {
b.add(RequestAddCookies.INSTANCE);
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java
index 5e8d9f1ee..31fd19f2a 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java
@@ -37,6 +37,7 @@ import org.apache.hc.client5.http.classic.ExecChain;
import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.classic.ExecRuntime;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
+import org.apache.hc.client5.http.impl.ProtocolSwitchStrategy;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Contract;
@@ -47,6 +48,9 @@ import org.apache.hc.core5.http.ClassicHttpResponse;
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.HttpStatus;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.message.RequestLine;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -74,6 +78,7 @@ public final class MainClientExec implements ExecChainHandler {
private final ConnectionReuseStrategy reuseStrategy;
private final ConnectionKeepAliveStrategy keepAliveStrategy;
private final UserTokenHandler userTokenHandler;
+ private final ProtocolSwitchStrategy protocolSwitchStrategy;
/**
* @since 4.4
@@ -89,6 +94,7 @@ public final class MainClientExec implements ExecChainHandler {
this.reuseStrategy = Args.notNull(reuseStrategy, "Connection reuse strategy");
this.keepAliveStrategy = Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
this.userTokenHandler = Args.notNull(userTokenHandler, "User token handler");
+ this.protocolSwitchStrategy = new ProtocolSwitchStrategy();
}
@Override
@@ -113,7 +119,27 @@ public final class MainClientExec implements ExecChainHandler {
httpProcessor.process(request, request.getEntity(), context);
- final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, null, context);
+ final ClassicHttpResponse response = execRuntime.execute(
+ exchangeId,
+ request,
+ (r, connection, c) -> {
+ if (r.getCode() == HttpStatus.SC_SWITCHING_PROTOCOLS) {
+ final ProtocolVersion upgradeProtocol = protocolSwitchStrategy.switchProtocol(r);
+ if (upgradeProtocol == null || !upgradeProtocol.getProtocol().equals("TLS")) {
+ throw new ProtocolException("Failure switching protocols");
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Switching to {}", upgradeProtocol);
+ }
+ try {
+ execRuntime.upgradeTls(context);
+ } catch (final IOException ex) {
+ throw new HttpException("Failure upgrading to TLS", ex);
+ }
+ LOG.debug("Successfully switched to {}", upgradeProtocol);
+ }
+ },
+ context);
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
httpProcessor.process(response, response.getEntity(), context);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java
index 64f0a6483..c95299496 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java
@@ -53,6 +53,7 @@ import org.apache.hc.core5.annotation.Internal;
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.URIScheme;
import org.apache.hc.core5.http.config.Lookup;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.protocol.HttpContext;
@@ -184,7 +185,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
final Timeout soTimeout = socketConfig.getSoTimeout();
final SocketAddress socksProxyAddress = socketConfig.getSocksProxyAddress();
final Proxy socksProxy = socksProxyAddress != null ? new Proxy(Proxy.Type.SOCKS, socksProxyAddress) : null;
- final int port = this.schemePortResolver.resolve(host);
+ final int port = this.schemePortResolver.resolve(host.getSchemeName(), host);
for (int i = 0; i < remoteAddresses.length; i++) {
final InetAddress address = remoteAddresses[i];
final boolean last = i == remoteAddresses.length - 1;
@@ -269,13 +270,14 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
if (socket == null) {
throw new ConnectionClosedException("Connection is closed");
}
- final TlsSocketStrategy tlsSocketStrategy = tlsSocketStrategyLookup != null ? tlsSocketStrategyLookup.lookup(host.getSchemeName()) : null;
+ final String newProtocol = URIScheme.HTTP.same(host.getSchemeName()) ? URIScheme.HTTPS.id : host.getSchemeName();
+ final TlsSocketStrategy tlsSocketStrategy = tlsSocketStrategyLookup != null ? tlsSocketStrategyLookup.lookup(newProtocol) : null;
if (tlsSocketStrategy != null) {
- final int port = this.schemePortResolver.resolve(host);
+ final int port = this.schemePortResolver.resolve(newProtocol, host);
final SSLSocket upgradedSocket = tlsSocketStrategy.upgrade(socket, host.getHostName(), port, attachment, context);
conn.bind(upgradedSocket);
} else {
- throw new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported");
+ throw new UnsupportedSchemeException(newProtocol + " protocol is not supported");
}
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
index 153e68ef7..9ef411cc6 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
@@ -505,10 +505,9 @@ public class PoolingHttpClientConnectionManager
Args.notNull(endpoint, "Managed endpoint");
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
- final HttpRoute route = poolEntry.getRoute();
- final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
- final TlsConfig tlsConfig = resolveTlsConfig(host);
- this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), tlsConfig, context);
+ final HttpHost target = poolEntry.getRoute().getTargetHost();
+ final TlsConfig tlsConfig = resolveTlsConfig(target);
+ this.connectionOperator.upgrade(poolEntry.getConnection(), target, tlsConfig, context);
}
@Override
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java
index 0a046887d..d31ab382f 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java
@@ -45,6 +45,7 @@ import org.apache.hc.core5.concurrent.ComplexFuture;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.concurrent.FutureContribution;
import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.config.Lookup;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http.protocol.HttpContext;
@@ -174,11 +175,13 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
final Object attachment,
final HttpContext context,
final FutureCallback<ManagedAsyncClientConnection> callback) {
- final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(host.getSchemeName()) : null;
+ final String newProtocol = URIScheme.HTTP.same(host.getSchemeName()) ? URIScheme.HTTPS.id : host.getSchemeName();
+ final HttpHost remoteEndpoint = RoutingSupport.normalize(host, schemePortResolver);
+ final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(newProtocol) : null;
if (tlsStrategy != null) {
tlsStrategy.upgrade(
connection,
- host,
+ remoteEndpoint,
attachment,
null,
new CallbackContribution<TransportSecurityLayer>(callback) {
@@ -192,7 +195,7 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
});
} else {
- callback.failed(new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported"));
+ callback.failed(new UnsupportedSchemeException(newProtocol + " protocol is not supported"));
}
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
index f2cc2ffc7..f013ccf9f 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
@@ -245,13 +245,21 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
}
- private TlsConfig resolveTlsConfig(final HttpHost host, final Object attachment) {
- if (attachment instanceof TlsConfig) {
- return (TlsConfig) attachment;
- }
+ private TlsConfig resolveTlsConfig(final HttpHost host) {
final Resolver<HttpHost, TlsConfig> resolver = this.tlsConfigResolver;
- final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
- return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
+ TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
+ if (tlsConfig == null) {
+ tlsConfig = TlsConfig.DEFAULT;
+ }
+ if (URIScheme.HTTP.same(host.getSchemeName())
+ && tlsConfig.getHttpVersionPolicy() == HttpVersionPolicy.NEGOTIATE) {
+ // Plain HTTP does not support protocol negotiation.
+ // Fall back to HTTP/1.1
+ tlsConfig = TlsConfig.copy(tlsConfig)
+ .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
+ .build();
+ }
+ return tlsConfig;
}
@Override
@@ -441,7 +449,6 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
}
final InetSocketAddress localAddress = route.getLocalSocketAddress();
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
- final TlsConfig tlsConfig = resolveTlsConfig(host, attachment);
final Timeout connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout();
if (LOG.isDebugEnabled()) {
@@ -452,9 +459,7 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
host,
localAddress,
connectTimeout,
- route.isTunnelled() ? TlsConfig.copy(tlsConfig)
- .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
- .build() : tlsConfig,
+ route.isTunnelled() ? null : resolveTlsConfig(host),
context,
new FutureCallback<ManagedAsyncClientConnection>() {
@@ -499,12 +504,11 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
final HttpRoute route = poolEntry.getRoute();
- final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
- final TlsConfig tlsConfig = resolveTlsConfig(host, attachment);
+ final HttpHost target = route.getTargetHost();
connectionOperator.upgrade(
poolEntry.getConnection(),
- route.getTargetHost(),
- attachment != null ? attachment : tlsConfig,
+ target,
+ attachment != null ? attachment : resolveTlsConfig(target),
context,
new CallbackContribution<ManagedAsyncClientConnection>(callback) {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java b/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java
index a60a7ea15..d78a28c10 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java
@@ -66,11 +66,11 @@ public class RequestExpectContinue implements HttpRequestInterceptor {
Args.notNull(request, "HTTP request");
if (!request.containsHeader(HttpHeaders.EXPECT)) {
- final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.HTTP_1_1;
+ final HttpClientContext clientContext = HttpClientContext.adapt(context);
+ final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : clientContext.getProtocolVersion();
// Do not send the expect header if request body is known to be empty
if (entity != null
&& entity.getContentLength() != 0 && !version.lessEquals(HttpVersion.HTTP_1_0)) {
- final HttpClientContext clientContext = HttpClientContext.adapt(context);
final RequestConfig config = clientContext.getRequestConfig();
if (config.isExpectContinueEnabled()) {
request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java b/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestUpgrade.java
similarity index 56%
copy from httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java
copy to httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestUpgrade.java
index a60a7ea15..d5ca81d22 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestExpectContinue.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestUpgrade.java
@@ -33,47 +33,50 @@ import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.EntityDetails;
-import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestInterceptor;
-import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * RequestExpectContinue is responsible for enabling the 'expect-continue'
- * handshake by adding {@code Expect} header.
- * <p>
- * This interceptor takes into account {@link RequestConfig#isExpectContinueEnabled()}
- * setting.
- * </p>
- *
- * @since 4.3
+ * @since 5.4
*/
@Contract(threading = ThreadingBehavior.STATELESS)
-public class RequestExpectContinue implements HttpRequestInterceptor {
+public final class RequestUpgrade implements HttpRequestInterceptor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RequestUpgrade.class);
- public RequestExpectContinue() {
- super();
+ public RequestUpgrade() {
}
@Override
- public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context)
- throws HttpException, IOException {
+ public void process(
+ final HttpRequest request,
+ final EntityDetails entity,
+ final HttpContext context) throws HttpException, IOException {
Args.notNull(request, "HTTP request");
+ Args.notNull(context, "HTTP context");
- if (!request.containsHeader(HttpHeaders.EXPECT)) {
- final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.HTTP_1_1;
- // Do not send the expect header if request body is known to be empty
- if (entity != null
- && entity.getContentLength() != 0 && !version.lessEquals(HttpVersion.HTTP_1_0)) {
- final HttpClientContext clientContext = HttpClientContext.adapt(context);
- final RequestConfig config = clientContext.getRequestConfig();
- if (config.isExpectContinueEnabled()) {
- request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
+ final HttpClientContext clientContext = HttpClientContext.adapt(context);
+ final RequestConfig requestConfig = clientContext.getRequestConfig();
+ if (requestConfig.isProtocolUpgradeEnabled()) {
+ final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : clientContext.getProtocolVersion();
+ if (!request.containsHeader(HttpHeaders.UPGRADE) && version.getMajor() == 1 && version.getMinor() >= 1) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Connection is upgradable: protocol version = {}", version);
+ }
+ final String method = request.getMethod();
+ if ((Method.OPTIONS.isSame(method) || Method.HEAD.isSame(method) || Method.GET.isSame(method)) &&
+ clientContext.getSSLSession() == null) {
+ LOG.debug("Connection is upgradable to TLS: method = {}", method);
+ request.addHeader(HttpHeaders.UPGRADE, "TLS/1.2");
+ request.addHeader(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE);
}
}
}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestProtocolSwitchStrategy.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestProtocolSwitchStrategy.java
new file mode 100644
index 000000000..1775bf49c
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestProtocolSwitchStrategy.java
@@ -0,0 +1,98 @@
+/*
+ * ====================================================================
+ * 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.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.ssl.TLS;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Simple tests for {@link DefaultAuthenticationStrategy}.
+ */
+public class TestProtocolSwitchStrategy {
+
+ ProtocolSwitchStrategy switchStrategy;
+
+ @BeforeEach
+ void setUp() {
+ switchStrategy = new ProtocolSwitchStrategy();
+ }
+
+ @Test
+ public void testSwitchToTLS() throws Exception {
+ final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response1.addHeader(HttpHeaders.UPGRADE, "TLS");
+ Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response1));
+
+ final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response2.addHeader(HttpHeaders.UPGRADE, "TLS/1.3");
+ Assertions.assertEquals(TLS.V_1_3.getVersion(), switchStrategy.switchProtocol(response2));
+ }
+
+ @Test
+ public void testSwitchToHTTP11AndTLS() throws Exception {
+ final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response1.addHeader(HttpHeaders.UPGRADE, "TLS, HTTP/1.1");
+ Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response1));
+
+ final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response2.addHeader(HttpHeaders.UPGRADE, ",, HTTP/1.1, TLS, ");
+ Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response2));
+
+ final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response3.addHeader(HttpHeaders.UPGRADE, "HTTP/1.1");
+ response3.addHeader(HttpHeaders.UPGRADE, "TLS/1.2");
+ Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response3));
+
+ final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response4.addHeader(HttpHeaders.UPGRADE, "HTTP/1.1");
+ response4.addHeader(HttpHeaders.UPGRADE, "TLS/1.2, TLS/1.3");
+ Assertions.assertEquals(TLS.V_1_3.getVersion(), switchStrategy.switchProtocol(response4));
+ }
+
+ @Test
+ public void testSwitchInvalid() throws Exception {
+ final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response1.addHeader(HttpHeaders.UPGRADE, "Crap");
+ Assertions.assertThrows(ProtocolException.class, () -> switchStrategy.switchProtocol(response1));
+
+ final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response2.addHeader(HttpHeaders.UPGRADE, "TLS, huh?");
+ Assertions.assertThrows(ProtocolException.class, () -> switchStrategy.switchProtocol(response2));
+
+ final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
+ response3.addHeader(HttpHeaders.UPGRADE, ",,,");
+ Assertions.assertThrows(ProtocolException.class, () -> switchStrategy.switchProtocol(response3));
+ }
+
+}
\ No newline at end of file
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
index b90104132..1e7c3a65d 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
@@ -56,7 +56,6 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-@SuppressWarnings({"boxing","static-access"}) // test code
public class TestMainClientExec {
@Mock
@@ -104,7 +103,7 @@ public class TestMainClientExec {
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
Mockito.verify(httpProcessor).process(request, null, context);
- Mockito.verify(execRuntime).execute("test", request, null, context);
+ Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
Mockito.verify(httpProcessor).process(response, responseEntity, context);
Assertions.assertEquals(route, context.getHttpRoute());
@@ -134,7 +133,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, null, context);
+ Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
Mockito.verify(execRuntime, Mockito.times(1)).markConnectionNonReusable();
Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
@@ -164,7 +163,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, null, context);
+ Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
Mockito.verify(execRuntime).markConnectionNonReusable();
Mockito.verify(execRuntime).releaseEndpoint();
@@ -200,7 +199,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, null, context);
+ Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
Mockito.verify(execRuntime).markConnectionReusable(null, TimeValue.ofMilliseconds(678L));
Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
@@ -231,7 +230,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, null, context);
+ Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
Mockito.verify(execRuntime).releaseEndpoint();
Assertions.assertNotNull(finalResponse);
@@ -261,7 +260,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, null, context);
+ Mockito.verify(execRuntime, Mockito.times(1)).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
Mockito.verify(execRuntime, Mockito.never()).disconnectEndpoint();
Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java
index 94fd2d38c..11075318f 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java
@@ -385,7 +385,7 @@ public class TestBasicHttpClientConnectionManager {
mgr.setTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {remote});
- Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
+ Mockito.when(schemePortResolver.resolve(target.getSchemeName(), target)).thenReturn(8443);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
Mockito.when(tlsSocketStrategyLookup.lookup("https")).thenReturn(tlsSocketStrategy);
@@ -399,7 +399,7 @@ public class TestBasicHttpClientConnectionManager {
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
- Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
+ Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target.getSchemeName(), target);
Mockito.verify(detachedSocketFactory, Mockito.times(1)).create(null);
Mockito.verify(socket, Mockito.times(1)).connect(new InetSocketAddress(remote, 8443), 234);
Mockito.verify(tlsSocketStrategy).upgrade(socket, "somehost", 8443, tlsConfig, context);
@@ -407,7 +407,7 @@ public class TestBasicHttpClientConnectionManager {
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost");
- Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target);
+ Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target.getSchemeName(), target);
Mockito.verify(detachedSocketFactory, Mockito.times(2)).create(null);
Mockito.verify(socket, Mockito.times(1)).connect(new InetSocketAddress(remote, 8443), 123);
Mockito.verify(tlsSocketStrategy, Mockito.times(2)).upgrade(socket, "somehost", 8443, tlsConfig, context);
@@ -442,15 +442,15 @@ public class TestBasicHttpClientConnectionManager {
mgr.setTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
- Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
- Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
+ Mockito.when(schemePortResolver.resolve(proxy.getSchemeName(), proxy)).thenReturn(8080);
+ Mockito.when(schemePortResolver.resolve(target.getSchemeName(), target)).thenReturn(8443);
Mockito.when(tlsSocketStrategyLookup.lookup("https")).thenReturn(tlsSocketStrategy);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
- Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
+ Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy.getSchemeName(), proxy);
Mockito.verify(detachedSocketFactory, Mockito.times(1)).create(null);
Mockito.verify(socket, Mockito.times(1)).connect(new InetSocketAddress(remote, 8080), 234);
@@ -458,7 +458,7 @@ public class TestBasicHttpClientConnectionManager {
mgr.upgrade(endpoint1, context);
- Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
+ Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target.getSchemeName(), target);
Mockito.verify(tlsSocketStrategy, Mockito.times(1)).upgrade(
socket, "somehost", 8443, tlsConfig, context);
}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java
index 28a40fa31..b3b925f92 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java
@@ -91,7 +91,7 @@ public class TestHttpClientConnectionOperator {
final InetAddress ip2 = InetAddress.getByAddress(new byte[] {127, 0, 0, 2});
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
- Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
+ Mockito.when(schemePortResolver.resolve(host.getSchemeName(), host)).thenReturn(80);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
final SocketConfig socketConfig = SocketConfig.custom()
@@ -129,7 +129,7 @@ public class TestHttpClientConnectionOperator {
.build();
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
- Mockito.when(schemePortResolver.resolve(host)).thenReturn(443);
+ Mockito.when(schemePortResolver.resolve(host.getSchemeName(), host)).thenReturn(443);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
Mockito.when(tlsSocketStrategyLookup.lookup("https")).thenReturn(tlsSocketStrategy);
@@ -194,7 +194,7 @@ public class TestHttpClientConnectionOperator {
final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
- Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
+ Mockito.when(schemePortResolver.resolve(host.getSchemeName(), host)).thenReturn(80);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
Mockito.doThrow(new ConnectException()).when(socket).connect(
Mockito.eq(new InetSocketAddress(ip1, 80)),
@@ -219,7 +219,7 @@ public class TestHttpClientConnectionOperator {
final InetAddress ip = InetAddress.getByAddress(new byte[] {127, 0, 0, 23});
final HttpHost host = new HttpHost(ip);
- Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
+ Mockito.when(schemePortResolver.resolve(host.getSchemeName(), host)).thenReturn(80);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
@@ -242,7 +242,7 @@ public class TestHttpClientConnectionOperator {
Mockito.when(conn.isOpen()).thenReturn(true);
Mockito.when(conn.getSocket()).thenReturn(socket);
Mockito.when(tlsSocketStrategyLookup.lookup("https")).thenReturn(tlsSocketStrategy);
- Mockito.when(schemePortResolver.resolve(host)).thenReturn(443);
+ Mockito.when(schemePortResolver.resolve(host.getSchemeName(), host)).thenReturn(443);
final SSLSocket upgradedSocket = Mockito.mock(SSLSocket.class);
Mockito.when(tlsSocketStrategy.upgrade(
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java
index 8496a8c69..9d37adda9 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java
@@ -263,7 +263,7 @@ public class TestPoolingHttpClientConnectionManager {
mgr.setDefaultTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[]{remote});
- Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
+ Mockito.when(schemePortResolver.resolve(target.getSchemeName(), target)).thenReturn(8443);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
Mockito.when(tlsSocketStrategyLookup.lookup("https")).thenReturn(tlsSocketStrategy);
@@ -277,7 +277,7 @@ public class TestPoolingHttpClientConnectionManager {
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
- Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
+ Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target.getSchemeName(), target);
Mockito.verify(detachedSocketFactory, Mockito.times(1)).create(null);
Mockito.verify(socket, Mockito.times(1)).connect(new InetSocketAddress(remote, 8443), 234);
Mockito.verify(tlsSocketStrategy).upgrade(socket, "somehost", 8443, tlsConfig, context);
@@ -285,7 +285,7 @@ public class TestPoolingHttpClientConnectionManager {
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost");
- Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target);
+ Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target.getSchemeName(), target);
Mockito.verify(detachedSocketFactory, Mockito.times(2)).create(null);
Mockito.verify(socket, Mockito.times(1)).connect(new InetSocketAddress(remote, 8443), 123);
Mockito.verify(tlsSocketStrategy, Mockito.times(2)).upgrade(socket, "somehost", 8443, tlsConfig, context);
@@ -330,15 +330,15 @@ public class TestPoolingHttpClientConnectionManager {
mgr.setDefaultTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
- Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
- Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
+ Mockito.when(schemePortResolver.resolve(proxy.getSchemeName(), proxy)).thenReturn(8080);
+ Mockito.when(schemePortResolver.resolve(target.getSchemeName(), target)).thenReturn(8443);
Mockito.when(tlsSocketStrategyLookup.lookup("https")).thenReturn(tlsSocketStrategy);
Mockito.when(detachedSocketFactory.create(Mockito.any())).thenReturn(socket);
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
- Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
+ Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy.getSchemeName(), proxy);
Mockito.verify(detachedSocketFactory, Mockito.times(1)).create(null);
Mockito.verify(socket, Mockito.times(1)).connect(new InetSocketAddress(remote, 8080), 234);
@@ -347,7 +347,7 @@ public class TestPoolingHttpClientConnectionManager {
mgr.upgrade(endpoint1, context);
- Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
+ Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target.getSchemeName(), target);
Mockito.verify(tlsSocketStrategy, Mockito.times(1)).upgrade(
socket, "somehost", 8443, tlsConfig, context);
}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/TestRequestUpgrade.java b/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/TestRequestUpgrade.java
new file mode 100644
index 000000000..42e126814
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/TestRequestUpgrade.java
@@ -0,0 +1,132 @@
+/*
+ * ====================================================================
+ * 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.protocol;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.hc.client5.http.HeadersMatcher;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class TestRequestUpgrade {
+
+ private RequestUpgrade interceptor;
+ private HttpClientContext context;
+
+ @BeforeEach
+ void setUp() {
+ interceptor = new RequestUpgrade();
+ context = HttpClientContext.create();
+ }
+
+ @Test
+ void testUpgrade() throws Exception {
+ final HttpRequest get = new BasicHttpRequest("GET", "/");
+ interceptor.process(get, null, context);
+ MatcherAssert.assertThat(get.getHeaders(),
+ HeadersMatcher.same(
+ new BasicHeader(HttpHeaders.UPGRADE, "TLS/1.2"),
+ new BasicHeader(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE)));
+ final HttpRequest options = new BasicHttpRequest("OPTIONS", "/");
+ interceptor.process(options, null, context);
+ MatcherAssert.assertThat(options.getHeaders(),
+ HeadersMatcher.same(
+ new BasicHeader(HttpHeaders.UPGRADE, "TLS/1.2"),
+ new BasicHeader(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE)));
+ final HttpRequest head = new BasicHttpRequest("HEAD", "/");
+ interceptor.process(head, null, context);
+ MatcherAssert.assertThat(head.getHeaders(),
+ HeadersMatcher.same(
+ new BasicHeader(HttpHeaders.UPGRADE, "TLS/1.2"),
+ new BasicHeader(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE)));
+ }
+
+ @Test
+ void testUpgradeDisabled() throws Exception {
+ context.setRequestConfig(RequestConfig.custom()
+ .setProtocolUpgradeEnabled(false)
+ .build());
+ final HttpRequest get = new BasicHttpRequest("GET", "/");
+ interceptor.process(get, null, context);
+ Assertions.assertFalse(get.containsHeader(HttpHeaders.UPGRADE));
+ }
+
+ @Test
+ void testDoNotUpgradeHTTP2() throws Exception {
+ context.setProtocolVersion(HttpVersion.HTTP_2);
+ final HttpRequest get = new BasicHttpRequest("GET", "/");
+ interceptor.process(get, null, context);
+ Assertions.assertFalse(get.containsHeader(HttpHeaders.UPGRADE));
+ }
+
+ @Test
+ void testDoNotUpgradeHTTP10() throws Exception {
+ context.setProtocolVersion(HttpVersion.HTTP_1_0);
+ final HttpRequest get = new BasicHttpRequest("GET", "/");
+ interceptor.process(get, null, context);
+ Assertions.assertFalse(get.containsHeader(HttpHeaders.UPGRADE));
+ }
+
+ @Test
+ void testDoUpgradeIfAlreadyTLS() throws Exception {
+ context.setAttribute(HttpCoreContext.SSL_SESSION, Mockito.mock(SSLSession.class));
+ final HttpRequest get = new BasicHttpRequest("GET", "/");
+ interceptor.process(get, null, context);
+ Assertions.assertFalse(get.containsHeader(HttpHeaders.UPGRADE));
+ }
+
+ @Test
+ void testDoUpgradeNonSafeMethodsOrTrace() throws Exception {
+ final HttpRequest post = new BasicHttpRequest("POST", "/");
+ interceptor.process(post, null, context);
+ Assertions.assertFalse(post.containsHeader(HttpHeaders.UPGRADE));
+
+ final HttpRequest put = new BasicHttpRequest("PUT", "/");
+ interceptor.process(put, null, context);
+ Assertions.assertFalse(put.containsHeader(HttpHeaders.UPGRADE));
+
+ final HttpRequest patch = new BasicHttpRequest("PATCH", "/");
+ interceptor.process(patch, null, context);
+ Assertions.assertFalse(patch.containsHeader(HttpHeaders.UPGRADE));
+
+ final HttpRequest trace = new BasicHttpRequest("TRACE", "/");
+ interceptor.process(trace, null, context);
+ Assertions.assertFalse(trace.containsHeader(HttpHeaders.UPGRADE));
+ }
+
+}
(httpcomponents-client) 01/04: Connection endpoints to provide TLS details and protocol version
Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
commit 9833c35728c582705c0da392240ffd83e79484c2
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sun Jan 14 16:47:35 2024 +0100
Connection endpoints to provide TLS details and protocol version
---
.../org/apache/hc/client5/http/EndpointInfo.java | 64 ++++++++++++++++++++++
.../hc/client5/http/async/AsyncExecRuntime.java | 8 +++
.../hc/client5/http/classic/ExecRuntime.java | 8 +++
.../impl/async/InternalH2AsyncExecRuntime.java | 14 +++++
.../impl/async/InternalHttpAsyncExecRuntime.java | 6 ++
.../http/impl/classic/InternalExecRuntime.java | 7 +++
.../impl/io/BasicHttpClientConnectionManager.java | 13 +++++
.../io/PoolingHttpClientConnectionManager.java | 17 ++++++
.../nio/PoolingAsyncClientConnectionManager.java | 14 +++++
.../hc/client5/http/io/ConnectionEndpoint.java | 10 ++++
.../client5/http/nio/AsyncConnectionEndpoint.java | 10 ++++
11 files changed, 171 insertions(+)
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/EndpointInfo.java b/httpclient5/src/main/java/org/apache/hc/client5/http/EndpointInfo.java
new file mode 100644
index 000000000..9df0eb593
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/EndpointInfo.java
@@ -0,0 +1,64 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.http.ProtocolVersion;
+
+/**
+ * @since 5.4
+ */
+public final class EndpointInfo {
+
+ private final ProtocolVersion protocol;
+ private final SSLSession sslSession;
+
+ @Internal
+ public EndpointInfo(final ProtocolVersion protocol, final SSLSession sslSession) {
+ this.protocol = protocol;
+ this.sslSession = sslSession;
+ }
+
+ public ProtocolVersion getProtocol() {
+ return protocol;
+ }
+
+ public SSLSession getSslSession() {
+ return sslSession;
+ }
+
+ @Override
+ public String toString() {
+ return "EndpointInfo{" +
+ "protocol=" + protocol +
+ ", sslSession=" + sslSession +
+ '}';
+ }
+
+}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecRuntime.java
index a82c778dd..314860d93 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecRuntime.java
@@ -27,6 +27,7 @@
package org.apache.hc.client5.http.async;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Internal;
@@ -133,6 +134,13 @@ public interface AsyncExecRuntime {
}
}
+ /**
+ * Returns information about the underlying endpoint when connected or {@code null} otherwise.
+ */
+ default EndpointInfo getEndpointInfo() {
+ return null;
+ }
+
/**
* Validates the connection making sure it can be used to execute requests.
*
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java
index 9679eca6b..53604ddb1 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java
@@ -29,6 +29,7 @@ package org.apache.hc.client5.http.classic;
import java.io.IOException;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Internal;
@@ -121,6 +122,13 @@ public interface ExecRuntime {
*/
void upgradeTls(HttpClientContext context) throws IOException;
+ /**
+ * Returns information about the underlying endpoint when connected or {@code null} otherwise.
+ */
+ default EndpointInfo getEndpointInfo() {
+ return null;
+ }
+
/**
* Executes HTTP request using the given context.
*
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncExecRuntime.java
index e4ebbcecd..493f64657 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncExecRuntime.java
@@ -30,6 +30,7 @@ package org.apache.hc.client5.http.impl.async;
import java.io.InterruptedIOException;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.async.AsyncExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig;
@@ -48,6 +49,8 @@ import org.apache.hc.core5.http.nio.command.RequestExecutionCommand;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.Command;
import org.apache.hc.core5.reactor.IOSession;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
+import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
import org.apache.hc.core5.util.Identifiable;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
@@ -233,6 +236,17 @@ class InternalH2AsyncExecRuntime implements AsyncExecRuntime {
throw new UnsupportedOperationException();
}
+ public EndpointInfo getEndpointInfo() {
+ final Endpoint endpoint = sessionRef.get();
+ if (endpoint != null && endpoint.session.isOpen()) {
+ if (endpoint.session instanceof TransportSecurityLayer) {
+ final TlsDetails tlsDetails = ((TransportSecurityLayer) endpoint.session).getTlsDetails();
+ return new EndpointInfo(HttpVersion.HTTP_2, tlsDetails != null ? tlsDetails.getSSLSession() : null);
+ }
+ }
+ return null;
+ }
+
@Override
public Cancellable execute(
final String id,
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java
index bf58c51be..197ac3730 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java
@@ -30,6 +30,7 @@ package org.apache.hc.client5.http.impl.async;
import java.io.InterruptedIOException;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.async.AsyncExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig;
@@ -266,6 +267,11 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
});
}
+ public EndpointInfo getEndpointInfo() {
+ final AsyncConnectionEndpoint endpoint = endpointRef.get();
+ return endpoint != null ? endpoint.getInfo() : null;
+ }
+
@Override
public Cancellable execute(
final String id, final AsyncClientExchangeHandler exchangeHandler, final HttpClientContext context) {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java
index cb86ec7b2..7f5c47a58 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java
@@ -32,6 +32,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.classic.ExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig;
@@ -193,6 +194,12 @@ class InternalExecRuntime implements ExecRuntime, Cancellable {
manager.upgrade(endpoint, context);
}
+ @Override
+ public EndpointInfo getEndpointInfo() {
+ final ConnectionEndpoint endpoint = endpointRef.get();
+ return endpoint != null ? endpoint.getInfo() : null;
+ }
+
@Override
public ClassicHttpResponse execute(
final String id,
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
index 52155a429..da8b0f9b8 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
@@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.hc.client5.http.DnsResolver;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
@@ -684,6 +685,18 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
return requestExecutor.execute(request, getValidatedConnection(), context);
}
+ /**
+ * @since 5.4
+ */
+ @Override
+ public EndpointInfo getInfo() {
+ final ManagedHttpClientConnection connection = this.connRef.get();
+ if (connection != null && connection.isOpen()) {
+ return new EndpointInfo(connection.getProtocolVersion(), connection.getSSLSession());
+ }
+ return null;
+ }
+
}
/**
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
index 8630559c7..153e68ef7 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
@@ -36,6 +36,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.hc.client5.http.DnsResolver;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
@@ -773,5 +774,21 @@ public class PoolingHttpClientConnectionManager
return requestExecutor.execute(request, connection, context);
}
+ /**
+ * @since 5.4
+ */
+ @Override
+ public EndpointInfo getInfo() {
+ final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
+ if (poolEntry != null) {
+ final ManagedHttpClientConnection connection = poolEntry.getConnection();
+ if (connection != null && connection.isOpen()) {
+ return new EndpointInfo(connection.getProtocolVersion(), connection.getSSLSession());
+ }
+ }
+ return null;
+ }
+
}
+
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
index 3aa5c4618..f2cc2ffc7 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
@@ -37,6 +37,7 @@ 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.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
@@ -752,6 +753,19 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
Command.Priority.NORMAL);
}
+ @Override
+ public EndpointInfo getInfo() {
+ final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry = poolEntryRef.get();
+ if (poolEntry != null) {
+ final ManagedAsyncClientConnection connection = poolEntry.getConnection();
+ if (connection != null && connection.isOpen()) {
+ final TlsDetails tlsDetails = connection.getTlsDetails();
+ return new EndpointInfo(connection.getProtocolVersion(), tlsDetails != null ? tlsDetails.getSSLSession() : null);
+ }
+ }
+ return null;
+ }
+
}
/**
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java
index 0bbff7e3b..4a72e9df2 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/io/ConnectionEndpoint.java
@@ -29,6 +29,7 @@ package org.apache.hc.client5.http.io;
import java.io.IOException;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.annotation.ThreadingBehavior;
@@ -97,6 +98,15 @@ public abstract class ConnectionEndpoint implements ModalCloseable {
*/
public abstract void setSocketTimeout(Timeout timeout);
+ /**
+ * Returns information about the endpoint or {@code null} when not connected.
+ *
+ * @since 5.4
+ */
+ public EndpointInfo getInfo() {
+ return null;
+ }
+
/**
* @since 5.4
*/
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncConnectionEndpoint.java b/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncConnectionEndpoint.java
index b92207468..8ae25aae2 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncConnectionEndpoint.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncConnectionEndpoint.java
@@ -30,6 +30,7 @@ package org.apache.hc.client5.http.nio;
import java.io.IOException;
import java.util.concurrent.Future;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.BasicFuture;
@@ -201,4 +202,13 @@ public abstract class AsyncConnectionEndpoint implements ModalCloseable {
return execute(id, requestProducer, responseConsumer, null, null, callback);
}
+ /**
+ * Returns information about the endpoint or {@code null} when not connected.
+ *
+ * @since 5.4
+ */
+ public EndpointInfo getInfo() {
+ return null;
+ }
+
}
(httpcomponents-client) 02/04: Connect executors to update the execution context with the protocol and TLS details upon successful connect
Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
commit 7cf469c5cc7313b214411e99d1822dd2dc0f7d2b
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Mon Jan 15 16:41:59 2024 +0100
Connect executors to update the execution context with the protocol and TLS details upon successful connect
---
.../client5/http/impl/async/AsyncConnectExec.java | 33 +++++++++++++++-------
.../hc/client5/http/impl/classic/ConnectExec.java | 7 +++++
2 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
index 493ab6c9e..5fa79ceb3 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
@@ -34,6 +34,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.hc.client5.http.AuthenticationStrategy;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.RouteTracker;
import org.apache.hc.client5.http.SchemePortResolver;
@@ -176,11 +177,7 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
}));
} else {
if (execRuntime.isEndpointConnected()) {
- try {
- chain.proceed(request, entityProducer, scope, asyncExecCallback);
- } catch (final HttpException | IOException ex) {
- asyncExecCallback.failed(ex);
- }
+ proceedConnected(request, entityProducer, scope, chain, asyncExecCallback);
} else {
proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback);
}
@@ -363,11 +360,7 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
if (LOG.isDebugEnabled()) {
LOG.debug("{} route fully established", exchangeId);
}
- try {
- chain.proceed(request, entityProducer, scope, asyncExecCallback);
- } catch (final HttpException | IOException ex) {
- asyncExecCallback.failed(ex);
- }
+ proceedConnected(request, entityProducer, scope, chain, asyncExecCallback);
break;
default:
@@ -543,4 +536,24 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
return false;
}
+ private void proceedConnected(
+ final HttpRequest request,
+ final AsyncEntityProducer entityProducer,
+ final AsyncExecChain.Scope scope,
+ final AsyncExecChain chain,
+ final AsyncExecCallback asyncExecCallback) {
+ final AsyncExecRuntime execRuntime = scope.execRuntime;
+ final HttpClientContext clientContext = scope.clientContext;
+ final EndpointInfo endpointInfo = execRuntime.getEndpointInfo();
+ if (endpointInfo != null) {
+ clientContext.setProtocolVersion(endpointInfo.getProtocol());
+ clientContext.setAttribute(HttpCoreContext.SSL_SESSION, endpointInfo.getSslSession());
+ }
+ try {
+ chain.proceed(request, entityProducer, scope, asyncExecCallback);
+ } catch (final HttpException | IOException ex) {
+ asyncExecCallback.failed(ex);
+ }
+ }
+
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
index 56fee6aad..54551666b 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
@@ -30,6 +30,7 @@ package org.apache.hc.client5.http.impl.classic;
import java.io.IOException;
import org.apache.hc.client5.http.AuthenticationStrategy;
+import org.apache.hc.client5.http.EndpointInfo;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.RouteTracker;
import org.apache.hc.client5.http.SchemePortResolver;
@@ -62,6 +63,7 @@ import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.protocol.HttpProcessor;
import org.apache.hc.core5.util.Args;
import org.slf4j.Logger;
@@ -185,6 +187,11 @@ public final class ConnectExec implements ExecChainHandler {
} while (step > HttpRouteDirector.COMPLETE);
}
+ final EndpointInfo endpointInfo = execRuntime.getEndpointInfo();
+ if (endpointInfo != null) {
+ context.setProtocolVersion(endpointInfo.getProtocol());
+ context.setAttribute(HttpCoreContext.SSL_SESSION, endpointInfo.getSslSession());
+ }
return chain.proceed(request, scope);
} catch (final IOException | HttpException | RuntimeException ex) {
(httpcomponents-client) 03/04: Classic ExecRuntime to support information responses (1xx)
Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
commit 1111c62dd3bb4b15c7804a6a3fd4386bf0b199c3
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Jan 17 16:58:46 2024 +0100
Classic ExecRuntime to support information responses (1xx)
---
.../testing/sync/TestClientRequestExecution.java | 12 +++++++++++-
.../hc/client5/http/classic/ExecRuntime.java | 19 +++++++++++++++++++
.../http/impl/classic/InternalExecRuntime.java | 12 +++++++++++-
.../client5/http/impl/classic/MainClientExec.java | 2 +-
.../http/impl/classic/TestMainClientExec.java | 22 ++++++++++++++++------
5 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java
index dce158a99..537fd9c54 100644
--- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java
@@ -57,6 +57,7 @@ import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
import org.apache.hc.core5.http.io.HttpClientConnection;
import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
import org.apache.hc.core5.http.io.entity.StringEntity;
@@ -137,8 +138,17 @@ public abstract class TestClientRequestExecution {
final ClassicHttpRequest request,
final HttpClientConnection conn,
final HttpContext context) throws IOException, HttpException {
+ return execute(request, conn, null, context);
+ }
+
+ @Override
+ public ClassicHttpResponse execute(
+ final ClassicHttpRequest request,
+ final HttpClientConnection conn,
+ final HttpResponseInformationCallback informationCallback,
+ final HttpContext context) throws IOException, HttpException {
- final ClassicHttpResponse response = super.execute(request, conn, context);
+ final ClassicHttpResponse response = super.execute(request, conn, informationCallback, context);
final Object marker = context.getAttribute(MARKER);
if (marker == null) {
context.setAttribute(MARKER, Boolean.TRUE);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java
index 53604ddb1..3bb334dd0 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java
@@ -37,6 +37,7 @@ import org.apache.hc.core5.concurrent.CancellableDependency;
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.io.HttpResponseInformationCallback;
import org.apache.hc.core5.util.TimeValue;
/**
@@ -141,6 +142,24 @@ public interface ExecRuntime {
ClassicHttpRequest request,
HttpClientContext context) throws IOException, HttpException;
+ /**
+ * Executes HTTP request using the given context.
+ *
+ * @param id unique operation ID or {@code null}.
+ * @param request the request message.
+ * @param informationCallback information (1xx) response handler
+ * @param context the execution context.
+ *
+ * @since 5.4
+ */
+ default ClassicHttpResponse execute(
+ String id,
+ ClassicHttpRequest request,
+ HttpResponseInformationCallback informationCallback,
+ HttpClientContext context) throws IOException, HttpException {
+ return execute(id, request, context);
+ }
+
/**
* Determines of the connection is considered re-usable.
*
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java
index 7f5c47a58..9206b9432 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java
@@ -48,6 +48,7 @@ import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ConnectionRequestTimeoutException;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
+import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.TimeValue;
@@ -205,6 +206,15 @@ class InternalExecRuntime implements ExecRuntime, Cancellable {
final String id,
final ClassicHttpRequest request,
final HttpClientContext context) throws IOException, HttpException {
+ return execute(id, request, null, context);
+ }
+
+ @Override
+ public ClassicHttpResponse execute(
+ final String id,
+ final ClassicHttpRequest request,
+ final HttpResponseInformationCallback informationCallback,
+ final HttpClientContext context) throws IOException, HttpException {
final ConnectionEndpoint endpoint = ensureValid();
if (!endpoint.isConnected()) {
connectEndpoint(endpoint, context);
@@ -223,7 +233,7 @@ class InternalExecRuntime implements ExecRuntime, Cancellable {
return endpoint.execute(
id,
request,
- requestExecutor::execute,
+ (r, conn, c) -> requestExecutor.execute(r, conn, informationCallback, c),
context);
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java
index 4ff703853..5e8d9f1ee 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java
@@ -113,7 +113,7 @@ public final class MainClientExec implements ExecChainHandler {
httpProcessor.process(request, request.getEntity(), context);
- final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, context);
+ final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, null, context);
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
httpProcessor.process(response, response.getEntity(), context);
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
index ef374748b..b90104132 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
@@ -97,13 +97,14 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenReturn(response);
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
Mockito.verify(httpProcessor).process(request, null, context);
- Mockito.verify(execRuntime).execute("test", request, context);
+ Mockito.verify(execRuntime).execute("test", request, null, context);
Mockito.verify(httpProcessor).process(response, responseEntity, context);
Assertions.assertEquals(route, context.getHttpRoute());
@@ -124,6 +125,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@@ -132,7 +134,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, context);
+ Mockito.verify(execRuntime).execute("test", request, null, context);
Mockito.verify(execRuntime, Mockito.times(1)).markConnectionNonReusable();
Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
@@ -152,6 +154,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@@ -161,7 +164,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, context);
+ Mockito.verify(execRuntime).execute("test", request, null, context);
Mockito.verify(execRuntime).markConnectionNonReusable();
Mockito.verify(execRuntime).releaseEndpoint();
@@ -184,6 +187,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@@ -196,7 +200,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, context);
+ Mockito.verify(execRuntime).execute("test", request, null, context);
Mockito.verify(execRuntime).markConnectionReusable(null, TimeValue.ofMilliseconds(678L));
Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
@@ -214,6 +218,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@@ -226,7 +231,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime).execute("test", request, context);
+ Mockito.verify(execRuntime).execute("test", request, null, context);
Mockito.verify(execRuntime).releaseEndpoint();
Assertions.assertNotNull(finalResponse);
@@ -247,6 +252,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenReturn(response);
Mockito.when(reuseStrategy.keepAlive(
Mockito.same(request),
@@ -255,7 +261,7 @@ public class TestMainClientExec {
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
- Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, context);
+ Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, null, context);
Mockito.verify(execRuntime, Mockito.never()).disconnectEndpoint();
Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
@@ -276,6 +282,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenThrow(new ConnectionShutdownException());
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
@@ -293,6 +300,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenThrow(new RuntimeException("Ka-boom"));
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
@@ -310,6 +318,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenThrow(new HttpException("Ka-boom"));
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
@@ -327,6 +336,7 @@ public class TestMainClientExec {
Mockito.when(execRuntime.execute(
Mockito.anyString(),
Mockito.same(request),
+ Mockito.any(),
Mockito.any())).thenThrow(new IOException("Ka-boom"));
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);