This is an automated email from the ASF dual-hosted git repository.

dkulp pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 1e7169c627d6908118f99dc98d774ffb53dd38c6
Author: Daniel Kulp <[email protected]>
AuthorDate: Fri Mar 17 07:49:01 2023 -0400

    Introduce new HTTP conduit based on javax.net.http.HttpClient which 
supports HTTP/2 without additional deps
    
    (cherry picked from commit 9b36a4bc996615e0ed02795c74167586a2bb11df)
---
 .../http/asyncclient/AsyncHTTPConduit.java         |   4 +-
 .../http/asyncclient/hc5/AsyncHTTPConduit.java     |   4 +-
 .../http/netty/client/NettyHttpConduit.java        |   4 +-
 .../cxf/transport/http/HTTPTransportFactory.java   |   3 +-
 .../org/apache/cxf/transport/http/Headers.java     |   7 +-
 .../cxf/transport/http/HttpClientHTTPConduit.java  | 649 +++++++++++++++++++++
 .../org/apache/cxf/transport/https/SSLUtils.java   | 381 +++++++++++-
 .../cxf/systest/grizzly/EndpointAPITest.java       |   4 +-
 .../jaxrs/ClientHttpConnectionOutInterceptor.java  |  30 +-
 .../systest/jaxrs/JAXRSClientServerBookTest.java   |   3 +
 .../systest/dispatch/DispatchClientServerTest.java |  14 +-
 .../apache/cxf/systest/jaxws/ClientServerTest.java |   9 +-
 .../systest/https/clientauth/ClientAuthTest.java   |  80 +--
 .../systest/https/conduit/HTTPSConduitTest.java    |   9 +-
 .../cxf/systest/ws/addr_wsdl/WSAPureWsdlTest.java  |  10 +-
 .../systest/ws/policy/HTTPClientPolicyTest.java    |   5 +-
 16 files changed, 1119 insertions(+), 97 deletions(-)

diff --git 
a/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java
 
b/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java
index b0294ad7ee..848e98b873 100755
--- 
a/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java
+++ 
b/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java
@@ -64,7 +64,7 @@ import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.service.model.EndpointInfo;
 import org.apache.cxf.transport.http.Address;
 import org.apache.cxf.transport.http.Headers;
-import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
+import org.apache.cxf.transport.http.HttpClientHTTPConduit;
 import 
org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduitFactory.UseAsyncPolicy;
 import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
 import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
@@ -96,7 +96,7 @@ import org.apache.http.nio.util.HeapByteBufferAllocator;
 /**
  *
  */
