/* * Copyright 2014 Red Hat, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.test.core; import io.netty.buffer.ByteBuf; import io.netty.util.internal.logging.InternalLoggerFactory; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.datagram.DatagramSocket; import io.vertx.core.datagram.DatagramSocketOptions; import io.vertx.core.json.JsonObject; import io.vertx.core.net.NetworkOptions; import io.vertx.core.streams.WriteStream; import io.vertx.test.netty.TestLoggerFactory; import org.junit.Test; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static io.vertx.test.core.TestUtils.*; /** * @author <a href="mailto:nmaurer@redhat.com">Norman Maurer</a> * @author <a href="http://tfox.org">Tim Fox</a> */ public class DatagramTest extends VertxTestBase { private volatile DatagramSocket peer1; private volatile DatagramSocket peer2; protected void tearDown() throws Exception { if (peer1 != null) { CountDownLatch latch = new CountDownLatch(2); peer1.close(ar -> { assertTrue(ar.succeeded()); latch.countDown(); if (peer2 != null) { peer2.close(ar2 -> { assertTrue(ar2.succeeded()); latch.countDown(); }); } else { latch.countDown(); } }); latch.await(10L, TimeUnit.SECONDS); } super.tearDown(); } @Test public void testDatagramSocket() throws Exception { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); assertNullPointerException(() -> peer1.send((Buffer) null, 1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.send(Buffer.buffer(), -1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.send(Buffer.buffer(), 65536, "127.0.0.1", ar -> {})); assertNullPointerException(() -> peer1.send((String) null, 1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.send("", -1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.send("", 65536, "127.0.0.1", ar -> {})); assertNullPointerException(() -> peer1.send((String) null, "UTF-8", 1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.send("", "UTF-8", -1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.send("", "UTF-8", 65536, "127.0.0.1", ar -> {})); assertNullPointerException(() -> peer1.send("", null, 1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.sender(-1, "127.0.0.1")); assertIllegalArgumentException(() -> peer1.sender(65536, "127.0.0.1")); assertNullPointerException(() -> peer1.sender(1, null)); assertIllegalArgumentException(() -> peer1.listen(-1, "127.0.0.1", ar -> {})); assertIllegalArgumentException(() -> peer1.listen(65536, "127.0.0.1", ar -> {})); assertNullPointerException(() -> peer1.listen(1, null, ar -> {})); assertNullPointerException(() -> peer1.listen(1, "127.0.0.1", null)); } @Test public void testSendReceive() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); Buffer buffer = TestUtils.randomBuffer(128); peer2.handler(packet -> { Buffer data = packet.data(); ByteBuf buff = data.getByteBuf(); while (buff != buff.unwrap() && buff.unwrap() != null) { buff = buff.unwrap(); } assertTrue("Was expecting an unpooled buffer instead of " + buff.getClass().getSimpleName(), buff.getClass().getSimpleName().contains("Unpooled")); assertEquals(buffer, data); testComplete(); }); peer1.send(buffer, 1234, "127.0.0.1", ar2 -> assertTrue(ar2.succeeded())); }); await(); } @Test public void testSendReceiveLargePacket() { int packetSize = 10000; peer1 = vertx.createDatagramSocket(new DatagramSocketOptions().setSendBufferSize(packetSize)); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions().setReceiveBufferSize(packetSize + 16)); // OSX needs 16 more peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); Buffer buffer = TestUtils.randomBuffer(packetSize); peer2.handler(packet -> { assertEquals(buffer, packet.data()); testComplete(); }); peer1.send(buffer, 1234, "127.0.0.1", ar2 -> assertTrue(ar2.succeeded())); }); await(); } @Test public void testEndHandler() { ThreadLocal<Object> stack = new ThreadLocal<>(); stack.set(true); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); peer2.endHandler(v -> { assertTrue(Vertx.currentContext().isEventLoopContext()); assertNull(stack.get()); testComplete(); }); peer2.close(); }); await(); } @Test public void testPauseResume() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "127.0.0.1", ar -> { Buffer buffer = TestUtils.randomBuffer(128); AtomicBoolean received = new AtomicBoolean(); peer2.handler(packet -> received.set(true)); peer2.pause(); peer1.send(buffer, 1234, "127.0.0.1", ar2 -> { assertTrue(ar2.succeeded()); }); vertx.setTimer(1000, l -> { AtomicInteger count = new AtomicInteger(); peer2.handler(packet -> { switch (count.getAndIncrement()) { case 0: assertEquals(buffer, packet.data()); peer1.send(buffer, 1234, "127.0.0.1", ar2 -> { assertTrue(ar2.succeeded()); }); break; case 1: assertFalse(received.get()); assertEquals(buffer, packet.data()); testComplete(); break; default: fail(); } }); peer2.resume(); }); }); await(); } @Test public void testSender() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "127.0.0.1", ar -> { Buffer buffer = TestUtils.randomBuffer(128); peer2.handler(packet -> { assertEquals(buffer, packet.data()); testComplete(); }); WriteStream<Buffer> sender1 = peer1.sender(1234, "127.0.0.1"); sender1.write(buffer); }); } @Test public void testListenHostPort() { peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); testComplete(); }); await(); } @Test public void testListenPort() { peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.listen(1234, "localhost", ar -> { assertTrue(ar.succeeded()); testComplete(); }); await(); } @Test public void testListenInetSocketAddress() { peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); testComplete(); }); await(); } @Test public void testListenSamePortMultipleTimes() { peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2.listen(1234, "127.0.0.1", ar1 -> { assertTrue(ar1.succeeded()); peer1.listen(1234, "127.0.0.1", ar2 -> { assertTrue(ar2.failed()); testComplete(); }); }); await(); } @Test public void testEcho() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer1.exceptionHandler(t -> fail(t.getMessage())); peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); Buffer buffer = TestUtils.randomBuffer(128); peer2.handler(packet -> { assertEquals("127.0.0.1", packet.sender().host()); assertEquals(1235, packet.sender().port()); assertEquals(buffer, packet.data()); peer2.send(packet.data(), 1235, "127.0.0.1", ar2 -> assertTrue(ar2.succeeded())); }); peer1.listen(1235, "127.0.0.1", ar2 -> { peer1.handler(packet -> { assertEquals(buffer, packet.data()); assertEquals("127.0.0.1", packet.sender().host()); assertEquals(1234, packet.sender().port()); testComplete(); }); peer1.send(buffer, 1234, "127.0.0.1", ar3 -> assertTrue(ar3.succeeded())); }); }); await(); } @Test public void testSendAfterCloseFails() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer1.close(ar -> { assertTrue(ar.succeeded()); peer1.send("Test", 1234, "127.0.0.1", ar2 -> { assertTrue(ar2.failed()); peer1 = null; peer2.close(ar3 -> { assertTrue(ar3.succeeded()); peer2.send("Test", 1234, "127.0.0.1", ar4 -> { assertTrue(ar4.failed()); peer2 = null; testComplete(); }); }); }); }); await(); } @Test public void testBroadcast() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions().setBroadcast(true)); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions().setBroadcast(true)); peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "0.0.0.0", ar1 -> { assertTrue(ar1.succeeded()); Buffer buffer = TestUtils.randomBuffer(128); peer2.handler(packet -> { assertEquals(buffer, packet.data()); testComplete(); }); peer1.send(buffer, 1234, "255.255.255.255", ar2 -> { assertTrue(ar2.succeeded()); }); }); await(); } @Test public void testBroadcastFailsIfNotConfigured() { peer1 = vertx.createDatagramSocket(new DatagramSocketOptions()); peer1.send("test", 1234, "255.255.255.255", ar -> { assertTrue(ar.failed()); testComplete(); }); await(); } @Test public void testPause() { } @Test public void testMulticastJoinLeave() throws Exception { Buffer buffer = TestUtils.randomBuffer(128); String groupAddress = "230.0.0.1"; String iface = NetworkInterface.getByInetAddress(InetAddress.getByName("127.0.0.1")).getName(); AtomicBoolean received = new AtomicBoolean(); peer1 = vertx.createDatagramSocket(new DatagramSocketOptions().setMulticastNetworkInterface(iface)); peer2 = vertx.createDatagramSocket(new DatagramSocketOptions().setMulticastNetworkInterface(iface)); peer1.handler(packet -> { assertEquals(buffer, packet.data()); received.set(true); }); peer1.listen(1234, "0.0.0.0", ar1 -> { assertTrue(ar1.succeeded()); peer1.listenMulticastGroup(groupAddress, iface, null, ar2 -> { assertTrue(ar2.succeeded()); peer2.send(buffer, 1234, groupAddress, ar3 -> { assertTrue(ar3.succeeded()); // leave group in 1 second so give it enough time to really receive the packet first vertx.setTimer(1000, id -> { peer1.unlistenMulticastGroup(groupAddress, iface, null, ar4 -> { assertTrue(ar4.succeeded()); AtomicBoolean receivedAfter = new AtomicBoolean(); peer1.handler(packet -> { // Should not receive any more event as it left the group receivedAfter.set(true); }); peer2.send(buffer, 1234, groupAddress, ar5 -> { assertTrue(ar5.succeeded()); // schedule a timer which will check in 1 second if we received a message after the group // was left before vertx.setTimer(1000, id2 -> { assertFalse(receivedAfter.get()); assertTrue(received.get()); testComplete(); }); }); }); }); }); }); }); await(); } @Test public void testOptions() { DatagramSocketOptions options = new DatagramSocketOptions(); 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)); assertFalse(options.isReuseAddress()); assertEquals(options, options.setReuseAddress(true)); assertTrue(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)); assertFalse(options.isBroadcast()); assertEquals(options, options.setBroadcast(true)); assertTrue(options.isBroadcast()); assertTrue(options.isLoopbackModeDisabled()); assertEquals(options, options.setLoopbackModeDisabled(false)); assertFalse(options.isLoopbackModeDisabled()); assertEquals(-1, options.getMulticastTimeToLive()); rand = TestUtils.randomPositiveInt(); assertEquals(options, options.setMulticastTimeToLive(rand)); assertEquals(rand, options.getMulticastTimeToLive()); assertIllegalArgumentException(() -> options.setMulticastTimeToLive(-1)); assertNull(options.getMulticastNetworkInterface()); String randString = TestUtils.randomUnicodeString(100); assertEquals(options, options.setMulticastNetworkInterface(randString)); assertEquals(randString, options.getMulticastNetworkInterface()); assertFalse(options.isIpV6()); assertEquals(options, options.setIpV6(true)); assertTrue(options.isIpV6()); testComplete(); } @Test public void testCopyOptions() { DatagramSocketOptions options = new DatagramSocketOptions(); Random rand = new Random(); boolean broadcast = rand.nextBoolean(); boolean loopbackModeDisabled = rand.nextBoolean(); int multicastTimeToLive = TestUtils.randomPositiveInt(); String multicastNetworkInterface = TestUtils.randomAlphaString(100); boolean reuseAddress = rand.nextBoolean(); boolean ipV6 = rand.nextBoolean(); options.setBroadcast(broadcast); options.setLoopbackModeDisabled(loopbackModeDisabled); options.setMulticastTimeToLive(multicastTimeToLive); options.setMulticastNetworkInterface(multicastNetworkInterface); options.setReuseAddress(reuseAddress); options.setIpV6(ipV6); DatagramSocketOptions copy = new DatagramSocketOptions(options); assertEquals(broadcast, copy.isBroadcast()); assertEquals(loopbackModeDisabled, copy.isLoopbackModeDisabled()); assertEquals(multicastTimeToLive, copy.getMulticastTimeToLive()); assertEquals(multicastNetworkInterface, copy.getMulticastNetworkInterface()); assertEquals(reuseAddress, copy.isReuseAddress()); assertEquals(ipV6, copy.isIpV6()); testComplete(); } @Test public void testDefaultJsonOptions() { DatagramSocketOptions def = new DatagramSocketOptions(); DatagramSocketOptions json = new DatagramSocketOptions(new JsonObject()); assertEquals(def.isBroadcast(), json.isBroadcast()); assertEquals(def.isLoopbackModeDisabled(), json.isLoopbackModeDisabled()); assertEquals(def.getMulticastTimeToLive(), json.getMulticastTimeToLive()); assertEquals(def.getMulticastNetworkInterface(), json.getMulticastNetworkInterface()); assertEquals(def.isIpV6(), json.isIpV6()); } @Test public void testCopyOptionsJson() { Random rand = new Random(); boolean broadcast = rand.nextBoolean(); boolean loopbackModeDisabled = rand.nextBoolean(); int multicastTimeToLive = TestUtils.randomPositiveInt(); String multicastNetworkInterface = TestUtils.randomAlphaString(100); boolean reuseAddress = rand.nextBoolean(); boolean ipV6 = rand.nextBoolean(); JsonObject json = new JsonObject().put("broadcast", broadcast) .put("loopbackModeDisabled", loopbackModeDisabled) .put("multicastTimeToLive", multicastTimeToLive) .put("multicastNetworkInterface", multicastNetworkInterface) .put("reuseAddress", reuseAddress) .put("ipV6", ipV6); DatagramSocketOptions copy = new DatagramSocketOptions(json); assertEquals(broadcast, copy.isBroadcast()); assertEquals(loopbackModeDisabled, copy.isLoopbackModeDisabled()); assertEquals(multicastTimeToLive, copy.getMulticastTimeToLive()); assertEquals(multicastNetworkInterface, copy.getMulticastNetworkInterface()); assertEquals(reuseAddress, copy.isReuseAddress()); assertEquals(ipV6, copy.isIpV6()); testComplete(); } @Test public void testOptionsCopied() { DatagramSocketOptions options = new DatagramSocketOptions(); options.setReuseAddress(true); peer1 = vertx.createDatagramSocket(options); peer2 = vertx.createDatagramSocket(options); // Listening on same address:port so will only work if reuseAddress = true // Set to false, but because options are copied internally should still work options.setReuseAddress(false); peer1.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); peer2.listen(1234, "127.0.0.1", ar2 -> { assertTrue(ar2.succeeded()); testComplete(); }); }); await(); } @Test public void testUseInMultithreadedWorker() throws Exception { class MyVerticle extends AbstractVerticle { @Override public void start() { assertIllegalStateException(() -> peer1 = vertx.createDatagramSocket(new DatagramSocketOptions())); testComplete(); } } MyVerticle verticle = new MyVerticle(); vertx.deployVerticle(verticle, new DeploymentOptions().setWorker(true).setMultiThreaded(true)); await(); } @Test public void testNoLogging() throws Exception { TestLoggerFactory factory = testLogging(new DatagramSocketOptions(), new DatagramSocketOptions()); assertFalse(factory.hasName("io.netty.handler.logging.LoggingHandler")); } @Test public void testSendLogging() throws Exception { TestLoggerFactory factory = testLogging(new DatagramSocketOptions().setLogActivity(true), new DatagramSocketOptions()); assertTrue(factory.hasName("io.netty.handler.logging.LoggingHandler")); } @Test public void testListenLogging() throws Exception { TestLoggerFactory factory = testLogging(new DatagramSocketOptions(), new DatagramSocketOptions().setLogActivity(true)); assertTrue(factory.hasName("io.netty.handler.logging.LoggingHandler")); } private TestLoggerFactory testLogging(DatagramSocketOptions sendOptions, DatagramSocketOptions listenOptions) throws Exception { InternalLoggerFactory prev = InternalLoggerFactory.getDefaultFactory(); TestLoggerFactory factory = new TestLoggerFactory(); InternalLoggerFactory.setDefaultFactory(factory); try { peer1 = vertx.createDatagramSocket(sendOptions); peer2 = vertx.createDatagramSocket(listenOptions); peer2.exceptionHandler(t -> fail(t.getMessage())); peer2.listen(1234, "127.0.0.1", ar -> { assertTrue(ar.succeeded()); Buffer buffer = TestUtils.randomBuffer(128); peer2.handler(packet -> { assertEquals(buffer, packet.data()); testComplete(); }); peer1.send(buffer, 1234, "127.0.0.1", ar2 -> assertTrue(ar2.succeeded())); }); await(); } finally { InternalLoggerFactory.setDefaultFactory(prev); } return factory; } }