/*
* Copyright 2014-2017 Real Logic Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.aeron.driver;
import io.aeron.driver.exceptions.InvalidChannelException;
import io.aeron.driver.media.UdpChannel;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assume;
import org.junit.Test;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import org.agrona.BitUtil;
import org.agrona.LangUtil;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import static java.lang.String.format;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(Theories.class)
public class UdpChannelTest
{
@Test
public void shouldHandleExplicitLocalAddressAndPortFormat() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?interface=localhost:40123|endpoint=localhost:40124");
assertThat(udpChannel.localData(), is(new InetSocketAddress("localhost", 40123)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("localhost", 40123)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Theory
public void shouldHandleExplicitLocalAddressAndPortFormatWithAeronUri(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(
uri(endpointKey, "localhost:40124", interfaceKey, "localhost:40123"));
assertThat(udpChannel.localData(), is(new InetSocketAddress("localhost", 40123)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("localhost", 40123)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Test
public void shouldHandleImpliedLocalAddressAndPortFormat() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=localhost:40124");
assertThat(udpChannel.localData(), is(new InetSocketAddress("0.0.0.0", 0)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("0.0.0.0", 0)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Theory
public void shouldHandleImpliedLocalAddressAndPortFormatWithAeronUri(
@Values({"endpoint"}) final String endpointKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(uri(endpointKey, "localhost:40124"));
assertThat(udpChannel.localData(), is(new InetSocketAddress("0.0.0.0", 0)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("0.0.0.0", 0)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Test(expected = InvalidChannelException.class)
public void shouldThrowExceptionForIncorrectScheme() throws Exception
{
UdpChannel.parse("unknownudp://localhost:40124");
}
@Test(expected = InvalidChannelException.class)
public void shouldThrowExceptionForMissingAddressWithAeronUri() throws Exception
{
UdpChannel.parse("aeron:udp");
}
@Test(expected = InvalidChannelException.class)
public void shouldThrowExceptionOnEvenMulticastAddress() throws Exception
{
UdpChannel.parse("aeron:udp?endpoint=224.10.9.8");
}
@Test
public void shouldParseValidMulticastAddress() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?interface=localhost|endpoint=224.10.9.9:40124");
assertThat(udpChannel.localControl(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.remoteControl(), isMulticastAddress("224.10.9.10", 40124));
assertThat(udpChannel.localData(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.remoteData(), isMulticastAddress("224.10.9.9", 40124));
assertThat(udpChannel.localInterface(),
is(NetworkInterface.getByInetAddress(InetAddress.getByName("localhost"))));
}
@Theory
public void shouldParseValidMulticastAddressWithAeronUri(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(
uri(endpointKey, "224.10.9.9:40124", interfaceKey, "localhost"));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.remoteControl(), isMulticastAddress("224.10.9.10", 40124));
assertThat(udpChannel.localData(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.remoteData(), isMulticastAddress("224.10.9.9", 40124));
assertThat(udpChannel.localInterface(),
is(NetworkInterface.getByInetAddress(InetAddress.getByName("localhost"))));
}
private Matcher<InetSocketAddress> isMulticastAddress(final String addressName, final int port)
throws UnknownHostException
{
final InetAddress inetAddress = InetAddress.getByName(addressName);
return is(new InetSocketAddress(inetAddress, port));
}
@Test
public void shouldHandleImpliedLocalPortFormat() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?interface=localhost|endpoint=localhost:40124");
assertThat(udpChannel.localData(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Theory
public void shouldHandleImpliedLocalPortFormatWithAeronUri(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(
uri(endpointKey, "localhost:40124", interfaceKey, "localhost"));
assertThat(udpChannel.localData(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("localhost", 0)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Theory
public void shouldHandleIPv4AnyAddressAsInterfaceAddressForUnicast(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(
uri(endpointKey, "localhost:40124", interfaceKey, "0.0.0.0"));
assertThat(udpChannel.localData(), is(new InetSocketAddress("0.0.0.0", 0)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("0.0.0.0", 0)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("localhost", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("localhost", 40124)));
}
@Theory
public void shouldHandleIPv6AnyAddressAsInterfaceAddressForUnicast(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(uri(endpointKey, "[::1]:40124", interfaceKey, "[::]"));
assertThat(udpChannel.localData(), is(new InetSocketAddress("::", 0)));
assertThat(udpChannel.localControl(), is(new InetSocketAddress("::", 0)));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("::1", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("::1", 40124)));
}
@Test
public void shouldHandleLocalhostLookup() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=localhost:40124");
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("127.0.0.1", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("127.0.0.1", 40124)));
}
@Theory
public void shouldHandleLocalhostLookupWithAeronUri(
@Values({"endpoint"}) final String endpointKey) throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(uri(endpointKey, "localhost:40124"));
assertThat(udpChannel.remoteData(), is(new InetSocketAddress("127.0.0.1", 40124)));
assertThat(udpChannel.remoteControl(), is(new InetSocketAddress("127.0.0.1", 40124)));
}
@Test
public void shouldHandleBeingUsedAsMapKey() throws Exception
{
final UdpChannel udpChannel1 = UdpChannel.parse("aeron:udp?endpoint=localhost:40124");
final UdpChannel udpChannel2 = UdpChannel.parse("aeron:udp?endpoint=localhost:40124");
final Map<UdpChannel, Integer> map = new HashMap<>();
map.put(udpChannel1, 1);
assertThat(map.get(udpChannel2), is(1));
}
@Test(expected = InvalidChannelException.class)
public void shouldThrowExceptionWhenNoPortSpecified() throws Exception
{
UdpChannel.parse("aeron:udp?endpoint=localhost");
}
@Test
public void shouldHandleCanonicalFormForUnicastCorrectly() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=192.168.0.1:40456");
final UdpChannel udpChannelLocal = UdpChannel.parse("aeron:udp?interface=127.0.0.1|endpoint=192.168.0.1:40456");
final UdpChannel udpChannelLocalPort = UdpChannel.parse(
"aeron:udp?interface=127.0.0.1:40455|endpoint=192.168.0.1:40456");
final UdpChannel udpChannelLocalhost = UdpChannel.parse(
"aeron:udp?interface=localhost|endpoint=localhost:40456");
assertThat(udpChannel.canonicalForm(), is("UDP-00000000-0-c0a80001-40456"));
assertThat(udpChannelLocal.canonicalForm(), is("UDP-7f000001-0-c0a80001-40456"));
assertThat(udpChannelLocalPort.canonicalForm(), is("UDP-7f000001-40455-c0a80001-40456"));
assertThat(udpChannelLocalhost.canonicalForm(), is("UDP-7f000001-0-7f000001-40456"));
}
@Theory
public void shouldHandleIpV6CanonicalFormForUnicastCorrectly(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannelLocal =
UdpChannel.parse(uri(endpointKey, "192.168.0.1:40456", interfaceKey, "[::1]"));
final UdpChannel udpChannelLocalPort =
UdpChannel.parse(uri(endpointKey, "[fe80::5246:5dff:fe73:df06]:40456", interfaceKey, "127.0.0.1:40455"));
assertThat(udpChannelLocal.canonicalForm(), is("UDP-00000000000000000000000000000001-0-c0a80001-40456"));
assertThat(udpChannelLocalPort.canonicalForm(),
is("UDP-7f000001-40455-fe8000000000000052465dfffe73df06-40456"));
}
@Theory
public void shouldHandleCanonicalFormForUnicastCorrectlyWithAeronUri(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(uri(endpointKey, "192.168.0.1:40456"));
final UdpChannel udpChannelLocal =
UdpChannel.parse(uri(endpointKey, "192.168.0.1:40456", interfaceKey, "127.0.0.1"));
final UdpChannel udpChannelLocalPort =
UdpChannel.parse(uri(endpointKey, "192.168.0.1:40456", interfaceKey, "127.0.0.1:40455"));
final UdpChannel udpChannelLocalhost =
UdpChannel.parse(uri(endpointKey, "localhost:40456", interfaceKey, "localhost"));
assertThat(udpChannel.canonicalForm(), is("UDP-00000000-0-c0a80001-40456"));
assertThat(udpChannelLocal.canonicalForm(), is("UDP-7f000001-0-c0a80001-40456"));
assertThat(udpChannelLocalPort.canonicalForm(), is("UDP-7f000001-40455-c0a80001-40456"));
assertThat(udpChannelLocalhost.canonicalForm(), is("UDP-7f000001-0-7f000001-40456"));
}
@Test
public void shouldGetProtocolFamilyForIpV4() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=127.0.0.1:12345|interface=127.0.0.1");
assertThat(udpChannel.protocolFamily(), is((ProtocolFamily) StandardProtocolFamily.INET));
}
@Test
public void shouldGetProtocolFamilyForIpV6() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=[::1]:12345|interface=[::1]");
assertThat(udpChannel.protocolFamily(), is((ProtocolFamily) StandardProtocolFamily.INET6));
}
@Test
public void shouldGetProtocolFamilyForIpV4WithoutLocalSpecified() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=127.0.0.1:12345");
assertThat(udpChannel.protocolFamily(), is((ProtocolFamily) StandardProtocolFamily.INET));
}
@Test
public void shouldGetProtocolFamilyForIpV6WithoutLocalSpecified() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?endpoint=[::1]:12345");
assertThat(udpChannel.protocolFamily(), is((ProtocolFamily) StandardProtocolFamily.INET6));
}
@Test
public void shouldHandleCanonicalFormWithNsLookup() throws Exception
{
final String localhostIpAsHex = resolveToHexAddress("localhost");
final UdpChannel udpChannelExampleCom = UdpChannel.parse("aeron:udp?endpoint=localhost:40456");
assertThat(udpChannelExampleCom.canonicalForm(), is("UDP-00000000-0-" + localhostIpAsHex + "-40456"));
}
@Test
public void shouldHandleCanonicalFormForMulticastWithLocalPort() throws Exception
{
final UdpChannel udpChannelLocalPort = UdpChannel.parse(
"aeron:udp?interface=127.0.0.1:40455|endpoint=224.0.1.1:40456");
assertThat(udpChannelLocalPort.canonicalForm(), is("UDP-7f000001-40455-e0000101-40456"));
final UdpChannel udpChannelSubnetLocalPort =
UdpChannel.parse("aeron:udp?interface=127.0.0.0:40455/24|endpoint=224.0.1.1:40456");
assertThat(udpChannelSubnetLocalPort.canonicalForm(), is("UDP-7f000001-40455-e0000101-40456"));
}
@Test
public void shouldHandleCanonicalFormForMulticastCorrectly() throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse("aeron:udp?interface=localhost|endpoint=224.0.1.1:40456");
final UdpChannel udpChannelLocal = UdpChannel.parse("aeron:udp?interface=127.0.0.1|endpoint=224.0.1.1:40456");
final UdpChannel udpChannelAllSystems = UdpChannel.parse(
"aeron:udp?interface=localhost|endpoint=224.0.0.1:40456");
final UdpChannel udpChannelDefault = UdpChannel.parse("aeron:udp?endpoint=224.0.1.1:40456");
final UdpChannel udpChannelSubnet = UdpChannel.parse(
"aeron:udp?interface=localhost/24|endpoint=224.0.1.1:40456");
final UdpChannel udpChannelSubnetLocal = UdpChannel.parse(
"aeron:udp?interface=127.0.0.0/24|endpoint=224.0.1.1:40456");
assertThat(udpChannel.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelLocal.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelAllSystems.canonicalForm(), is("UDP-7f000001-0-e0000001-40456"));
assertThat(udpChannelSubnet.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelSubnetLocal.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelDefault.localInterface(), supportsMulticastOrIsLoopback());
}
@Theory
public void shouldHandleCanonicalFormForMulticastCorrectlyWithAeronUri(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
final UdpChannel udpChannel = UdpChannel.parse(uri(endpointKey, "224.0.1.1:40456", interfaceKey, "localhost"));
final UdpChannel udpChannelLocal =
UdpChannel.parse(uri(endpointKey, "224.0.1.1:40456", interfaceKey, "127.0.0.1"));
final UdpChannel udpChannelAllSystems =
UdpChannel.parse(uri(endpointKey, "224.0.0.1:40456", interfaceKey, "127.0.0.1"));
final UdpChannel udpChannelDefault = UdpChannel.parse(uri(endpointKey, "224.0.1.1:40456"));
final UdpChannel udpChannelSubnet =
UdpChannel.parse(uri(endpointKey, "224.0.1.1:40456", interfaceKey, "localhost/24"));
final UdpChannel udpChannelSubnetLocal =
UdpChannel.parse(uri(endpointKey, "224.0.1.1:40456", interfaceKey, "127.0.0.0/24"));
assertThat(udpChannel.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelLocal.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelAllSystems.canonicalForm(), is("UDP-7f000001-0-e0000001-40456"));
assertThat(udpChannelSubnet.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelSubnetLocal.canonicalForm(), is("UDP-7f000001-0-e0000101-40456"));
assertThat(udpChannelDefault.localInterface(), supportsMulticastOrIsLoopback());
}
@Theory
public void shouldHandleIpV6CanonicalFormForMulticastCorrectly(
@Values({"endpoint"}) final String endpointKey,
@Values({"interface"}) final String interfaceKey)
throws Exception
{
Assume.assumeTrue(System.getProperty("java.net.preferIPv4Stack") == null);
final UdpChannel udpChannel =
UdpChannel.parse(uri(endpointKey, "[FF01::FD]:40456", interfaceKey, "localhost"));
final UdpChannel udpChannelLocal =
UdpChannel.parse(uri(endpointKey, "224.0.1.1:40456", interfaceKey, "[::1]:54321/64"));
assertThat(udpChannel.canonicalForm(), is("UDP-7f000001-0-ff0100000000000000000000000000fd-40456"));
assertThat(udpChannelLocal.canonicalForm(), is("UDP-00000000000000000000000000000001-54321-e0000101-40456"));
}
private static Matcher<NetworkInterface> supportsMulticastOrIsLoopback()
{
return new TypeSafeMatcher<NetworkInterface>()
{
public void describeTo(final Description description)
{
description.appendText("Interface supports multicast or is loopack");
}
protected boolean matchesSafely(final NetworkInterface item)
{
boolean matchesSafely = false;
try
{
matchesSafely = item.supportsMulticast() || item.isLoopback();
}
catch (final SocketException ex)
{
LangUtil.rethrowUnchecked(ex);
}
return matchesSafely;
}
};
}
private String resolveToHexAddress(final String host) throws UnknownHostException
{
return BitUtil.toHex(InetAddress.getByName(host).getAddress());
}
private static String uri(
final String endpointKey, final String endpointValue, final String interfaceKey, final String interfaceValue)
{
return format("aeron:udp?%s=%s|%s=%s", endpointKey, endpointValue, interfaceKey, interfaceValue);
}
private static String uri(final String endpointKey, final String endpointValue)
{
return format("aeron:udp?%s=%s", endpointKey, endpointValue);
}
}