-public class AsyncHTTPConduit extends URLConnectionHTTPConduit {
+public class AsyncHTTPConduit extends HttpClientHTTPConduit {
     public static final String USE_ASYNC = "use.async.http.conduit";
 
     final AsyncHTTPConduitFactory factory;
diff --git 
a/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java
 
b/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java
index ec15b42b40..1e05f6fa8c 100644
--- 
a/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java
+++ 
b/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java
@@ -64,7 +64,7 @@ import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.service.model.EndpointInfo;
 import org.apache.cxf.transport.http.Address;
 import org.apache.cxf.transport.http.Headers;
-import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
+import org.apache.cxf.transport.http.HttpClientHTTPConduit;
 import 
org.apache.cxf.transport.http.asyncclient.hc5.AsyncHTTPConduitFactory.UseAsyncPolicy;
 import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
 import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
@@ -97,7 +97,7 @@ import org.apache.hc.core5.util.Timeout;
 /**
  * Async HTTP Conduit using Apache HttpClient 5
  */
-public class AsyncHTTPConduit extends URLConnectionHTTPConduit {
+public class AsyncHTTPConduit extends HttpClientHTTPConduit {
     public static final String USE_ASYNC = "use.async.http.conduit";
 
     private final AsyncHTTPConduitFactory factory;
diff --git 
a/rt/transports/http-netty/netty-client/src/main/java/org/apache/cxf/transport/http/netty/client/NettyHttpConduit.java
 
b/rt/transports/http-netty/netty-client/src/main/java/org/apache/cxf/transport/http/netty/client/NettyHttpConduit.java
index de94af8ddf..2828890b24 100644
--- 
a/rt/transports/http-netty/netty-client/src/main/java/org/apache/cxf/transport/http/netty/client/NettyHttpConduit.java
+++ 
b/rt/transports/http-netty/netty-client/src/main/java/org/apache/cxf/transport/http/netty/client/NettyHttpConduit.java
@@ -58,7 +58,7 @@ import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.service.model.EndpointInfo;
 import org.apache.cxf.transport.http.Address;
 import org.apache.cxf.transport.http.Headers;
-import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
+import org.apache.cxf.transport.http.HttpClientHTTPConduit;
 import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
 import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 import org.apache.cxf.version.Version;
@@ -79,7 +79,7 @@ import io.netty.handler.codec.http.HttpResponse;
 import io.netty.handler.ssl.SslHandler;
 
 
-public class NettyHttpConduit extends URLConnectionHTTPConduit implements 
BusLifeCycleListener {
+public class NettyHttpConduit extends HttpClientHTTPConduit implements 
BusLifeCycleListener {
     public static final String USE_ASYNC = "use.async.http.conduit";
     public static final String MAX_RESPONSE_CONTENT_LENGTH =
         "org.apache.cxf.transport.http.netty.maxResponseContentLength";
diff --git 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
index b9088b9a8b..f13e236425 100644
--- 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
+++ 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
@@ -233,7 +233,8 @@ public class HTTPTransportFactory
             conduit = factory.createConduit(this, bus, endpointInfo, target);
         }
         if (conduit == null) {
-            conduit = new URLConnectionHTTPConduit(bus, endpointInfo, target);
+            //conduit = new URLConnectionHTTPConduit(bus, endpointInfo, 
target);
+            conduit = new HttpClientHTTPConduit(bus, endpointInfo, target);
         }
 
         // Spring configure the conduit.
diff --git 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
index db2dbff73c..59ba141089 100644
--- 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
+++ 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
@@ -66,6 +66,7 @@ public class Headers {
     public static final String HTTP_HEADERS_SETCOOKIE = "Set-Cookie";
     public static final String HTTP_HEADERS_LINK = "Link";
     public static final String EMPTY_REQUEST_PROPERTY = 
"org.apache.cxf.empty.request";
+    public static final String USER_AGENT = initUserAgent();
     private static final String SET_EMPTY_REQUEST_CT_PROPERTY = 
"set.content.type.for.empty.request";
     private static final TimeZone TIME_ZONE_GMT = TimeZone.getTimeZone("GMT");
     private static final Logger LOG = LogUtils.getL7dLogger(Headers.class);
@@ -73,7 +74,6 @@ public class Headers {
     private static final List<String> SENSITIVE_HEADERS = 
Arrays.asList("Authorization", "Proxy-Authorization");
     private static final List<Object> SENSITIVE_HEADER_MARKER = 
Arrays.asList("***");
     private static final String ALLOW_LOGGING_SENSITIVE_HEADERS = 
"allow.logging.sensitive.headers";
-    private static final String USER_AGENT = initUserAgent();
 
     private final Message message;
     private final Map<String, List<String>> headers;
@@ -278,7 +278,9 @@ public class Headers {
     }
 
     public void readFromConnection(HttpURLConnection connection) {
-        Map<String, List<String>> origHeaders = connection.getHeaderFields();
+        readFromConnection(connection.getHeaderFields());
+    }
+    public void readFromConnection(Map<String, List<String>> origHeaders) {
         headers.clear();
         for (Entry<String, List<String>> entry : origHeaders.entrySet()) {
             if (entry.getKey() != null) {
@@ -295,7 +297,6 @@ public class Headers {
             }
         }
     }
-
     private static List<String> createMutableList(String val) {
         return new ArrayList<>(Arrays.asList(val));
     }
diff --git 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
new file mode 100644
index 0000000000..1338109a22
--- /dev/null
+++ 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
@@ -0,0 +1,649 @@
+/**
+ * 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.
+ */
+package org.apache.cxf.transport.http;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PushbackInputStream;
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.net.http.HttpClient;
+import java.net.http.HttpClient.Redirect;
+import java.net.http.HttpClient.Version;
+import java.net.http.HttpRequest;
+import java.net.http.HttpRequest.BodyPublisher;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandler;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.net.http.HttpTimeoutException;
+import java.nio.ByteBuffer;
+import java.nio.channels.UnresolvedAddressException;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.helpers.HttpHeaderHelper;
+import org.apache.cxf.io.CacheAndWriteOutputStream;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
+import org.apache.cxf.transport.https.SSLUtils;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+
+
+public class HttpClientHTTPConduit extends URLConnectionHTTPConduit {
+
+    volatile HttpClient client;
+    volatile int lastTlsHash = -1;
+    volatile URI sslURL;
+    
+    public HttpClientHTTPConduit(Bus b, EndpointInfo ei) throws IOException {
+        super(b, ei);
+    }
+
+    public HttpClientHTTPConduit(Bus b, EndpointInfo ei, EndpointReferenceType 
t) throws IOException {
+        super(b, ei, t);
+    }
+    
+    
+    private boolean isSslTargetDifferent(URI lastURL, URI url) {
+        return !lastURL.getScheme().equals(url.getScheme())
+                || !lastURL.getHost().equals(url.getHost())
+                || lastURL.getPort() != url.getPort();
+    }
+    
+    @Override
+    protected void setupConnection(Message message, Address address, 
HTTPClientPolicy csPolicy) throws IOException {
+        URI uri = address.getURI();
+        message.put("http.scheme", uri.getScheme());
+        // check tlsClientParameters from message header
+        TLSClientParameters clientParameters = 
message.get(TLSClientParameters.class);
+        if (clientParameters == null) {
+            clientParameters = tlsClientParameters;
+        }
+
+        Object o = 
message.getContextualProperty("force.urlconnection.http.conduit");
+        //o = true;
+        if ("https".equals(uri.getScheme()) && clientParameters != null) {
+            if (clientParameters.getSSLSocketFactory() != null) {
+                //if they configured in an SSLSocketFactory, we cannot do 
anything
+                //with it as the NIO based transport cannot use socket created 
from
+                //the SSLSocketFactory.
+                o = Boolean.TRUE;
+            }
+            if (clientParameters.getSslContext() != null 
+                && clientParameters.isDisableCNCheck()) {
+                // If they specify their own SSLContext, we cannot handle the
+                // HostnameVerifier so we'll need to use the URLConnection
+                o = Boolean.TRUE;
+            }
+            
+        }
+        if (Boolean.TRUE.equals(o)) {
+            message.put("USING_URLCONNECTION", Boolean.TRUE);
+            super.setupConnection(message, address, csPolicy);
+            return;
+        }
+        
+        if (sslURL != null && isSslTargetDifferent(sslURL, uri)) {
+            sslURL = null;
+            client = null;
+        }
+        HttpClient cl = client;
+        if (cl == null) {
+            int ctimeout = determineConnectionTimeout(message, csPolicy);      
  
+            ProxySelector ps = new ProxySelector() {
+                public List<Proxy> select(URI uri) {
+                    Proxy proxy = proxyFactory.createProxy(csPolicy, uri);
+                    if (proxy !=  null) {
+                        return Arrays.asList(proxy);
+                    }
+                    return ProxySelector.getDefault().select(uri);
+                }
+                public void connectFailed(URI uri, SocketAddress sa, 
IOException ioe) {
+                }
+            };
+            
+            HttpClient.Builder cb = HttpClient.newBuilder()
+                .proxy(ps)
+                .followRedirects(Redirect.NEVER);
+            
+            if (ctimeout > 0) {
+                cb.connectTimeout(Duration.ofMillis(ctimeout));
+            }
+            
+            if ("https".equals(uri.getScheme())) {
+                sslURL = uri;
+                try {
+                    SSLContext sslContext = clientParameters.getSslContext();
+                    if (sslContext == null) {
+                        sslContext = SSLUtils.getSSLContext(clientParameters, 
true);                    
+                        cb.sslContext(sslContext);
+                    }
+                    if (sslContext != null) {
+                        String[] supportedCiphers =  
org.apache.cxf.configuration.jsse.SSLUtils
+                                .getSupportedCipherSuites(sslContext);
+                        String[] cipherSuites = 
org.apache.cxf.configuration.jsse.SSLUtils
+                                
.getCiphersuitesToInclude(clientParameters.getCipherSuites(),
+                                                          
clientParameters.getCipherSuitesFilter(),
+                                                          
sslContext.getSocketFactory().getDefaultCipherSuites(),
+                                                          supportedCiphers,
+                                                          LOG);
+                        
+                        if (clientParameters.getSecureSocketProtocol() != 
null) {
+                            String protocol = 
clientParameters.getSecureSocketProtocol();
+                            SSLParameters params = new 
SSLParameters(cipherSuites, new String[] {protocol});
+                            cb.sslParameters(params);
+                        } else {
+                            SSLParameters params = new 
SSLParameters(cipherSuites, 
+                                                                     new 
String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
+                            cb.sslParameters(params);
+                        }
+                    }
+                } catch (GeneralSecurityException e) {
+                    throw new IOException(e);
+                }
+            }
+            if ("1.1".equals(HTTP_VERSION) || 
"1.1".equals(csPolicy.getVersion())) {
+                cb.version(Version.HTTP_1_1);  
+            }    
+            
+            //TODO
+            //cb.authenticator(Authenticator.getDefault())
+
+            cl = cb.build();
+            client = cl;
+        }        
+        message.put(HttpClient.class, cl);
+        
+        // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
+        String httpRequestMethod =
+            (String)message.get(Message.HTTP_REQUEST_METHOD);
+        if (httpRequestMethod == null) {
+            httpRequestMethod = "POST";
+            message.put(Message.HTTP_REQUEST_METHOD, "POST");
+        }
+        message.put(KEY_HTTP_CONNECTION_ADDRESS, address);        
+    }
+
+    @Override
+    protected OutputStream createOutputStream(Message message, boolean 
needToCacheRequest, boolean isChunking,
+                                              int chunkThreshold)
+        throws IOException {
+        
+        Object o = message.get("USING_URLCONNECTION");
+        if (Boolean.TRUE == o) {
+            return super.createOutputStream(message, needToCacheRequest, 
isChunking, chunkThreshold);
+        }
+        return new HttpClientWrappedOutputStream(message,
+                                                 needToCacheRequest,
+                                                 isChunking,
+                                                 chunkThreshold,
+                                                 getConduitName());
+    }
+
+
+    class HttpClientWrappedOutputStream extends WrappedOutputStream {
+        List<Flow.Subscriber<? super ByteBuffer>> subscribers = new 
LinkedList<>();
+        CompletableFuture<HttpResponse<InputStream>> future;
+        long contentLen = -1;
+        int rtimeout;
+        volatile Throwable exception;
+        volatile boolean connectionComplete;
+        PipedInputStream pin;
+        PipedOutputStream pout;
+        HttpRequest request;
+        
+        
+        HttpClientWrappedOutputStream(Message message,
+                                      boolean needToCacheRequest, boolean 
isChunking,
+                                      int chunkThreshold, String conduitName) {
+            super(message, needToCacheRequest, isChunking,
+                  chunkThreshold, conduitName, 
((Address)message.get(KEY_HTTP_CONNECTION_ADDRESS)).getURI());
+        }
+
+        
+        void addSubscriber(Flow.Subscriber<? super ByteBuffer> subscriber) {
+            subscribers.add(subscriber);
+        }
+
+        @Override
+        protected void setFixedLengthStreamingMode(int i) {
+            contentLen = i;
+        }
+        
+        @Override
+        protected void handleNoOutput() throws IOException {
+            contentLen = 0;
+            pout.close();
+            if (exception != null) {
+                if (exception instanceof IOException) {
+                    throw (IOException)exception;
+                } else {
+                    throw new IOException(exception);
+                }
+            }
+        }
+
+        public void setProtocolHeadersInBuilder(HttpRequest.Builder rb) throws 
IOException {
+            boolean addHeaders = MessageUtils.getContextualBoolean(outMessage, 
Headers.ADD_HEADERS_PROPERTY, false);
+            Headers h = new Headers(outMessage);
+            boolean hasCT = false;
+            for (Map.Entry<String, List<String>>  head : 
h.headerMap().entrySet()) {
+                List<String> headerList = head.getValue();
+                String header = head.getKey();
+                if ("Connection".equals(header)) {
+                    //HttpClient does not allow the Connection header
+                    continue;
+                }
+                if (HttpHeaderHelper.CONTENT_TYPE.equalsIgnoreCase(header)) {
+                    hasCT = true;
+                    continue;
+                }
+                if (addHeaders || 
HttpHeaderHelper.COOKIE.equalsIgnoreCase(header)) {
+                    headerList.forEach(s -> rb.header(header, s));
+                } else {
+                    rb.header(header, String.join(",", headerList));
+                }
+            }
+            if (!h.headerMap().containsKey("User-Agent")) {
+                rb.header("User-Agent", Headers.USER_AGENT);
+            }   
+            if (hasCT || 
!KNOWN_HTTP_VERBS_WITH_NO_CONTENT.contains(outMessage.get(Message.HTTP_REQUEST_METHOD)))
 {
+                rb.header(HttpHeaderHelper.CONTENT_TYPE, 
h.determineContentType());
+            }            
+        }
+        
+        @Override
+        protected void setProtocolHeaders() throws IOException {
+            HttpClient cl = outMessage.get(HttpClient.class);
+            Address address = 
(Address)outMessage.get(KEY_HTTP_CONNECTION_ADDRESS);
+            HTTPClientPolicy csPolicy = getClient(outMessage);
+            String httpRequestMethod =
+                (String)outMessage.get(Message.HTTP_REQUEST_METHOD);
+
+            pin = new PipedInputStream(csPolicy.getChunkLength() <= 0
+                                        ? 4096 : csPolicy.getChunkLength());
+            pout = new PipedOutputStream(pin);
+            
+            
+            
+            if (KNOWN_HTTP_VERBS_WITH_NO_CONTENT.contains(httpRequestMethod)
+                || 
PropertyUtils.isTrue(outMessage.get(Headers.EMPTY_REQUEST_PROPERTY))) {
+                contentLen = 0;
+            }
+
+            BodyPublisher bp = new BodyPublisher() {
+                @Override
+                public void subscribe(Subscriber<? super ByteBuffer> 
subscriber) {
+                    connectionComplete = true;
+                    BodyPublishers.ofInputStream(new Supplier<InputStream>() {
+                        public InputStream get() {
+                            return pin;
+                        }                
+                    }).subscribe(subscriber);
+                }
+
+                @Override
+                public long contentLength() {
+                    return contentLen;
+                }                
+            };
+
+            HttpRequest.Builder rb = HttpRequest.newBuilder()
+                .method(httpRequestMethod, bp);  
+            if ("1.1".equals(HTTP_VERSION) || 
"1.1".equals(csPolicy.getVersion())) {
+                rb.version(Version.HTTP_1_1);  
+            }            
+            try {
+                rb.uri(address.getURI());
+            } catch (IllegalArgumentException iae) {
+                MalformedURLException mex = new 
MalformedURLException(iae.getMessage());
+                mex.initCause(iae);
+                throw mex;
+            }
+            
+            rtimeout = determineReceiveTimeout(outMessage, csPolicy);
+            if (rtimeout > 0) {
+                rb.timeout(Duration.ofMillis(rtimeout));
+            }
+
+            setProtocolHeadersInBuilder(rb);
+                
+            request = rb.build();
+            
+            
+            final BodyHandler<InputStream> handler =  
BodyHandlers.ofInputStream();
+            
+            future = cl.sendAsync(request, handler);
+        }
+        @Override
+        protected void setupWrappedStream() throws IOException {
+            if (cachingForRetransmission) {
+                cachedStream =
+                    new CacheAndWriteOutputStream(pout);
+                wrappedStream = cachedStream;
+            } else {
+                wrappedStream = pout;
+            }
+            if (exception != null) {
+                if (exception instanceof IOException) {
+                    throw (IOException)exception;
+                } else {
+                    throw new IOException(exception);
+                }
+            }
+        }
+        @Override
+        protected String getExceptionMessage(Throwable t) {
+            if (t instanceof ConnectException && t.getMessage() == null) {
+                return "Connection refused";
+            }
+            return t.getMessage();
+        }
+
+        HttpResponse<InputStream> getResponse() throws IOException {
+            try {
+                if (rtimeout > 0) {
+                    return future.get(rtimeout, TimeUnit.MILLISECONDS);
+                }
+                return future.get();
+            } catch (ExecutionException e) {
+                Throwable t = e.getCause();
+                if (t instanceof ConnectException) {
+                    Throwable cause = t.getCause();
+                    if (cause instanceof UnresolvedAddressException) {
+                        UnknownHostException uhe = new UnknownHostException();
+                        uhe.initCause(cause);
+                        throw uhe;
+                    }
+                        
+                }
+                if (t instanceof IOException) {
+                    IOException iot = (IOException)t;
+                    throw iot;
+                }
+                throw new IOException(t);
+            } catch (InterruptedException e) {
+                throw new IOException(e);
+            } catch (TimeoutException e) {
+                throw (IOException)(new 
HttpTimeoutException("Timeout").initCause(e));
+            }
+            
+        }
+        
+        @Override
+        protected int getResponseCode() throws IOException {
+            return getResponse().statusCode();
+        }
+        @Override
+        protected void updateResponseHeaders(Message inMessage) throws 
IOException {
+            Headers h = new Headers(inMessage);
+            HttpResponse<InputStream> rsp = getResponse();
+            h.readFromConnection(rsp.headers().map());
+            if (rsp.headers().map().containsKey(Message.CONTENT_TYPE)) {
+                List<String> s = rsp.headers().allValues(Message.CONTENT_TYPE);
+                inMessage.put(Message.CONTENT_TYPE, String.join(",", s));      
          
+            } else {
+                inMessage.put(Message.CONTENT_TYPE, null);
+            }
+            cookies.readFromHeaders(h);
+        }
+        
+        @Override
+        protected InputStream getInputStream() throws IOException {
+            HttpResponse<InputStream> resp = getResponse();
+            String method = 
(String)outMessage.get(Message.HTTP_REQUEST_METHOD);
+            int sc = resp.statusCode();
+            if ("HEAD".equals(method)) {
+                return null;
+            }
+            if (sc == 204) {
+                //no content
+                return null;
+            }
+            if ("OPTIONS".equals(method) || (sc >= 300 && sc < 500)) {
+                Optional<String> f = 
resp.headers().firstValue("content-length");
+                Optional<String> fChunk = 
resp.headers().firstValue("transfer-encoding");
+                if (f.isPresent()) {
+                    long l = Long.parseLong(f.get());
+                    if (l == 0) {
+                        return null;
+                    }
+                } else if (!fChunk.isPresent() || 
!"chunked".equals(fChunk.get())) {
+                    if (resp.version() == Version.HTTP_2) {
+                        InputStream in = resp.body();
+                        if (in.available() <= 0) {
+                            return null;
+                        }
+                    } else {
+                        return null;
+                    }
+                }
+            }
+            return new FilterInputStream(resp.body()) {
+                boolean closed;
+                @Override
+                public int read() throws IOException {
+                    if (closed) {
+                        throw new IOException("stream is closed");
+                    }
+                    return super.read();
+                }
+
+                @Override
+                public int read(byte[] b) throws IOException {
+                    if (closed) {
+                        throw new IOException("stream is closed");
+                    }
+                    return super.read(b);
+                }
+
+                @Override
+                public int read(byte[] b, int off, int len) throws IOException 
{
+                    if (closed) {
+                        throw new IOException("stream is closed");
+                    }
+                    return super.read(b, off, len);
+                }
+
+                @Override
+                public void close() throws IOException {
+                    closed = true;
+                    super.close();
+                }
+            };
+        }
+
+        @Override
+        protected void closeInputStream() throws IOException {
+            getInputStream().close();
+        }
+
+        @Override
+        protected void handleResponseAsync() throws IOException {
+            handleResponseOnWorkqueue(true, false);
+        }
+        @Override
+        public void thresholdReached() throws IOException {
+            //not really a way to set the chunk size so not really anything to 
do
+            if (exception != null) {
+                if (exception instanceof IOException) {
+                    throw (IOException)exception;
+                } else {
+                    throw new IOException(exception);
+                }
+            }
+        }
+
+        @Override
+        protected String getResponseMessage() throws IOException {
+            try {
+                // HttpClient does not provide access to the actual status 
message
+                // We'll map some of the status codes to match the
+                // returns from the HTTPUrlConnection
+                HttpResponse<InputStream> in = getResponse();
+                switch (in.statusCode()) {
+                case 404:
+                    return "Not Found";
+                case 405:
+                    return "Method Not Allowed";
+                case 503:
+                    return "Service Unavailable";
+                case 200:
+                    return "OK";
+                default:
+                    return in.toString();
+                }
+            } catch (IOException e) {
+                //ignore
+            }
+            return null;
+        }
+        
+        @Override
+        protected HttpsURLConnectionInfo getHttpsURLConnectionInfo() throws 
IOException {
+            Address addrss = 
(Address)outMessage.get(KEY_HTTP_CONNECTION_ADDRESS);
+            URI uri = addrss.getURI();
+            
+            if ("http".equals(uri.getScheme())) {
+                return null;
+            }
+            String method = 
(String)outMessage.get(Message.HTTP_REQUEST_METHOD);
+            HttpClient cl = outMessage.get(HttpClient.class);
+            
+            while (!connectionComplete || 
!cl.sslContext().getClientSessionContext().getIds().hasMoreElements()) {
+                Thread.yield();
+            }
+            byte[] key = 
cl.sslContext().getClientSessionContext().getIds().nextElement();
+            SSLSession session = 
cl.sslContext().getClientSessionContext().getSession(key);
+            Certificate[] localCerts = session.getLocalCertificates();
+            String cipherSuite = session.getCipherSuite();
+            Principal principal = session.getLocalPrincipal();
+            Certificate[] serverCerts = session.getPeerCertificates();
+            Principal peer = session.getPeerPrincipal();
+            
+            HttpsURLConnectionInfo info = new HttpsURLConnectionInfo(uri, 
method, cipherSuite,
+                                                                     
localCerts, principal,
+                                                                     
serverCerts, peer);
+            
+            return info;
+        }
+
+        
+
+        @Override
+        protected boolean usingProxy() {
+            HttpClient cl = outMessage.get(HttpClient.class);
+            return cl.proxy().isPresent();
+        }
+
+
+        @Override
+        protected InputStream getPartialResponse() throws IOException {
+            HttpResponse<InputStream> rsp = getResponse();
+            int responseCode = rsp.statusCode();
+            if (responseCode == HttpURLConnection.HTTP_ACCEPTED
+                || responseCode == HttpURLConnection.HTTP_OK) {
+                try {
+                    PushbackInputStream pbin =
+                        new PushbackInputStream(rsp.body());
+                    int c = pbin.read();
+                    if (c != -1) {
+                        pbin.unread((byte)c);
+                        return pbin;
+                    }
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }            
+            // Don't need to do anything
+            return null;
+        }
+
+        @Override
+        protected void setupNewConnection(String newURL) throws IOException {
+            connectionComplete = false;
+            
+            HTTPClientPolicy cp = getClient(outMessage);
+            Address address;
+            try {
+                if (defaultAddress.getString().equals(newURL)) {
+                    address = defaultAddress;
+                } else {
+                    address = new Address(newURL);
+                }
+            } catch (URISyntaxException e) {
+                throw new IOException(e);
+            }
+            setupConnection(outMessage, address, cp);
+            this.url = address.getURI();
+        }
+
+        @Override
+        protected void retransmitStream() throws IOException {
+            cachedStream.writeCacheTo(pout);
+            pout.close();
+        }
+
+        @Override
+        protected void updateCookiesBeforeRetransmit() throws IOException {
+            Headers h = new Headers();
+            HttpResponse<InputStream> rsp = getResponse();
+            h.readFromConnection(rsp.headers().map());
+            cookies.readFromHeaders(h);
+        }
+
+    }
+
+}
diff --git 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
index f43253fe33..9c6da933ba 100644
--- 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
+++ 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
@@ -18,17 +18,37 @@
  */
 package org.apache.cxf.transport.https;
 
+import java.net.Socket;
+import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.Arrays;
+import java.util.List;
 import java.util.logging.Logger;
 
+import javax.net.ssl.ExtendedSSLSession;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.KeyManager;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.StandardConstants;
 import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
 import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
 
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.configuration.jsse.TLSClientParameters;
@@ -83,6 +103,11 @@ public final class SSLUtils {
     }
 
     public static SSLContext getSSLContext(TLSParameterBase parameters) throws 
GeneralSecurityException {
+        return getSSLContext(parameters, false);
+    }
+    public static SSLContext getSSLContext(TLSParameterBase parameters, 
boolean addHNV)
+        throws GeneralSecurityException {
+        
         // TODO do we need to cache the context
         String provider = parameters.getJsseProvider();
 
@@ -93,7 +118,16 @@ public final class SSLUtils {
             .getInstance(protocol, provider);
 
         final SSLContextInitParameters initParams = 
getSSLContextInitParameters(parameters);
-        ctx.init(initParams.getKeyManagers(), initParams.getTrustManagers(), 
parameters.getSecureRandom());
+        TrustManager[] tms = initParams.getTrustManagers();
+        if (tms != null && addHNV && parameters instanceof 
TLSClientParameters) {
+            HostnameVerifier hnv = 
getHostnameVerifier((TLSClientParameters)parameters);
+            for (int i = 0; i < tms.length; i++) {
+                if (tms[i] instanceof  X509TrustManager) {
+                    tms[i] = new 
X509TrustManagerWrapper((X509TrustManager)tms[i], hnv);                    
+                }
+            }
+        }
+        ctx.init(initParams.getKeyManagers(), tms, 
parameters.getSecureRandom());
 
         if (parameters instanceof TLSClientParameters && 
ctx.getClientSessionContext() != null) {
             
ctx.getClientSessionContext().setSessionTimeout(((TLSClientParameters)parameters).getSslCacheTimeout());
@@ -140,5 +174,350 @@ public final class SSLUtils {
         return clientEngine;
     }
 
+    /*
+     * The classes below are used by the HttpClient implementation to allow 
use of the 
+     * HostNameVerifier that is configured.  HttpClient does not provide a 
hook or
+     * anything to call into the HostNameVerifier after the certs are 
verified.  It
+     * prefers that the Hostname is verified at the same time as the 
certificates
+     * but the only option for hostname is the global on/off system property. 
Thus,
+     * we have to provide a X509TrustManagerWrapper that would turn off the 
+     * EndpointIdentificationAlgorithm and then handle the hostname 
verification
+     * directly. However, since the peer certs are not yet verified, we also 
need to wrapper
+     * the session so the HostnameVerifier things they are.
+     */
+    static class X509TrustManagerWrapper extends X509ExtendedTrustManager {
+
+        private final X509TrustManager delegate;
+        private final X509ExtendedTrustManager extendedDelegate;
+        private final HostnameVerifier verifier;
+        
+        X509TrustManagerWrapper(X509TrustManager delegate, HostnameVerifier 
hnv) {
+            this.delegate = delegate;
+            this.verifier = hnv;
+            this.extendedDelegate = delegate instanceof 
X509ExtendedTrustManager 
+                ? (X509ExtendedTrustManager)delegate : null;
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String s) 
throws CertificateException {
+            delegate.checkClientTrusted(chain, s);
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String s, 
Socket socket)
+                throws CertificateException {
+            if (extendedDelegate != null) {
+                extendedDelegate.checkClientTrusted(chain, s, socket);
+            } else {
+                delegate.checkClientTrusted(chain, s);
+            }
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String s, 
SSLEngine sslEngine)
+                throws CertificateException {
+            if (extendedDelegate != null) {
+                extendedDelegate.checkClientTrusted(chain, s, sslEngine);
+            } else {
+                delegate.checkClientTrusted(chain, s);
+            }
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String s) 
throws CertificateException {
+            System.out.println("cst1: " + s);
+            delegate.checkServerTrusted(chain, s);
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String s, 
Socket socket)
+                throws CertificateException {
+            System.out.println("cst2: " + s);
+            if (extendedDelegate != null) {
+                extendedDelegate.checkServerTrusted(chain, s, socket);
+            } else {
+                delegate.checkServerTrusted(chain, s);
+            }
+        }
+
+        private String getHostName(List<SNIServerName> names) {
+            if (names == null) {
+                return null;
+            }
+            for (SNIServerName n : names) {
+                if (n.getType() != StandardConstants.SNI_HOST_NAME) {
+                    continue;
+                }
+                if (n instanceof SNIHostName) {
+                    SNIHostName hostname = (SNIHostName)n;
+                    return hostname.getAsciiName();
+                }
+            }
+            return null;
+        }
+        
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String s, 
SSLEngine engine)
+                throws CertificateException {
+            if (extendedDelegate != null) {
+                extendedDelegate.checkServerTrusted(chain, s, new 
SSLEngineWrapper(engine));
+                //certificates are valid, now check hostnames
+                SSLSession session = engine.getHandshakeSession();
+                List<SNIServerName> names = null;
+                if (session instanceof ExtendedSSLSession) {
+                    ExtendedSSLSession extSession = 
(ExtendedSSLSession)session;
+                    names = extSession.getRequestedServerNames();
+                }
+                
+                boolean identifiable = false;
+                String peerHost = session.getPeerHost();
+                String hostname = getHostName(names);
+                session = new SSLSessionWrapper(session, chain);
+                if (hostname != null && verifier.verify(hostname, session)) {
+                    identifiable = true;
+                }
+                if (!identifiable && !verifier.verify(peerHost, session)) {
+                    throw new CertificateException("No name matching " + 
peerHost + " found");
+                }                
+            } else {
+                delegate.checkServerTrusted(chain, s);
+            }
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return delegate.getAcceptedIssuers();
+        }
+    }
+    
+    static class SSLEngineWrapper extends SSLEngine {
+        final SSLEngine delegate;
+        SSLEngineWrapper(SSLEngine delegate) {
+            this.delegate = delegate;
+        }
+        public SSLParameters getSSLParameters() {
+            //make sure the hostname verification is not done in the default 
X509 stuff
+            //so we can do it later
+            SSLParameters params = delegate.getSSLParameters();
+            params.setEndpointIdentificationAlgorithm(null);
+            return params;
+        }
+        @Override
+        public SSLSession getHandshakeSession() {
+            return delegate.getHandshakeSession();
+        }
+        @Override
+        public void beginHandshake() throws SSLException {
+            delegate.beginHandshake();
+        }
+
+        @Override
+        public void closeInbound() throws SSLException {
+            delegate.closeInbound();
+        }
+        @Override
+        public void closeOutbound() {
+            delegate.closeOutbound();
+        }
+        @Override
+        public Runnable getDelegatedTask() {
+            return delegate.getDelegatedTask();
+        }
+        @Override
+        public boolean getEnableSessionCreation() {
+            return delegate.getEnableSessionCreation();
+        }
+        @Override
+        public String[] getEnabledCipherSuites() {
+            return delegate.getEnabledCipherSuites();
+        }
+        @Override
+        public String[] getEnabledProtocols() {
+            return delegate.getEnabledProtocols();
+        }
+        @Override
+        public HandshakeStatus getHandshakeStatus() {
+            return delegate.getHandshakeStatus();
+        }
+        @Override
+        public boolean getNeedClientAuth() {
+            return delegate.getNeedClientAuth();
+        }
+        @Override
+        public SSLSession getSession() {
+            return delegate.getSession();
+        }
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return delegate.getSupportedCipherSuites();
+        }
+        @Override
+        public String[] getSupportedProtocols() {
+            return delegate.getSupportedProtocols();
+        }
+        @Override
+        public boolean getUseClientMode() {
+            return delegate.getUseClientMode();
+        }
+        @Override
+        public boolean getWantClientAuth() {
+            return delegate.getWantClientAuth();
+        }
+        @Override
+        public boolean isInboundDone() {
+            return delegate.isInboundDone();
+        }
+        @Override
+        public boolean isOutboundDone() {
+            return delegate.isInboundDone();
+        }
+        @Override
+        public void setEnableSessionCreation(boolean arg0) {
+            delegate.setEnableSessionCreation(arg0);            
+        }
+        @Override
+        public void setEnabledCipherSuites(String[] arg0) {
+            delegate.setEnabledCipherSuites(arg0);
+        }
+        @Override
+        public void setEnabledProtocols(String[] arg0) {
+            delegate.setEnabledProtocols(arg0);
+        }
+        @Override
+        public void setNeedClientAuth(boolean arg0) {
+            delegate.setNeedClientAuth(arg0);
+        }
+        @Override
+        public void setUseClientMode(boolean arg0) {
+            delegate.setUseClientMode(arg0);
+        }
+        @Override
+        public void setWantClientAuth(boolean arg0) {
+            delegate.setWantClientAuth(arg0);
+        }
+        @Override
+        public SSLEngineResult unwrap(ByteBuffer arg0, ByteBuffer[] arg1, int 
arg2, int arg3)
+            throws SSLException {
+            return null;
+        }
+        @Override
+        public SSLEngineResult wrap(ByteBuffer[] arg0, int arg1, int arg2, 
ByteBuffer arg3)
+            throws SSLException {
+            return null;
+        }
+        
+    }
+    
+    static class SSLSessionWrapper implements SSLSession {
+        SSLSession session;
+        Certificate[] certificates;
+        SSLSessionWrapper(SSLSession s, Certificate[] certs) {
+            this.certificates = certs;
+            this.session = s;
+        }
+        @Override
+        public byte[] getId() {
+            return session.getId();
+        }
+
+        @Override
+        public SSLSessionContext getSessionContext() {
+            return session.getSessionContext();
+        }
+
+        @Override
+        public long getCreationTime() {
+            return session.getCreationTime();
+        }
+
+        @Override
+        public long getLastAccessedTime() {
+            return session.getLastAccessedTime();
+        }
+
+        @Override
+        public void invalidate() {
+            session.invalidate();
+        }
+
+        @Override
+        public boolean isValid() {
+            return session.isValid();
+        }
+
+        @Override
+        public void putValue(String s, Object o) {
+            session.putValue(s, o);
+        }
+
+        @Override
+        public Object getValue(String s) {
+            return session.getValue(s);
+        }
+
+        @Override
+        public void removeValue(String s) {
+            session.removeValue(s);
+        }
+
+        @Override
+        public String[] getValueNames() {
+            return session.getValueNames();
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws 
SSLPeerUnverifiedException {
+            return certificates;
+        }
+
+        @Override
+        public Certificate[] getLocalCertificates() {
+            return session.getLocalCertificates();
+        }
+        @Override
+        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+            return session.getPeerPrincipal();
+        }
+
+        @Override
+        public Principal getLocalPrincipal() {
+            return session.getLocalPrincipal();
+        }
+
+        @Override
+        public String getCipherSuite() {
+            return session.getCipherSuite();
+        }
+
+        @Override
+        public String getProtocol() {
+            return session.getProtocol();
+        }
+
+        @Override
+        public String getPeerHost() {
+            return session.getPeerHost();
+        }
+
+        @Override
+        public int getPeerPort() {
+            return session.getPeerPort();
+        }
+
+        @Override
+        public int getPacketBufferSize() {
+            return session.getPacketBufferSize();
+        }
+
+        @Override
+        public int getApplicationBufferSize() {
+            return session.getApplicationBufferSize();
+        }
+        @SuppressWarnings("removal")
+        @Override
+        public javax.security.cert.X509Certificate[] getPeerCertificateChain() 
throws SSLPeerUnverifiedException {
+            return session.getPeerCertificateChain();
+        }
+    };
 
 }
diff --git 
a/systests/container-integration/grizzly/src/test/java/org/apache/cxf/systest/grizzly/EndpointAPITest.java
 
b/systests/container-integration/grizzly/src/test/java/org/apache/cxf/systest/grizzly/EndpointAPITest.java
index b445339cda..7c1b62aac1 100644
--- 
a/systests/container-integration/grizzly/src/test/java/org/apache/cxf/systest/grizzly/EndpointAPITest.java
+++ 
b/systests/container-integration/grizzly/src/test/java/org/apache/cxf/systest/grizzly/EndpointAPITest.java
@@ -51,6 +51,8 @@ public class EndpointAPITest {
 
     @Before
     public void setUp() {
+        //grizzly gets confused with the 2.0 "Connect: upgrade" header
+        System.setProperty("org.apache.cxf.transport.http.forceVersion", 
"1.1");
         currentPort = 
Integer.valueOf(TestUtil.getPortNumber(EndpointAPITest.class, counter++));
         server = new com.sun.grizzly.http.embed.GrizzlyWebServer(currentPort);
     }
@@ -203,4 +205,4 @@ public class EndpointAPITest {
         assertEquals("text/plain", contentType);
     }
 
-}
\ No newline at end of file
+}
diff --git 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ClientHttpConnectionOutInterceptor.java
 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ClientHttpConnectionOutInterceptor.java
index 66d8413ebe..3820587308 100644
--- 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ClientHttpConnectionOutInterceptor.java
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ClientHttpConnectionOutInterceptor.java
@@ -21,7 +21,6 @@ package org.apache.cxf.systest.jaxrs;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.HttpURLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
 
@@ -31,7 +30,7 @@ import org.apache.cxf.phase.AbstractPhaseInterceptor;
 import org.apache.cxf.phase.Phase;
 
 class ClientHttpConnectionOutInterceptor extends 
AbstractPhaseInterceptor<Message> {
-    private Collection<HttpURLConnection> connections = new ArrayList<>();
+    private Collection<Message> messages = new ArrayList<>();
 
     ClientHttpConnectionOutInterceptor() {
         super(Phase.SEND_ENDING);
@@ -39,33 +38,36 @@ class ClientHttpConnectionOutInterceptor extends 
AbstractPhaseInterceptor<Messag
 
     @Override
     public void handleMessage(Message message) throws Fault {
-        final HttpURLConnection connection = (HttpURLConnection) 
message.get("http.connection");
-        synchronized (connections) {
-            connections.add(connection);
+        synchronized (messages) {
+            messages.add(message);
         }
     }
 
     public boolean checkAllClosed() {
-        synchronized (connections) {
-            if (connections.isEmpty()) {
+        synchronized (messages) {
+            if (messages.isEmpty()) {
                 return false;
             }
             
-            return !connections
+            return messages
                 .stream()
-                .anyMatch(this::hasUnclosedInputStream);
+                .anyMatch(this::isClosedInputStream);
         }
     }
     
-    private boolean hasUnclosedInputStream(HttpURLConnection connection) {
+    private boolean isClosedInputStream(Message message) {
         try {
-            final InputStream inputStream = connection.getInputStream();
-            inputStream.read(new byte [] {}); /* 0 bytes to read */
-            return true;
+            final InputStream inputStream = 
message.getExchange().getInMessage().getContent(InputStream.class);
+            if (inputStream == null) {
+                return true;
+            }
+            inputStream.read(new byte [0]); /* 0 bytes to read */
+            return false;
         } catch (IOException ex) {
             // The HttpInputStream throws an IOException in case the input 
stream is already
             // closed (since we actually read nothing).
-            return !ex.getMessage().equals("stream is closed");
+            String msg = ex.getMessage();
+            return msg.contains("closed");
         }
     }
 }
\ No newline at end of file
diff --git 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
index 28a38fcb52..da28c3e4c3 100644
--- 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
@@ -137,6 +137,7 @@ public class JAXRSClientServerBookTest extends 
AbstractBusClientServerTestBase {
         String address = "http://localhost:"; + PORT + "/bookstore/retrieve";
         WebClient wc = WebClient.create(address);
         wc.type("application/xml").accept("application/xml");
+        
WebClient.getConfig(wc).getRequestContext().put("force.urlconnection.http.conduit",
 true);
         if (!useReflection) {
             
WebClient.getConfig(wc).getRequestContext().put("use.httpurlconnection.method.reflection",
 false);
         }
@@ -238,6 +239,7 @@ public class JAXRSClientServerBookTest extends 
AbstractBusClientServerTestBase {
         
WebClient.getConfig(wc).getHttpConduit().getClient().setAutoRedirect(true);
         WebClient.getConfig(wc).getRequestContext().put(
             org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE);
+        
//WebClient.getConfig(wc).getRequestContext().put("force.urlconnection.http.conduit",
 true);
         Response r = wc.get();
         Book book = r.readEntity(Book.class);
         assertEquals(123L, book.getId());
@@ -549,6 +551,7 @@ public class JAXRSClientServerBookTest extends 
AbstractBusClientServerTestBase {
 
     @Test
     public void testProxyGetFormParam() throws Exception {
+        
         BookStore store = JAXRSClientFactory.create("http://localhost:"; + 
PORT, BookStore.class);
         Book book = store.getFormParamsBook(100L, 23L, 123L);
         assertEquals(123L, book.getId());
diff --git 
a/systests/jaxws/src/test/java/org/apache/cxf/systest/dispatch/DispatchClientServerTest.java
 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/dispatch/DispatchClientServerTest.java
index b50a85b91c..0c52a947cb 100644
--- 
a/systests/jaxws/src/test/java/org/apache/cxf/systest/dispatch/DispatchClientServerTest.java
+++ 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/dispatch/DispatchClientServerTest.java
@@ -22,6 +22,8 @@ package org.apache.cxf.systest.dispatch;
 import java.io.InputStream;
 import java.net.SocketTimeoutException;
 import java.net.URL;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpTimeoutException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -200,7 +202,8 @@ public class DispatchClientServerTest extends 
AbstractBusClientServerTestBase {
             //expected
             assertTrue(ex.getCause().getClass().getName(),
                        ex.getCause() instanceof java.net.ConnectException
-                       || ex.getCause() instanceof 
java.net.SocketTimeoutException);
+                       || ex.getCause() instanceof 
java.net.SocketTimeoutException
+                       || ex.getCause()  instanceof 
HttpConnectTimeoutException);
         }
         dispImpl.close();
 
@@ -604,10 +607,13 @@ public class DispatchClientServerTest extends 
AbstractBusClientServerTestBase {
         disp.getRequestContext().put(HTTPClientPolicy.class.getName(), pol);
         Response<Object> o = disp.invokeAsync(later);
         try {
-            o.get(10, TimeUnit.SECONDS);
-            fail("Should have gotten a SocketTimeoutException");
+            Object o2 = o.get(10, TimeUnit.SECONDS);
+            fail("Should have gotten a SocketTimeoutException: " + o2);
+        } catch (TimeoutException tex) {
+            // this is ok
         } catch (ExecutionException ex) {
-            assertTrue(ex.getCause() instanceof SocketTimeoutException);
+            assertTrue(ex.getCause() instanceof SocketTimeoutException
+                       || ex.getCause() instanceof HttpTimeoutException);
         }
 
         later.setRequestType(20000);
diff --git 
a/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java
 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java
index 72b37e6e52..b892ae8618 100644
--- 
a/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java
+++ 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java
@@ -28,6 +28,8 @@ import java.lang.reflect.Proxy;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpTimeoutException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -189,7 +191,7 @@ public class ClientServerTest extends 
AbstractBusClientServerTestBase {
     }
 
     @Test
-    public void testTimeoutConfigutation() throws Exception {
+    public void testTimeoutConfiguration() throws Exception {
 
         SOAPService service = new SOAPService();
         assertNotNull(service);
@@ -206,7 +208,10 @@ public class ClientServerTest extends 
AbstractBusClientServerTestBase {
             if (ex.getCause() != null) {
                 cause = ex.getCause();
             }
-            assertTrue("Timeout cause is expected", cause instanceof 
java.net.SocketTimeoutException);
+            assertTrue("Timeout cause is expected: " + 
cause.getClass().getName(), 
+                       cause instanceof java.net.SocketTimeoutException
+                       || cause instanceof HttpConnectTimeoutException
+                       || cause instanceof HttpTimeoutException);
         }
     }
 
diff --git 
a/systests/transports/src/test/java/org/apache/cxf/systest/https/clientauth/ClientAuthTest.java
 
b/systests/transports/src/test/java/org/apache/cxf/systest/https/clientauth/ClientAuthTest.java
index 6bb653af51..e58a8eb979 100644
--- 
a/systests/transports/src/test/java/org/apache/cxf/systest/https/clientauth/ClientAuthTest.java
+++ 
b/systests/transports/src/test/java/org/apache/cxf/systest/https/clientauth/ClientAuthTest.java
@@ -66,10 +66,17 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
     static final String PORT = allocatePort(ClientAuthServer.class);
     static final String PORT2 = allocatePort(ClientAuthServer.class, 2);
 
-    final Boolean async;
+    final String clientKey;
 
-    public ClientAuthTest(Boolean async) {
-        this.async = async;
+    public ClientAuthTest(String ck) {
+        this.clientKey = ck;
+    }
+    
+    @Parameters(name = "{0}")
+    public static Collection<String> data() {
+        return Arrays.asList(new String[] {"force.urlconnection.http.conduit",
+                                           "defaultConduit",
+                                           "use.async.http.conduit"});
     }
 
     @BeforeClass
@@ -82,12 +89,6 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
         );
     }
 
-    @Parameters(name = "{0}")
-    public static Collection<Boolean> data() {
-
-        return Arrays.asList(new Boolean[] {Boolean.FALSE, Boolean.TRUE});
-    }
-
     @AfterClass
     public static void cleanup() throws Exception {
         stopAllServers();
@@ -111,11 +112,9 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
+        
         assertEquals(port.greetMe("Kitty"), "Hello Kitty");
 
         ((java.io.Closeable)port).close();
@@ -140,10 +139,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         try {
             port.greetMe("Kitty");
@@ -174,10 +170,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         try {
             port.greetMe("Kitty");
@@ -215,10 +208,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
             updateAddressPort(port, PORT);
 
-            // Enable Async
-            if (async) {
-                
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-            }
+            ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
             assertEquals(port.greetMe("Kitty"), "Hello Kitty");
 
@@ -250,10 +240,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT2);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         assertEquals(port.greetMe("Kitty"), "Hello Kitty");
 
@@ -279,10 +266,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT2);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         try {
             port.greetMe("Kitty");
@@ -313,10 +297,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         try {
             port.greetMe("Kitty");
@@ -347,10 +328,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT2);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         try {
             port.greetMe("Kitty");
@@ -431,10 +409,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         Client client = ClientProxy.getClient(port);
         HTTPConduit http = (HTTPConduit) client.getConduit();
@@ -455,10 +430,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT2);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         client = ClientProxy.getClient(port);
         http = (HTTPConduit) client.getConduit();
@@ -480,10 +452,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         // Set up KeyManagers/TrustManagers
         KeyStore ts = KeyStore.getInstance("JKS");
@@ -530,10 +499,7 @@ public class ClientAuthTest extends 
AbstractBusClientServerTestBase {
 
         updateAddressPort(port, PORT);
 
-        // Enable Async
-        if (async) {
-            
((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true);
-        }
+        ((BindingProvider)port).getRequestContext().put(clientKey, true);
 
         // Set up KeyManagers/TrustManagers
         KeyStore ts = KeyStore.getInstance("JKS");
diff --git 
a/systests/transports/src/test/java/org/apache/cxf/systest/https/conduit/HTTPSConduitTest.java
 
b/systests/transports/src/test/java/org/apache/cxf/systest/https/conduit/HTTPSConduitTest.java
index 7a2326cbdd..788f88230e 100644
--- 
a/systests/transports/src/test/java/org/apache/cxf/systest/https/conduit/HTTPSConduitTest.java
+++ 
b/systests/transports/src/test/java/org/apache/cxf/systest/https/conduit/HTTPSConduitTest.java
@@ -594,6 +594,7 @@ public class HTTPSConduitTest extends 
AbstractBusClientServerTestBase {
         // We actually get our answer from Bethal at the end of the
         // redirects.
         configureProxy(ClientProxy.getClient(tarpin));
+
         String answer = tarpin.sayHi();
         assertProxyRequestCount(0);
 
@@ -606,8 +607,8 @@ public class HTTPSConduitTest extends 
AbstractBusClientServerTestBase {
         http.getClient().setMaxRetransmits(1);
 
         try {
-            answer = tarpin.sayHi();
-            fail("Unexpected answer from Tarpin: " + answer);
+            String a2 = tarpin.sayHi();
+            fail("Unexpected answer from Tarpin: " + a2);
         } catch (Exception e) {
             //e.printStackTrace();
         }
@@ -623,8 +624,8 @@ public class HTTPSConduitTest extends 
AbstractBusClientServerTestBase {
         http.setTrustDecider(trustDecider);
 
         try {
-            answer = tarpin.sayHi();
-            fail("Unexpected answer from Tarpin: " + answer);
+            String a2 = tarpin.sayHi();
+            fail("Unexpected answer from Tarpin: " + a2);
         } catch (Exception e) {
             //e.printStackTrace();
             assertTrue("Trust Decider wasn't called correctly",
diff --git 
a/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/addr_wsdl/WSAPureWsdlTest.java
 
b/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/addr_wsdl/WSAPureWsdlTest.java
index 1f494e7e68..75e748c896 100644
--- 
a/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/addr_wsdl/WSAPureWsdlTest.java
+++ 
b/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/addr_wsdl/WSAPureWsdlTest.java
@@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.StringReader;
 import java.net.URL;
+import java.net.http.HttpTimeoutException;
 import java.util.concurrent.ExecutionException;
 
 import javax.xml.namespace.QName;
@@ -90,7 +91,9 @@ public class WSAPureWsdlTest extends AbstractWSATestBase {
             fail("should have failed");
         } catch (Exception t) {
             //expected
-            assertTrue(t.getCause().toString(), t.getCause() instanceof 
java.net.SocketTimeoutException);
+            assertTrue(t.getCause().toString(), 
+                       t.getCause() instanceof java.net.SocketTimeoutException
+                       || t.getCause() instanceof HttpTimeoutException);
         }
 
         AsyncHandler<AddNumbersResponse> handler = new 
AsyncHandler<AddNumbersResponse>() {
@@ -115,8 +118,9 @@ public class WSAPureWsdlTest extends AbstractWSATestBase {
         } catch (Exception t) {
             //expected
             assertTrue(t.getCause().getCause().toString(),
-                       t.getCause().getCause() instanceof 
java.net.ConnectException
-                       ||  t.getCause().getCause() instanceof 
java.net.SocketTimeoutException);
+                       t.getCause() instanceof java.net.ConnectException
+                       || t.getCause().getCause() instanceof 
java.net.ConnectException
+                       || t.getCause().getCause() instanceof 
java.net.SocketTimeoutException);
         }
         synchronized (handler) {
             port.addNumbersAsync(25,  25, handler);
diff --git 
a/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/policy/HTTPClientPolicyTest.java
 
b/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/policy/HTTPClientPolicyTest.java
index 05728334fb..7eabae5510 100644
--- 
a/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/policy/HTTPClientPolicyTest.java
+++ 
b/systests/ws-specs/src/test/java/org/apache/cxf/systest/ws/policy/HTTPClientPolicyTest.java
@@ -22,6 +22,7 @@ package org.apache.cxf.systest.ws.policy;
 import java.io.Closeable;
 import java.net.SocketTimeoutException;
 import java.net.URL;
+import java.net.http.HttpTimeoutException;
 import java.util.logging.Logger;
 
 import javax.xml.namespace.QName;
@@ -161,7 +162,9 @@ public class HTTPClientPolicyTest extends 
AbstractBusClientServerTestBase {
             fail("Didn't get the exception");
         } catch (Exception ex) {
             //ex.printStackTrace();
-            assertTrue(ex.getCause().getClass().getName(), ex.getCause() 
instanceof SocketTimeoutException);
+            assertTrue(ex.getCause().getClass().getName(), 
+                       ex.getCause() instanceof SocketTimeoutException
+                       || ex.getCause() instanceof HttpTimeoutException);
         }
 
         // pingMe - policy attached to binding operation fault should have no 
effect

Reply via email to