Java tutorial
/* * Copyright (c) 2014 Red Hat, Inc. and others * * 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; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; import io.vertx.core.*; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.eventbus.Message; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.*; import io.vertx.core.impl.ConcurrentHashSet; import io.vertx.core.impl.NetSocketInternal; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.net.impl.NetServerImpl; import io.vertx.core.net.impl.SocketAddressImpl; import io.vertx.core.streams.ReadStream; import io.vertx.test.core.*; import io.vertx.test.tls.Cert; import io.vertx.test.tls.Trust; import io.vertx.test.netty.TestLoggerFactory; import io.vertx.test.proxy.HttpProxy; import io.vertx.test.proxy.Socks4Proxy; import io.vertx.test.proxy.SocksProxy; import io.vertx.test.proxy.TestProxyBase; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.security.cert.X509Certificate; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import static io.vertx.test.core.TestUtils.*; /** * @author <a href="http://tfox.org">Tim Fox</a> */ public class NetTest extends VertxTestBase { private static final Logger log = LoggerFactory.getLogger(NetTest.class); private SocketAddress testAddress; private NetServer server; private NetClient client; private TestProxyBase proxy; private File tmp; @Rule public TemporaryFolder testFolder = new TemporaryFolder(); 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; } protected void awaitClose(NetServer server) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); server.close((asyncResult) -> { latch.countDown(); }); awaitLatch(latch); } protected void tearDown() throws Exception { if (tmp != null) { tmp.delete(); } if (client != null) { client.close(); } if (server != null) { awaitClose(server); } if (proxy != null) { proxy.stop(); } super.tearDown(); } @Test public void testClientOptions() { NetClientOptions options = new NetClientOptions(); assertEquals(NetworkOptions.DEFAULT_SEND_BUFFER_SIZE, options.getSendBufferSize()); int rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setSendBufferSize(rand)); assertEquals(rand, options.getSendBufferSize()); assertIllegalArgumentException(() -> options.setSendBufferSize(0)); assertIllegalArgumentException(() -> options.setSendBufferSize(-123)); assertEquals(NetworkOptions.DEFAULT_RECEIVE_BUFFER_SIZE, options.getReceiveBufferSize()); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setReceiveBufferSize(rand)); assertEquals(rand, options.getReceiveBufferSize()); assertIllegalArgumentException(() -> options.setReceiveBufferSize(0)); assertIllegalArgumentException(() -> options.setReceiveBufferSize(-123)); assertTrue(options.isReuseAddress()); assertEquals(options, options.setReuseAddress(false)); assertFalse(options.isReuseAddress()); assertEquals(NetworkOptions.DEFAULT_TRAFFIC_CLASS, options.getTrafficClass()); rand = 23; assertEquals(options, options.setTrafficClass(rand)); assertEquals(rand, options.getTrafficClass()); assertIllegalArgumentException(() -> options.setTrafficClass(-2)); assertIllegalArgumentException(() -> options.setTrafficClass(256)); assertTrue(options.isTcpNoDelay()); assertEquals(options, options.setTcpNoDelay(false)); assertFalse(options.isTcpNoDelay()); boolean tcpKeepAlive = false; assertEquals(tcpKeepAlive, options.isTcpKeepAlive()); assertEquals(options, options.setTcpKeepAlive(!tcpKeepAlive)); assertEquals(!tcpKeepAlive, options.isTcpKeepAlive()); int soLinger = -1; assertEquals(soLinger, options.getSoLinger()); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setSoLinger(rand)); assertEquals(rand, options.getSoLinger()); assertIllegalArgumentException(() -> options.setSoLinger(-2)); rand = TestUtils.randomPositiveInt(); assertEquals(0, options.getIdleTimeout()); assertEquals(options, options.setIdleTimeout(rand)); assertEquals(rand, options.getIdleTimeout()); assertFalse(options.isSsl()); assertEquals(options, options.setSsl(true)); assertTrue(options.isSsl()); assertNull(options.getKeyCertOptions()); JksOptions keyStoreOptions = new JksOptions().setPath(TestUtils.randomAlphaString(100)) .setPassword(TestUtils.randomAlphaString(100)); assertEquals(options, options.setKeyStoreOptions(keyStoreOptions)); assertEquals(keyStoreOptions, options.getKeyCertOptions()); assertNull(options.getTrustOptions()); JksOptions trustStoreOptions = new JksOptions().setPath(TestUtils.randomAlphaString(100)) .setPassword(TestUtils.randomAlphaString(100)); assertEquals(options, options.setTrustStoreOptions(trustStoreOptions)); assertEquals(trustStoreOptions, options.getTrustOptions()); assertFalse(options.isTrustAll()); assertEquals(options, options.setTrustAll(true)); assertTrue(options.isTrustAll()); String randomAlphaString = TestUtils.randomAlphaString(10); assertTrue(options.getHostnameVerificationAlgorithm().isEmpty()); assertEquals(options, options.setHostnameVerificationAlgorithm(randomAlphaString)); assertEquals(randomAlphaString, options.getHostnameVerificationAlgorithm()); assertEquals(0, options.getReconnectAttempts()); assertIllegalArgumentException(() -> options.setReconnectAttempts(-2)); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setReconnectAttempts(rand)); assertEquals(rand, options.getReconnectAttempts()); assertEquals(1000, options.getReconnectInterval()); assertIllegalArgumentException(() -> options.setReconnectInterval(0)); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setReconnectInterval(rand)); assertEquals(rand, options.getReconnectInterval()); assertTrue(options.getEnabledCipherSuites().isEmpty()); assertEquals(options, options.addEnabledCipherSuite("foo")); assertEquals(options, options.addEnabledCipherSuite("bar")); assertNotNull(options.getEnabledCipherSuites()); assertTrue(options.getEnabledCipherSuites().contains("foo")); assertTrue(options.getEnabledCipherSuites().contains("bar")); assertEquals(false, options.isUseAlpn()); assertEquals(options, options.setUseAlpn(true)); assertEquals(true, options.isUseAlpn()); assertNull(options.getSslEngineOptions()); assertEquals(options, options.setSslEngineOptions(new JdkSSLEngineOptions())); assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); assertEquals(TCPSSLOptions.DEFAULT_SSL_HANDSHAKE_TIMEOUT, options.getSslHandshakeTimeout()); long randLong = TestUtils.randomPositiveLong(); assertEquals(options, options.setSslHandshakeTimeout(randLong)); assertEquals(randLong, options.getSslHandshakeTimeout()); assertIllegalArgumentException(() -> options.setSslHandshakeTimeout(-123)); testComplete(); } @Test public void testServerOptions() { NetServerOptions options = new NetServerOptions(); assertEquals(NetworkOptions.DEFAULT_SEND_BUFFER_SIZE, options.getSendBufferSize()); int rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setSendBufferSize(rand)); assertEquals(rand, options.getSendBufferSize()); assertIllegalArgumentException(() -> options.setSendBufferSize(0)); assertIllegalArgumentException(() -> options.setSendBufferSize(-123)); assertEquals(NetworkOptions.DEFAULT_RECEIVE_BUFFER_SIZE, options.getReceiveBufferSize()); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setReceiveBufferSize(rand)); assertEquals(rand, options.getReceiveBufferSize()); assertIllegalArgumentException(() -> options.setReceiveBufferSize(0)); assertIllegalArgumentException(() -> options.setReceiveBufferSize(-123)); assertTrue(options.isReuseAddress()); assertEquals(options, options.setReuseAddress(false)); assertFalse(options.isReuseAddress()); assertEquals(NetworkOptions.DEFAULT_TRAFFIC_CLASS, options.getTrafficClass()); rand = 23; assertEquals(options, options.setTrafficClass(rand)); assertEquals(rand, options.getTrafficClass()); assertIllegalArgumentException(() -> options.setTrafficClass(-2)); assertIllegalArgumentException(() -> options.setTrafficClass(256)); assertTrue(options.isTcpNoDelay()); assertEquals(options, options.setTcpNoDelay(false)); assertFalse(options.isTcpNoDelay()); boolean tcpKeepAlive = false; assertEquals(tcpKeepAlive, options.isTcpKeepAlive()); assertEquals(options, options.setTcpKeepAlive(!tcpKeepAlive)); assertEquals(!tcpKeepAlive, options.isTcpKeepAlive()); int soLinger = -1; assertEquals(soLinger, options.getSoLinger()); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setSoLinger(rand)); assertEquals(rand, options.getSoLinger()); assertIllegalArgumentException(() -> options.setSoLinger(-2)); rand = TestUtils.randomPositiveInt(); assertEquals(0, options.getIdleTimeout()); assertEquals(options, options.setIdleTimeout(rand)); assertEquals(rand, options.getIdleTimeout()); assertIllegalArgumentException(() -> options.setIdleTimeout(-1)); assertFalse(options.isSsl()); assertEquals(options, options.setSsl(true)); assertTrue(options.isSsl()); assertNull(options.getKeyCertOptions()); JksOptions keyStoreOptions = new JksOptions().setPath(TestUtils.randomAlphaString(100)) .setPassword(TestUtils.randomAlphaString(100)); assertEquals(options, options.setKeyStoreOptions(keyStoreOptions)); assertEquals(keyStoreOptions, options.getKeyCertOptions()); assertNull(options.getTrustOptions()); JksOptions trustStoreOptions = new JksOptions().setPath(TestUtils.randomAlphaString(100)) .setPassword(TestUtils.randomAlphaString(100)); assertEquals(options, options.setTrustStoreOptions(trustStoreOptions)); assertEquals(trustStoreOptions, options.getTrustOptions()); assertEquals(-1, options.getAcceptBacklog()); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setAcceptBacklog(rand)); assertEquals(rand, options.getAcceptBacklog()); assertEquals(0, options.getPort()); assertEquals(options, options.setPort(1234)); assertEquals(1234, options.getPort()); assertIllegalArgumentException(() -> options.setPort(-1)); assertIllegalArgumentException(() -> options.setPort(65536)); assertEquals("0.0.0.0", options.getHost()); String randString = TestUtils.randomUnicodeString(100); assertEquals(options, options.setHost(randString)); assertEquals(randString, options.getHost()); assertTrue(options.getEnabledCipherSuites().isEmpty()); assertEquals(options, options.addEnabledCipherSuite("foo")); assertEquals(options, options.addEnabledCipherSuite("bar")); assertNotNull(options.getEnabledCipherSuites()); assertTrue(options.getEnabledCipherSuites().contains("foo")); assertTrue(options.getEnabledCipherSuites().contains("bar")); assertEquals(false, options.isUseAlpn()); assertEquals(options, options.setUseAlpn(true)); assertEquals(true, options.isUseAlpn()); assertNull(options.getSslEngineOptions()); assertEquals(options, options.setSslEngineOptions(new JdkSSLEngineOptions())); assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); assertFalse(options.isSni()); assertEquals(options, options.setSni(true)); assertTrue(options.isSni()); assertEquals(TCPSSLOptions.DEFAULT_SSL_HANDSHAKE_TIMEOUT, options.getSslHandshakeTimeout()); long randLong = TestUtils.randomPositiveLong(); assertEquals(options, options.setSslHandshakeTimeout(randLong)); assertEquals(randLong, options.getSslHandshakeTimeout()); assertIllegalArgumentException(() -> options.setSslHandshakeTimeout(-123)); testComplete(); } @Test public void testCopyClientOptions() { NetClientOptions options = new NetClientOptions(); int sendBufferSize = TestUtils.randomPositiveInt(); int receiverBufferSize = TestUtils.randomPortInt(); Random rand = new Random(); boolean reuseAddress = rand.nextBoolean(); int trafficClass = TestUtils.randomByte() + 128; boolean tcpNoDelay = rand.nextBoolean(); boolean tcpKeepAlive = rand.nextBoolean(); int soLinger = TestUtils.randomPositiveInt(); int idleTimeout = TestUtils.randomPositiveInt(); boolean ssl = rand.nextBoolean(); String hostnameVerificationAlgorithm = TestUtils.randomAlphaString(10); JksOptions keyStoreOptions = new JksOptions(); String ksPassword = TestUtils.randomAlphaString(100); keyStoreOptions.setPassword(ksPassword); JksOptions trustStoreOptions = new JksOptions(); String tsPassword = TestUtils.randomAlphaString(100); trustStoreOptions.setPassword(tsPassword); String enabledCipher = TestUtils.randomAlphaString(100); int connectTimeout = TestUtils.randomPositiveInt(); boolean trustAll = rand.nextBoolean(); String crlPath = TestUtils.randomUnicodeString(100); Buffer crlValue = TestUtils.randomBuffer(100); int reconnectAttempts = TestUtils.randomPositiveInt(); long reconnectInterval = TestUtils.randomPositiveInt(); boolean useAlpn = TestUtils.randomBoolean(); boolean openSslSessionCacheEnabled = rand.nextBoolean(); long sslHandshakeTimeout = TestUtils.randomPositiveLong(); SSLEngineOptions sslEngine = TestUtils.randomBoolean() ? new JdkSSLEngineOptions() : new OpenSSLEngineOptions(); options.setSendBufferSize(sendBufferSize); options.setReceiveBufferSize(receiverBufferSize); options.setReuseAddress(reuseAddress); options.setTrafficClass(trafficClass); options.setSsl(ssl); options.setTcpNoDelay(tcpNoDelay); options.setTcpKeepAlive(tcpKeepAlive); options.setSoLinger(soLinger); options.setIdleTimeout(idleTimeout); options.setKeyStoreOptions(keyStoreOptions); options.setTrustStoreOptions(trustStoreOptions); options.addEnabledCipherSuite(enabledCipher); options.setConnectTimeout(connectTimeout); options.setTrustAll(trustAll); options.addCrlPath(crlPath); options.addCrlValue(crlValue); options.setReconnectAttempts(reconnectAttempts); options.setReconnectInterval(reconnectInterval); options.setUseAlpn(useAlpn); options.setSslEngineOptions(sslEngine); options.setHostnameVerificationAlgorithm(hostnameVerificationAlgorithm); options.setSslHandshakeTimeout(sslHandshakeTimeout); NetClientOptions copy = new NetClientOptions(options); assertEquals(options.toJson(), copy.toJson()); } @Test public void testDefaultClientOptionsJson() { NetClientOptions def = new NetClientOptions(); NetClientOptions json = new NetClientOptions(new JsonObject()); assertEquals(def.getReconnectAttempts(), json.getReconnectAttempts()); assertEquals(def.getReconnectInterval(), json.getReconnectInterval()); assertEquals(def.isTrustAll(), json.isTrustAll()); assertEquals(def.getCrlPaths(), json.getCrlPaths()); assertEquals(def.getCrlValues(), json.getCrlValues()); assertEquals(def.getConnectTimeout(), json.getConnectTimeout()); assertEquals(def.isTcpNoDelay(), json.isTcpNoDelay()); assertEquals(def.isTcpKeepAlive(), json.isTcpKeepAlive()); assertEquals(def.getSoLinger(), json.getSoLinger()); assertEquals(def.isSsl(), json.isSsl()); assertEquals(def.isUseAlpn(), json.isUseAlpn()); assertEquals(def.getSslEngineOptions(), json.getSslEngineOptions()); assertEquals(def.getHostnameVerificationAlgorithm(), json.getHostnameVerificationAlgorithm()); assertEquals(def.getSslHandshakeTimeout(), json.getSslHandshakeTimeout()); } @Test public void testClientOptionsJson() { int sendBufferSize = TestUtils.randomPositiveInt(); int receiverBufferSize = TestUtils.randomPortInt(); Random rand = new Random(); boolean reuseAddress = rand.nextBoolean(); int trafficClass = TestUtils.randomByte() + 128; boolean tcpNoDelay = rand.nextBoolean(); boolean tcpKeepAlive = rand.nextBoolean(); int soLinger = TestUtils.randomPositiveInt(); int idleTimeout = TestUtils.randomPositiveInt(); boolean ssl = rand.nextBoolean(); JksOptions keyStoreOptions = new JksOptions(); String ksPassword = TestUtils.randomAlphaString(100); keyStoreOptions.setPassword(ksPassword); String ksPath = TestUtils.randomAlphaString(100); keyStoreOptions.setPath(ksPath); JksOptions trustStoreOptions = new JksOptions(); String tsPassword = TestUtils.randomAlphaString(100); trustStoreOptions.setPassword(tsPassword); String tsPath = TestUtils.randomAlphaString(100); trustStoreOptions.setPath(tsPath); String enabledCipher = TestUtils.randomAlphaString(100); int connectTimeout = TestUtils.randomPositiveInt(); boolean trustAll = rand.nextBoolean(); String crlPath = TestUtils.randomUnicodeString(100); int reconnectAttempts = TestUtils.randomPositiveInt(); long reconnectInterval = TestUtils.randomPositiveInt(); boolean useAlpn = TestUtils.randomBoolean(); String hostnameVerificationAlgorithm = TestUtils.randomAlphaString(10); String sslEngine = TestUtils.randomBoolean() ? "jdkSslEngineOptions" : "openSslEngineOptions"; boolean openSslSessionCacheEnabled = rand.nextBoolean(); long sslHandshakeTimeout = TestUtils.randomPositiveLong(); JsonObject json = new JsonObject(); json.put("sendBufferSize", sendBufferSize).put("receiveBufferSize", receiverBufferSize) .put("reuseAddress", reuseAddress).put("trafficClass", trafficClass).put("tcpNoDelay", tcpNoDelay) .put("tcpKeepAlive", tcpKeepAlive).put("soLinger", soLinger).put("idleTimeout", idleTimeout) .put("ssl", ssl).put("enabledCipherSuites", new JsonArray().add(enabledCipher)) .put("connectTimeout", connectTimeout).put("trustAll", trustAll) .put("crlPaths", new JsonArray().add(crlPath)) .put("keyStoreOptions", new JsonObject().put("password", ksPassword).put("path", ksPath)) .put("trustStoreOptions", new JsonObject().put("password", tsPassword).put("path", tsPath)) .put("reconnectAttempts", reconnectAttempts).put("reconnectInterval", reconnectInterval) .put("useAlpn", useAlpn).put(sslEngine, new JsonObject()) .put("hostnameVerificationAlgorithm", hostnameVerificationAlgorithm) .put("openSslSessionCacheEnabled", openSslSessionCacheEnabled) .put("sslHandshakeTimeout", sslHandshakeTimeout); NetClientOptions options = new NetClientOptions(json); assertEquals(sendBufferSize, options.getSendBufferSize()); assertEquals(receiverBufferSize, options.getReceiveBufferSize()); assertEquals(reuseAddress, options.isReuseAddress()); assertEquals(trafficClass, options.getTrafficClass()); assertEquals(tcpKeepAlive, options.isTcpKeepAlive()); assertEquals(tcpNoDelay, options.isTcpNoDelay()); assertEquals(soLinger, options.getSoLinger()); assertEquals(idleTimeout, options.getIdleTimeout()); assertEquals(ssl, options.isSsl()); assertEquals(sslHandshakeTimeout, options.getSslHandshakeTimeout()); assertNotSame(keyStoreOptions, options.getKeyCertOptions()); assertEquals(ksPassword, ((JksOptions) options.getKeyCertOptions()).getPassword()); assertEquals(ksPath, ((JksOptions) options.getKeyCertOptions()).getPath()); assertNotSame(trustStoreOptions, options.getTrustOptions()); assertEquals(tsPassword, ((JksOptions) options.getTrustOptions()).getPassword()); assertEquals(tsPath, ((JksOptions) options.getTrustOptions()).getPath()); assertEquals(1, options.getEnabledCipherSuites().size()); assertTrue(options.getEnabledCipherSuites().contains(enabledCipher)); assertEquals(connectTimeout, options.getConnectTimeout()); assertEquals(trustAll, options.isTrustAll()); assertEquals(1, options.getCrlPaths().size()); assertEquals(crlPath, options.getCrlPaths().get(0)); assertEquals(reconnectAttempts, options.getReconnectAttempts()); assertEquals(reconnectInterval, options.getReconnectInterval()); assertEquals(useAlpn, options.isUseAlpn()); switch (sslEngine) { case "jdkSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); break; case "openSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof OpenSSLEngineOptions); break; default: fail(); break; } assertEquals(hostnameVerificationAlgorithm, options.getHostnameVerificationAlgorithm()); // Test other keystore/truststore types json.remove("keyStoreOptions"); json.remove("trustStoreOptions"); json.put("pfxKeyCertOptions", new JsonObject().put("password", ksPassword)).put("pfxTrustOptions", new JsonObject().put("password", tsPassword)); options = new NetClientOptions(json); assertTrue(options.getTrustOptions() instanceof PfxOptions); assertTrue(options.getKeyCertOptions() instanceof PfxOptions); json.remove("pfxKeyCertOptions"); json.remove("pfxTrustOptions"); json.put("pemKeyCertOptions", new JsonObject()).put("pemTrustOptions", new JsonObject()); options = new NetClientOptions(json); assertTrue(options.getTrustOptions() instanceof PemTrustOptions); assertTrue(options.getKeyCertOptions() instanceof PemKeyCertOptions); } @Test public void testCopyServerOptions() { NetServerOptions options = new NetServerOptions(); int sendBufferSize = TestUtils.randomPositiveInt(); int receiverBufferSize = TestUtils.randomPortInt(); Random rand = new Random(); boolean reuseAddress = rand.nextBoolean(); int trafficClass = TestUtils.randomByte() + 128; boolean tcpNoDelay = rand.nextBoolean(); boolean tcpKeepAlive = rand.nextBoolean(); int soLinger = TestUtils.randomPositiveInt(); boolean usePooledBuffers = rand.nextBoolean(); int idleTimeout = TestUtils.randomPositiveInt(); boolean ssl = rand.nextBoolean(); JksOptions keyStoreOptions = new JksOptions(); String ksPassword = TestUtils.randomAlphaString(100); keyStoreOptions.setPassword(ksPassword); JksOptions trustStoreOptions = new JksOptions(); String tsPassword = TestUtils.randomAlphaString(100); trustStoreOptions.setPassword(tsPassword); String enabledCipher = TestUtils.randomAlphaString(100); String crlPath = TestUtils.randomUnicodeString(100); Buffer crlValue = TestUtils.randomBuffer(100); int port = 1234; String host = TestUtils.randomAlphaString(100); int acceptBacklog = TestUtils.randomPortInt(); boolean useAlpn = TestUtils.randomBoolean(); boolean openSslSessionCacheEnabled = rand.nextBoolean(); SSLEngineOptions sslEngine = TestUtils.randomBoolean() ? new JdkSSLEngineOptions() : new OpenSSLEngineOptions(); boolean sni = TestUtils.randomBoolean(); long sslHandshakeTimeout = TestUtils.randomPositiveLong(); options.setSendBufferSize(sendBufferSize); options.setReceiveBufferSize(receiverBufferSize); options.setReuseAddress(reuseAddress); options.setTrafficClass(trafficClass); options.setTcpNoDelay(tcpNoDelay); options.setTcpKeepAlive(tcpKeepAlive); options.setSoLinger(soLinger); options.setIdleTimeout(idleTimeout); options.setSsl(ssl); options.setKeyStoreOptions(keyStoreOptions); options.setTrustStoreOptions(trustStoreOptions); options.addEnabledCipherSuite(enabledCipher); options.addCrlPath(crlPath); options.addCrlValue(crlValue); options.setPort(port); options.setHost(host); options.setAcceptBacklog(acceptBacklog); options.setUseAlpn(useAlpn); options.setSslEngineOptions(sslEngine); options.setSni(sni); options.setSslHandshakeTimeout(sslHandshakeTimeout); NetServerOptions copy = new NetServerOptions(options); assertEquals(options.toJson(), copy.toJson()); } @Test @SuppressWarnings("deprecation") public void testDefaultServerOptionsJson() { NetServerOptions def = new NetServerOptions(); NetServerOptions json = new NetServerOptions(new JsonObject()); assertEquals(def.getCrlPaths(), json.getCrlPaths()); assertEquals(def.getCrlValues(), json.getCrlValues()); assertEquals(def.getAcceptBacklog(), json.getAcceptBacklog()); assertEquals(def.getPort(), json.getPort()); assertEquals(def.getHost(), json.getHost()); assertEquals(def.getCrlPaths(), json.getCrlPaths()); assertEquals(def.getCrlValues(), json.getCrlValues()); assertEquals(def.getAcceptBacklog(), json.getAcceptBacklog()); assertEquals(def.getPort(), json.getPort()); assertEquals(def.getHost(), json.getHost()); assertEquals(def.isTcpNoDelay(), json.isTcpNoDelay()); assertEquals(def.isTcpKeepAlive(), json.isTcpKeepAlive()); assertEquals(def.getSoLinger(), json.getSoLinger()); assertEquals(def.isSsl(), json.isSsl()); assertEquals(def.isUseAlpn(), json.isUseAlpn()); assertEquals(def.getSslEngineOptions(), json.getSslEngineOptions()); assertEquals(def.isSni(), json.isSni()); assertEquals(def.getSslHandshakeTimeout(), json.getSslHandshakeTimeout()); } @Test public void testServerOptionsJson() { int sendBufferSize = TestUtils.randomPositiveInt(); int receiverBufferSize = TestUtils.randomPortInt(); Random rand = new Random(); boolean reuseAddress = rand.nextBoolean(); int trafficClass = TestUtils.randomByte() + 128; boolean tcpNoDelay = rand.nextBoolean(); boolean tcpKeepAlive = rand.nextBoolean(); int soLinger = TestUtils.randomPositiveInt(); boolean usePooledBuffers = rand.nextBoolean(); int idleTimeout = TestUtils.randomPositiveInt(); boolean ssl = rand.nextBoolean(); JksOptions keyStoreOptions = new JksOptions(); String ksPassword = TestUtils.randomAlphaString(100); keyStoreOptions.setPassword(ksPassword); String ksPath = TestUtils.randomAlphaString(100); keyStoreOptions.setPath(ksPath); JksOptions trustStoreOptions = new JksOptions(); String tsPassword = TestUtils.randomAlphaString(100); trustStoreOptions.setPassword(tsPassword); String tsPath = TestUtils.randomAlphaString(100); trustStoreOptions.setPath(tsPath); String enabledCipher = TestUtils.randomAlphaString(100); String crlPath = TestUtils.randomUnicodeString(100); int port = 1234; String host = TestUtils.randomAlphaString(100); int acceptBacklog = TestUtils.randomPortInt(); boolean useAlpn = TestUtils.randomBoolean(); boolean openSslSessionCacheEnabled = rand.nextBoolean(); String sslEngine = TestUtils.randomBoolean() ? "jdkSslEngineOptions" : "openSslEngineOptions"; boolean sni = TestUtils.randomBoolean(); long sslHandshakeTimeout = TestUtils.randomPositiveLong(); JsonObject json = new JsonObject(); json.put("sendBufferSize", sendBufferSize).put("receiveBufferSize", receiverBufferSize) .put("reuseAddress", reuseAddress).put("trafficClass", trafficClass).put("tcpNoDelay", tcpNoDelay) .put("tcpKeepAlive", tcpKeepAlive).put("soLinger", soLinger) .put("usePooledBuffers", usePooledBuffers).put("idleTimeout", idleTimeout).put("ssl", ssl) .put("enabledCipherSuites", new JsonArray().add(enabledCipher)) .put("crlPaths", new JsonArray().add(crlPath)) .put("keyStoreOptions", new JsonObject().put("password", ksPassword).put("path", ksPath)) .put("trustStoreOptions", new JsonObject().put("password", tsPassword).put("path", tsPath)) .put("port", port).put("host", host).put("acceptBacklog", acceptBacklog).put("useAlpn", useAlpn) .put(sslEngine, new JsonObject()).put("openSslSessionCacheEnabled", openSslSessionCacheEnabled) .put("sni", sni).put("sslHandshakeTimeout", sslHandshakeTimeout); NetServerOptions options = new NetServerOptions(json); assertEquals(sendBufferSize, options.getSendBufferSize()); assertEquals(receiverBufferSize, options.getReceiveBufferSize()); assertEquals(reuseAddress, options.isReuseAddress()); assertEquals(trafficClass, options.getTrafficClass()); assertEquals(tcpKeepAlive, options.isTcpKeepAlive()); assertEquals(tcpNoDelay, options.isTcpNoDelay()); assertEquals(soLinger, options.getSoLinger()); assertEquals(idleTimeout, options.getIdleTimeout()); assertEquals(ssl, options.isSsl()); assertEquals(sslHandshakeTimeout, options.getSslHandshakeTimeout()); assertNotSame(keyStoreOptions, options.getKeyCertOptions()); assertEquals(ksPassword, ((JksOptions) options.getKeyCertOptions()).getPassword()); assertEquals(ksPath, ((JksOptions) options.getKeyCertOptions()).getPath()); assertNotSame(trustStoreOptions, options.getTrustOptions()); assertEquals(tsPassword, ((JksOptions) options.getTrustOptions()).getPassword()); assertEquals(tsPath, ((JksOptions) options.getTrustOptions()).getPath()); assertEquals(1, options.getEnabledCipherSuites().size()); assertTrue(options.getEnabledCipherSuites().contains(enabledCipher)); assertEquals(1, options.getCrlPaths().size()); assertEquals(crlPath, options.getCrlPaths().get(0)); assertEquals(port, options.getPort()); assertEquals(host, options.getHost()); assertEquals(acceptBacklog, options.getAcceptBacklog()); assertEquals(useAlpn, options.isUseAlpn()); switch (sslEngine) { case "jdkSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); break; case "openSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof OpenSSLEngineOptions); break; default: fail(); break; } assertEquals(sni, options.isSni()); // Test other keystore/truststore types json.remove("keyStoreOptions"); json.remove("trustStoreOptions"); json.put("pfxKeyCertOptions", new JsonObject().put("password", ksPassword)).put("pfxTrustOptions", new JsonObject().put("password", tsPassword)); options = new NetServerOptions(json); assertTrue(options.getTrustOptions() instanceof PfxOptions); assertTrue(options.getKeyCertOptions() instanceof PfxOptions); json.remove("pfxKeyCertOptions"); json.remove("pfxTrustOptions"); json.put("pemKeyCertOptions", new JsonObject()).put("pemTrustOptions", new JsonObject()); options = new NetServerOptions(json); assertTrue(options.getTrustOptions() instanceof PemTrustOptions); assertTrue(options.getKeyCertOptions() instanceof PemKeyCertOptions); } @Test public void testSocketAddress() throws Exception { assertNullPointerException(() -> new SocketAddressImpl(0, null)); assertIllegalArgumentException(() -> new SocketAddressImpl(0, "")); assertIllegalArgumentException(() -> new SocketAddressImpl(-1, "someHost")); assertIllegalArgumentException(() -> new SocketAddressImpl(65536, "someHost")); } @Test public void testWriteHandlerSuccess() throws Exception { CompletableFuture<Void> close = new CompletableFuture<>(); server.connectHandler(socket -> { socket.pause(); close.thenAccept(v -> { socket.resume(); }); }); startServer(); client.connect(testAddress, onSuccess(so -> { writeUntilFull(so, v -> { so.write(Buffer.buffer("lost buffer"), onSuccess(ack -> testComplete())); close.complete(null); }); })); await(); } @Test public void testWriteHandlerFailure() throws Exception { CompletableFuture<Void> close = new CompletableFuture<>(); server.connectHandler(socket -> { socket.pause(); close.thenAccept(v -> { socket.close(); }); }); startServer(); client.connect(testAddress, onSuccess(so -> { writeUntilFull(so, v -> { so.write(Buffer.buffer("lost buffer"), onFailure(err -> { testComplete(); })); close.complete(null); }); })); await(); } private void writeUntilFull(NetSocket so, Handler<Void> handler) { if (so.writeQueueFull()) { handler.handle(null); } else { // Give enough time to report a proper full so.write(TestUtils.randomBuffer(16384)); vertx.setTimer(10, id -> writeUntilFull(so, handler)); } } @Test public void testEchoBytes() { Buffer sent = TestUtils.randomBuffer(100); testEcho(sock -> sock.write(sent), buff -> assertEquals(sent, buff), sent.length()); } @Test public void testEchoString() { String sent = TestUtils.randomUnicodeString(100); Buffer buffSent = Buffer.buffer(sent); testEcho(sock -> sock.write(sent), buff -> assertEquals(buffSent, buff), buffSent.length()); } @Test public void testEchoStringUTF8() { testEchoStringWithEncoding("UTF-8"); } @Test public void testEchoStringUTF16() { testEchoStringWithEncoding("UTF-16"); } void testEchoStringWithEncoding(String encoding) { String sent = TestUtils.randomUnicodeString(100); Buffer buffSent = Buffer.buffer(sent, encoding); testEcho(sock -> sock.write(sent, encoding), buff -> assertEquals(buffSent, buff), buffSent.length()); } void testEcho(Consumer<NetSocket> writer, Consumer<Buffer> dataChecker, int length) { Handler<AsyncResult<NetSocket>> clientHandler = (asyncResult) -> { if (asyncResult.succeeded()) { NetSocket sock = asyncResult.result(); Buffer buff = Buffer.buffer(); sock.handler((buffer) -> { buff.appendBuffer(buffer); if (buff.length() == length) { dataChecker.accept(buff); testComplete(); } if (buff.length() > length) { fail("Too many bytes received"); } }); writer.accept(sock); } else { fail("failed to connect"); } }; startEchoServer(testAddress, s -> client.connect(testAddress, clientHandler)); await(); } void startEchoServer(SocketAddress address, Handler<AsyncResult<NetServer>> listenHandler) { Handler<NetSocket> serverHandler = socket -> socket.handler(socket::write); server.connectHandler(serverHandler).listen(address, listenHandler); } @Test public void testConnectLocalHost() { connect(testAddress); } void connect(SocketAddress address) { startEchoServer(testAddress, s -> { final int numConnections = 100; final AtomicInteger connCount = new AtomicInteger(0); for (int i = 0; i < numConnections; i++) { Handler<AsyncResult<NetSocket>> handler = res -> { if (res.succeeded()) { res.result().close(); if (connCount.incrementAndGet() == numConnections) { testComplete(); } } }; client.connect(address, handler); } }); await(); } @Test public void testConnectInvalidPort() { assertIllegalArgumentException(() -> client.connect(-1, "localhost", res -> { })); assertIllegalArgumentException(() -> client.connect(65536, "localhost", res -> { })); client.connect(9998, "localhost", res -> { assertTrue(res.failed()); assertFalse(res.succeeded()); assertNotNull(res.cause()); testComplete(); }); await(); } @Test public void testConnectInvalidHost() { assertNullPointerException(() -> client.connect(80, null, res -> { })); client.connect(1234, "127.0.0.2", res -> { assertTrue(res.failed()); assertFalse(res.succeeded()); assertNotNull(res.cause()); testComplete(); }); await(); } @Test public void testConnectInvalidConnectHandler() throws Exception { assertNullPointerException(() -> client.connect(80, "localhost", null)); } @Test public void testListenInvalidPort() { /* Port 80 is free to use by any application on Windows, so this test fails. */ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows")); server.close(); server = vertx.createNetServer(new NetServerOptions().setPort(80)); server.connectHandler((netSocket) -> { }).listen(ar -> { assertTrue(ar.failed()); assertFalse(ar.succeeded()); assertNotNull(ar.cause()); testComplete(); }); await(); } @Test public void testListenInvalidHost() { server.close(); server = vertx.createNetServer( new NetServerOptions().setPort(1234).setHost("uhqwduhqwudhqwuidhqwiudhqwudqwiuhd")); server.connectHandler(netSocket -> { }).listen(ar -> { assertTrue(ar.failed()); assertFalse(ar.succeeded()); assertNotNull(ar.cause()); testComplete(); }); await(); } @Test public void testListenOnWildcardPort() { server.close(); server = vertx.createNetServer(new NetServerOptions().setPort(0)); server.connectHandler((netSocket) -> { }).listen(ar -> { assertFalse(ar.failed()); assertTrue(ar.succeeded()); assertNull(ar.cause()); assertTrue(server.actualPort() > 1024); assertEquals(server, ar.result()); testComplete(); }); await(); } @Test public void testClientCloseHandlersCloseFromClient() { startEchoServer(testAddress, s -> clientCloseHandlers(true)); await(); } @Test public void testClientCloseHandlersCloseFromServer() { server.connectHandler(NetSocket::close).listen(testAddress, (s) -> clientCloseHandlers(false)); await(); } void clientCloseHandlers(boolean closeFromClient) { client.connect(testAddress, onSuccess(so -> { AtomicInteger counter = new AtomicInteger(0); so.endHandler(v -> assertEquals(1, counter.incrementAndGet())); so.closeHandler(v -> { assertEquals(2, counter.incrementAndGet()); testComplete(); }); if (closeFromClient) { so.close(); } })); } @Test public void testServerCloseHandlersCloseFromClient() { serverCloseHandlers(false, s -> client.connect(testAddress, ar -> ar.result().close())); await(); } @Test public void testServerCloseHandlersCloseFromServer() { serverCloseHandlers(true, s -> client.connect(testAddress, ar -> { })); await(); } void serverCloseHandlers(boolean closeFromServer, Handler<AsyncResult<NetServer>> listenHandler) { server.connectHandler((sock) -> { AtomicInteger counter = new AtomicInteger(0); sock.endHandler(v -> assertEquals(1, counter.incrementAndGet())); sock.closeHandler(v -> { assertEquals(2, counter.incrementAndGet()); testComplete(); }); if (closeFromServer) { sock.close(); } }).listen(testAddress, listenHandler); } @Test public void testClientDrainHandler() { pausingServer((s) -> { client.connect(testAddress, onSuccess(sock -> { assertFalse(sock.writeQueueFull()); sock.setWriteQueueMaxSize(1000); Buffer buff = TestUtils.randomBuffer(10000); vertx.setPeriodic(1, id -> { sock.write(buff.copy()); if (sock.writeQueueFull()) { vertx.cancelTimer(id); sock.drainHandler(v -> { assertFalse(sock.writeQueueFull()); testComplete(); }); // Tell the server to resume vertx.eventBus().send("server_resume", ""); } }); })); }); await(); } void pausingServer(Handler<AsyncResult<NetServer>> listenHandler) { server.connectHandler(sock -> { sock.pause(); Handler<Message<Buffer>> resumeHandler = (m) -> sock.resume(); MessageConsumer reg = vertx.eventBus().<Buffer>consumer("server_resume").handler(resumeHandler); sock.closeHandler(v -> reg.unregister()); }).listen(testAddress, listenHandler); } @Test public void testServerDrainHandler() { drainingServer(s -> { client.connect(testAddress, onSuccess(sock -> { sock.pause(); setHandlers(sock); sock.handler(buf -> { }); })); }); await(); } void setHandlers(NetSocket sock) { Handler<Message<Buffer>> resumeHandler = m -> sock.resume(); MessageConsumer reg = vertx.eventBus().<Buffer>consumer("client_resume").handler(resumeHandler); sock.closeHandler(v -> reg.unregister()); } void drainingServer(Handler<AsyncResult<NetServer>> listenHandler) { server.connectHandler(sock -> { assertFalse(sock.writeQueueFull()); sock.setWriteQueueMaxSize(1000); Buffer buff = TestUtils.randomBuffer(10000); //Send data until the buffer is full vertx.setPeriodic(1, id -> { sock.write(buff.copy()); if (sock.writeQueueFull()) { vertx.cancelTimer(id); sock.drainHandler(v -> { assertFalse(sock.writeQueueFull()); // End test after a short delay to give the client some time to read the data vertx.setTimer(100, id2 -> testComplete()); }); // Tell the client to resume vertx.eventBus().send("client_resume", ""); } }); }).listen(testAddress, listenHandler); } @Test public void testReconnectAttemptsInfinite() { reconnectAttempts(-1); } @Test public void testReconnectAttemptsMany() { reconnectAttempts(100000); } private void reconnectAttempts(int attempts) { client.close(); client = vertx .createNetClient(new NetClientOptions().setReconnectAttempts(attempts).setReconnectInterval(10)); //The server delays starting for a a few seconds, but it should still connect client.connect(testAddress, onSuccess(so -> testComplete())); // Start the server after a delay vertx.setTimer(2000, id -> startEchoServer(testAddress, s -> { })); await(); } @Test public void testReconnectAttemptsNotEnough() { client.close(); client = vertx.createNetClient(new NetClientOptions().setReconnectAttempts(100).setReconnectInterval(10)); client.connect(testAddress, (res) -> { assertFalse(res.succeeded()); assertTrue(res.failed()); testComplete(); }); await(); } @Test public void testServerIdleTimeout() { server.close(); NetServerOptions netServerOptions = new NetServerOptions(); netServerOptions.setIdleTimeout(1000); netServerOptions.setIdleTimeoutUnit(TimeUnit.MILLISECONDS); server = vertx.createNetServer(netServerOptions); server.connectHandler(s -> { }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, res -> { assertTrue(res.succeeded()); NetSocket socket = res.result(); socket.closeHandler(v -> testComplete()); }); }); await(); } @Test public void testClientIdleTimeout() { client.close(); NetClientOptions netClientOptions = new NetClientOptions(); netClientOptions.setIdleTimeout(1000); netClientOptions.setIdleTimeoutUnit(TimeUnit.MILLISECONDS); client = vertx.createNetClient(netClientOptions); server.connectHandler(s -> { }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, res -> { assertTrue(res.succeeded()); NetSocket socket = res.result(); socket.closeHandler(v -> testComplete()); }); }); await(); } @Test // StartTLS public void testStartTLSClientTrustAll() throws Exception { testTLS(Cert.NONE, Trust.NONE, Cert.SERVER_JKS, Trust.NONE, false, true, true, true); } @Test // Client trusts all server certs public void testTLSClientTrustAll() throws Exception { testTLS(Cert.NONE, Trust.NONE, Cert.SERVER_JKS, Trust.NONE, false, true, true, false); } @Test // Server specifies cert that the client trusts (not trust all) public void testTLSClientTrustServerCert() throws Exception { testTLS(Cert.NONE, Trust.SERVER_JKS, Cert.SERVER_JKS, Trust.NONE, false, false, true, false); } @Test // Server specifies cert that the client doesn't trust public void testTLSClientUntrustedServer() throws Exception { testTLS(Cert.NONE, Trust.NONE, Cert.SERVER_JKS, Trust.NONE, false, false, false, false); } @Test //Client specifies cert even though it's not required public void testTLSClientCertNotRequired() throws Exception { testTLS(Cert.CLIENT_JKS, Trust.SERVER_JKS, Cert.SERVER_JKS, Trust.CLIENT_JKS, false, false, true, false); } @Test //Client specifies cert and it's not required public void testTLSClientCertRequired() throws Exception { testTLS(Cert.CLIENT_JKS, Trust.SERVER_JKS, Cert.SERVER_JKS, Trust.CLIENT_JKS, true, false, true, false); } @Test //Client doesn't specify cert but it's required public void testTLSClientCertRequiredNoClientCert() throws Exception { testTLS(Cert.NONE, Trust.SERVER_JKS, Cert.SERVER_JKS, Trust.CLIENT_JKS, true, false, false, false); } @Test //Client specifies cert but it's not trusted public void testTLSClientCertClientNotTrusted() throws Exception { testTLS(Cert.NONE, Trust.SERVER_JKS, Cert.SERVER_JKS, Trust.NONE, true, false, false, false); } @Test // StartTLS client specifies cert but it's not trusted public void testStartTLSClientCertClientNotTrusted() throws Exception { testTLS(Cert.NONE, Trust.SERVER_JKS, Cert.SERVER_JKS, Trust.CLIENT_JKS, true, false, false, true); } @Test // Specify some cipher suites public void testTLSCipherSuites() throws Exception { testTLS(Cert.NONE, Trust.NONE, Cert.SERVER_JKS, Trust.NONE, false, true, true, false, ENABLED_CIPHER_SUITES); } @Test // Specify some bogus protocol public void testInvalidTlsProtocolVersion() throws Exception { testTLS(Cert.NONE, Trust.NONE, Cert.SERVER_JKS, Trust.NONE, false, true, false, false, new String[0], new String[] { "TLSv1.999" }); } @Test // Specify a valid protocol public void testSpecificTlsProtocolVersion() throws Exception { testTLS(Cert.NONE, Trust.NONE, Cert.SERVER_JKS, Trust.NONE, false, true, true, false, new String[0], new String[] { "TLSv1.2" }); } @Test // SNI without server name should use the first keystore entry public void testSniWithoutServerNameUsesTheFirstKeyStoreEntry1() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SERVER_JKS).serverCert(Cert.SNI_JKS).sni(true); test.run(true); await(); assertEquals("localhost", cnOf(test.clientPeerCert())); } @Test // SNI without server name should use the first keystore entry public void testSniWithoutServerNameUsesTheFirstKeyStoreEntry2() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST1).serverCert(Cert.SNI_JKS).sni(true); test.run(false); await(); } @Test public void testSniImplicitServerName() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2) .address(SocketAddress.inetSocketAddress(4043, "host2.com")).serverCert(Cert.SNI_JKS).sni(true); test.run(true); await(); assertEquals("host2.com", cnOf(test.clientPeerCert())); assertEquals("host2.com", test.indicatedServerName); } @Test public void testSniImplicitServerNameDisabledForShortname1() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST1) .address(SocketAddress.inetSocketAddress(4043, "host1")).serverCert(Cert.SNI_JKS).sni(true); test.run(false); await(); } @Test public void testSniImplicitServerNameDisabledForShortname2() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SERVER_JKS) .address(SocketAddress.inetSocketAddress(4043, "host1")).serverCert(Cert.SNI_JKS).sni(true); test.run(true); await(); assertEquals("localhost", cnOf(test.clientPeerCert())); } @Test public void testSniForceShortname() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST1) .address(SocketAddress.inetSocketAddress(4043, "host1")).serverName("host1") .serverCert(Cert.SNI_JKS).sni(true); test.run(true); await(); assertEquals("host1", cnOf(test.clientPeerCert())); } @Test public void testSniOverrideServerName() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2) .address(SocketAddress.inetSocketAddress(4043, "example.com")).serverName("host2.com") .serverCert(Cert.SNI_JKS).sni(true); test.run(true); await(); assertEquals("host2.com", cnOf(test.clientPeerCert())); } @Test // SNI present an unknown server public void testSniWithUnknownServer1() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SERVER_JKS).serverCert(Cert.SNI_JKS).sni(true) .serverName("unknown"); test.run(true); await(); assertEquals("localhost", cnOf(test.clientPeerCert())); } @Test // SNI present an unknown server public void testSniWithUnknownServer2() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2).serverCert(Cert.SNI_JKS).sni(true) .serverName("unknown"); test.run(false); await(); } @Test // SNI returns the certificate for the indicated server name public void testSniWithServerNameStartTLS() throws Exception { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST1).startTLS(true).serverCert(Cert.SNI_JKS) .sni(true).serverName("host1"); test.run(true); await(); assertEquals("host1", cnOf(test.clientPeerCert())); } @Test public void testSniWithServerNameTrust() { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2).clientCert(Cert.CLIENT_PEM_ROOT_CA) .requireClientAuth(true).serverCert(Cert.SNI_JKS).sni(true).serverName("host2.com") .serverTrust(Trust.SNI_SERVER_ROOT_CA_AND_OTHER_CA_1); test.run(true); await(); } @Test public void testSniWithServerNameTrustFallback() { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2).clientCert(Cert.CLIENT_PEM_ROOT_CA) .requireClientAuth(true).serverCert(Cert.SNI_JKS).sni(true).serverName("host2.com") .serverTrust(Trust.SNI_SERVER_ROOT_CA_FALLBACK); test.run(true); await(); } @Test public void testSniWithServerNameTrustFallbackFail() { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2).clientCert(Cert.CLIENT_PEM_ROOT_CA) .requireClientAuth(true).serverCert(Cert.SNI_JKS).sni(true).serverName("host2.com") .serverTrust(Trust.SNI_SERVER_OTHER_CA_FALLBACK); test.run(false); await(); } @Test public void testSniWithServerNameTrustFail() { TLSTest test = new TLSTest().clientTrust(Trust.SNI_JKS_HOST2).clientCert(Cert.CLIENT_PEM_ROOT_CA) .requireClientAuth(true).serverCert(Cert.SNI_JKS).sni(true).serverName("host2.com") .serverTrust(Trust.SNI_SERVER_ROOT_CA_AND_OTHER_CA_2); test.run(false); await(); } void testTLS(Cert<?> clientCert, Trust<?> clientTrust, Cert<?> serverCert, Trust<?> serverTrust, boolean requireClientAuth, boolean clientTrustAll, boolean shouldPass, boolean startTLS) throws Exception { testTLS(clientCert, clientTrust, serverCert, serverTrust, requireClientAuth, clientTrustAll, shouldPass, startTLS, new String[0], new String[0]); } void testTLS(Cert<?> clientCert, Trust<?> clientTrust, Cert<?> serverCert, Trust<?> serverTrust, boolean requireClientAuth, boolean clientTrustAll, boolean shouldPass, boolean startTLS, String[] enabledCipherSuites) throws Exception { testTLS(clientCert, clientTrust, serverCert, serverTrust, requireClientAuth, clientTrustAll, shouldPass, startTLS, enabledCipherSuites, new String[0]); } void testTLS(Cert<?> clientCert, Trust<?> clientTrust, Cert<?> serverCert, Trust<?> serverTrust, boolean requireClientAuth, boolean clientTrustAll, boolean shouldPass, boolean startTLS, String[] enabledCipherSuites, String[] enabledSecureTransportProtocols) throws Exception { TLSTest test = new TLSTest().clientCert(clientCert).clientTrust(clientTrust).serverCert(serverCert) .serverTrust(serverTrust).requireClientAuth(requireClientAuth).clientTrustAll(clientTrustAll) .startTLS(startTLS).enabledCipherSuites(enabledCipherSuites) .enabledSecureTransportProtocols(enabledSecureTransportProtocols); test.run(shouldPass); await(); } class TLSTest { Cert<?> clientCert = Cert.NONE; Trust<?> clientTrust = Trust.NONE; Cert<?> serverCert = Cert.NONE; Trust<?> serverTrust = Trust.NONE; boolean requireClientAuth; boolean clientTrustAll; boolean startTLS; String[] enabledCipherSuites = new String[0]; String[] enabledSecureTransportProtocols = new String[0]; boolean sni; SocketAddress address = SocketAddress.inetSocketAddress(4043, "localhost"); String serverName; X509Certificate clientPeerCert; String indicatedServerName; public TLSTest clientCert(Cert<?> clientCert) { this.clientCert = clientCert; return this; } public TLSTest clientTrust(Trust<?> clientTrust) { this.clientTrust = clientTrust; return this; } public TLSTest serverCert(Cert<?> serverCert) { this.serverCert = serverCert; return this; } public TLSTest serverTrust(Trust<?> serverTrust) { this.serverTrust = serverTrust; return this; } public TLSTest requireClientAuth(boolean requireClientAuth) { this.requireClientAuth = requireClientAuth; return this; } public TLSTest clientTrustAll(boolean clientTrustAll) { this.clientTrustAll = clientTrustAll; return this; } public TLSTest startTLS(boolean startTLS) { this.startTLS = startTLS; return this; } public TLSTest enabledCipherSuites(String[] enabledCipherSuites) { this.enabledCipherSuites = enabledCipherSuites; return this; } public TLSTest enabledSecureTransportProtocols(String[] enabledSecureTransportProtocols) { this.enabledSecureTransportProtocols = enabledSecureTransportProtocols; return this; } public TLSTest address(SocketAddress address) { this.address = address; return this; } public TLSTest serverName(String serverName) { this.serverName = serverName; return this; } public TLSTest sni(boolean sni) { this.sni = sni; return this; } public X509Certificate clientPeerCert() { return clientPeerCert; } void run(boolean shouldPass) { server.close(); NetServerOptions options = new NetServerOptions(); if (!startTLS) { options.setSsl(true); } options.setTrustOptions(serverTrust.get()); options.setKeyCertOptions(serverCert.get()); if (requireClientAuth) { options.setClientAuth(ClientAuth.REQUIRED); } for (String suite : enabledCipherSuites) { options.addEnabledCipherSuite(suite); } if (enabledSecureTransportProtocols.length > 0) { options.getEnabledSecureTransportProtocols().forEach(options::removeEnabledSecureTransportProtocol); } for (String protocol : enabledSecureTransportProtocols) { options.addEnabledSecureTransportProtocol(protocol); } options.setSni(sni); Consumer<NetSocket> certificateChainChecker = socket -> { try { X509Certificate[] certs = socket.peerCertificateChain(); if (clientCert != Cert.NONE) { assertNotNull(certs); assertEquals(1, certs.length); } else { assertNull(certs); } } catch (SSLPeerUnverifiedException e) { assertTrue(clientTrust.get() != Trust.NONE || clientTrustAll); } }; server = vertx.createNetServer(options); if (!shouldPass) { waitForMore(1); } server.exceptionHandler(err -> complete()); Handler<NetSocket> serverHandler = socket -> { indicatedServerName = socket.indicatedServerName(); SSLSession sslSession = socket.sslSession(); if (socket.isSsl()) { assertNotNull(sslSession); certificateChainChecker.accept(socket); } else { assertNull(sslSession); } AtomicBoolean upgradedServer = new AtomicBoolean(); AtomicInteger upgradedServerCount = new AtomicInteger(); socket.handler(buff -> { socket.write(buff); // echo the data if (startTLS) { if (upgradedServer.compareAndSet(false, true)) { indicatedServerName = socket.indicatedServerName(); assertFalse(socket.isSsl()); socket.upgradeToSsl(ar -> { assertEquals(shouldPass, ar.succeeded()); if (ar.succeeded()) { certificateChainChecker.accept(socket); upgradedServerCount.incrementAndGet(); assertTrue(socket.isSsl()); } else { complete(); } }); } else { assertTrue(socket.isSsl()); assertEquals(1, upgradedServerCount.get()); } } else { assertTrue(socket.isSsl()); } }); }; server.connectHandler(serverHandler).listen(address, ar -> { assertTrue(ar.succeeded()); client.close(); NetClientOptions clientOptions = new NetClientOptions(); if (!startTLS) { clientOptions.setSsl(true); } if (clientTrustAll) { clientOptions.setTrustAll(true); } clientOptions.setTrustOptions(clientTrust.get()); clientOptions.setKeyCertOptions(clientCert.get()); for (String suite : enabledCipherSuites) { clientOptions.addEnabledCipherSuite(suite); } if (enabledSecureTransportProtocols.length > 0) { clientOptions.getEnabledSecureTransportProtocols() .forEach(clientOptions::removeEnabledSecureTransportProtocol); } for (String protocol : enabledSecureTransportProtocols) { clientOptions.addEnabledSecureTransportProtocol(protocol); } client = vertx.createNetClient(clientOptions); client.connect(address, serverName, ar2 -> { if (ar2.succeeded()) { if (!startTLS && !shouldPass) { fail("Should not connect"); return; } final int numChunks = 100; final int chunkSize = 100; final List<Buffer> toSend = new ArrayList<>(); final Buffer expected = Buffer.buffer(); for (int i = 0; i < numChunks; i++) { Buffer chunk = TestUtils.randomBuffer(chunkSize); toSend.add(chunk); expected.appendBuffer(chunk); } final Buffer received = Buffer.buffer(); final NetSocket socket = ar2.result(); if (socket.isSsl()) { try { clientPeerCert = socket.peerCertificateChain()[0]; } catch (SSLPeerUnverifiedException ignore) { } } final AtomicBoolean upgradedClient = new AtomicBoolean(); socket.handler(buffer -> { received.appendBuffer(buffer); if (received.length() == expected.length()) { assertEquals(expected, received); complete(); } if (startTLS && !upgradedClient.get()) { upgradedClient.set(true); assertFalse(socket.isSsl()); Handler<AsyncResult<Void>> handler; if (shouldPass) { handler = onSuccess(v -> { assertTrue(socket.isSsl()); try { clientPeerCert = socket.peerCertificateChain()[0]; } catch (SSLPeerUnverifiedException ignore) { } // Now send the rest for (int i = 1; i < numChunks; i++) { socket.write(toSend.get(i)); } }); } else { handler = onFailure(err -> complete()); } if (serverName != null) { socket.upgradeToSsl(serverName, handler); } else { socket.upgradeToSsl(handler); } } else { assertTrue(socket.isSsl()); } }); //Now send some data int numToSend = startTLS ? 1 : numChunks; for (int i = 0; i < numToSend; i++) { socket.write(toSend.get(i)); } } else { if (shouldPass) { fail("Should not fail to connect"); } else { complete(); } } }); }); } } @Test // Need to: // sudo sysctl -w net.core.somaxconn=10000 // sudo sysctl -w net.ipv4.tcp_max_syn_backlog=10000 // To get this to reliably pass with a lot of connections. public void testSharedServersRoundRobin() throws Exception { boolean domainSocket = testAddress.path() != null; int numServers = VertxOptions.DEFAULT_EVENT_LOOP_POOL_SIZE / 2 - 1; int numConnections = numServers * (domainSocket ? 10 : 20); List<NetServer> servers = new ArrayList<>(); Map<NetServer, Integer> connectCount = new ConcurrentHashMap<>(); CountDownLatch latchListen = new CountDownLatch(numServers); CountDownLatch latchConns = new CountDownLatch(numConnections); for (int i = 0; i < numServers; i++) { NetServer theServer = vertx.createNetServer(); servers.add(theServer); theServer.connectHandler(sock -> { connectCount.compute(theServer, (s, cur) -> cur == null ? 1 : cur + 1); latchConns.countDown(); }).listen(testAddress, ar -> { if (ar.succeeded()) { latchListen.countDown(); } else { fail("Failed to bind server"); } }); } assertTrue(latchListen.await(10, TimeUnit.SECONDS)); // Create a bunch of connections client.close(); client = vertx.createNetClient(new NetClientOptions()); CountDownLatch latchClient = new CountDownLatch(numConnections); for (int i = 0; i < numConnections; i++) { client.connect(testAddress, res -> { if (res.succeeded()) { latchClient.countDown(); } else { res.cause().printStackTrace(); fail("Failed to connect"); } }); } awaitLatch(latchClient); awaitLatch(latchConns); assertEquals(numServers, connectCount.size()); for (NetServer server : servers) { assertTrue(connectCount.containsKey(server)); } assertEquals(numServers, connectCount.size()); for (int cnt : connectCount.values()) { assertEquals(numConnections / numServers, cnt); } testComplete(); } @Test public void testSharedServersRoundRobinWithOtherServerRunningOnDifferentPort() throws Exception { CountDownLatch latch = new CountDownLatch(1); // Have a server running on a different port to make sure it doesn't interact server.close(); server = vertx.createNetServer(new NetServerOptions().setPort(4321)); server.connectHandler(sock -> { fail("Should not connect"); }).listen(ar2 -> { if (ar2.succeeded()) { latch.countDown(); } else { fail("Failed to bind server"); } }); awaitLatch(latch); testSharedServersRoundRobin(); } @Test public void testSharedServersRoundRobinButFirstStartAndStopServer() throws Exception { // Start and stop a server on the same port/host before hand to make sure it doesn't interact server.close(); CountDownLatch latch = new CountDownLatch(1); server = vertx.createNetServer(); server.connectHandler(sock -> { fail("Should not connect"); }).listen(testAddress, ar -> { if (ar.succeeded()) { latch.countDown(); } else { fail("Failed to bind server"); } }); awaitLatch(latch); CountDownLatch closeLatch = new CountDownLatch(1); server.close(ar -> { assertTrue(ar.succeeded()); closeLatch.countDown(); }); assertTrue(closeLatch.await(10, TimeUnit.SECONDS)); testSharedServersRoundRobin(); } @Test public void testClosingVertxCloseSharedServers() throws Exception { int numServers = 2; Vertx vertx = Vertx.vertx(getOptions()); List<NetServerImpl> servers = new ArrayList<>(); for (int i = 0; i < numServers; i++) { NetServer server = vertx.createNetServer().connectHandler(so -> { fail(); }); startServer(server); servers.add((NetServerImpl) server); } CountDownLatch latch = new CountDownLatch(1); vertx.close(onSuccess(v -> { latch.countDown(); })); awaitLatch(latch); servers.forEach(server -> { assertTrue(server.isClosed()); }); } @Test // This tests using NetSocket.writeHandlerID (on the server side) // Send some data and make sure it is fanned out to all connections public void testFanout() throws Exception { int numConnections = 10; Set<String> connections = new ConcurrentHashSet<>(); server.connectHandler(socket -> { connections.add(socket.writeHandlerID()); if (connections.size() == numConnections) { for (String actorID : connections) { vertx.eventBus().publish(actorID, Buffer.buffer("some data")); } } socket.closeHandler(v -> { connections.remove(socket.writeHandlerID()); }); }); startServer(); CountDownLatch receivedLatch = new CountDownLatch(numConnections); for (int i = 0; i < numConnections; i++) { client.connect(testAddress, onSuccess(socket -> { socket.handler(data -> { receivedLatch.countDown(); }); })); } assertTrue(receivedLatch.await(10, TimeUnit.SECONDS)); testComplete(); } @Test public void testRemoteAddress() { server.connectHandler(socket -> { SocketAddress addr = socket.remoteAddress(); assertEquals("127.0.0.1", addr.host()); socket.close(); }).listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); vertx.createNetClient(new NetClientOptions()).connect(1234, "localhost", onSuccess(socket -> { SocketAddress addr = socket.remoteAddress(); assertEquals("127.0.0.1", addr.host()); assertEquals(addr.port(), 1234); socket.closeHandler(v -> testComplete()); })); }); await(); } @Test public void testWriteSameBufferMoreThanOnce() throws Exception { server.connectHandler(socket -> { Buffer received = Buffer.buffer(); socket.handler(buff -> { received.appendBuffer(buff); if (received.toString().equals("foofoo")) { testComplete(); } }); }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, result -> { NetSocket socket = result.result(); Buffer buff = Buffer.buffer("foo"); socket.write(buff); socket.write(buff); }); }); await(); } @Test public void sendFileClientToServer() throws Exception { File fDir = testFolder.newFolder(); String content = TestUtils.randomUnicodeString(10000); File file = setupFile(fDir.toString(), "some-file.txt", content); Buffer expected = Buffer.buffer(content); Buffer received = Buffer.buffer(); server.connectHandler(sock -> { sock.handler(buff -> { received.appendBuffer(buff); if (received.length() == expected.length()) { assertEquals(expected, received); testComplete(); } }); // Send some data to the client to trigger the sendfile sock.write("foo"); }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, ar2 -> { assertTrue(ar2.succeeded()); NetSocket sock = ar2.result(); sock.handler(buf -> { sock.sendFile(file.getAbsolutePath()); }); }); }); await(); } @Test public void sendFileServerToClient() throws Exception { File fDir = testFolder.newFolder(); String content = TestUtils.randomUnicodeString(10000); File file = setupFile(fDir.toString(), "some-file.txt", content); Buffer expected = Buffer.buffer(content); Buffer received = Buffer.buffer(); server.connectHandler(sock -> { sock.handler(buf -> { sock.sendFile(file.getAbsolutePath()); }); }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, ar2 -> { assertTrue(ar2.succeeded()); NetSocket sock = ar2.result(); sock.handler(buff -> { received.appendBuffer(buff); if (received.length() == expected.length()) { assertEquals(expected, received); testComplete(); } }); sock.write("foo"); }); }); await(); } @Test public void testSendFileDirectory() throws Exception { File fDir = testFolder.newFolder(); server.connectHandler(socket -> { socket.handler(buff -> { fail("Should not receive any data"); }); }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, result -> { assertTrue(result.succeeded()); NetSocket socket = result.result(); try { socket.sendFile(fDir.getAbsolutePath().toString()); // should throw exception and never hit the assert fail("Should throw exception"); } catch (IllegalArgumentException e) { testComplete(); } }); }); await(); } @Test public void testServerOptionsCopiedBeforeUse() { server.close(); NetServerOptions options = new NetServerOptions().setPort(1234); server = vertx.createNetServer(options); // Now change something - but server should still listen at previous port options.setPort(1235); server.connectHandler(sock -> { testComplete(); }); server.listen(ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { assertTrue(ar2.succeeded()); }); }); await(); } @Test public void testClientOptionsCopiedBeforeUse() { client.close(); NetClientOptions options = new NetClientOptions(); client = vertx.createNetClient(options); options.setSsl(true); // Now change something - but server should ignore this server.connectHandler(sock -> { testComplete(); }); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { assertTrue(ar2.succeeded()); }); }); await(); } @Test public void testListenWithNoHandler() { try { server.listen(testAddress); fail("Should throw exception"); } catch (IllegalStateException e) { // OK } } @Test public void testListenWithNoHandler2() { try { server.listen(testAddress, ar -> { assertFalse(ar.succeeded()); }); fail("Should throw exception"); } catch (IllegalStateException e) { // OK } } @Test public void testSetHandlerAfterListen() { server.connectHandler(sock -> { }); server.listen(testAddress, onSuccess(v -> testComplete())); try { server.connectHandler(sock -> { }); fail("Should throw exception"); } catch (IllegalStateException e) { // OK } await(); } @Test public void testSetHandlerAfterListen2() { server.connectHandler(sock -> { }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); try { server.connectHandler(sock -> { }); fail("Should throw exception"); } catch (IllegalStateException e) { // OK } testComplete(); }); await(); } @Test public void testListenTwice() { server.connectHandler(sock -> { }); server.listen(testAddress, onSuccess(s -> { try { server.listen(testAddress, res -> { }); fail("Should throw exception"); } catch (IllegalStateException e) { // OK testComplete(); } catch (Exception e) { fail(e.getMessage()); } })); await(); } @Test public void testListenOnPortNoHandler() { server.connectHandler(NetSocket::close); server.listen(1234, onSuccess(ns -> { client.connect(1234, "localhost", onSuccess(so -> { so.closeHandler(v -> { testComplete(); }); })); })); await(); } @Test public void testListen() { server.connectHandler(NetSocket::close); server.listen(testAddress, onSuccess(ns -> { client.connect(testAddress, onSuccess(so -> { so.closeHandler(v -> { testComplete(); }); })); })); await(); } @Test public void testListenTwice2() { server.connectHandler(sock -> { }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); try { server.listen(testAddress, sock -> { }); fail("Should throw exception"); } catch (IllegalStateException e) { // OK } testComplete(); }); await(); } @Test public void testCloseTwice() { client.close(); client.close(); // OK } @Test public void testAttemptConnectAfterClose() { client.close(); try { client.connect(testAddress, ar -> { }); fail("Should throw exception"); } catch (IllegalStateException e) { //OK } } @Test public void testCloseWithHandler() { waitFor(2); server.connectHandler(so -> { so.closeHandler(v -> { complete(); }); }).listen(testAddress, onSuccess(s -> { client.connect(testAddress, onSuccess(so -> { so.close(onSuccess(v -> { complete(); })); })); })); await(); } @Test public void testClientMultiThreaded() throws Exception { int numThreads = 10; Thread[] threads = new Thread[numThreads]; CountDownLatch latch = new CountDownLatch(numThreads); server.connectHandler(socket -> { socket.handler(socket::write); }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); for (int i = 0; i < numThreads; i++) { threads[i] = new Thread() { public void run() { client.connect(testAddress, result -> { assertTrue(result.succeeded()); Buffer buff = TestUtils.randomBuffer(100000); NetSocket sock = result.result(); sock.write(buff); Buffer received = Buffer.buffer(); sock.handler(rec -> { received.appendBuffer(rec); if (received.length() == buff.length()) { assertEquals(buff, received); latch.countDown(); } }); }); } }; threads[i].start(); } }); awaitLatch(latch); for (int i = 0; i < numThreads; i++) { threads[i].join(); } } @Test public void testInVerticle() throws Exception { testInVerticle(false); } private void testInVerticle(boolean worker) throws Exception { client.close(); server.close(); class MyVerticle extends AbstractVerticle { Context ctx; @Override public void start() { ctx = context; if (worker) { assertTrue(ctx.isWorkerContext()); } else { assertTrue(ctx.isEventLoopContext()); } Thread thr = Thread.currentThread(); server = vertx.createNetServer(); server.connectHandler(sock -> { sock.handler(buff -> { sock.write(buff); }); assertSame(ctx, context); if (!worker) { assertSame(thr, Thread.currentThread()); } }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); assertSame(ctx, context); if (!worker) { assertSame(thr, Thread.currentThread()); } client = vertx.createNetClient(new NetClientOptions()); client.connect(testAddress, ar2 -> { assertSame(ctx, context); if (!worker) { assertSame(thr, Thread.currentThread()); } assertTrue(ar2.succeeded()); NetSocket sock = ar2.result(); Buffer buff = TestUtils.randomBuffer(10000); sock.write(buff); Buffer brec = Buffer.buffer(); sock.handler(rec -> { assertSame(ctx, context); if (!worker) { assertSame(thr, Thread.currentThread()); } brec.appendBuffer(rec); if (brec.length() == buff.length()) { testComplete(); } }); }); }); } } MyVerticle verticle = new MyVerticle(); vertx.deployVerticle(verticle, new DeploymentOptions().setWorker(worker)); await(); } @Test public void testContexts() throws Exception { int numConnections = 10; CountDownLatch serverLatch = new CountDownLatch(numConnections); AtomicReference<Context> serverConnectContext = new AtomicReference<>(); server.connectHandler(sock -> { // Server connect handler should always be called with same context Context serverContext = Vertx.currentContext(); if (serverConnectContext.get() != null) { assertSame(serverConnectContext.get(), serverContext); } else { serverConnectContext.set(serverContext); } serverLatch.countDown(); }); CountDownLatch listenLatch = new CountDownLatch(1); AtomicReference<Context> listenContext = new AtomicReference<>(); server.listen(testAddress, onSuccess(v -> { listenContext.set(Vertx.currentContext()); listenLatch.countDown(); })); awaitLatch(listenLatch); Set<Context> contexts = new ConcurrentHashSet<>(); AtomicInteger connectCount = new AtomicInteger(); CountDownLatch clientLatch = new CountDownLatch(1); // Each connect should be in its own context for (int i = 0; i < numConnections; i++) { client.connect(testAddress, conn -> { contexts.add(Vertx.currentContext()); if (connectCount.incrementAndGet() == numConnections) { assertEquals(numConnections, contexts.size()); clientLatch.countDown(); } }); } awaitLatch(clientLatch); awaitLatch(serverLatch); // Close should be in own context server.close(ar -> { assertTrue(ar.succeeded()); Context closeContext = Vertx.currentContext(); assertFalse(contexts.contains(closeContext)); assertNotSame(serverConnectContext.get(), closeContext); assertFalse(contexts.contains(listenContext.get())); assertSame(serverConnectContext.get(), listenContext.get()); testComplete(); }); await(); } @Test public void testReadStreamPauseResume() { server.close(); server = vertx.createNetServer(new NetServerOptions().setAcceptBacklog(1)); ReadStream<NetSocket> socketStream = server.connectStream(); AtomicBoolean paused = new AtomicBoolean(); socketStream.handler(so -> { assertTrue(!paused.get()); so.write("hello"); so.close(); }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); paused.set(true); socketStream.pause(); client.connect(testAddress, ar2 -> { assertTrue(ar2.succeeded()); NetSocket so2 = ar2.result(); so2.handler(buffer -> { fail(); }); so2.closeHandler(v -> { paused.set(false); socketStream.resume(); client.connect(testAddress, ar3 -> { assertTrue(ar3.succeeded()); NetSocket so3 = ar3.result(); Buffer buffer = Buffer.buffer(); so3.handler(buffer::appendBuffer); so3.closeHandler(v3 -> { assertEquals("hello", buffer.toString("utf-8")); testComplete(); }); }); }); }); }); await(); } @Test public void testNetSocketStreamCallbackIsAsync() { this.server = vertx.createNetServer(new NetServerOptions()); AtomicInteger done = new AtomicInteger(); ReadStream<NetSocket> stream = server.connectStream(); stream.handler(req -> { }); ThreadLocal<Object> stack = new ThreadLocal<>(); stack.set(true); stream.endHandler(v -> { assertTrue(Vertx.currentContext().isEventLoopContext()); assertNull(stack.get()); if (done.incrementAndGet() == 2) { testComplete(); } }); server.listen(testAddress, ar -> { assertTrue(Vertx.currentContext().isEventLoopContext()); assertNull(stack.get()); ThreadLocal<Object> stack2 = new ThreadLocal<>(); stack2.set(true); server.close(v -> { assertTrue(Vertx.currentContext().isEventLoopContext()); assertNull(stack2.get()); if (done.incrementAndGet() == 2) { testComplete(); } }); stack2.set(null); }); await(); } @Test public void testMultipleServerClose() { this.server = vertx.createNetServer(); AtomicInteger times = new AtomicInteger(); // We assume the endHandler and the close completion handler are invoked in the same context task ThreadLocal stack = new ThreadLocal(); stack.set(true); server.connectStream().endHandler(v -> { assertNull(stack.get()); assertTrue(Vertx.currentContext().isEventLoopContext()); times.incrementAndGet(); }); server.close(ar1 -> { assertNull(stack.get()); assertTrue(Vertx.currentContext().isEventLoopContext()); server.close(ar2 -> { server.close(ar3 -> { assertEquals(1, times.get()); testComplete(); }); }); }); await(); } @Test public void testInWorker() throws Exception { vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { assertTrue(Vertx.currentContext().isWorkerContext()); assertTrue(Context.isOnWorkerThread()); final Context context = Vertx.currentContext(); NetServer server1 = vertx.createNetServer(); server1.connectHandler(conn -> { assertTrue(Vertx.currentContext().isWorkerContext()); assertTrue(Context.isOnWorkerThread()); assertSame(context, Vertx.currentContext()); conn.handler(conn::write); conn.closeHandler(v -> { testComplete(); }); }).listen(testAddress, onSuccess(s -> { assertTrue(Vertx.currentContext().isWorkerContext()); assertTrue(Context.isOnWorkerThread()); assertSame(context, Vertx.currentContext()); NetClient client = vertx.createNetClient(); client.connect(testAddress, onSuccess(res -> { assertTrue(Vertx.currentContext().isWorkerContext()); assertTrue(Context.isOnWorkerThread()); assertSame(context, Vertx.currentContext()); res.write("foo"); res.handler(buff -> { assertTrue(Vertx.currentContext().isWorkerContext()); assertTrue(Context.isOnWorkerThread()); assertSame(context, Vertx.currentContext()); res.close(); }); })); })); } }, new DeploymentOptions().setWorker(true)); await(); } @Test public void testAsyncWriteIsFlushed() throws Exception { // Test that if we do a concurrent write (by another thread) during a channel read operation // the channel will be flished after the concurrent write int num = 128; Buffer expected = TestUtils.randomBuffer(1024); ExecutorService exec = Executors.newFixedThreadPool(1); try { server.connectHandler(so -> { so.handler(buff -> { assertEquals(256, buff.length()); CountDownLatch latch = new CountDownLatch(1); exec.execute(() -> { latch.countDown(); so.write(expected); }); try { awaitLatch(latch); } catch (InterruptedException e) { fail(e); } }); }); startServer(); AtomicInteger done = new AtomicInteger(); for (int i = 0; i < num; i++) { client.connect(testAddress, ar -> { if (ar.succeeded()) { NetSocket so = ar.result(); so.handler(buff -> { assertEquals(expected, buff); so.close(); int val = done.incrementAndGet(); if (val == num) { testComplete(); } }); so.write(TestUtils.randomBuffer(256)); } else { ar.cause().printStackTrace(); } }); } await(); } finally { exec.shutdown(); } } private File setupFile(String testDir, String fileName, String content) throws Exception { File file = new File(testDir, fileName); if (file.exists()) { file.delete(); } BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); out.write(content); out.close(); return file; } @Test public void testServerWorkerMissBufferWhenBufferArriveBeforeConnectCallback() throws Exception { int size = getOptions().getWorkerPoolSize(); List<Context> workers = createWorkers(size + 1); CountDownLatch latch1 = new CountDownLatch(workers.size() - 1); workers.get(0).runOnContext(v -> { NetServer server = vertx.createNetServer(); server.connectHandler(so -> { so.handler(buf -> { assertEquals("hello", buf.toString()); testComplete(); }); }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); // Create a one second worker starvation for (int i = 1; i < workers.size(); i++) { workers.get(i).runOnContext(v2 -> { latch1.countDown(); try { Thread.sleep(1000); } catch (InterruptedException ignore) { } }); } }); }); awaitLatch(latch1); NetClient client = vertx.createNetClient(); client.connect(testAddress, ar -> { assertTrue(ar.succeeded()); NetSocket so = ar.result(); so.write(Buffer.buffer("hello")); }); await(); } @Test public void testClientWorkerMissBufferWhenBufferArriveBeforeConnectCallback() throws Exception { int size = getOptions().getWorkerPoolSize(); List<Context> workers = createWorkers(size + 1); CountDownLatch latch1 = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(size); NetServer server = vertx.createNetServer(); server.connectHandler(so -> { try { awaitLatch(latch2); } catch (InterruptedException e) { fail(e.getMessage()); return; } so.write(Buffer.buffer("hello")); }); server.listen(testAddress, ar -> { assertTrue(ar.succeeded()); latch1.countDown(); }); awaitLatch(latch1); workers.get(0).runOnContext(v -> { NetClient client = vertx.createNetClient(); client.connect(testAddress, ar -> { assertTrue(ar.succeeded()); NetSocket so = ar.result(); so.handler(buf -> { assertEquals("hello", buf.toString()); testComplete(); }); }); // Create a one second worker starvation for (int i = 1; i < workers.size(); i++) { workers.get(i).runOnContext(v2 -> { latch2.countDown(); try { Thread.sleep(1000); } catch (InterruptedException ignore) { } }); } }); await(); } @Test public void testHostVerificationHttpsNotMatching() { server.close(); NetServerOptions options = new NetServerOptions().setPort(1234).setHost("localhost").setSsl(true) .setKeyStoreOptions(new JksOptions().setPath("tls/mim-server-keystore.jks").setPassword("wibble")); NetServer server = vertx.createNetServer(options); NetClientOptions clientOptions = new NetClientOptions().setSsl(true).setTrustAll(true) .setHostnameVerificationAlgorithm("HTTPS"); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); server.listen(ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { //Should not be able to connect assertTrue(ar2.failed()); testComplete(); }); }); await(); } // this test sets HostnameVerification but also trustAll, it fails if hostname is // incorrect but does not verify the certificate validity @Test public void testHostVerificationHttpsMatching() { server.close(); NetServerOptions options = new NetServerOptions().setPort(1234).setHost("localhost").setSsl(true) .setKeyStoreOptions(new JksOptions().setPath("tls/server-keystore.jks").setPassword("wibble")); NetServer server = vertx.createNetServer(options); NetClientOptions clientOptions = new NetClientOptions().setSsl(true).setTrustAll(true) .setHostnameVerificationAlgorithm("HTTPS"); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); server.listen(ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { //Should be able to connect assertTrue(ar2.succeeded()); testComplete(); }); }); await(); } @Test public void testNoLogging() throws Exception { TestLoggerFactory factory = testLogging(); assertFalse(factory.hasName("io.netty.handler.logging.LoggingHandler")); } @Test public void testServerLogging() throws Exception { server.close(); server = vertx.createNetServer(new NetServerOptions().setLogActivity(true)); TestLoggerFactory factory = testLogging(); assertTrue(factory.hasName("io.netty.handler.logging.LoggingHandler")); } @Test public void testClientLogging() throws Exception { client.close(); client = vertx.createNetClient(new NetClientOptions().setLogActivity(true)); TestLoggerFactory factory = testLogging(); assertTrue(factory.hasName("io.netty.handler.logging.LoggingHandler")); } private TestLoggerFactory testLogging() throws Exception { return TestUtils.testLogging(() -> { server.connectHandler(so -> { so.write("fizzbuzz").end(); }); server.listen(testAddress, onSuccess(v1 -> { client.connect(testAddress, onSuccess(so -> { so.closeHandler(v2 -> testComplete()); })); })); await(); }); } /** * test socks5 proxy for accessing arbitrary server port. */ @Test public void testWithSocks5Proxy() throws Exception { NetClientOptions clientOptions = new NetClientOptions() .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKS5).setPort(11080)); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new SocksProxy(null); proxy.start(vertx); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { if (ar2.failed()) { log.warn("failed", ar2.cause()); } assertTrue(ar2.succeeded()); // make sure we have gone through the proxy assertEquals("localhost:1234", proxy.getLastUri()); testComplete(); }); }); await(); } /** * test socks5 proxy for accessing arbitrary server port with authentication. */ @Test public void testWithSocks5ProxyAuth() throws Exception { NetClientOptions clientOptions = new NetClientOptions().setProxyOptions(new ProxyOptions() .setType(ProxyType.SOCKS5).setPort(11080).setUsername("username").setPassword("username")); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new SocksProxy("username"); proxy.start(vertx); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { assertTrue(ar2.succeeded()); testComplete(); }); }); await(); } /** * test socks5 proxy when accessing ssl server port with correct cert. */ @Test public void testConnectSSLWithSocks5Proxy() throws Exception { server.close(); NetServerOptions options = new NetServerOptions().setPort(1234).setHost("localhost").setSsl(true) .setKeyCertOptions(Cert.SERVER_JKS_ROOT_CA.get()); NetServer server = vertx.createNetServer(options); NetClientOptions clientOptions = new NetClientOptions().setHostnameVerificationAlgorithm("HTTPS") .setSsl(true) .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKS5).setHost("127.0.0.1").setPort(11080)) .setTrustOptions(Trust.SERVER_JKS_ROOT_CA.get()); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new SocksProxy(null); proxy.start(vertx); server.listen(ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { assertTrue(ar2.succeeded()); testComplete(); }); }); await(); } /** * test socks5 proxy for accessing ssl server port with upgradeToSsl. * https://github.com/eclipse/vert.x/issues/1602 */ @Test public void testUpgradeSSLWithSocks5Proxy() throws Exception { server.close(); NetServerOptions options = new NetServerOptions().setPort(1234).setHost("localhost").setSsl(true) .setKeyCertOptions(Cert.SERVER_JKS_ROOT_CA.get()); NetServer server = vertx.createNetServer(options); NetClientOptions clientOptions = new NetClientOptions().setHostnameVerificationAlgorithm("HTTPS") .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKS5).setHost("127.0.0.1").setPort(11080)) .setTrustOptions(Trust.SERVER_JKS_ROOT_CA.get()); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new SocksProxy(null); proxy.start(vertx); server.listen(ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { assertTrue(ar2.succeeded()); NetSocket ns = ar2.result(); ns.upgradeToSsl(onSuccess(v2 -> { testComplete(); })); }); }); await(); } /** * test http connect proxy for accessing a arbitrary server port * note that this may not work with a "real" proxy since there are usually access rules defined * that limit the target host and ports (e.g. connecting to localhost or to port 25 may not be allowed) */ @Test public void testWithHttpConnectProxy() throws Exception { NetClientOptions clientOptions = new NetClientOptions() .setProxyOptions(new ProxyOptions().setType(ProxyType.HTTP).setPort(13128)); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new HttpProxy(null); proxy.start(vertx); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { if (ar2.failed()) { log.warn("failed", ar2.cause()); } assertTrue(ar2.succeeded()); // make sure we have gone through the proxy assertEquals("localhost:1234", proxy.getLastUri()); testComplete(); }); }); await(); } /** * test socks4a proxy for accessing arbitrary server port. */ @Test public void testWithSocks4aProxy() throws Exception { NetClientOptions clientOptions = new NetClientOptions() .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKS4).setPort(11080)); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new Socks4Proxy(null); proxy.start(vertx); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { if (ar2.failed()) { log.warn("failed", ar2.cause()); } assertTrue(ar2.succeeded()); // make sure we have gone through the proxy assertEquals("localhost:1234", proxy.getLastUri()); testComplete(); }); }); await(); } /** * test socks4a proxy for accessing arbitrary server port using username auth. */ @Test public void testWithSocks4aProxyAuth() throws Exception { NetClientOptions clientOptions = new NetClientOptions().setProxyOptions( new ProxyOptions().setType(ProxyType.SOCKS4).setPort(11080).setUsername("username")); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new Socks4Proxy("username"); proxy.start(vertx); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "localhost", ar2 -> { if (ar2.failed()) { log.warn("failed", ar2.cause()); } assertTrue(ar2.succeeded()); // make sure we have gone through the proxy assertEquals("localhost:1234", proxy.getLastUri()); testComplete(); }); }); await(); } /** * test socks4a proxy for accessing arbitrary server port using an already resolved address. */ @Test public void testWithSocks4LocalResolver() throws Exception { NetClientOptions clientOptions = new NetClientOptions() .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKS4).setPort(11080)); NetClient client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { }); proxy = new Socks4Proxy(null).start(vertx); server.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); client.connect(1234, "127.0.0.1", ar2 -> { if (ar2.failed()) { log.warn("failed", ar2.cause()); } assertTrue(ar2.succeeded()); // make sure we have gone through the proxy assertEquals("127.0.0.1:1234", proxy.getLastUri()); testComplete(); }); }); await(); } @Test public void testTLSHostnameCertCheckCorrect() { server.close(); server = vertx.createNetServer( new NetServerOptions().setSsl(true).setPort(4043).setKeyCertOptions(Cert.SERVER_JKS_ROOT_CA.get())); server.connectHandler(netSocket -> netSocket.close()).listen(ar -> { NetClientOptions options = new NetClientOptions().setHostnameVerificationAlgorithm("HTTPS") .setTrustOptions(Trust.SERVER_JKS_ROOT_CA.get()); NetClient client = vertx.createNetClient(options); client.connect(4043, "localhost", arSocket -> { if (arSocket.succeeded()) { NetSocket ns = arSocket.result(); ns.upgradeToSsl(onSuccess(v -> { testComplete(); })); } else { fail(ar.cause()); } }); }); await(); } @Test public void testTLSHostnameCertCheckIncorrect() { server.close(); server = vertx.createNetServer( new NetServerOptions().setSsl(true).setPort(4043).setKeyCertOptions(Cert.SERVER_JKS_ROOT_CA.get())); server.connectHandler(netSocket -> netSocket.close()).listen(ar -> { NetClientOptions options = new NetClientOptions().setHostnameVerificationAlgorithm("HTTPS") .setTrustOptions(Trust.SERVER_JKS_ROOT_CA.get()); NetClient client = vertx.createNetClient(options); client.connect(4043, "127.0.0.1", arSocket -> { if (arSocket.succeeded()) { NetSocket ns = arSocket.result(); ns.upgradeToSsl(onFailure(err -> { testComplete(); })); } else { fail(ar.cause()); } }); }); await(); } @Test public void testClientLocalAddress() { String expectedAddress = TestUtils.loopbackAddress(); NetClientOptions clientOptions = new NetClientOptions().setLocalAddress(expectedAddress); client.close(); client = vertx.createNetClient(clientOptions); server.connectHandler(sock -> { assertEquals(expectedAddress, sock.remoteAddress().host()); sock.close(); }); server.listen(1234, "localhost", onSuccess(v -> { client.connect(1234, "localhost", onSuccess(socket -> { socket.closeHandler(v2 -> { testComplete(); }); })); })); await(); } @Test public void testSelfSignedCertificate() throws Exception { CountDownLatch latch = new CountDownLatch(2); SelfSignedCertificate certificate = SelfSignedCertificate.create(); NetServerOptions serverOptions = new NetServerOptions().setSsl(true) .setKeyCertOptions(certificate.keyCertOptions()).setTrustOptions(certificate.trustOptions()); NetClientOptions clientOptions = new NetClientOptions().setSsl(true) .setKeyCertOptions(certificate.keyCertOptions()).setTrustOptions(certificate.trustOptions()); NetClientOptions clientTrustAllOptions = new NetClientOptions().setSsl(true).setTrustAll(true); server = vertx.createNetServer(serverOptions).connectHandler(socket -> { socket.write("123").end(); }).listen(testAddress, onSuccess(s -> { client = vertx.createNetClient(clientOptions); client.connect(testAddress, onSuccess(socket -> { socket.handler(buffer -> { assertEquals("123", buffer.toString()); latch.countDown(); }); })); client = vertx.createNetClient(clientTrustAllOptions); client.connect(testAddress, onSuccess(socket -> { socket.handler(buffer -> { assertEquals("123", buffer.toString()); latch.countDown(); }); })); })); awaitLatch(latch); } @Test public void testWorkerClient() throws Exception { String expected = TestUtils.randomAlphaString(2000); server.connectHandler(so -> { so.write(expected).close(); }); startServer(); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { NetClient client = vertx.createNetClient(); client.connect(testAddress, onSuccess(so -> { Buffer received = Buffer.buffer(); so.handler(received::appendBuffer); so.closeHandler(v -> { assertEquals(expected, received.toString()); testComplete(); }); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } })); } }, new DeploymentOptions().setWorker(true)); await(); } @Test public void testWorkerServer() throws Exception { String expected = TestUtils.randomAlphaString(2000); vertx.deployVerticle(new AbstractVerticle() { @Override public void start(Future<Void> startFuture) throws Exception { NetServer server = vertx.createNetServer(); server.connectHandler(so -> { Buffer received = Buffer.buffer(); so.handler(received::appendBuffer); so.closeHandler(v -> { assertEquals(expected, received.toString()); testComplete(); }); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); server.listen(testAddress, ar -> startFuture.handle(ar.mapEmpty())); } }, new DeploymentOptions().setWorker(true), onSuccess(v -> { client.connect(testAddress, onSuccess(so -> { so.write(expected).close(); })); })); await(); } @Test public void testNetServerInternal() throws Exception { testNetServerInternal_(new HttpClientOptions(), false); } @Test public void testNetServerInternalTLS() throws Exception { server.close(); server = vertx.createNetServer(new NetServerOptions().setPort(1234).setHost("localhost").setSsl(true) .setKeyStoreOptions(Cert.SERVER_JKS.get())); testNetServerInternal_(new HttpClientOptions().setSsl(true).setTrustStoreOptions(Trust.SERVER_JKS.get()), true); } private void testNetServerInternal_(HttpClientOptions clientOptions, boolean expectSSL) 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()); 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.getNow(1234, "localhost", "/somepath", onSuccess(resp -> { assertEquals(200, resp.statusCode()); resp.bodyHandler(buff -> { assertEquals("Hello World", buff.toString()); complete(); }); })); await(); } @Test public void testNetClientInternal() throws Exception { testNetClientInternal_(new HttpServerOptions().setHost("localhost").setPort(1234), false); } @Test public void testNetClientInternalTLS() throws Exception { client.close(); client = vertx .createNetClient(new NetClientOptions().setSsl(true).setTrustStoreOptions(Trust.SERVER_JKS.get())); testNetClientInternal_(new HttpServerOptions().setHost("localhost").setPort(1234).setSsl(true) .setKeyStoreOptions(Cert.SERVER_JKS.get()), true); } private void testNetClientInternal_(HttpServerOptions options, boolean expectSSL) 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()); AtomicInteger status = new AtomicInteger(); soInt.handler(buff -> fail()); soInt.messageHandler(obj -> { switch (status.getAndIncrement()) { case 0: assertTrue(obj instanceof HttpResponse); HttpResponse resp = (HttpResponse) obj; assertEquals(200, resp.status().code()); break; case 1: assertTrue(obj instanceof LastHttpContent); ByteBuf content = ((LastHttpContent) obj).content(); assertEquals(!expectSSL, content.isDirect()); assertEquals(1, content.refCnt()); String val = content.toString(StandardCharsets.UTF_8); assertTrue(content.release()); assertEquals("Hello World", val); complete(); break; default: fail(); } }); soInt.writeMessage(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/somepath"), onSuccess(v -> complete())); })); await(); } @Test public void testNetSocketInternalBuffer() throws Exception { server.connectHandler(so -> { NetSocketInternal soi = (NetSocketInternal) so; soi.messageHandler(msg -> fail("Unexpected")); soi.handler(msg -> { ByteBuf byteBuf = msg.getByteBuf(); assertFalse(byteBuf.isDirect()); assertEquals(1, byteBuf.refCnt()); assertFalse(byteBuf.release()); assertEquals(1, byteBuf.refCnt()); soi.write(msg); }); }); startServer(); client.connect(testAddress, onSuccess(so -> { NetSocketInternal soi = (NetSocketInternal) so; soi.write(Buffer.buffer("Hello World")); soi.messageHandler(msg -> fail("Unexpected")); soi.handler(msg -> { ByteBuf byteBuf = msg.getByteBuf(); assertFalse(byteBuf.isDirect()); assertEquals(1, byteBuf.refCnt()); assertFalse(byteBuf.release()); assertEquals(1, byteBuf.refCnt()); assertEquals("Hello World", msg.toString()); testComplete(); }); })); await(); } @Test public void testCloseCompletionHandlerNotCalledWhenActualServerFailed() { server.close(); server = vertx.createNetServer(new NetServerOptions().setSsl(true) .setPemKeyCertOptions(new PemKeyCertOptions().setKeyPath("invalid"))).connectHandler(c -> { }); try { server.listen(10000, r -> fail()); } catch (Exception ignore) { // Expected } server.close(onSuccess(v -> { testComplete(); })); await(); } // We only do it for server, as client uses the same NetSocket implementation @Test public void testServerNetSocketShouldBeClosedWhenTheClosedHandlerIsCalled() throws Exception { waitFor(2); server.connectHandler(so -> { CheckingSender sender = new CheckingSender(vertx.getOrCreateContext(), 2, so); sender.send(); so.closeHandler(v -> { Throwable failure = sender.close(); if (failure != null) { fail(failure); } else { complete(); } }); so.endHandler(v -> { Throwable failure = sender.close(); if (failure != null) { fail(failure); } else { complete(); } }); }); startServer(); client.connect(testAddress, onSuccess(so -> { vertx.setTimer(1000, id -> { so.close(); }); })); await(); } @Test public void testServerWithIdleTimeoutSendChunkedFile() throws Exception { testIdleTimeoutSendChunkedFile(true); } @Test public void testClientWithIdleTimeoutSendChunkedFile() throws Exception { testIdleTimeoutSendChunkedFile(false); } private void testIdleTimeoutSendChunkedFile(boolean idleOnServer) throws Exception { int expected = 16 * 1024 * 1024; // We estimate this will take more than 200ms to transfer with a 1ms pause in chunks File sent = TestUtils.tmpFile(".dat", expected); server.close(); AtomicReference<AsyncResult<Void>> sendResult = new AtomicReference<>(); AtomicReference<Integer> remaining = new AtomicReference<>(); AtomicLong now = new AtomicLong(); Runnable testChecker = () -> { if (sendResult.get() != null && remaining.get() != null) { if (remaining.get() > 0) { // It might fail sometimes assertTrue(sendResult.get().failed()); } else { assertTrue(sendResult.get().succeeded()); assertTrue(System.currentTimeMillis() - now.get() > 200); } testComplete(); } }; Consumer<NetSocket> sender = so -> { so.sendFile(sent.getAbsolutePath(), ar -> { sendResult.set(ar); testChecker.run(); }); }; Consumer<NetSocket> receiver = so -> { now.set(System.currentTimeMillis()); int[] len = { 0 }; so.handler(buff -> { len[0] += buff.length(); so.pause(); vertx.setTimer(1, id -> { so.resume(); }); }); so.exceptionHandler(this::fail); so.endHandler(v -> { remaining.set(expected - len[0]); testChecker.run(); }); }; server = vertx .createNetServer( new NetServerOptions().setIdleTimeout(200).setIdleTimeoutUnit(TimeUnit.MILLISECONDS)) .connectHandler((idleOnServer ? sender : receiver)::accept); startServer(); client.close(); client = vertx.createNetClient( new NetClientOptions().setIdleTimeout(200).setIdleTimeoutUnit(TimeUnit.MILLISECONDS)); client.connect(testAddress, onSuccess(idleOnServer ? receiver : sender)); await(); } @Test public void testHalfCloseCallsEndHandlerAfterBuffersAreDelivered() throws Exception { // Synchronized on purpose StringBuffer expected = new StringBuffer(); server.connectHandler(so -> { Context ctx = vertx.getOrCreateContext(); for (int i = 0; i < 8; i++) { int val = i; ctx.runOnContext(v -> { String chunk = "chunk-" + val + "\r\n"; so.write(chunk); expected.append(chunk); }); } ctx.runOnContext(v -> { // This will half close the connection so.close(); }); }); startServer(); client.connect(testAddress, "localhost", onSuccess(so -> { so.pause(); AtomicBoolean closed = new AtomicBoolean(); AtomicBoolean ended = new AtomicBoolean(); Buffer received = Buffer.buffer(); so.handler(received::appendBuffer); so.closeHandler(v -> { assertFalse(ended.get()); assertEquals(Buffer.buffer(), received); closed.set(true); so.resume(); }); so.endHandler(v -> { assertEquals(expected.toString(), received.toString()); ended.set(true); testComplete(); }); })); await(); } @Test public void testSslHandshakeTimeoutHappened() throws Exception { server.close(); client.close(); // set up a normal server to force the SSL handshake time out in client NetServerOptions serverOptions = new NetServerOptions().setSsl(false); server = vertx.createNetServer(serverOptions); NetClientOptions clientOptions = new NetClientOptions().setSsl(true).setTrustAll(true) .setSslHandshakeTimeout(200).setSslHandshakeTimeoutUnit(TimeUnit.MILLISECONDS); client = vertx.createNetClient(clientOptions); server.connectHandler(s -> { }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, onFailure(err -> { assertTrue(err instanceof SSLHandshakeException); assertEquals("handshake timed out", err.getCause().getMessage()); testComplete(); })); }); await(); } @Test public void testSslHandshakeTimeoutNotHappened() throws Exception { server.close(); client.close(); NetServerOptions serverOptions = new NetServerOptions().setSsl(true) .setKeyStoreOptions(Cert.SERVER_JKS.get()) // set 100ms to let the connection established .setSslHandshakeTimeout(100).setSslHandshakeTimeoutUnit(TimeUnit.MILLISECONDS); server = vertx.createNetServer(serverOptions); NetClientOptions clientOptions = new NetClientOptions().setSsl(true).setTrustAll(true); client = vertx.createNetClient(clientOptions); server.connectHandler(s -> { }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, res -> { assertTrue(res.succeeded()); testComplete(); }); }); await(); } @Test public void testSslHandshakeTimeoutHappenedWhenUpgradeSsl() { server.close(); client.close(); // set up a normal server to force the SSL handshake time out in client NetServerOptions serverOptions = new NetServerOptions().setSsl(false); server = vertx.createNetServer(serverOptions); NetClientOptions clientOptions = new NetClientOptions().setSsl(false).setTrustAll(true) .setSslHandshakeTimeout(200).setSslHandshakeTimeoutUnit(TimeUnit.MILLISECONDS); client = vertx.createNetClient(clientOptions); server.connectHandler(s -> { }).listen(testAddress, ar -> { assertTrue(ar.succeeded()); client.connect(testAddress, res -> { assertTrue(res.succeeded()); NetSocket socket = res.result(); assertFalse(socket.isSsl()); socket.upgradeToSsl(onFailure(err -> { assertTrue(err instanceof SSLException); assertEquals("handshake timed out", err.getMessage()); testComplete(); })); }); }); await(); } protected void startServer(SocketAddress remoteAddress) throws Exception { startServer(remoteAddress, vertx.getOrCreateContext()); } protected void startServer(SocketAddress remoteAddress, NetServer server) throws Exception { startServer(remoteAddress, vertx.getOrCreateContext(), server); } 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); } @Test public void testPausedDuringLastChunk() throws Exception { server.connectHandler(so -> { AtomicBoolean paused = new AtomicBoolean(); paused.set(true); so.pause(); so.closeHandler(v -> { paused.set(false); so.resume(); }); so.endHandler(v -> { assertFalse(paused.get()); testComplete(); }); }); startServer(); client.connect(testAddress, "localhost", onSuccess(so -> { so.close(); })); await(); } 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); } }