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
