diff --git a/src/main/java/io/vertx/core/Vertx.java b/src/main/java/io/vertx/core/Vertx.java
index 56f769dc1ed..14644d7512c 100644
--- a/src/main/java/io/vertx/core/Vertx.java
+++ b/src/main/java/io/vertx/core/Vertx.java
@@ -63,6 +63,7 @@
* Please see the user manual for more detailed usage information.
*
* @author Tim Fox
+ * @author Julien Viet
*/
@VertxGen
public interface Vertx extends Measured {
diff --git a/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java b/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java
index 6eb2b70b9c3..fa60e3e1317 100644
--- a/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java
+++ b/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java
@@ -19,6 +19,7 @@
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.impl.future.PromiseInternal;
import io.vertx.core.net.*;
+import io.vertx.core.net.impl.NetClientBuilder;
import io.vertx.core.net.impl.NetClientImpl;
import io.vertx.core.net.impl.ProxyFilter;
import io.vertx.core.net.impl.pool.ConnectionManager;
@@ -146,17 +147,16 @@ public HttpClientImpl(VertxInternal vertx, HttpClientOptions options, CloseFutur
throw new IllegalStateException("Cannot have pipelining with no keep alive");
}
this.proxyFilter = options.getNonProxyHosts() != null ? ProxyFilter.nonProxyHosts(options.getNonProxyHosts()) : ProxyFilter.DEFAULT_PROXY_FILTER;
- this.netClient = new NetClientImpl(
- vertx,
- metrics,
- new NetClientOptions(options)
- .setHostnameVerificationAlgorithm(options.isVerifyHost() ? "HTTPS": "")
- .setProxyOptions(null)
- .setApplicationLayerProtocols(alpnVersions
- .stream()
- .map(HttpVersion::alpnName)
- .collect(Collectors.toList())),
- closeFuture);
+ this.netClient = (NetClientImpl) new NetClientBuilder(vertx, new NetClientOptions(options)
+ .setHostnameVerificationAlgorithm(options.isVerifyHost() ? "HTTPS": "")
+ .setProxyOptions(null)
+ .setApplicationLayerProtocols(alpnVersions
+ .stream()
+ .map(HttpVersion::alpnName)
+ .collect(Collectors.toList())))
+ .metrics(metrics)
+ .closeFuture(closeFuture)
+ .build();
webSocketCM = webSocketConnectionManager();
httpCM = httpConnectionManager();
if (options.getPoolCleanerPeriod() > 0 && (options.getKeepAliveTimeout() > 0L || options.getHttp2KeepAliveTimeout() > 0L)) {
diff --git a/src/main/java/io/vertx/core/http/impl/HttpServerImpl.java b/src/main/java/io/vertx/core/http/impl/HttpServerImpl.java
index 47b500d0363..7d379cd6dda 100644
--- a/src/main/java/io/vertx/core/http/impl/HttpServerImpl.java
+++ b/src/main/java/io/vertx/core/http/impl/HttpServerImpl.java
@@ -200,13 +200,11 @@ protected Handler childHandler(ContextInternal context, SocketAddress a
@Override
protected SSLHelper createSSLHelper() {
- return super.createSSLHelper()
- .setApplicationProtocols(options
- .getAlpnVersions()
- .stream()
- .map(HttpVersion::alpnName)
- .collect(Collectors.toList())
- );
+ return new SSLHelper(options, options
+ .getAlpnVersions()
+ .stream()
+ .map(HttpVersion::alpnName)
+ .collect(Collectors.toList()));
}
@Override
diff --git a/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java b/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java
index af496c2596e..1cabb7ed838 100644
--- a/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java
+++ b/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java
@@ -10,6 +10,7 @@
*/
package io.vertx.core.http.impl;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.compression.CompressionOptions;
@@ -19,6 +20,7 @@
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SniHandler;
+import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleState;
@@ -134,14 +136,7 @@ public void handle(Channel ch) {
private void configurePipeline(Channel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (sslHelper.isSSL()) {
- if (options.isSni()) {
- SniHandler sniHandler = new SniHandler(sslHelper.serverNameMapper(vertx));
- pipeline.addLast(sniHandler);
- } else {
- SslHandler handler = new SslHandler(sslHelper.createEngine(vertx));
- handler.setHandshakeTimeout(sslHelper.getSslHandshakeTimeout(), sslHelper.getSslHandshakeTimeoutUnit());
- pipeline.addLast("ssl", handler);
- }
+ pipeline.addLast("ssl", sslHelper.createHandler(vertx));
ChannelPromise p = ch.newPromise();
pipeline.addLast("handshaker", new SslHandshakeCompletionHandler(p));
p.addListener(future -> {
diff --git a/src/main/java/io/vertx/core/impl/VertxImpl.java b/src/main/java/io/vertx/core/impl/VertxImpl.java
index 8d07939bf27..13c0ffdb49b 100644
--- a/src/main/java/io/vertx/core/impl/VertxImpl.java
+++ b/src/main/java/io/vertx/core/impl/VertxImpl.java
@@ -31,6 +31,7 @@
import io.vertx.core.eventbus.impl.clustered.ClusteredEventBus;
import io.vertx.core.file.FileSystem;
import io.vertx.core.impl.btc.BlockedThreadChecker;
+import io.vertx.core.net.impl.NetClientBuilder;
import io.vertx.core.spi.file.FileResolver;
import io.vertx.core.file.impl.FileSystemImpl;
import io.vertx.core.file.impl.WindowsFileSystem;
@@ -296,19 +297,14 @@ public NetServer createNetServer(NetServerOptions options) {
return new NetServerImpl(this, options);
}
- @Override
- public NetClient createNetClient(NetClientOptions options, CloseFuture closeFuture) {
- NetClientImpl client = new NetClientImpl(this, options, closeFuture);
- closeFuture.add(client);
- return client;
- }
-
public NetClient createNetClient(NetClientOptions options) {
CloseFuture closeFuture = new CloseFuture(log);
- NetClient client = createNetClient(options, closeFuture);
CloseFuture fut = resolveCloseFuture();
fut.add(closeFuture);
- return client;
+ NetClientBuilder builder = new NetClientBuilder(this, options);
+ builder.metrics(metricsSPI() != null ? metricsSPI().createNetClientMetrics(options) : null);
+ builder.closeFuture(closeFuture);
+ return builder.build();
}
@Override
diff --git a/src/main/java/io/vertx/core/impl/VertxInternal.java b/src/main/java/io/vertx/core/impl/VertxInternal.java
index 36da3b9dffc..4ecbc7c8314 100644
--- a/src/main/java/io/vertx/core/impl/VertxInternal.java
+++ b/src/main/java/io/vertx/core/impl/VertxInternal.java
@@ -21,8 +21,8 @@
import io.vertx.core.http.impl.HttpServerImpl;
import io.vertx.core.impl.btc.BlockedThreadChecker;
import io.vertx.core.impl.future.PromiseInternal;
-import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
+import io.vertx.core.net.impl.NetClientBuilder;
import io.vertx.core.net.impl.NetServerImpl;
import io.vertx.core.net.impl.ServerID;
import io.vertx.core.net.impl.TCPServerBase;
@@ -46,6 +46,7 @@
* developers creating vert.x applications
*
* @author Tim Fox
+ * @author Julien Viet
*/
public interface VertxInternal extends Vertx {
@@ -85,15 +86,6 @@ public interface VertxInternal extends Vertx {
Transport transport();
- /**
- * Create a TCP/SSL client using the specified options and close future
- *
- * @param options the options to use
- * @param closeFuture the close future
- * @return the client
- */
- NetClient createNetClient(NetClientOptions options, CloseFuture closeFuture);
-
/**
* Create a HTTP/HTTPS client using the specified options and close future
*
diff --git a/src/main/java/io/vertx/core/impl/VertxWrapper.java b/src/main/java/io/vertx/core/impl/VertxWrapper.java
index b563d67bf84..ebdd4ed9b98 100644
--- a/src/main/java/io/vertx/core/impl/VertxWrapper.java
+++ b/src/main/java/io/vertx/core/impl/VertxWrapper.java
@@ -414,11 +414,6 @@ public Transport transport() {
return delegate.transport();
}
- @Override
- public NetClient createNetClient(NetClientOptions options, CloseFuture closeFuture) {
- return delegate.createNetClient(options, closeFuture);
- }
-
@Override
public HttpClient createHttpClient(HttpClientOptions options, CloseFuture closeFuture) {
return delegate.createHttpClient(options, closeFuture);
diff --git a/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java b/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java
index b68c90593b1..30ea6abae16 100644
--- a/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java
+++ b/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java
@@ -11,10 +11,12 @@
package io.vertx.core.net;
+import io.netty.handler.ssl.SslProvider;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.core.json.JsonObject;
+import io.vertx.core.spi.tls.DefaultSslContextFactory;
+import io.vertx.core.spi.tls.SslContextFactory;
-import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
/**
@@ -70,4 +72,9 @@ public JsonObject toJson() {
public JdkSSLEngineOptions copy() {
return new JdkSSLEngineOptions();
}
+
+ @Override
+ public SslContextFactory sslContextFactory() {
+ return new DefaultSslContextFactory(SslProvider.JDK, false);
+ }
}
diff --git a/src/main/java/io/vertx/core/net/OpenSSLEngineOptions.java b/src/main/java/io/vertx/core/net/OpenSSLEngineOptions.java
index d59842cac0e..b1d36d51694 100644
--- a/src/main/java/io/vertx/core/net/OpenSSLEngineOptions.java
+++ b/src/main/java/io/vertx/core/net/OpenSSLEngineOptions.java
@@ -12,8 +12,11 @@
package io.vertx.core.net;
import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.SslProvider;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.core.json.JsonObject;
+import io.vertx.core.spi.tls.DefaultSslContextFactory;
+import io.vertx.core.spi.tls.SslContextFactory;
/**
* Configures a {@link TCPSSLOptions} to use OpenSsl.
@@ -86,4 +89,9 @@ public JsonObject toJson() {
public OpenSSLEngineOptions copy() {
return new OpenSSLEngineOptions(this);
}
+
+ @Override
+ public SslContextFactory sslContextFactory() {
+ return new DefaultSslContextFactory(SslProvider.OPENSSL, sessionCacheEnabled);
+ }
}
diff --git a/src/main/java/io/vertx/core/net/SSLEngineOptions.java b/src/main/java/io/vertx/core/net/SSLEngineOptions.java
index adde61d6ff8..34bad418226 100644
--- a/src/main/java/io/vertx/core/net/SSLEngineOptions.java
+++ b/src/main/java/io/vertx/core/net/SSLEngineOptions.java
@@ -11,6 +11,8 @@
package io.vertx.core.net;
+import io.vertx.core.spi.tls.SslContextFactory;
+
/**
* The SSL engine implementation to use in a Vert.x server or client.
*
@@ -20,4 +22,9 @@ public abstract class SSLEngineOptions {
public abstract SSLEngineOptions copy();
+ /**
+ * @return a {@link SslContextFactory} that will be used to produce the Netty {@code SslContext}
+ */
+ public abstract SslContextFactory sslContextFactory();
+
}
diff --git a/src/main/java/io/vertx/core/net/impl/ChannelProvider.java b/src/main/java/io/vertx/core/net/impl/ChannelProvider.java
index a0aa7b02395..7aca6f0c32f 100644
--- a/src/main/java/io/vertx/core/net/impl/ChannelProvider.java
+++ b/src/main/java/io/vertx/core/net/impl/ChannelProvider.java
@@ -12,8 +12,10 @@
package io.vertx.core.net.impl;
import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.handler.proxy.*;
+import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.resolver.NoopAddressResolverGroup;
@@ -107,8 +109,7 @@ private void connect(Handler handler, SocketAddress remoteAddress, Sock
private void initSSL(Handler handler, SocketAddress peerAddress, String serverName, boolean ssl, boolean useAlpn, Channel ch, Promise channelHandler) {
if (ssl) {
- SslHandler sslHandler = new SslHandler(sslHelper.createEngine(context.owner(), peerAddress, serverName, useAlpn));
- sslHandler.setHandshakeTimeout(sslHelper.getSslHandshakeTimeout(), sslHelper.getSslHandshakeTimeoutUnit());
+ SslHandler sslHandler = sslHelper.createSslHandler(context.owner(), peerAddress, serverName, useAlpn);
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("ssl", sslHandler);
pipeline.addLast(new ChannelInboundHandlerAdapter() {
diff --git a/src/main/java/io/vertx/core/net/impl/NetClientBuilder.java b/src/main/java/io/vertx/core/net/impl/NetClientBuilder.java
new file mode 100644
index 00000000000..9a2fe3ab18e
--- /dev/null
+++ b/src/main/java/io/vertx/core/net/impl/NetClientBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011-2022 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package io.vertx.core.net.impl;
+
+import io.vertx.core.impl.CloseFuture;
+import io.vertx.core.impl.VertxInternal;
+import io.vertx.core.net.NetClient;
+import io.vertx.core.net.NetClientOptions;
+import io.vertx.core.spi.metrics.TCPMetrics;
+
+/**
+ * A builder to configure NetClient plugins.
+ */
+public class NetClientBuilder {
+
+ private VertxInternal vertx;
+ private CloseFuture closeFuture;
+ private NetClientOptions options;
+ private TCPMetrics metrics;
+
+ public NetClientBuilder(VertxInternal vertx, NetClientOptions options) {
+ this.vertx = vertx;
+ this.options = options;
+ }
+
+ public NetClientBuilder closeFuture(CloseFuture closeFuture) {
+ this.closeFuture = closeFuture;
+ return this;
+ }
+
+ public NetClientBuilder metrics(TCPMetrics metrics) {
+ this.metrics = metrics;
+ return this;
+ }
+
+ public NetClient build() {
+ NetClientImpl client = new NetClientImpl(vertx, metrics, options, closeFuture);
+ closeFuture.add(client);
+ return client;
+ }
+}
diff --git a/src/main/java/io/vertx/core/net/impl/NetClientImpl.java b/src/main/java/io/vertx/core/net/impl/NetClientImpl.java
index 9c1b07cf77c..37bb6acb3bf 100644
--- a/src/main/java/io/vertx/core/net/impl/NetClientImpl.java
+++ b/src/main/java/io/vertx/core/net/impl/NetClientImpl.java
@@ -44,7 +44,6 @@
import io.vertx.core.spi.metrics.MetricsProvider;
import io.vertx.core.spi.metrics.TCPMetrics;
-import javax.net.ssl.SSLContext;
import java.io.FileNotFoundException;
import java.net.ConnectException;
import java.util.Objects;
@@ -74,15 +73,11 @@ public class NetClientImpl implements MetricsProvider, NetClient, Closeable {
private final CloseFuture closeFuture;
private final Predicate proxyFilter;
- public NetClientImpl(VertxInternal vertx, NetClientOptions options, CloseFuture closeFuture) {
- this(vertx, vertx.metricsSPI() != null ? vertx.metricsSPI().createNetClientMetrics(options) : null, options, closeFuture);
- }
-
public NetClientImpl(VertxInternal vertx, TCPMetrics metrics, NetClientOptions options, CloseFuture closeFuture) {
this.vertx = vertx;
this.channelGroup = new DefaultChannelGroup(vertx.getAcceptorEventLoopGroup().next());
this.options = new NetClientOptions(options);
- this.sslHelper = new SSLHelper(options, options.getKeyCertOptions(), options.getTrustOptions()).setApplicationProtocols(options.getApplicationLayerProtocols());
+ this.sslHelper = new SSLHelper(options, options.getApplicationLayerProtocols());
this.metrics = metrics;
this.logEnabled = options.getLogActivity();
this.idleTimeout = options.getIdleTimeout();
@@ -91,8 +86,6 @@ public NetClientImpl(VertxInternal vertx, TCPMetrics metrics, NetClientOptions o
this.idleTimeoutUnit = options.getIdleTimeoutUnit();
this.closeFuture = closeFuture;
this.proxyFilter = options.getNonProxyHosts() != null ? ProxyFilter.nonProxyHosts(options.getNonProxyHosts()) : ProxyFilter.DEFAULT_PROXY_FILTER;
-
- sslHelper.validate(vertx);
}
protected void initChannel(ChannelPipeline pipeline) {
@@ -177,13 +170,6 @@ public boolean isMetricsEnabled() {
return metrics != null;
}
- /**
- * Must be called before calling connect().
- */
- public void setSuppliedSSLContext(SSLContext suppliedSSLContext) {
- sslHelper.setSuppliedSslContext(suppliedSSLContext);
- }
-
@Override
public Metrics getMetrics() {
return metrics;
@@ -225,7 +211,41 @@ private void connect(SocketAddress remoteAddress, String serverName, Promise connectHandler,
+ ContextInternal context,
+ int remainingAttempts) {
+ checkClosed();
+ sslHelper.init(context).onComplete(ar -> {
+ if (ar.succeeded()) {
+ connectInternal2(proxyOptions, remoteAddress, peerAddress, serverName, ssl, useAlpn, registerWriteHandlers, connectHandler, context, remainingAttempts);
+ } else {
+ connectHandler.fail(ar.cause());
+ }
+ });
+ }
+
+ private void connectInternal2(ProxyOptions proxyOptions,
SocketAddress remoteAddress,
SocketAddress peerAddress,
String serverName,
@@ -235,8 +255,6 @@ public void connectInternal(ProxyOptions proxyOptions,
Promise connectHandler,
ContextInternal context,
int remainingAttempts) {
- checkClosed();
-
EventLoop eventLoop = context.nettyEventLoop();
if (eventLoop.inEventLoop()) {
@@ -272,7 +290,7 @@ public void connectInternal(ProxyOptions proxyOptions,
}
});
} else {
- eventLoop.execute(() -> connectInternal(proxyOptions, remoteAddress, peerAddress, serverName, ssl, useAlpn, registerWriteHandlers, connectHandler, context, remainingAttempts));
+ eventLoop.execute(() -> connectInternal2(proxyOptions, remoteAddress, peerAddress, serverName, ssl, useAlpn, registerWriteHandlers, connectHandler, context, remainingAttempts));
}
}
diff --git a/src/main/java/io/vertx/core/net/impl/NetServerImpl.java b/src/main/java/io/vertx/core/net/impl/NetServerImpl.java
index 4a49d79ebb6..38503082d0f 100644
--- a/src/main/java/io/vertx/core/net/impl/NetServerImpl.java
+++ b/src/main/java/io/vertx/core/net/impl/NetServerImpl.java
@@ -11,12 +11,14 @@
package io.vertx.core.net.impl;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.haproxy.HAProxyMessageDecoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SniHandler;
+import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
@@ -252,14 +254,7 @@ public void handle(Channel ch) {
private void configurePipeline(Channel ch) {
if (sslHelper.isSSL()) {
- if (options.isSni()) {
- SniHandler sniHandler = new SniHandler(sslHelper.serverNameMapper(vertx));
- ch.pipeline().addLast("ssl", sniHandler);
- } else {
- SslHandler sslHandler = new SslHandler(sslHelper.createEngine(vertx));
- sslHandler.setHandshakeTimeout(sslHelper.getSslHandshakeTimeout(), sslHelper.getSslHandshakeTimeoutUnit());
- ch.pipeline().addLast("ssl", sslHandler);
- }
+ ch.pipeline().addLast("ssl", sslHelper.createHandler(vertx));
ChannelPromise p = ch.newPromise();
ch.pipeline().addLast("handshaker", new SslHandshakeCompletionHandler(p));
p.addListener(future -> {
diff --git a/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java b/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java
index 989686d7d9c..71e80c2da9f 100644
--- a/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java
+++ b/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java
@@ -12,9 +12,11 @@
package io.vertx.core.net.impl;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.ssl.SniHandler;
+import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCounted;
@@ -337,7 +339,7 @@ public NetSocket upgradeToSsl(Handler> handler) {
@Override
public NetSocket upgradeToSsl(String serverName, Handler> handler) {
- ChannelOutboundHandler sslHandler = (ChannelOutboundHandler) chctx.pipeline().get("ssl");
+ ChannelHandler sslHandler = chctx.pipeline().get("ssl");
if (sslHandler == null) {
ChannelPromise p = chctx.newPromise();
chctx.pipeline().addFirst("handshaker", new SslHandshakeCompletionHandler(p));
@@ -353,15 +355,9 @@ public NetSocket upgradeToSsl(String serverName, Handler> hand
}
});
if (remoteAddress != null) {
- sslHandler = new SslHandler(helper.createEngine(vertx, remoteAddress, serverName, false));
- ((SslHandler) sslHandler).setHandshakeTimeout(helper.getSslHandshakeTimeout(), helper.getSslHandshakeTimeoutUnit());
+ sslHandler = helper.createSslHandler(vertx, remoteAddress, serverName, false);
} else {
- if (helper.isSNI()) {
- sslHandler = new SniHandler(helper.serverNameMapper(vertx));
- } else {
- sslHandler = new SslHandler(helper.createEngine(vertx));
- ((SslHandler) sslHandler).setHandshakeTimeout(helper.getSslHandshakeTimeout(), helper.getSslHandshakeTimeoutUnit());
- }
+ sslHandler = helper.createHandler(vertx);
}
chctx.pipeline().addFirst("ssl", sslHandler);
}
diff --git a/src/main/java/io/vertx/core/net/impl/SSLHelper.java b/src/main/java/io/vertx/core/net/impl/SSLHelper.java
index bdb8f6cd433..8935955c050 100755
--- a/src/main/java/io/vertx/core/net/impl/SSLHelper.java
+++ b/src/main/java/io/vertx/core/net/impl/SSLHelper.java
@@ -12,13 +12,21 @@
package io.vertx.core.net.impl;
import io.netty.buffer.ByteBufAllocator;
-import io.netty.handler.ssl.*;
+import io.netty.channel.ChannelHandler;
+import io.netty.handler.ssl.DelegatingSslContext;
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.SniHandler;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslHandler;
import io.netty.util.Mapping;
+import io.vertx.core.Future;
+import io.vertx.core.Promise;
import io.vertx.core.VertxException;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.ClientAuth;
-import io.vertx.core.http.HttpClientOptions;
+import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.VertxInternal;
+import io.vertx.core.impl.future.PromiseInternal;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.net.ClientOptionsBase;
@@ -31,11 +39,12 @@
import io.vertx.core.net.SocketAddress;
import io.vertx.core.net.TCPSSLOptions;
import io.vertx.core.net.TrustOptions;
+import io.vertx.core.spi.tls.SslContextFactory;
import javax.net.ssl.*;
import java.io.ByteArrayInputStream;
+import java.security.KeyStore;
import java.security.cert.CRL;
-import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -43,24 +52,30 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
- *
- * This is a pretty sucky class - could do with a refactoring
- *
* @author Tim Fox
+ * @author Julien Viet
*/
public class SSLHelper {
+ private static final EnumMap CLIENT_AUTH_MAPPING = new EnumMap<>(ClientAuth.class);
+
+ static {
+ CLIENT_AUTH_MAPPING.put(ClientAuth.REQUIRED, io.netty.handler.ssl.ClientAuth.REQUIRE);
+ CLIENT_AUTH_MAPPING.put(ClientAuth.REQUEST, io.netty.handler.ssl.ClientAuth.OPTIONAL);
+ CLIENT_AUTH_MAPPING.put(ClientAuth.NONE, io.netty.handler.ssl.ClientAuth.NONE);
+ }
+
/**
* Resolve the ssl engine options to use for properly running the configured options.
*/
- public static SSLEngineOptions resolveEngineOptions(TCPSSLOptions options) {
- SSLEngineOptions engineOptions = options.getSslEngineOptions();
+ public static SSLEngineOptions resolveEngineOptions(SSLEngineOptions engineOptions, boolean useAlpn) {
if (engineOptions == null) {
- if (options.isUseAlpn()) {
+ if (useAlpn) {
if (JdkSSLEngineOptions.isAlpnAvailable()) {
engineOptions = new JdkSSLEngineOptions();
} else if (OpenSSLEngineOptions.isAlpnAvailable()) {
@@ -81,7 +96,7 @@ public static SSLEngineOptions resolveEngineOptions(TCPSSLOptions options) {
}
}
- if (options.isUseAlpn()) {
+ if (useAlpn) {
if (engineOptions instanceof JdkSSLEngineOptions) {
if (!JdkSSLEngineOptions.isAlpnAvailable()) {
throw new VertxException("ALPN not available for JDK SSL/TLS engine");
@@ -98,216 +113,255 @@ public static SSLEngineOptions resolveEngineOptions(TCPSSLOptions options) {
private static final Logger log = LoggerFactory.getLogger(SSLHelper.class);
- private boolean ssl;
- private boolean sni;
- private long sslHandshakeTimeout;
- private TimeUnit sslHandshakeTimeoutUnit;
- private KeyCertOptions keyCertOptions;
- private TrustOptions trustOptions;
- private boolean trustAll;
- private ArrayList crlPaths;
- private ArrayList crlValues;
- private ClientAuth clientAuth = ClientAuth.NONE;
- private Set enabledCipherSuites;
- private boolean openSsl;
- private boolean client;
- private boolean useAlpn;
- private List applicationProtocols;
- private Set enabledProtocols;
-
- private String endpointIdentificationAlgorithm = "";
-
+ private final boolean ssl;
+ private final boolean sni;
+ private final long sslHandshakeTimeout;
+ private final TimeUnit sslHandshakeTimeoutUnit;
+ private final boolean trustAll;
+ private final ClientAuth clientAuth;
+ private final boolean client;
+ private final boolean useAlpn;
+ private final Set enabledProtocols;
+ private final String endpointIdentificationAlgorithm;
+ private final SSLEngineOptions sslEngineOptions;
+ private final KeyCertOptions keyCertOptions;
+ private final TrustOptions trustOptions;
+ private final ArrayList crlPaths;
+ private final ArrayList crlValues;
+ private final Set enabledCipherSuites;
+ private final List applicationProtocols;
+
+ private Future> sslProvider;
private SslContext[] sslContexts = new SslContext[2];
- private Map[] sslContextMaps = new Map[] {
+ private Map[] sslContextMaps = new Map[] {
new ConcurrentHashMap<>(), new ConcurrentHashMap<>()
};
- private boolean openSslSessionCacheEnabled = true;
- private volatile SSLContext suppliedSslContext;
- private SSLHelper(TCPSSLOptions options, KeyCertOptions keyCertOptions, TrustOptions trustOptions) {
- SSLEngineOptions sslEngineOptions = resolveEngineOptions(options);
+ public SSLHelper(TCPSSLOptions options, List applicationProtocols) {
+ this.sslEngineOptions = options.getSslEngineOptions();
+ this.crlPaths = new ArrayList<>(options.getCrlPaths());
+ this.crlValues = new ArrayList<>(options.getCrlValues());
+ this.enabledCipherSuites = new HashSet<>(options.getEnabledCipherSuites());
this.ssl = options.isSsl();
this.sslHandshakeTimeout = options.getSslHandshakeTimeout();
this.sslHandshakeTimeoutUnit = options.getSslHandshakeTimeoutUnit();
- this.keyCertOptions = keyCertOptions;
- this.trustOptions = trustOptions;
- this.crlPaths = new ArrayList<>(options.getCrlPaths());
- this.crlValues = new ArrayList<>(options.getCrlValues());
- this.enabledCipherSuites = options.getEnabledCipherSuites();
- this.openSsl = sslEngineOptions instanceof OpenSSLEngineOptions;
this.useAlpn = options.isUseAlpn();
this.enabledProtocols = options.getEnabledSecureTransportProtocols();
- this.openSslSessionCacheEnabled = (sslEngineOptions instanceof OpenSSLEngineOptions) && ((OpenSSLEngineOptions) sslEngineOptions).isSessionCacheEnabled();
- }
-
- private SSLHelper(ClientOptionsBase options, KeyCertOptions keyCertOptions, TrustOptions trustOptions) {
- this((TCPSSLOptions) options, keyCertOptions, trustOptions);
- this.client = true;
- this.trustAll = options.isTrustAll();
+ this.client = options instanceof ClientOptionsBase;
+ this.trustAll = options instanceof ClientOptionsBase && ((ClientOptionsBase)options).isTrustAll();
+ this.keyCertOptions = options.getKeyCertOptions() != null ? options.getKeyCertOptions().copy() : null;
+ this.trustOptions = options.getTrustOptions() != null ? options.getTrustOptions().copy() : null;
+ this.clientAuth = options instanceof NetServerOptions ? ((NetServerOptions)options).getClientAuth() : ClientAuth.NONE;
+ this.endpointIdentificationAlgorithm = options instanceof NetClientOptions ? ((NetClientOptions)options).getHostnameVerificationAlgorithm() : "";
+ this.sni = options instanceof NetServerOptions && ((NetServerOptions) options).isSni();
+ this.applicationProtocols = applicationProtocols;
}
- public SSLHelper(HttpClientOptions options, KeyCertOptions keyCertOptions, TrustOptions trustOptions) {
- this((ClientOptionsBase) options, keyCertOptions, trustOptions);
+ public boolean isSSL() {
+ return ssl;
}
- public SSLHelper(NetClientOptions options, KeyCertOptions keyCertOptions, TrustOptions trustOptions) {
- this((ClientOptionsBase) options, keyCertOptions, trustOptions);
- this.endpointIdentificationAlgorithm = options.getHostnameVerificationAlgorithm();
+ public boolean isSNI() {
+ return sni;
}
- public SSLHelper(NetServerOptions options, KeyCertOptions keyCertOptions, TrustOptions trustOptions) {
- this((TCPSSLOptions) options, keyCertOptions, trustOptions);
- this.clientAuth = options.getClientAuth();
- this.client = false;
- this.sni = options.isSni();
+ private void configureEngine(SSLEngine engine, String serverName) {
+ Set protocols = new LinkedHashSet<>(enabledProtocols);
+ protocols.retainAll(Arrays.asList(engine.getSupportedProtocols()));
+ if (protocols.isEmpty()) {
+ log.warn("no SSL/TLS protocols are enabled due to configuration restrictions");
+ }
+ engine.setEnabledProtocols(protocols.toArray(new String[protocols.size()]));
+ if (client && !endpointIdentificationAlgorithm.isEmpty()) {
+ SSLParameters sslParameters = engine.getSSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
+ engine.setSSLParameters(sslParameters);
+ }
+ if (serverName != null) {
+ SSLParameters sslParameters = engine.getSSLParameters();
+ sslParameters.setServerNames(Collections.singletonList(new SNIHostName(serverName)));
+ engine.setSSLParameters(sslParameters);
+ }
}
/**
- * Copy constructor, only configuration field are copied.
+ * Initialize the helper, this loads and validates the configuration.
+ *
+ * @param ctx the context
+ * @return a future resolved when the helper is initialized
*/
- public SSLHelper(SSLHelper that) {
- this.ssl = that.ssl;
- this.sni = that.sni;
- this.sslHandshakeTimeout = that.sslHandshakeTimeout;
- this.sslHandshakeTimeoutUnit = that.sslHandshakeTimeoutUnit;
- this.keyCertOptions = that.keyCertOptions;
- this.trustOptions = that.trustOptions;
- this.trustAll = that.trustAll;
- this.crlPaths = that.crlPaths;
- this.crlValues = that.crlValues;
- this.clientAuth = that.clientAuth;
- this.enabledCipherSuites = that.enabledCipherSuites;
- this.openSsl = that.openSsl;
- this.client = that.client;
- this.useAlpn = that.useAlpn;
- this.applicationProtocols = that.applicationProtocols;
- this.enabledProtocols = that.enabledProtocols;
- this.endpointIdentificationAlgorithm = that.endpointIdentificationAlgorithm;
- this.openSslSessionCacheEnabled = that.openSslSessionCacheEnabled;
- this.suppliedSslContext = that.suppliedSslContext;
+ public synchronized Future init(ContextInternal ctx) {
+ Future> fut = sslProvider;
+ if (fut == null) {
+ if (keyCertOptions != null || trustOptions != null || trustAll || ssl) {
+ Promise> promise = Promise.promise();
+ fut = promise.future();
+ ctx.executeBlockingInternal(p -> {
+ KeyManagerFactory kmf;
+ try {
+ getTrustMgrFactory(ctx.owner(), null, false);
+ kmf = getKeyMgrFactory(ctx.owner());
+ } catch (Exception e) {
+ p.fail(e);
+ return;
+ }
+ if (client || kmf != null) {
+ p.complete();
+ } else {
+ p.fail("Key/certificate is mandatory for SSL");
+ }
+ }).compose(v2 -> ctx.>executeBlockingInternal(p -> {
+ Supplier sslProvider;
+ try {
+ SSLEngineOptions resolvedEngineOptions = resolveEngineOptions(sslEngineOptions, useAlpn);
+ sslProvider = resolvedEngineOptions::sslContextFactory;
+ } catch (Exception e) {
+ p.fail(e);
+ return;
+ }
+ p.complete(sslProvider);
+ })).onComplete(promise);
+ } else {
+ fut = Future.succeededFuture();
+ }
+ sslProvider = fut;
+ }
+ PromiseInternal promise = ctx.promise();
+ fut.mapEmpty().onComplete(promise);
+ return promise.future();
+ }
+
+ public Mapping super String, ? extends SslContext> serverNameMapper(VertxInternal vertx) {
+ return serverName -> {
+ SslContext ctx = createContext(vertx, serverName, useAlpn, client, trustAll);
+ if (ctx != null) {
+ ctx = new DelegatingSslContext(ctx) {
+ @Override
+ protected void initEngine(SSLEngine engine) {
+ configureEngine(engine, serverName);
+ }
+ };
+ }
+ return ctx;
+ };
}
- public boolean isUseAlpn() {
- return useAlpn;
+ public SSLEngine createEngine(VertxInternal vertx) {
+ SSLEngine engine = createContext(vertx).newEngine(ByteBufAllocator.DEFAULT);
+ configureEngine(engine, null);
+ return engine;
}
- public SSLHelper setUseAlpn(boolean useAlpn) {
- this.useAlpn = useAlpn;
- return this;
+ public SslContext createContext(VertxInternal vertx) {
+ return createContext(vertx, null, useAlpn, client, trustAll);
}
- public boolean isSSL() {
- return ssl;
+ public SslContext createContext(VertxInternal vertx, String serverName, boolean useAlpn, boolean client, boolean trustAll) {
+ int idx = useAlpn ? 0 : 1;
+ if (serverName == null) {
+ if (sslContexts[idx] == null) {
+ sslContexts[idx] = createContext2(vertx, serverName, useAlpn, client, trustAll);
+ }
+ return sslContexts[idx];
+ } else {
+ return sslContextMaps[idx].computeIfAbsent(serverName, s -> createContext2(vertx, serverName, useAlpn, client, trustAll));
+ }
}
- public boolean isSNI() {
- return sni;
+ public SslContext sslContext(VertxInternal vertx, String serverName, boolean useAlpn) {
+ SslContext context = createContext(vertx, null, useAlpn, client, trustAll);
+ return new DelegatingSslContext(context) {
+ @Override
+ protected void initEngine(SSLEngine engine) {
+ configureEngine(engine, serverName);
+ }
+ };
}
- public long getSslHandshakeTimeout() {
- return sslHandshakeTimeout;
+ private SslContext createContext2(VertxInternal vertx, String serverName, boolean useAlpn, boolean client, boolean trustAll) {
+ try {
+ TrustManagerFactory tmf = getTrustMgrFactory(vertx, serverName, trustAll);
+ KeyManagerFactory kmf = getKeyMgrFactory(vertx, serverName);
+ SslContextFactory factory = sslProvider.result().get()
+ .useAlpn(useAlpn)
+ .forClient(client)
+ .enabledCipherSuites(enabledCipherSuites)
+ .applicationProtocols(applicationProtocols);
+ if (!client) {
+ factory.clientAuth(CLIENT_AUTH_MAPPING.get(clientAuth));
+ }
+ if (kmf != null) {
+ factory.keyMananagerFactory(kmf);
+ }
+ if (tmf != null) {
+ factory.trustManagerFactory(tmf);
+ }
+ if (serverName != null) {
+ factory.serverName(serverName);
+ }
+ return factory.create();
+ } catch (Exception e) {
+ throw new VertxException(e);
+ }
}
- public TimeUnit getSslHandshakeTimeoutUnit() {
- return sslHandshakeTimeoutUnit;
+ public SslHandler createSslHandler(VertxInternal vertx, String serverName) {
+ return createSslHandler(vertx, null, serverName);
}
- public ClientAuth getClientAuth() {
- return clientAuth;
+ public SslHandler createSslHandler(VertxInternal vertx, SocketAddress remoteAddress, String serverName) {
+ return createSslHandler(vertx, remoteAddress, serverName, useAlpn);
}
- public List getApplicationProtocols() {
- return applicationProtocols;
+ public SslHandler createSslHandler(VertxInternal vertx, SocketAddress remoteAddress, String serverName, boolean useAlpn) {
+ SslContext sslContext = sslContext(vertx, serverName, useAlpn);
+ SslHandler sslHandler;
+ if (remoteAddress == null || remoteAddress.isDomainSocket()) {
+ sslHandler = sslContext.newHandler(ByteBufAllocator.DEFAULT);
+ } else {
+ sslHandler = sslContext.newHandler(ByteBufAllocator.DEFAULT, remoteAddress.host(), remoteAddress.port());
+ }
+ sslHandler.setHandshakeTimeout(sslHandshakeTimeout, sslHandshakeTimeoutUnit);
+ return sslHandler;
}
- public SSLHelper setApplicationProtocols(List applicationProtocols) {
- this.applicationProtocols = applicationProtocols;
- return this;
+ public SniHandler createSniHandler(VertxInternal vertx) {
+ return new SniHandler(serverNameMapper(vertx));
}
- /**
- * Must be called before createEngine()
- */
- public void setSuppliedSslContext(SSLContext suppliedSslContext) {
- if (this.suppliedSslContext != null) {
- throw new IllegalArgumentException("suppliedSslContext already set");
+ public ChannelHandler createHandler(VertxInternal vertx) {
+ if (sni) {
+ return createSniHandler(vertx);
+ } else {
+ return createSslHandler(vertx, null);
}
- Objects.requireNonNull(suppliedSslContext, "suppliedSslContext should not be null");
- this.suppliedSslContext = suppliedSslContext;
}
- /*
- If you don't specify a trust store, and you haven't set system properties, the system will try to use either a file
- called jsssecacerts or cacerts in the JDK/JRE security directory.
- You can override this by specifying the javax.echo.ssl.trustStore system property
-
- If you don't specify a key store, and don't specify a system property no key store will be used
- You can override this by specifying the javax.echo.ssl.keyStore system property
- */
- private SslContext createContext(VertxInternal vertx, boolean useAlpn, X509KeyManager mgr, TrustManagerFactory trustMgrFactory) {
- try {
- SslContextBuilder builder;
- if (client) {
- builder = SslContextBuilder.forClient();
- KeyManagerFactory keyMgrFactory = getKeyMgrFactory(vertx);
- if (keyMgrFactory != null) {
- builder.keyManager(keyMgrFactory);
- }
- } else {
- if (mgr != null) {
- builder = SslContextBuilder.forServer(mgr.getPrivateKey(null), null, mgr.getCertificateChain(null));
- } else {
- KeyManagerFactory keyMgrFactory = getKeyMgrFactory(vertx);
- if (keyMgrFactory == null) {
- throw new VertxException("Key/certificate is mandatory for SSL");
- }
- builder = SslContextBuilder.forServer(keyMgrFactory);
- }
- }
- Collection cipherSuites = enabledCipherSuites;
- if (openSsl) {
- builder.sslProvider(SslProvider.OPENSSL);
- if (cipherSuites == null || cipherSuites.isEmpty()) {
- cipherSuites = OpenSsl.availableOpenSslCipherSuites();
- }
- } else {
- builder.sslProvider(SslProvider.JDK);
- if (cipherSuites == null || cipherSuites.isEmpty()) {
- cipherSuites = DefaultJDKCipherSuite.get();
- }
- }
- if (trustMgrFactory != null) {
- builder.trustManager(trustMgrFactory);
- }
- if (cipherSuites != null && cipherSuites.size() > 0) {
- builder.ciphers(cipherSuites);
- }
- if (useAlpn && applicationProtocols != null && applicationProtocols.size() > 0) {
- builder.applicationProtocolConfig(new ApplicationProtocolConfig(
- ApplicationProtocolConfig.Protocol.ALPN,
- ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
- ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
- applicationProtocols
- ));
- }
- SslContext ctx = builder.build();
- if (ctx instanceof OpenSslServerContext){
- SSLSessionContext sslSessionContext = ctx.sessionContext();
- if (sslSessionContext instanceof OpenSslServerSessionContext){
- ((OpenSslServerSessionContext)sslSessionContext).setSessionCacheEnabled(openSslSessionCacheEnabled);
- }
+ private KeyManagerFactory getKeyMgrFactory(VertxInternal vertx, String serverName) throws Exception {
+ KeyManagerFactory kmf = null;
+ if (serverName != null) {
+ X509KeyManager mgr = keyCertOptions.keyManagerMapper(vertx).apply(serverName);
+ if (mgr != null) {
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore ks = KeyStore.getInstance(keyStoreType);
+ ks.load(null, null);
+ ks.setKeyEntry("key", mgr.getPrivateKey(null), new char[0], mgr.getCertificateChain(null));
+ String keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+ kmf = KeyManagerFactory.getInstance(keyAlgorithm);
+ kmf.init(ks, new char[0]);
}
- return ctx;
- } catch (Exception e) {
- throw new VertxException(e);
}
+ if (kmf == null) {
+ kmf = getKeyMgrFactory(vertx);
+ }
+ return kmf;
}
private KeyManagerFactory getKeyMgrFactory(VertxInternal vertx) throws Exception {
return keyCertOptions == null ? null : keyCertOptions.getKeyManagerFactory(vertx);
}
- private TrustManagerFactory getTrustMgrFactory(VertxInternal vertx, String serverName) throws Exception {
+ private TrustManagerFactory getTrustMgrFactory(VertxInternal vertx, String serverName, boolean trustAll) throws Exception {
TrustManager[] mgrs = null;
if (trustAll) {
mgrs = new TrustManager[]{createTrustAllTrustManager()};
@@ -335,9 +389,9 @@ private TrustManagerFactory getTrustMgrFactory(VertxInternal vertx, String serve
}
if (crlPaths != null && crlValues != null && (crlPaths.size() > 0 || crlValues.size() > 0)) {
Stream tmp = crlPaths.
- stream().
- map(path -> vertx.resolveFile(path).getAbsolutePath()).
- map(vertx.fileSystem()::readFileBlocking);
+ stream().
+ map(path -> vertx.resolveFile(path).getAbsolutePath()).
+ map(vertx.fileSystem()::readFileBlocking);
tmp = Stream.concat(tmp, crlValues.stream());
CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
ArrayList crls = new ArrayList<>();
@@ -406,160 +460,4 @@ public X509Certificate[] getAcceptedIssuers() {
}
};
}
-
- public Mapping super String, ? extends SslContext> serverNameMapper(VertxInternal vertx) {
- return serverName -> {
- SslContext ctx = getContext(vertx, serverName);
- if (ctx != null) {
- ctx = new DelegatingSslContext(ctx) {
- @Override
- protected void initEngine(SSLEngine engine) {
- configureEngine(engine, serverName);
- }
- };
- }
- return ctx;
- };
- }
-
- public void configureEngine(SSLEngine engine, String serverName) {
- if (enabledCipherSuites != null && !enabledCipherSuites.isEmpty()) {
- String[] toUse = enabledCipherSuites.toArray(new String[enabledCipherSuites.size()]);
- engine.setEnabledCipherSuites(toUse);
- }
- engine.setUseClientMode(client);
- Set protocols = new LinkedHashSet<>(enabledProtocols);
- protocols.retainAll(Arrays.asList(engine.getSupportedProtocols()));
- if (protocols.isEmpty()) {
- log.warn("no SSL/TLS protocols are enabled due to configuration restrictions");
- }
- engine.setEnabledProtocols(protocols.toArray(new String[protocols.size()]));
- if (!client) {
- switch (getClientAuth()) {
- case REQUEST: {
- engine.setWantClientAuth(true);
- break;
- }
- case REQUIRED: {
- engine.setNeedClientAuth(true);
- break;
- }
- case NONE: {
- engine.setNeedClientAuth(false);
- break;
- }
- }
- } else if (!endpointIdentificationAlgorithm.isEmpty()) {
- SSLParameters sslParameters = engine.getSSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
- engine.setSSLParameters(sslParameters);
- }
- if (serverName != null) {
- SSLParameters sslParameters = engine.getSSLParameters();
- sslParameters.setServerNames(Collections.singletonList(new SNIHostName(serverName)));
- engine.setSSLParameters(sslParameters);
- }
- }
-
- public SslContext getContext(VertxInternal vertx) {
- return getContext(vertx, null);
- }
-
- public SslContext getContext(VertxInternal vertx, String serverName) {
- return getContext(vertx, serverName, useAlpn);
- }
-
- public SslContext getContext(VertxInternal vertx, String serverName, boolean useAlpn) {
- int idx = useAlpn ? 0 : 1;
- if (serverName == null) {
- if (sslContexts[idx] == null) {
- TrustManagerFactory trustMgrFactory;
- try {
- trustMgrFactory = getTrustMgrFactory(vertx, null);
- } catch (Exception e) {
- throw new VertxException(e);
- }
- sslContexts[idx] = createContext(vertx, useAlpn, null, trustMgrFactory);
- }
- return sslContexts[idx];
- } else {
- X509KeyManager mgr;
- try {
- mgr = keyCertOptions.keyManagerMapper(vertx).apply(serverName);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- if (mgr == null) {
- return sslContexts[idx];
- }
- try {
- TrustManagerFactory trustMgrFactory = getTrustMgrFactory(vertx, serverName);
- return sslContextMaps[idx].computeIfAbsent(mgr.getCertificateChain(null)[0], s -> createContext(vertx, useAlpn, mgr, trustMgrFactory));
- } catch (Exception e) {
- throw new VertxException(e);
- }
- }
- }
-
- // This is called to validate some of the SSL params as that only happens when the context is created
- public synchronized void validate(VertxInternal vertx) {
- if (ssl) {
- getContext(vertx, null);
- }
- }
-
- public SSLEngine createEngine(SslContext sslContext) {
- SSLEngine engine = sslContext.newEngine(ByteBufAllocator.DEFAULT);
- configureEngine(engine, null);
- return engine;
- }
-
- public SSLEngine createEngine(VertxInternal vertx, SocketAddress socketAddress, String serverName, boolean useAlpn) {
- if (suppliedSslContext == null) {
- SslContext context = getContext(vertx, null, useAlpn);
- SSLEngine engine;
- if (socketAddress.isDomainSocket()) {
- engine = context.newEngine(ByteBufAllocator.DEFAULT);
- } else {
- engine = context.newEngine(ByteBufAllocator.DEFAULT, socketAddress.host(), socketAddress.port());
- }
- configureEngine(engine, serverName);
- return engine;
- } else {
- if (useAlpn) {
- throw new IllegalStateException("Can not use useAlpn=true together with a supplied SSLContext");
- }
- if (!client) {
- throw new IllegalStateException("Can only use a supplied SSLContext with clients");
- }
- SSLEngine engine;
- if (socketAddress.isDomainSocket()) {
- engine = suppliedSslContext.createSSLEngine();
- } else {
- engine = suppliedSslContext.createSSLEngine(socketAddress.host(), socketAddress.port());
- }
-
- configureEngine(engine, serverName);
- return engine;
-
- }
- }
-
- public SSLEngine createEngine(VertxInternal vertx, String host, int port, boolean forceSNI) {
- SSLEngine engine = getContext(vertx, null).newEngine(ByteBufAllocator.DEFAULT, host, port);
- configureEngine(engine, forceSNI ? host : null);
- return engine;
- }
-
- public SSLEngine createEngine(VertxInternal vertx, String host, int port) {
- SSLEngine engine = getContext(vertx, null).newEngine(ByteBufAllocator.DEFAULT, host, port);
- configureEngine(engine, null);
- return engine;
- }
-
- public SSLEngine createEngine(VertxInternal vertx) {
- SSLEngine engine = getContext(vertx, null).newEngine(ByteBufAllocator.DEFAULT);
- configureEngine(engine, null);
- return engine;
- }
}
diff --git a/src/main/java/io/vertx/core/net/impl/TCPServerBase.java b/src/main/java/io/vertx/core/net/impl/TCPServerBase.java
index 44dac3704dc..7848e9e0dbe 100644
--- a/src/main/java/io/vertx/core/net/impl/TCPServerBase.java
+++ b/src/main/java/io/vertx/core/net/impl/TCPServerBase.java
@@ -18,9 +18,7 @@
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop;
import io.netty.util.concurrent.GenericFutureListener;
-import io.vertx.core.AsyncResult;
import io.vertx.core.Closeable;
-import io.vertx.core.CompositeFuture;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
@@ -37,12 +35,9 @@
import io.vertx.core.spi.metrics.TCPMetrics;
import java.net.InetSocketAddress;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
/**
* Base class for TCP servers
@@ -68,7 +63,7 @@ public abstract class TCPServerBase implements Closeable, MetricsProvider {
// Main
private SSLHelper sslHelper;
private ServerChannelLoadBalancer channelBalancer;
- private io.netty.util.concurrent.Future bindFuture;
+ private Future bindFuture;
private Set servers;
private TCPMetrics> metrics;
private volatile int actualPort;
@@ -87,7 +82,7 @@ public int actualPort() {
protected abstract Handler childHandler(ContextInternal context, SocketAddress socketAddress, SSLHelper sslHelper);
protected SSLHelper createSSLHelper() {
- return new SSLHelper(options, options.getKeyCertOptions(), options.getTrustOptions());
+ return new SSLHelper(options, null);
}
public synchronized SSLHelper sslHelper() {
@@ -96,21 +91,10 @@ public synchronized SSLHelper sslHelper() {
public Future bind(SocketAddress address) {
ContextInternal listenContext = vertx.getOrCreateContext();
-
- io.netty.util.concurrent.Future bindFuture = listen(address, listenContext);
-
- Promise promise = listenContext.promise();
- bindFuture.addListener(res -> {
- if (res.isSuccess()) {
- promise.complete(this);
- } else {
- promise.fail(res.cause());
- }
- });
- return promise.future();
+ return listen(address, listenContext).map(this);
}
- private synchronized io.netty.util.concurrent.Future listen(SocketAddress localAddress, ContextInternal context) {
+ private synchronized Future listen(SocketAddress localAddress, ContextInternal context) {
if (listening) {
throw new IllegalStateException("Listen already called");
}
@@ -145,62 +129,78 @@ private synchronized io.netty.util.concurrent.Future listen(SocketAddre
bindAddress = localAddress;
}
}
+ PromiseInternal promise = listenContext.promise();
if (main == null) {
- try {
- sslHelper = createSSLHelper();
- sslHelper.validate(vertx);
- worker = childHandler(listenContext, localAddress, sslHelper);
- servers = new HashSet<>();
- servers.add(this);
- channelBalancer = new ServerChannelLoadBalancer(vertx.getAcceptorEventLoopGroup().next());
- channelBalancer.addWorker(eventLoop, worker);
-
- ServerBootstrap bootstrap = new ServerBootstrap();
- bootstrap.group(vertx.getAcceptorEventLoopGroup(), channelBalancer.workers());
- if (sslHelper.isSSL()) {
- bootstrap.childOption(ChannelOption.ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE);
- } else {
- bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
- }
+ // The first server binds the socket
+ actualServer = this;
+ bindFuture = promise;
+ sslHelper = createSSLHelper();
+ worker = childHandler(listenContext, localAddress, sslHelper);
+ servers = new HashSet<>();
+ servers.add(this);
+ channelBalancer = new ServerChannelLoadBalancer(vertx.getAcceptorEventLoopGroup().next());
- bootstrap.childHandler(channelBalancer);
- applyConnectionOptions(localAddress.isDomainSocket(), bootstrap);
-
- bindFuture = AsyncResolveConnectHelper.doBind(vertx, bindAddress, bootstrap);
- bindFuture.addListener((GenericFutureListener>) res -> {
- if (res.isSuccess()) {
- Channel ch = res.getNow();
- log.trace("Net server listening on " + hostOrPath + ":" + ch.localAddress());
- if (shared) {
- ch.closeFuture().addListener((ChannelFutureListener) channelFuture -> {
- synchronized (sharedNetServers) {
- sharedNetServers.remove(id);
- }
- });
- }
- // Update port to actual port when it is not a domain socket as wildcard port 0 might have been used
- if (bindAddress.isInetSocket()) {
- actualPort = ((InetSocketAddress)ch.localAddress()).getPort();
- }
- listenContext.addCloseHook(this);
- metrics = createMetrics(localAddress);
+ // Register the server in the shared server list
+ if (shared) {
+ sharedNetServers.put(id, this);
+ }
+
+ // Initialize SSL before binding
+ sslHelper.init(listenContext).onComplete(ar -> {
+ if (ar.succeeded()) {
+
+ // Socket bind
+ channelBalancer.addWorker(eventLoop, worker);
+ ServerBootstrap bootstrap = new ServerBootstrap();
+ bootstrap.group(vertx.getAcceptorEventLoopGroup(), channelBalancer.workers());
+ if (sslHelper.isSSL()) {
+ bootstrap.childOption(ChannelOption.ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE);
} else {
- if (shared) {
- synchronized (sharedNetServers) {
- sharedNetServers.remove(id);
+ bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+ }
+
+ bootstrap.childHandler(channelBalancer);
+ applyConnectionOptions(localAddress.isDomainSocket(), bootstrap);
+
+ // Actual bind
+ io.netty.util.concurrent.Future bindFuture = AsyncResolveConnectHelper.doBind(vertx, bindAddress, bootstrap);
+ bindFuture.addListener((GenericFutureListener>) res -> {
+ if (res.isSuccess()) {
+ Channel ch = res.getNow();
+ log.trace("Net server listening on " + hostOrPath + ":" + ch.localAddress());
+ if (shared) {
+ ch.closeFuture().addListener((ChannelFutureListener) channelFuture -> {
+ synchronized (sharedNetServers) {
+ sharedNetServers.remove(id);
+ }
+ });
+ }
+ // Update port to actual port when it is not a domain socket as wildcard port 0 might have been used
+ if (bindAddress.isInetSocket()) {
+ actualPort = ((InetSocketAddress)ch.localAddress()).getPort();
}
+ listenContext.addCloseHook(this);
+ metrics = createMetrics(localAddress);
+ promise.complete(ch);
+ } else {
+ promise.fail(res.cause());
}
- listening = false;
+ });
+ } else {
+ promise.fail(ar.cause());
+ }
+ });
+
+ bindFuture.onFailure(err -> {
+ if (shared) {
+ synchronized (sharedNetServers) {
+ sharedNetServers.remove(id);
}
- });
- } catch (Throwable t) {
+ }
listening = false;
- return vertx.getAcceptorEventLoopGroup().next().newFailedFuture(t);
- }
- if (shared) {
- sharedNetServers.put(id, this);
- }
- actualServer = this;
+ });
+
+ return bindFuture;
} else {
// Server already exists with that host/port - we will use that
actualServer = main;
@@ -210,10 +210,10 @@ private synchronized io.netty.util.concurrent.Future listen(SocketAddre
actualServer.servers.add(this);
actualServer.channelBalancer.addWorker(eventLoop, worker);
listenContext.addCloseHook(this);
+ main.bindFuture.onComplete(promise);
+ return promise.future();
}
}
-
- return actualServer.bindFuture;
}
public boolean isListening() {
@@ -268,9 +268,9 @@ public synchronized void close(Promise completion) {
private void actualClose(Promise done) {
channelBalancer.close();
- bindFuture.addListener((GenericFutureListener>) fut -> {
- if (fut.isSuccess()) {
- Channel channel = fut.getNow();
+ bindFuture.onComplete(ar -> {
+ if (ar.succeeded()) {
+ Channel channel = ar.result();
ChannelFuture a = channel.close();
if (metrics != null) {
a.addListener(cg -> metrics.close());
diff --git a/src/main/java/io/vertx/core/net/impl/DefaultJDKCipherSuite.java b/src/main/java/io/vertx/core/spi/tls/DefaultJDKCipherSuite.java
similarity index 97%
rename from src/main/java/io/vertx/core/net/impl/DefaultJDKCipherSuite.java
rename to src/main/java/io/vertx/core/spi/tls/DefaultJDKCipherSuite.java
index 2a87005a600..1e528304a9d 100644
--- a/src/main/java/io/vertx/core/net/impl/DefaultJDKCipherSuite.java
+++ b/src/main/java/io/vertx/core/spi/tls/DefaultJDKCipherSuite.java
@@ -8,7 +8,7 @@
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
-package io.vertx.core.net.impl;
+package io.vertx.core.spi.tls;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
diff --git a/src/main/java/io/vertx/core/spi/tls/DefaultSslContextFactory.java b/src/main/java/io/vertx/core/spi/tls/DefaultSslContextFactory.java
new file mode 100755
index 00000000000..f71885b4f1d
--- /dev/null
+++ b/src/main/java/io/vertx/core/spi/tls/DefaultSslContextFactory.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+
+package io.vertx.core.spi.tls;
+
+import io.netty.handler.ssl.ApplicationProtocolConfig;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.OpenSslServerContext;
+import io.netty.handler.ssl.OpenSslServerSessionContext;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.TrustManagerFactory;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The default implementation of {@link SslContextFactory} that creates and configures a Netty {@link SslContext} using a
+ * {@link SslContextBuilder}.
+ *
+ * @author Tim Fox
+ * @author Julien Viet
+ */
+public class DefaultSslContextFactory implements SslContextFactory {
+
+ private final SslProvider sslProvider;
+ private final boolean sslSessionCacheEnabled;
+
+ public DefaultSslContextFactory(SslProvider sslProvider,
+ boolean sslSessionCacheEnabled) {
+ this.sslProvider = sslProvider;
+ this.sslSessionCacheEnabled = sslSessionCacheEnabled;
+ }
+
+ private Set enabledCipherSuites;
+ private List applicationProtocols;
+ private boolean useAlpn;
+ private ClientAuth clientAuth;
+ private boolean forClient;
+ private KeyManagerFactory kmf;
+ private TrustManagerFactory tmf;
+
+ @Override
+ public SslContextFactory useAlpn(boolean useAlpn) {
+ this.useAlpn = useAlpn;
+ return this;
+ }
+
+ @Override
+ public SslContextFactory clientAuth(ClientAuth clientAuth) {
+ this.clientAuth = clientAuth;
+ return this;
+ }
+
+ @Override
+ public SslContextFactory forClient(boolean forClient) {
+ this.forClient = forClient;
+ return this;
+ }
+
+ @Override
+ public SslContextFactory keyMananagerFactory(KeyManagerFactory kmf) {
+ this.kmf = kmf;
+ return this;
+ }
+
+ @Override
+ public SslContextFactory trustManagerFactory(TrustManagerFactory tmf) {
+ this.tmf = tmf;
+ return this;
+ }
+
+ @Override
+ public SslContext create() throws SSLException {
+ return createContext(useAlpn, forClient, kmf, tmf);
+ }
+
+ @Override
+ public SslContextFactory enabledCipherSuites(Set enabledCipherSuites) {
+ this.enabledCipherSuites = enabledCipherSuites;
+ return this;
+ }
+
+ @Override
+ public SslContextFactory applicationProtocols(List applicationProtocols) {
+ this.applicationProtocols = applicationProtocols;
+ return this;
+ }
+
+ /*
+ If you don't specify a trust store, and you haven't set system properties, the system will try to use either a file
+ called jsssecacerts or cacerts in the JDK/JRE security directory.
+ You can override this by specifying the javax.echo.ssl.trustStore system property
+
+ If you don't specify a key store, and don't specify a system property no key store will be used
+ You can override this by specifying the javax.echo.ssl.keyStore system property
+ */
+ private SslContext createContext(boolean useAlpn, boolean client, KeyManagerFactory kmf, TrustManagerFactory tmf) throws SSLException {
+ SslContextBuilder builder;
+ if (client) {
+ builder = SslContextBuilder.forClient();
+ if (kmf != null) {
+ builder.keyManager(kmf);
+ }
+ } else {
+ builder = SslContextBuilder.forServer(kmf);
+ }
+ Collection cipherSuites = enabledCipherSuites;
+ switch (sslProvider) {
+ case OPENSSL:
+ builder.sslProvider(SslProvider.OPENSSL);
+ if (cipherSuites == null || cipherSuites.isEmpty()) {
+ cipherSuites = OpenSsl.availableOpenSslCipherSuites();
+ }
+ break;
+ case JDK:
+ builder.sslProvider(SslProvider.JDK);
+ if (cipherSuites == null || cipherSuites.isEmpty()) {
+ cipherSuites = DefaultJDKCipherSuite.get();
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ if (tmf != null) {
+ builder.trustManager(tmf);
+ }
+ if (cipherSuites != null && cipherSuites.size() > 0) {
+ builder.ciphers(cipherSuites);
+ }
+ if (useAlpn && applicationProtocols != null && applicationProtocols.size() > 0) {
+ builder.applicationProtocolConfig(new ApplicationProtocolConfig(
+ ApplicationProtocolConfig.Protocol.ALPN,
+ ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
+ ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
+ applicationProtocols
+ ));
+ }
+ if (clientAuth != null) {
+ builder.clientAuth(clientAuth);
+ }
+ SslContext ctx = builder.build();
+ if (ctx instanceof OpenSslServerContext){
+ SSLSessionContext sslSessionContext = ctx.sessionContext();
+ if (sslSessionContext instanceof OpenSslServerSessionContext){
+ ((OpenSslServerSessionContext)sslSessionContext).setSessionCacheEnabled(sslSessionCacheEnabled);
+ }
+ }
+ return ctx;
+ }
+}
diff --git a/src/main/java/io/vertx/core/spi/tls/SslContextFactory.java b/src/main/java/io/vertx/core/spi/tls/SslContextFactory.java
new file mode 100644
index 00000000000..61201e9c12a
--- /dev/null
+++ b/src/main/java/io/vertx/core/spi/tls/SslContextFactory.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2011-2022 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package io.vertx.core.spi.tls;
+
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContext;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A factory for a Netty {@link SslContext}, the factory is configured with the fluent setters until {@link #create()}
+ * to obtain a properly configured {@link SslContext}.
+ *
+ * @author Julien Viet
+ */
+public interface SslContextFactory {
+
+ /**
+ * Set whether to use ALPN.
+ *
+ * @param useAlpn {@code true} to use ALPN
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory useAlpn(boolean useAlpn) {
+ return this;
+ }
+
+ /**
+ * Configures the client auth
+ * @param clientAuth the client auth to use
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory clientAuth(ClientAuth clientAuth) {
+ return this;
+ }
+
+ /**
+ * Set whether to build a context for clients or for servers
+ * @param forClient {@code true} for client otherwise for servers
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory forClient(boolean forClient) {
+ return this;
+ }
+
+ /**
+ * Set the key manager factory to use.
+ * @param kmf the key manager factory instance
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory keyMananagerFactory(KeyManagerFactory kmf) {
+ return this;
+ }
+
+ /**
+ * Set the trust manager factory to use.
+ * @param tmf the trust manager factory instance
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory trustManagerFactory(TrustManagerFactory tmf) {
+ return this;
+ }
+
+ /**
+ * Set the enabled cipher suites.
+ * @param enabledCipherSuites the set of cipher suites
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory enabledCipherSuites(Set enabledCipherSuites) {
+ return this;
+ }
+
+ /**
+ * Set the application protocols to use when using ALPN.
+ * @param applicationProtocols this list of application protocols
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory applicationProtocols(List applicationProtocols) {
+ return this;
+ }
+
+ /**
+ * Set the SNI server name.
+ * @param serverName the server name
+ * @return a reference to this, so the API can be used fluently
+ */
+ default SslContextFactory serverName(String serverName) {
+ return this;
+ }
+
+ /**
+ * @return a configured {@link SslContext}
+ */
+ SslContext create() throws SSLException;
+
+}
diff --git a/src/test/java/io/vertx/core/VertxTest.java b/src/test/java/io/vertx/core/VertxTest.java
index 8b0925bfd52..f1be96a3fd1 100644
--- a/src/test/java/io/vertx/core/VertxTest.java
+++ b/src/test/java/io/vertx/core/VertxTest.java
@@ -20,6 +20,7 @@
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.NetSocket;
+import io.vertx.core.net.impl.NetClientBuilder;
import io.vertx.test.core.AsyncTestBase;
import io.vertx.test.core.Repeat;
import io.vertx.test.core.RepeatRule;
@@ -197,7 +198,7 @@ public void testFinalizeNetClient() throws Exception {
awaitLatch(latch);
AtomicBoolean closed = new AtomicBoolean();
CloseFuture closeFuture = new CloseFuture();
- NetClient client = vertx.createNetClient(new NetClientOptions(), closeFuture);
+ NetClient client = new NetClientBuilder(vertx, new NetClientOptions()).closeFuture(closeFuture).build();
vertx.addCloseHook(closeFuture);
closeFuture.future().onComplete(ar -> closed.set(true));
closeFuture = null;
diff --git a/src/test/java/io/vertx/core/http/Http2ClientTest.java b/src/test/java/io/vertx/core/http/Http2ClientTest.java
index 27cde9d4d66..5aee5ff4211 100644
--- a/src/test/java/io/vertx/core/http/Http2ClientTest.java
+++ b/src/test/java/io/vertx/core/http/Http2ClientTest.java
@@ -13,6 +13,7 @@
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
@@ -24,8 +25,11 @@
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.*;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AsciiString;
import io.vertx.core.AsyncResult;
@@ -36,12 +40,10 @@
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.impl.HttpClientConnection;
-import io.vertx.core.impl.VertxInternal;
import io.vertx.core.net.NetServer;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.core.net.impl.ConnectionBase;
-import io.vertx.core.net.impl.SSLHelper;
import io.vertx.test.core.AsyncTestBase;
import io.vertx.test.core.TestUtils;
import io.vertx.test.tls.Cert;
@@ -1151,8 +1153,16 @@ private ServerBootstrap createH2Server(BiFunction() {
@Override
protected void initChannel(Channel ch) throws Exception {
- SSLHelper sslHelper = new SSLHelper(serverOptions, Cert.SERVER_JKS.get(), null);
- SslHandler sslHandler = new SslHandler(sslHelper.setApplicationProtocols(Arrays.asList(HttpVersion.HTTP_2.alpnName(), HttpVersion.HTTP_1_1.alpnName())).createEngine((VertxInternal) vertx, DEFAULT_HTTPS_HOST, DEFAULT_HTTPS_PORT));
+ SslContext sslContext = SslContextBuilder
+ .forServer(Cert.SERVER_JKS.get().getKeyManagerFactory(vertx))
+ .applicationProtocolConfig(new ApplicationProtocolConfig(
+ ApplicationProtocolConfig.Protocol.ALPN,
+ ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
+ ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
+ HttpVersion.HTTP_2.alpnName(), HttpVersion.HTTP_1_1.alpnName()
+ ))
+ .build();
+ SslHandler sslHandler = sslContext.newHandler(ByteBufAllocator.DEFAULT);
ch.pipeline().addLast(sslHandler);
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("whatever") {
@Override
diff --git a/src/test/java/io/vertx/core/http/Http2ServerTest.java b/src/test/java/io/vertx/core/http/Http2ServerTest.java
index 9ae9f592db4..d74136967d8 100644
--- a/src/test/java/io/vertx/core/http/Http2ServerTest.java
+++ b/src/test/java/io/vertx/core/http/Http2ServerTest.java
@@ -13,6 +13,7 @@
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
@@ -41,8 +42,11 @@
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.Http2Stream;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
@@ -55,8 +59,6 @@
import io.vertx.core.http.impl.Http1xOrH2CHandler;
import io.vertx.core.http.impl.HttpUtils;
import io.vertx.core.impl.Utils;
-import io.vertx.core.impl.VertxInternal;
-import io.vertx.core.net.impl.SSLHelper;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
import io.vertx.test.core.DetectFileDescriptorLeaks;
@@ -213,8 +215,16 @@ protected ChannelInitializer channelInitializer(int port, String host, Consumer<
return new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
- SSLHelper sslHelper = new SSLHelper(new HttpClientOptions().setUseAlpn(true).setSsl(true), null, Trust.SERVER_JKS.get());
- SslHandler sslHandler = new SslHandler(sslHelper.setApplicationProtocols(Arrays.asList(HttpVersion.HTTP_2.alpnName(), HttpVersion.HTTP_1_1.alpnName())).createEngine((VertxInternal) vertx, host, port));
+ SslContext sslContext = SslContextBuilder
+ .forClient()
+ .applicationProtocolConfig(new ApplicationProtocolConfig(
+ ApplicationProtocolConfig.Protocol.ALPN,
+ ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
+ ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
+ HttpVersion.HTTP_2.alpnName(), HttpVersion.HTTP_1_1.alpnName()
+ )).trustManager(Trust.SERVER_JKS.get().getTrustManagerFactory(vertx))
+ .build();
+ SslHandler sslHandler = sslContext.newHandler(ByteBufAllocator.DEFAULT, host, port);
ch.pipeline().addLast(sslHandler);
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("whatever") {
@Override
diff --git a/src/test/java/io/vertx/core/http/HttpTLSTest.java b/src/test/java/io/vertx/core/http/HttpTLSTest.java
index c524477ae2f..a32bf0259f0 100755
--- a/src/test/java/io/vertx/core/http/HttpTLSTest.java
+++ b/src/test/java/io/vertx/core/http/HttpTLSTest.java
@@ -1487,9 +1487,9 @@ private void testStore(HttpServerOptions serverOptions, List expectedPos
AtomicReference failure = new AtomicReference<>();
server.listen(onFailure(failure::set));
assertWaitUntil(() -> failure.get() != null);
- Throwable cause = failure.get().getCause();
+ Throwable cause = failure.get();
String exceptionMessage = cause.getMessage();
- if(expectedSuffix == null) {
+ if (expectedSuffix == null) {
boolean ok = expectedPossiblePrefixes.isEmpty();
for (String expectedPossiblePrefix : expectedPossiblePrefixes) {
ok |= expectedPossiblePrefix.equals(exceptionMessage);
@@ -1510,18 +1510,17 @@ private void testStore(HttpServerOptions serverOptions, List expectedPos
}
@Test
- public void testCrlInvalidPath() throws Exception {
+ public void testCrlInvalidPath() {
HttpClientOptions clientOptions = new HttpClientOptions();
clientOptions.setTrustOptions(Trust.SERVER_PEM_ROOT_CA.get());
clientOptions.setSsl(true);
clientOptions.addCrlPath("/invalid.pem");
- try {
- vertx.createHttpClient(clientOptions);
- fail("Was expecting a failure");
- } catch (VertxException e) {
- assertNotNull(e.getCause());
- assertEquals(NoSuchFileException.class, e.getCause().getCause().getClass());
- }
+ HttpClient client = vertx.createHttpClient(clientOptions);
+ client.request(HttpMethod.GET, 9292, "localhost", "/", onFailure(err -> {
+ assertEquals(NoSuchFileException.class, TestUtils.rootCause(err).getClass());
+ testComplete();
+ }));
+ await();
}
// Proxy tests
diff --git a/src/test/java/io/vertx/core/net/NetTest.java b/src/test/java/io/vertx/core/net/NetTest.java
index b031eda6e59..c96c8941528 100755
--- a/src/test/java/io/vertx/core/net/NetTest.java
+++ b/src/test/java/io/vertx/core/net/NetTest.java
@@ -21,6 +21,10 @@
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.*;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
+import io.netty.handler.ssl.IdentityCipherSuiteFilter;
+import io.netty.handler.ssl.JdkSslContext;
+import io.netty.handler.ssl.SslContext;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.internal.PlatformDependent;
import io.vertx.core.*;
@@ -36,10 +40,10 @@
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.impl.HAProxyMessageCompletionHandler;
-import io.vertx.core.net.impl.NetClientImpl;
import io.vertx.core.net.impl.NetServerImpl;
import io.vertx.core.net.impl.NetSocketInternal;
import io.vertx.core.net.impl.VertxHandler;
+import io.vertx.core.spi.tls.SslContextFactory;
import io.vertx.core.streams.ReadStream;
import io.vertx.test.core.CheckingSender;
import io.vertx.test.core.TestUtils;
@@ -65,7 +69,6 @@
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
-import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -1036,7 +1039,7 @@ public void testServerCloseHandlersCloseFromServer() {
await();
}
- void serverCloseHandlers(boolean closeFromServer, Handler> listenHandler) {
+ void serverCloseHandlers(boolean closeFromServer, Handler listenHandler) {
server.connectHandler((sock) -> {
AtomicInteger counter = new AtomicInteger(0);
sock.endHandler(v -> assertEquals(1, counter.incrementAndGet()));
@@ -1047,7 +1050,7 @@ void serverCloseHandlers(boolean closeFromServer, Handler
if (closeFromServer) {
sock.close();
}
- }).listen(testAddress, listenHandler);
+ }).listen(testAddress, onSuccess(listenHandler::handle));
}
@Test
@@ -1611,9 +1614,8 @@ public void testServerCertificateMultipleWrongAlias() throws Exception {
.clientTrustAll(true);
test.setupServer(true);
server.listen(test.bindAddress, onFailure(t -> {
- assertThat(t, is(instanceOf(VertxException.class)));
- assertThat(t.getCause(), is(instanceOf(IllegalArgumentException.class)));
- assertThat(t.getCause().getMessage(), containsString("alias does not exist in the keystore"));
+ assertThat(t, is(instanceOf(IllegalArgumentException.class)));
+ assertThat(t.getMessage(), containsString("alias does not exist in the keystore"));
testComplete();
}));
await();
@@ -3508,7 +3510,6 @@ public void testNetClientInternalTLS() throws Exception {
@Test
public void testNetClientInternalTLSWithSuppliedSSLContext() throws Exception {
client.close();
- client = vertx.createNetClient(new NetClientOptions().setSsl(true));
Path tsPath = Paths.get(this.getClass().getClassLoader().getResource(Trust.SERVER_JKS.get().getPath()).toURI());
TrustManagerFactory tmFactory;
@@ -3525,7 +3526,27 @@ public void testNetClientInternalTLSWithSuppliedSSLContext() throws Exception {
tmFactory.getTrustManagers(),
null
);
- ((NetClientImpl)client).setSuppliedSSLContext(sslContext);
+
+ client = vertx.createNetClient(new NetClientOptions().setSsl(true)
+ .setSslEngineOptions(new JdkSSLEngineOptions() {
+ @Override
+ public SslContextFactory sslContextFactory() {
+ return new SslContextFactory() {
+ @Override
+ public SslContext create() {
+ return new JdkSslContext(
+ sslContext,
+ true,
+ null,
+ IdentityCipherSuiteFilter.INSTANCE,
+ ApplicationProtocolConfig.DISABLED,
+ io.netty.handler.ssl.ClientAuth.NONE,
+ null,
+ false);
+ }
+ };
+ }
+ }));
testNetClientInternal_(new HttpServerOptions()
.setHost("localhost")
diff --git a/src/test/java/io/vertx/core/net/SSLHelperTest.java b/src/test/java/io/vertx/core/net/SSLHelperTest.java
index 53f8388b1e1..4150371a2f4 100755
--- a/src/test/java/io/vertx/core/net/SSLHelperTest.java
+++ b/src/test/java/io/vertx/core/net/SSLHelperTest.java
@@ -17,6 +17,7 @@
import io.netty.handler.ssl.SslContext;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpServerOptions;
+import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.impl.SSLHelper;
@@ -41,22 +42,30 @@ public void testUseJdkCiphersWhenNotSpecified() throws Exception {
context.init(null, null, null);
SSLEngine engine = context.createSSLEngine();
String[] expected = engine.getEnabledCipherSuites();
- SSLHelper helper = new SSLHelper(new HttpClientOptions(),
- Cert.CLIENT_JKS.get(),
- Trust.SERVER_JKS.get());
- SslContext ctx = helper.getContext((VertxInternal) vertx);
- assertEquals(new HashSet<>(Arrays.asList(expected)), new HashSet<>(ctx.cipherSuites()));
+ SSLHelper helper = new SSLHelper(new HttpClientOptions().setKeyStoreOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()),
+ null);
+ helper
+ .init((ContextInternal) vertx.getOrCreateContext())
+ .onComplete(onSuccess(v -> {
+ SslContext ctx = helper.createContext((VertxInternal) vertx);
+ assertEquals(new HashSet<>(Arrays.asList(expected)), new HashSet<>(ctx.cipherSuites()));
+ testComplete();
+ }));
+ await();
}
@Test
public void testUseOpenSSLCiphersWhenNotSpecified() throws Exception {
Set expected = OpenSsl.availableOpenSslCipherSuites();
SSLHelper helper = new SSLHelper(
- new HttpClientOptions().setOpenSslEngineOptions(new OpenSSLEngineOptions()),
- Cert.CLIENT_PEM.get(),
- Trust.SERVER_PEM.get());
- SslContext ctx = helper.getContext((VertxInternal) vertx);
- assertEquals(expected, new HashSet<>(ctx.cipherSuites()));
+ new HttpClientOptions().setOpenSslEngineOptions(new OpenSSLEngineOptions()).setPemKeyCertOptions(Cert.CLIENT_PEM.get()).setTrustOptions(Trust.SERVER_PEM.get()),
+ null);
+ helper.init((ContextInternal) vertx.getOrCreateContext()).onComplete(onSuccess(v -> {
+ SslContext ctx = helper.createContext((VertxInternal) vertx);
+ assertEquals(expected, new HashSet<>(ctx.cipherSuites()));
+ testComplete();
+ }));
+ await();
}
@Test
@@ -76,19 +85,25 @@ private void testOpenSslServerSessionContext(boolean testDefault){
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions().setSessionCacheEnabled(false));
}
- SSLHelper defaultHelper = new SSLHelper(httpServerOptions,
- Cert.SERVER_PEM.get(),
- Trust.SERVER_PEM.get());
+ SSLHelper defaultHelper = new SSLHelper(httpServerOptions.setPemKeyCertOptions(Cert.SERVER_PEM.get()).setTrustOptions(Trust.SERVER_PEM.get()),
+ null);
- SslContext ctx = defaultHelper.getContext((VertxInternal) vertx);
- assertTrue(ctx instanceof OpenSslServerContext);
+ defaultHelper
+ .init((ContextInternal) vertx.getOrCreateContext())
+ .onComplete(onSuccess(v -> {
+ SslContext ctx = defaultHelper.createContext((VertxInternal) vertx);
+ assertTrue(ctx instanceof OpenSslServerContext);
- SSLSessionContext sslSessionContext = ctx.sessionContext();
- assertTrue(sslSessionContext instanceof OpenSslServerSessionContext);
+ SSLSessionContext sslSessionContext = ctx.sessionContext();
+ assertTrue(sslSessionContext instanceof OpenSslServerSessionContext);
- if (sslSessionContext instanceof OpenSslServerSessionContext) {
- assertEquals(testDefault, ((OpenSslServerSessionContext) sslSessionContext).isSessionCacheEnabled());
- }
+ if (sslSessionContext instanceof OpenSslServerSessionContext) {
+ assertEquals(testDefault, ((OpenSslServerSessionContext) sslSessionContext).isSessionCacheEnabled());
+ }
+ testComplete();
+ }));
+
+ await();
}
@Test
@@ -104,8 +119,14 @@ public void testPreserveEnabledCipherSuitesOrder() throws Exception {
assertEquals(new ArrayList<>(new HttpServerOptions(options).getEnabledCipherSuites()), Arrays.asList(engine.getEnabledCipherSuites()));
JsonObject json = options.toJson();
assertEquals(new ArrayList<>(new HttpServerOptions(json).getEnabledCipherSuites()), Arrays.asList(engine.getEnabledCipherSuites()));
- SSLHelper helper = new SSLHelper(options, Cert.SERVER_JKS.get(), null);
- assertEquals(Arrays.asList(helper.createEngine((VertxInternal) vertx).getEnabledCipherSuites()), Arrays.asList(engine.getEnabledCipherSuites()));
+ SSLHelper helper = new SSLHelper(options.setKeyCertOptions(Cert.SERVER_JKS.get()), null);
+ helper
+ .init((ContextInternal) vertx.getOrCreateContext())
+ .onComplete(onSuccess(v -> {
+ assertEquals(new HashSet<>(Arrays.asList(helper.createEngine((VertxInternal) vertx).getEnabledCipherSuites())), new HashSet<>(Arrays.asList(engine.getEnabledCipherSuites())));
+ testComplete();
+ }));
+ await();
}
@Test
diff --git a/src/test/java/io/vertx/it/SSLEngineTest.java b/src/test/java/io/vertx/it/SSLEngineTest.java
index 97884cf40f2..cd6577cdc02 100644
--- a/src/test/java/io/vertx/it/SSLEngineTest.java
+++ b/src/test/java/io/vertx/it/SSLEngineTest.java
@@ -14,7 +14,6 @@
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.OpenSslContext;
import io.netty.handler.ssl.SslContext;
-import io.vertx.core.VertxException;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerOptions;
@@ -106,7 +105,7 @@ private void doTest(SSLEngineOptions engine,
}
}
SSLHelper sslHelper = ((HttpServerImpl)server).sslHelper();
- SslContext ctx = sslHelper.getContext((VertxInternal) vertx);
+ SslContext ctx = sslHelper.createContext((VertxInternal) vertx);
switch (expectedSslContext != null ? expectedSslContext : "jdk") {
case "jdk":
assertTrue(ctx instanceof JdkSslContext);
diff --git a/src/test/java/io/vertx/test/core/TestUtils.java b/src/test/java/io/vertx/test/core/TestUtils.java
index eecb14079b7..27a2129413b 100644
--- a/src/test/java/io/vertx/test/core/TestUtils.java
+++ b/src/test/java/io/vertx/test/core/TestUtils.java
@@ -531,4 +531,15 @@ public static boolean isECCSupportedByVM() {
return false;
}
}
+
+ /**
+ * @return the most inner root cause of {@code throwable}
+ */
+ public static Throwable rootCause(Throwable throwable) {
+ Throwable root = throwable;
+ while (root.getCause() != null) {
+ root = root.getCause();
+ }
+ return root;
+ }
}