From 41d9c33f802d70a15ef7566e6020d1d9b1c71c38 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Wed, 11 Sep 2024 18:19:24 +0200 Subject: [PATCH 1/3] Enable buffer pooling settings with a SSL configurable option --- .../vertx/core/net/JdkSSLEngineOptions.java | 20 ++- .../io/vertx/core/net/impl/NetClientImpl.java | 5 +- .../io/vertx/core/net/impl/SSLHelper.java | 42 ++++- .../core/net/impl/SslContextProvider.java | 10 +- .../io/vertx/core/net/impl/TCPServerBase.java | 8 +- src/test/java/io/vertx/core/net/NetTest.java | 6 +- .../vertx/core/net/impl/SSLAllocatorTest.java | 145 ++++++++++++++++++ 7 files changed, 221 insertions(+), 15 deletions(-) create mode 100644 src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java diff --git a/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java b/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java index 35a3f5d59a5..a7a0f0771d2 100644 --- a/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java +++ b/src/main/java/io/vertx/core/net/JdkSSLEngineOptions.java @@ -55,15 +55,31 @@ public static synchronized boolean isAlpnAvailable() { return jdkAlpnAvailable; } + private boolean pooledHeapBuffers = false; + public JdkSSLEngineOptions() { } public JdkSSLEngineOptions(JsonObject json) { super(json); + pooledHeapBuffers = json.getBoolean("pooledHeapBuffers", false); } public JdkSSLEngineOptions(JdkSSLEngineOptions that) { super(that); + pooledHeapBuffers = that.pooledHeapBuffers; + } + + /** + * Set whether to use pooled heap buffers. Default is {@code false}, but it is recommended to use pooled buffers + */ + public JdkSSLEngineOptions setPooledHeapBuffers(boolean pooledHeapBuffers) { + this.pooledHeapBuffers = pooledHeapBuffers; + return this; + } + + public boolean isPooledHeapBuffers() { + return pooledHeapBuffers; } @Override @@ -72,7 +88,9 @@ public JdkSSLEngineOptions setUseWorkerThread(boolean useWorkerThread) { } public JsonObject toJson() { - return new JsonObject(); + JsonObject jsonObject = new JsonObject(); + jsonObject.put("pooledHeapBuffers", pooledHeapBuffers); + return jsonObject; } @Override 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 426f3182898..9f56501204d 100644 --- a/src/main/java/io/vertx/core/net/impl/NetClientImpl.java +++ b/src/main/java/io/vertx/core/net/impl/NetClientImpl.java @@ -28,7 +28,6 @@ import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; -import io.vertx.core.buffer.impl.PartialPooledByteBufAllocator; import io.vertx.core.impl.CloseFuture; import io.vertx.core.impl.ContextInternal; import io.vertx.core.impl.VertxInternal; @@ -49,7 +48,6 @@ import java.net.ConnectException; import java.util.Objects; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; /** @@ -297,7 +295,8 @@ private void connectInternal2(ProxyOptions proxyOptions, Objects.requireNonNull(connectHandler, "No null connectHandler accepted"); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoop); - bootstrap.option(ChannelOption.ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE); + bootstrap.option(ChannelOption.ALLOCATOR, + sslHelper.clientByteBufAllocator(sslChannelProvider.sslContextProvider())); vertx.transport().configure(options, remoteAddress.isDomainSocket(), bootstrap); 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 6e12705176b..b02629dcf4d 100755 --- a/src/main/java/io/vertx/core/net/impl/SSLHelper.java +++ b/src/main/java/io/vertx/core/net/impl/SSLHelper.java @@ -11,12 +11,15 @@ package io.vertx.core.net.impl; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.PooledByteBufAllocator; import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.SslProvider; 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.buffer.impl.PartialPooledByteBufAllocator; import io.vertx.core.http.ClientAuth; import io.vertx.core.impl.ContextInternal; import io.vertx.core.net.ClientOptionsBase; @@ -57,6 +60,27 @@ public class SSLHelper { CLIENT_AUTH_MAPPING.put(ClientAuth.NONE, io.netty.handler.ssl.ClientAuth.NONE); } + ByteBufAllocator clientByteBufAllocator(SslContextProvider ctxProvider) { + if (usesJDKSSLWithPooledHeapBuffers(ctxProvider)) { + return PooledByteBufAllocator.DEFAULT; + } + return PartialPooledByteBufAllocator.INSTANCE; + } + + ByteBufAllocator serverByteBufAllocator(SslContextProvider ctxProvider) { + if (!ssl || usesJDKSSLWithPooledHeapBuffers(ctxProvider)) { + return PooledByteBufAllocator.DEFAULT; + } + return PartialPooledByteBufAllocator.INSTANCE; + } + + private boolean usesJDKSSLWithPooledHeapBuffers(SslContextProvider ctxProvider) { + return ssl && sslEngineOptions instanceof JdkSSLEngineOptions && + ctxProvider.sslProvider() == SslProvider.JDK && + ((JdkSSLEngineOptions) sslEngineOptions).isPooledHeapBuffers(); + } + + /** * Resolve the ssl engine options to use for properly running the configured options. */ @@ -149,11 +173,14 @@ private static class CachedProvider { private class EngineConfig { + private final SslProvider sslProvider; private final SSLOptions sslOptions; private final Supplier supplier; private final boolean useWorkerPool; - public EngineConfig(SSLOptions sslOptions, Supplier supplier, boolean useWorkerPool) { + public EngineConfig(SslProvider sslProvider, SSLOptions sslOptions, Supplier supplier, + boolean useWorkerPool) { + this.sslProvider = sslProvider; this.sslOptions = sslOptions; this.supplier = supplier; this.useWorkerPool = useWorkerPool; @@ -161,6 +188,7 @@ public EngineConfig(SSLOptions sslOptions, Supplier supplier, SslContextProvider sslContextProvider() { return new SslContextProvider( + sslProvider, clientAuth, endpointIdentificationAlgorithm, applicationProtocols, @@ -291,18 +319,26 @@ private Future build(SSLOptions sslOptions, ContextInternal ctx) { }).compose(v2 -> ctx.executeBlockingInternal(p -> { Supplier supplier; boolean useWorkerPool; + SslProvider sslProvider; try { SSLEngineOptions resolvedEngineOptions = resolveEngineOptions(sslEngineOptions, useAlpn); supplier = resolvedEngineOptions::sslContextFactory; useWorkerPool = resolvedEngineOptions.getUseWorkerThread(); + if (resolvedEngineOptions instanceof JdkSSLEngineOptions) { + sslProvider = SslProvider.JDK; + } else if (resolvedEngineOptions instanceof OpenSSLEngineOptions) { + sslProvider = SslProvider.OPENSSL; + } else { + sslProvider = SslProvider.JDK; + } } catch (Exception e) { p.fail(e); return; } - p.complete(new EngineConfig(sslOptions, supplier, useWorkerPool)); + p.complete(new EngineConfig(sslProvider, sslOptions, supplier, useWorkerPool)); })).onComplete(promise); } else { - sslContextFactorySupplier = Future.succeededFuture(new EngineConfig(sslOptions, () -> new DefaultSslContextFactory(SslProvider.JDK, false), SSLEngineOptions.DEFAULT_USE_WORKER_POOL)); + sslContextFactorySupplier = Future.succeededFuture(new EngineConfig(SslProvider.JDK, sslOptions, () -> new DefaultSslContextFactory(SslProvider.JDK, false), SSLEngineOptions.DEFAULT_USE_WORKER_POOL)); } return sslContextFactorySupplier; } diff --git a/src/main/java/io/vertx/core/net/impl/SslContextProvider.java b/src/main/java/io/vertx/core/net/impl/SslContextProvider.java index e44cefda4b6..6d16bf70a98 100644 --- a/src/main/java/io/vertx/core/net/impl/SslContextProvider.java +++ b/src/main/java/io/vertx/core/net/impl/SslContextProvider.java @@ -11,6 +11,7 @@ package io.vertx.core.net.impl; import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslProvider; import io.vertx.core.VertxException; import io.vertx.core.http.ClientAuth; import io.vertx.core.spi.tls.SslContextFactory; @@ -30,6 +31,7 @@ */ public class SslContextProvider { + private final SslProvider providerType; private final Supplier provider; private final Set enabledProtocols; private final List crls; @@ -42,7 +44,8 @@ public class SslContextProvider { private final Function keyManagerFactoryMapper; private final Function trustManagerMapper; - public SslContextProvider(ClientAuth clientAuth, + public SslContextProvider(SslProvider providerType, + ClientAuth clientAuth, String endpointIdentificationAlgorithm, List applicationProtocols, Set enabledCipherSuites, @@ -53,6 +56,7 @@ public SslContextProvider(ClientAuth clientAuth, Function trustManagerMapper, List crls, Supplier provider) { + this.providerType = providerType; this.provider = provider; this.clientAuth = clientAuth; this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm; @@ -66,6 +70,10 @@ public SslContextProvider(ClientAuth clientAuth, this.crls = crls; } + SslProvider sslProvider() { + return providerType; + } + public VertxSslContext createContext(boolean server, KeyManagerFactory keyManagerFactory, TrustManager[] trustManagers, 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 08cac0c2773..95ca048aa3a 100644 --- a/src/main/java/io/vertx/core/net/impl/TCPServerBase.java +++ b/src/main/java/io/vertx/core/net/impl/TCPServerBase.java @@ -237,16 +237,12 @@ private synchronized Future listen(SocketAddress localAddress, ContextI // Initialize SSL before binding sslChannelProvider = sslHelper.updateSslContext(options.getSslOptions(), true, listenContext).onComplete(ar -> { if (ar.succeeded()) { - // Socket bind channelBalancer.addWorker(eventLoop, worker); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(vertx.getAcceptorEventLoopGroup(), channelBalancer.workers()); - if (options.isSsl()) { - bootstrap.childOption(ChannelOption.ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE); - } else { - bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); - } + bootstrap.childOption(ChannelOption.ALLOCATOR, + sslHelper.serverByteBufAllocator(ar.result().sslChannelProvider().sslContextProvider())); bootstrap.childHandler(channelBalancer); applyConnectionOptions(localAddress.isDomainSocket(), bootstrap); diff --git a/src/test/java/io/vertx/core/net/NetTest.java b/src/test/java/io/vertx/core/net/NetTest.java index 78852d4dc96..6fd8f4615ae 100755 --- a/src/test/java/io/vertx/core/net/NetTest.java +++ b/src/test/java/io/vertx/core/net/NetTest.java @@ -525,12 +525,14 @@ public void testClientOptionsJson() { int reconnectAttempts = TestUtils.randomPositiveInt(); long reconnectInterval = TestUtils.randomPositiveInt(); boolean useAlpn = TestUtils.randomBoolean(); + boolean pooledHeapBuffers = rand.nextBoolean(); String hostnameVerificationAlgorithm = TestUtils.randomAlphaString(10); String sslEngine; JsonObject sslEngineOptions; if (TestUtils.randomBoolean()) { sslEngine = "jdkSslEngineOptions"; - sslEngineOptions = new JsonObject(); + sslEngineOptions = new JsonObject() + .put("pooledHeapBuffers", pooledHeapBuffers); } else { sslEngine = "openSslEngineOptions"; boolean sessionCacheEnabled = rand.nextBoolean(); @@ -598,6 +600,8 @@ public void testClientOptionsJson() { switch (sslEngine) { case "jdkSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); + JdkSSLEngineOptions jdkSSLEngineOptions = (JdkSSLEngineOptions) options.getSslEngineOptions(); + assertEquals(pooledHeapBuffers, jdkSSLEngineOptions.isPooledHeapBuffers()); break; case "openSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof OpenSSLEngineOptions); diff --git a/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java b/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java new file mode 100644 index 00000000000..f25c1715c14 --- /dev/null +++ b/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java @@ -0,0 +1,145 @@ +/* + * 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.net.impl; + +import javax.net.ssl.SSLContext; + +import org.junit.Test; + +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.handler.ssl.SslProvider; +import io.vertx.core.buffer.impl.PartialPooledByteBufAllocator; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.net.JdkSSLEngineOptions; +import io.vertx.core.net.OpenSSLEngineOptions; +import io.vertx.core.net.SSLOptions; +import io.vertx.test.core.VertxTestBase; +import io.vertx.test.tls.Cert; +import io.vertx.test.tls.Trust; + +public class SSLAllocatorTest extends VertxTestBase { + + @Test + public void testUsePartialPooledByteBufAllocatorInstanceWhenNotSpecified() throws Exception { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + SSLHelper helper = new SSLHelper(new HttpClientOptions() + .setSsl(true) + .setKeyStoreOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), + null); + helper + .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) + .onComplete(onSuccess(provider -> { + assertSame(SslProvider.JDK, provider.sslProvider()); + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.serverByteBufAllocator(provider)); + testComplete(); + })); + await(); + } + + @Test + public void testUsePartialPooledByteBufAllocatorInstanceIfDefaultJDKSSLIsConfigured() throws Exception { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + SSLHelper helper = new SSLHelper(new HttpClientOptions() + .setSsl(true) + .setSslEngineOptions(new JdkSSLEngineOptions()) + .setKeyStoreOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), + null); + helper + .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) + .onComplete(onSuccess(provider -> { + assertSame(SslProvider.JDK, provider.sslProvider()); + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.serverByteBufAllocator(provider)); + testComplete(); + })); + await(); + } + + @Test + public void testUsePooledByteBufAllocatorDefaultIfJDKSSLPooledHeapBufferConfigured() throws Exception { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + SSLHelper helper = new SSLHelper(new HttpClientOptions() + .setSsl(true) + .setSslEngineOptions(new JdkSSLEngineOptions().setPooledHeapBuffers(true)) + .setKeyStoreOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), + null); + helper + .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) + .onComplete(onSuccess(provider -> { + assertSame(SslProvider.JDK, provider.sslProvider()); + assertSame(PooledByteBufAllocator.DEFAULT, helper.clientByteBufAllocator(provider)); + assertSame(PooledByteBufAllocator.DEFAULT, helper.serverByteBufAllocator(provider)); + testComplete(); + })); + await(); + } + + @Test + public void testClientUsePartialPooledByteBufAllocatorInstanceIfSSLNotConfigured() throws Exception { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + SSLHelper helper = new SSLHelper(new HttpClientOptions() + .setSsl(false) + .setKeyStoreOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), + null); + helper + .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) + .onComplete(onSuccess(provider -> { + // this shouldn't happen, really, because of options::setSsl(false) + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); + testComplete(); + })); + await(); + } + + @Test + public void testServerUsePooledByteBufAllocatorInstanceIfSSLNotConfigured() throws Exception { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + SSLHelper helper = new SSLHelper(new HttpServerOptions() + .setSsl(false) + .setKeyStoreOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), + null); + helper + .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) + .onComplete(onSuccess(provider -> { + // this shouldn't happen, really, because of options::setSsl(false) + assertSame(PooledByteBufAllocator.DEFAULT, helper.serverByteBufAllocator(provider)); + testComplete(); + })); + await(); + } + + @Test + public void testUsePooledByteBufAllocatorDefaultIfOpenSSLIsConfigured() { + SSLHelper helper = new SSLHelper( + new HttpClientOptions().setOpenSslEngineOptions(new OpenSSLEngineOptions()) + .setSsl(true) + .setPemKeyCertOptions(Cert.CLIENT_PEM.get()).setTrustOptions(Trust.SERVER_PEM.get()), + null); + helper.buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_PEM.get()).setTrustOptions(Trust.SERVER_PEM.get()), (ContextInternal) vertx.getOrCreateContext()).onComplete(onSuccess(provider -> { + assertSame(SslProvider.OPENSSL, provider.sslProvider()); + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); + assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.serverByteBufAllocator(provider)); + testComplete(); + })); + await(); + } + + +} From 1fbfdab1b2857cd83e5b18501431aa70d5661f43 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Mon, 21 Oct 2024 21:59:17 +0200 Subject: [PATCH 2/3] Remove instanceof check vs non-JDK SSL providers --- .../io/vertx/core/net/impl/SSLHelper.java | 24 +++++++------------ .../core/net/impl/SslContextProvider.java | 11 ++++----- .../vertx/core/net/impl/SSLAllocatorTest.java | 9 ++++--- 3 files changed, 18 insertions(+), 26 deletions(-) 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 b02629dcf4d..17747e45702 100755 --- a/src/main/java/io/vertx/core/net/impl/SSLHelper.java +++ b/src/main/java/io/vertx/core/net/impl/SSLHelper.java @@ -76,7 +76,7 @@ ByteBufAllocator serverByteBufAllocator(SslContextProvider ctxProvider) { private boolean usesJDKSSLWithPooledHeapBuffers(SslContextProvider ctxProvider) { return ssl && sslEngineOptions instanceof JdkSSLEngineOptions && - ctxProvider.sslProvider() == SslProvider.JDK && + ctxProvider.jdkSSLProvider() && ((JdkSSLEngineOptions) sslEngineOptions).isPooledHeapBuffers(); } @@ -173,14 +173,14 @@ private static class CachedProvider { private class EngineConfig { - private final SslProvider sslProvider; + private final boolean jdkSSLProvider; private final SSLOptions sslOptions; private final Supplier supplier; private final boolean useWorkerPool; - public EngineConfig(SslProvider sslProvider, SSLOptions sslOptions, Supplier supplier, + public EngineConfig(boolean jdkSSLProvider, SSLOptions sslOptions, Supplier supplier, boolean useWorkerPool) { - this.sslProvider = sslProvider; + this.jdkSSLProvider = jdkSSLProvider; this.sslOptions = sslOptions; this.supplier = supplier; this.useWorkerPool = useWorkerPool; @@ -188,7 +188,7 @@ public EngineConfig(SslProvider sslProvider, SSLOptions sslOptions, Supplier build(SSLOptions sslOptions, ContextInternal ctx) { }).compose(v2 -> ctx.executeBlockingInternal(p -> { Supplier supplier; boolean useWorkerPool; - SslProvider sslProvider; + final boolean jdkSSLProvider; try { SSLEngineOptions resolvedEngineOptions = resolveEngineOptions(sslEngineOptions, useAlpn); supplier = resolvedEngineOptions::sslContextFactory; useWorkerPool = resolvedEngineOptions.getUseWorkerThread(); - if (resolvedEngineOptions instanceof JdkSSLEngineOptions) { - sslProvider = SslProvider.JDK; - } else if (resolvedEngineOptions instanceof OpenSSLEngineOptions) { - sslProvider = SslProvider.OPENSSL; - } else { - sslProvider = SslProvider.JDK; - } + jdkSSLProvider = resolvedEngineOptions instanceof JdkSSLEngineOptions; } catch (Exception e) { p.fail(e); return; } - p.complete(new EngineConfig(sslProvider, sslOptions, supplier, useWorkerPool)); + p.complete(new EngineConfig(jdkSSLProvider, sslOptions, supplier, useWorkerPool)); })).onComplete(promise); } else { - sslContextFactorySupplier = Future.succeededFuture(new EngineConfig(SslProvider.JDK, sslOptions, () -> new DefaultSslContextFactory(SslProvider.JDK, false), SSLEngineOptions.DEFAULT_USE_WORKER_POOL)); + sslContextFactorySupplier = Future.succeededFuture(new EngineConfig(true, sslOptions, () -> new DefaultSslContextFactory(SslProvider.JDK, false), SSLEngineOptions.DEFAULT_USE_WORKER_POOL)); } return sslContextFactorySupplier; } diff --git a/src/main/java/io/vertx/core/net/impl/SslContextProvider.java b/src/main/java/io/vertx/core/net/impl/SslContextProvider.java index 6d16bf70a98..cee204fe382 100644 --- a/src/main/java/io/vertx/core/net/impl/SslContextProvider.java +++ b/src/main/java/io/vertx/core/net/impl/SslContextProvider.java @@ -11,7 +11,6 @@ package io.vertx.core.net.impl; import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslProvider; import io.vertx.core.VertxException; import io.vertx.core.http.ClientAuth; import io.vertx.core.spi.tls.SslContextFactory; @@ -31,7 +30,7 @@ */ public class SslContextProvider { - private final SslProvider providerType; + private final boolean jdkSSLProvider; private final Supplier provider; private final Set enabledProtocols; private final List crls; @@ -44,7 +43,7 @@ public class SslContextProvider { private final Function keyManagerFactoryMapper; private final Function trustManagerMapper; - public SslContextProvider(SslProvider providerType, + public SslContextProvider(boolean jdkSSLProvider, ClientAuth clientAuth, String endpointIdentificationAlgorithm, List applicationProtocols, @@ -56,7 +55,7 @@ public SslContextProvider(SslProvider providerType, Function trustManagerMapper, List crls, Supplier provider) { - this.providerType = providerType; + this.jdkSSLProvider = jdkSSLProvider; this.provider = provider; this.clientAuth = clientAuth; this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm; @@ -70,8 +69,8 @@ public SslContextProvider(SslProvider providerType, this.crls = crls; } - SslProvider sslProvider() { - return providerType; + boolean jdkSSLProvider() { + return jdkSSLProvider; } public VertxSslContext createContext(boolean server, diff --git a/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java b/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java index f25c1715c14..f37bef3f488 100644 --- a/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java +++ b/src/test/java/io/vertx/core/net/impl/SSLAllocatorTest.java @@ -16,7 +16,6 @@ import org.junit.Test; import io.netty.buffer.PooledByteBufAllocator; -import io.netty.handler.ssl.SslProvider; import io.vertx.core.buffer.impl.PartialPooledByteBufAllocator; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; @@ -41,7 +40,7 @@ public void testUsePartialPooledByteBufAllocatorInstanceWhenNotSpecified() throw helper .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) .onComplete(onSuccess(provider -> { - assertSame(SslProvider.JDK, provider.sslProvider()); + assertTrue(provider.jdkSSLProvider()); assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.serverByteBufAllocator(provider)); testComplete(); @@ -61,7 +60,7 @@ public void testUsePartialPooledByteBufAllocatorInstanceIfDefaultJDKSSLIsConfigu helper .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) .onComplete(onSuccess(provider -> { - assertSame(SslProvider.JDK, provider.sslProvider()); + assertTrue(provider.jdkSSLProvider()); assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.serverByteBufAllocator(provider)); testComplete(); @@ -81,7 +80,7 @@ public void testUsePooledByteBufAllocatorDefaultIfJDKSSLPooledHeapBufferConfigur helper .buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_JKS.get()).setTrustOptions(Trust.SERVER_JKS.get()), (ContextInternal) vertx.getOrCreateContext()) .onComplete(onSuccess(provider -> { - assertSame(SslProvider.JDK, provider.sslProvider()); + assertTrue(provider.jdkSSLProvider()); assertSame(PooledByteBufAllocator.DEFAULT, helper.clientByteBufAllocator(provider)); assertSame(PooledByteBufAllocator.DEFAULT, helper.serverByteBufAllocator(provider)); testComplete(); @@ -133,7 +132,7 @@ public void testUsePooledByteBufAllocatorDefaultIfOpenSSLIsConfigured() { .setPemKeyCertOptions(Cert.CLIENT_PEM.get()).setTrustOptions(Trust.SERVER_PEM.get()), null); helper.buildContextProvider(new SSLOptions().setKeyCertOptions(Cert.CLIENT_PEM.get()).setTrustOptions(Trust.SERVER_PEM.get()), (ContextInternal) vertx.getOrCreateContext()).onComplete(onSuccess(provider -> { - assertSame(SslProvider.OPENSSL, provider.sslProvider()); + assertFalse(provider.jdkSSLProvider()); assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.clientByteBufAllocator(provider)); assertSame(PartialPooledByteBufAllocator.INSTANCE, helper.serverByteBufAllocator(provider)); testComplete(); From a3393ace29680911e11cb8b6705c2369964dbf98 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Tue, 22 Oct 2024 14:11:58 +0200 Subject: [PATCH 3/3] Adding new end 2 end test --- .../core/net/impl/NetAllocatorsTest.java | 341 ++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100755 src/test/java/io/vertx/core/net/impl/NetAllocatorsTest.java diff --git a/src/test/java/io/vertx/core/net/impl/NetAllocatorsTest.java b/src/test/java/io/vertx/core/net/impl/NetAllocatorsTest.java new file mode 100755 index 00000000000..ea00bbe244a --- /dev/null +++ b/src/test/java/io/vertx/core/net/impl/NetAllocatorsTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2011-2021 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 java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CountDownLatch; + +import org.junit.Assume; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.LastHttpContent; +import io.vertx.core.Context; +import io.vertx.core.VertxOptions; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.buffer.impl.PartialPooledByteBufAllocator; +import io.vertx.core.buffer.impl.VertxByteBufAllocator; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.net.JdkSSLEngineOptions; +import io.vertx.core.net.NetClient; +import io.vertx.core.net.NetClientOptions; +import io.vertx.core.net.NetServer; +import io.vertx.core.net.NetServerOptions; +import io.vertx.core.net.OpenSSLEngineOptions; +import io.vertx.core.net.SocketAddress; +import io.vertx.test.core.TestUtils; +import io.vertx.test.core.VertxTestBase; +import io.vertx.test.tls.Cert; +import io.vertx.test.tls.Trust; + +public class NetAllocatorsTest extends VertxTestBase { + + private SocketAddress testAddress; + private NetServer server; + private NetClient client; + private File tmp; + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Override + public void setUp() throws Exception { + super.setUp(); + if (USE_DOMAIN_SOCKETS) { + assertTrue("Native transport not enabled", USE_NATIVE_TRANSPORT); + tmp = TestUtils.tmpFile(".sock"); + testAddress = SocketAddress.domainSocketAddress(tmp.getAbsolutePath()); + } else { + testAddress = SocketAddress.inetSocketAddress(1234, "localhost"); + } + client = vertx.createNetClient(new NetClientOptions().setConnectTimeout(1000)); + server = vertx.createNetServer(); + } + + @Override + protected VertxOptions getOptions() { + VertxOptions options = super.getOptions(); + options.getAddressResolverOptions().setHostsValue(Buffer.buffer("" + + "127.0.0.1 localhost\n" + + "127.0.0.1 host1\n" + + "127.0.0.1 host2.com\n" + + "127.0.0.1 example.com")); + return options; + } + + @Override + protected void tearDown() throws Exception { + if (tmp != null) { + tmp.delete(); + } + super.tearDown(); + } + + @Test + public void testServerAllocatorNoSSL() throws Exception { + server.close(); + server = vertx.createNetServer(new NetServerOptions() + .setPort(1234) + .setHost("localhost")); + testServerAllocator(new HttpClientOptions(), false, + PooledByteBufAllocator.DEFAULT, PooledByteBufAllocator.DEFAULT, false); + } + + @Test + public void testHeapPoolingServerAllocatorJdkSSL() throws Exception { + server.close(); + server = vertx.createNetServer(new NetServerOptions() + .setPort(1234) + .setHost("localhost") + .setSsl(true) + .setSslEngineOptions(new JdkSSLEngineOptions().setPooledHeapBuffers(true)) + .setKeyStoreOptions(Cert.SERVER_JKS.get())); + testServerAllocator(new HttpClientOptions() + .setSsl(true) + .setTrustStoreOptions(Trust.SERVER_JKS.get()), true, + // the JDK SSL engine wrapping buffer is heap-based, but the output one not, see: + // see https://github.com/netty/netty/blob/f377e7e23f71fbf1e682bfd5b69b8720338ee8b9/handler/src/main/java/io/netty/handler/ssl/SslHandler.java#L2407 + // It uses the allocator's buffer method, which is direct-based on PooledByteBufAllocator.DEFAULT + PooledByteBufAllocator.DEFAULT, PooledByteBufAllocator.DEFAULT, false); + } + + @Test + public void testServerAllocatorJdkSSL() throws Exception { + server.close(); + server = vertx.createNetServer(new NetServerOptions() + .setPort(1234) + .setHost("localhost") + .setSsl(true) + .setSslEngineOptions(new JdkSSLEngineOptions()) + .setKeyStoreOptions(Cert.SERVER_JKS.get())); + testServerAllocator(new HttpClientOptions() + .setSsl(true) + .setTrustStoreOptions(Trust.SERVER_JKS.get()), true, + VertxByteBufAllocator.UNPOOLED_ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE, true); + } + + @Test + public void testServerAllocatorOpenSSL() throws Exception { + Assume.assumeTrue(OpenSSLEngineOptions.isAvailable()); + server.close(); + server = vertx.createNetServer(new NetServerOptions() + .setPort(1234) + .setHost("localhost") + .setSsl(true) + .setSslEngineOptions(new OpenSSLEngineOptions()) + .setKeyStoreOptions(Cert.SERVER_JKS.get())); + testServerAllocator(new HttpClientOptions() + .setSsl(true) + .setTrustStoreOptions(Trust.SERVER_JKS.get()), true, + VertxByteBufAllocator.POOLED_ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE, false); + } + + private void testServerAllocator(HttpClientOptions clientOptions, boolean expectSSL, + ByteBufAllocator bufferAllocator, ByteBufAllocator channelAllocator, + boolean expectHeapBuffer) throws Exception { + waitFor(2); + server.connectHandler(so -> { + NetSocketInternal internal = (NetSocketInternal) so; + assertEquals(expectSSL, internal.isSsl()); + ChannelHandlerContext chctx = internal.channelHandlerContext(); + ChannelPipeline pipeline = chctx.pipeline(); + pipeline.addBefore("handler", "http", new HttpServerCodec()); + // add a new handler which feeds the raw buffer to the http handler: this should receive the buffer + // from the SSL handler, if configured + pipeline.addBefore("http", "raw", new io.netty.channel.ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + assertTrue(msg instanceof ByteBuf); + ByteBuf byteBuf = (ByteBuf) msg; + assertSame(bufferAllocator, byteBuf.alloc()); + assertSame(channelAllocator, ctx.channel().config().getAllocator()); + assertTrue(expectHeapBuffer == byteBuf.hasArray()); + super.channelRead(ctx, msg); + } + }); + internal.handler(buff -> fail()); + internal.messageHandler(obj -> { + if (obj instanceof LastHttpContent) { + DefaultFullHttpResponse response = new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + HttpResponseStatus.OK, + Unpooled.copiedBuffer("Hello World", StandardCharsets.UTF_8)); + response.headers().set(HttpHeaderNames.CONTENT_LENGTH, "11"); + internal.writeMessage(response, onSuccess(v -> complete())); + } + }); + }); + startServer(SocketAddress.inetSocketAddress(1234, "localhost")); + HttpClient client = vertx.createHttpClient(clientOptions); + client.request(io.vertx.core.http.HttpMethod.GET, 1234, "localhost", "/somepath", onSuccess(req -> { + req.send(onSuccess(resp -> { + assertEquals(200, resp.statusCode()); + resp.body(onSuccess(body -> { + assertEquals("Hello World", body.toString()); + complete(); + })); + })); + })); + await(); + } + + @Test + public void testClientAllocatorNoSSL() throws Exception { + testClientAllocator(new HttpServerOptions() + .setHost("localhost") + .setPort(1234), false, + VertxByteBufAllocator.POOLED_ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE, false); + } + + @Test + public void testHeapPoolingClientAllocatorJdkSSL() throws Exception { + client.close(); + client = vertx.createNetClient(new NetClientOptions() + .setSsl(true) + .setSslEngineOptions(new JdkSSLEngineOptions().setPooledHeapBuffers(true)) + .setHostnameVerificationAlgorithm("") + .setTrustStoreOptions(Trust.SERVER_JKS.get())); + testClientAllocator(new HttpServerOptions() + .setHost("localhost") + .setPort(1234) + .setSsl(true) + .setKeyStoreOptions(Cert.SERVER_JKS.get()), true, + // the JDK SSL engine wrapping buffer is heap-based, but the output one not, see: + // see https://github.com/netty/netty/blob/f377e7e23f71fbf1e682bfd5b69b8720338ee8b9/handler/src/main/java/io/netty/handler/ssl/SslHandler.java#L2407 + // It uses the allocator's buffer method, which is direct-based on PooledByteBufAllocator.DEFAULT + PooledByteBufAllocator.DEFAULT, PooledByteBufAllocator.DEFAULT, false); + } + + @Test + public void testClientAllocatorJdkSSL() throws Exception { + client.close(); + client = vertx.createNetClient(new NetClientOptions() + .setSsl(true) + .setSslEngineOptions(new JdkSSLEngineOptions()) + .setHostnameVerificationAlgorithm("") + .setTrustStoreOptions(Trust.SERVER_JKS.get())); + testClientAllocator(new HttpServerOptions() + .setHost("localhost") + .setPort(1234) + .setSsl(true) + .setKeyStoreOptions(Cert.SERVER_JKS.get()), true, + VertxByteBufAllocator.UNPOOLED_ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE, true); + } + + @Test + public void testClientAllocatorOpenSSL() throws Exception { + Assume.assumeTrue(OpenSSLEngineOptions.isAvailable()); + client.close(); + client = vertx.createNetClient(new NetClientOptions() + .setSsl(true) + .setSslEngineOptions(new OpenSSLEngineOptions()) + .setHostnameVerificationAlgorithm("") + .setTrustStoreOptions(Trust.SERVER_JKS.get())); + testClientAllocator(new HttpServerOptions() + .setHost("localhost") + .setPort(1234) + .setSsl(true) + .setKeyStoreOptions(Cert.SERVER_JKS.get()), true, + VertxByteBufAllocator.POOLED_ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE, false); + } + + private void testClientAllocator(HttpServerOptions options, + boolean expectSSL, + ByteBufAllocator expectedBufferAllocator, + ByteBufAllocator expectedChannelAllocator, + boolean expectHeapBuffer) throws Exception { + waitFor(2); + HttpServer server = vertx.createHttpServer(options); + server.requestHandler(req -> { + req.response().end("Hello World"); }); + CountDownLatch latch = new CountDownLatch(1); + server.listen(onSuccess(v -> { + latch.countDown(); + })); + awaitLatch(latch); + client.connect(1234, "localhost", onSuccess(so -> { + NetSocketInternal soInt = (NetSocketInternal) so; + assertEquals(expectSSL, soInt.isSsl()); + ChannelHandlerContext chctx = soInt.channelHandlerContext(); + ChannelPipeline pipeline = chctx.pipeline(); + pipeline.addBefore("handler", "http", new HttpClientCodec()); + // add a new handler which feeds the raw buffer to the http handler: this should receive the buffer + // from the SSL handler, if configured + pipeline.addBefore("http", "raw", new io.netty.channel.ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + assertTrue(msg instanceof ByteBuf); + ByteBuf byteBuf = (ByteBuf) msg; + assertSame(expectedBufferAllocator, byteBuf.alloc()); + assertSame(expectedChannelAllocator, ctx.channel().config().getAllocator()); + assertTrue(expectHeapBuffer == byteBuf.hasArray()); + super.channelRead(ctx, msg); + complete(); + } + }); + soInt.handler(buff -> fail()); + soInt.writeMessage(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/somepath"), onSuccess(v -> complete())); + })); + await(); + } + + protected void startServer(SocketAddress remoteAddress) throws Exception { + startServer(remoteAddress, vertx.getOrCreateContext()); + } + + protected void startServer(SocketAddress remoteAddress, Context context) throws Exception { + startServer(remoteAddress, context, server); + } + + protected void startServer(SocketAddress remoteAddress, Context context, NetServer server) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + context.runOnContext(v -> { + server.listen(remoteAddress, onSuccess(s -> latch.countDown())); + }); + awaitLatch(latch); + } + + protected void startServer() throws Exception { + startServer(testAddress, vertx.getOrCreateContext()); + } + + protected void startServer(NetServer server) throws Exception { + startServer(testAddress, vertx.getOrCreateContext(), server); + } + + protected void startServer(Context context) throws Exception { + startServer(testAddress, context, server); + } + + protected void startServer(Context context, NetServer server) throws Exception { + startServer(testAddress, context, server); + } +}