diff --git a/common/src/main/java/io/netty/util/NetUtil.java b/common/src/main/java/io/netty/util/NetUtil.java index 545a7c878c..d23808493a 100644 --- a/common/src/main/java/io/netty/util/NetUtil.java +++ b/common/src/main/java/io/netty/util/NetUtil.java @@ -15,8 +15,8 @@ */ package io.netty.util; +import io.netty.util.NetUtilInitializations.NetworkIfaceAndInetAddress; import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SocketUtils; import io.netty.util.internal.StringUtil; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.logging.InternalLogger; @@ -33,13 +33,9 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; -import java.net.SocketException; import java.net.UnknownHostException; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; import static io.netty.util.AsciiString.indexOf; @@ -139,113 +135,15 @@ public final class NetUtil { logger.debug("-Djava.net.preferIPv4Stack: {}", IPV4_PREFERRED); logger.debug("-Djava.net.preferIPv6Addresses: {}", IPV6_ADDRESSES_PREFERRED); - byte[] LOCALHOST4_BYTES = {127, 0, 0, 1}; - byte[] LOCALHOST6_BYTES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - // Create IPv4 loopback address. - Inet4Address localhost4 = null; - try { - localhost4 = (Inet4Address) InetAddress.getByAddress("localhost", LOCALHOST4_BYTES); - } catch (Exception e) { - // We should not get here as long as the length of the address is correct. - PlatformDependent.throwException(e); - } - LOCALHOST4 = localhost4; + LOCALHOST4 = NetUtilInitializations.createLocalhost4(); // Create IPv6 loopback address. - Inet6Address localhost6 = null; - try { - localhost6 = (Inet6Address) InetAddress.getByAddress("localhost", LOCALHOST6_BYTES); - } catch (Exception e) { - // We should not get here as long as the length of the address is correct. - PlatformDependent.throwException(e); - } - LOCALHOST6 = localhost6; + LOCALHOST6 = NetUtilInitializations.createLocalhost6(); - // Retrieve the list of available network interfaces. - List ifaces = new ArrayList(); - try { - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - if (interfaces != null) { - while (interfaces.hasMoreElements()) { - NetworkInterface iface = interfaces.nextElement(); - // Use the interface with proper INET addresses only. - if (SocketUtils.addressesFromNetworkInterface(iface).hasMoreElements()) { - ifaces.add(iface); - } - } - } - } catch (SocketException e) { - logger.warn("Failed to retrieve the list of available network interfaces", e); - } - - // Find the first loopback interface available from its INET address (127.0.0.1 or ::1) - // Note that we do not use NetworkInterface.isLoopback() in the first place because it takes long time - // on a certain environment. (e.g. Windows with -Djava.net.preferIPv4Stack=true) - NetworkInterface loopbackIface = null; - InetAddress loopbackAddr = null; - loop: for (NetworkInterface iface: ifaces) { - for (Enumeration i = SocketUtils.addressesFromNetworkInterface(iface); i.hasMoreElements();) { - InetAddress addr = i.nextElement(); - if (addr.isLoopbackAddress()) { - // Found - loopbackIface = iface; - loopbackAddr = addr; - break loop; - } - } - } - - // If failed to find the loopback interface from its INET address, fall back to isLoopback(). - if (loopbackIface == null) { - try { - for (NetworkInterface iface: ifaces) { - if (iface.isLoopback()) { - Enumeration i = SocketUtils.addressesFromNetworkInterface(iface); - if (i.hasMoreElements()) { - // Found the one with INET address. - loopbackIface = iface; - loopbackAddr = i.nextElement(); - break; - } - } - } - - if (loopbackIface == null) { - logger.warn("Failed to find the loopback interface"); - } - } catch (SocketException e) { - logger.warn("Failed to find the loopback interface", e); - } - } - - if (loopbackIface != null) { - // Found the loopback interface with an INET address. - logger.debug( - "Loopback interface: {} ({}, {})", - loopbackIface.getName(), loopbackIface.getDisplayName(), loopbackAddr.getHostAddress()); - } else { - // Could not find the loopback interface, but we can't leave LOCALHOST as null. - // Use LOCALHOST6 or LOCALHOST4, preferably the IPv6 one. - if (loopbackAddr == null) { - try { - if (NetworkInterface.getByInetAddress(LOCALHOST6) != null) { - logger.debug("Using hard-coded IPv6 localhost address: {}", localhost6); - loopbackAddr = localhost6; - } - } catch (Exception e) { - // Ignore - } finally { - if (loopbackAddr == null) { - logger.debug("Using hard-coded IPv4 localhost address: {}", localhost4); - loopbackAddr = localhost4; - } - } - } - } - - LOOPBACK_IF = loopbackIface; - LOCALHOST = loopbackAddr; + NetworkIfaceAndInetAddress loopback = NetUtilInitializations.determineLoopback(LOCALHOST4, LOCALHOST6); + LOOPBACK_IF = loopback.iface(); + LOCALHOST = loopback.address(); // As a SecurityManager may prevent reading the somaxconn file we wrap this in a privileged block. // diff --git a/common/src/main/java/io/netty/util/NetUtilInitializations.java b/common/src/main/java/io/netty/util/NetUtilInitializations.java new file mode 100644 index 0000000000..f2d65a5c6e --- /dev/null +++ b/common/src/main/java/io/netty/util/NetUtilInitializations.java @@ -0,0 +1,172 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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.netty.util; + +import io.netty.util.internal.PlatformDependent; +import io.netty.util.internal.SocketUtils; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +final class NetUtilInitializations { + /** + * The logger being used by this class + */ + private static final InternalLogger logger = InternalLoggerFactory.getInstance(NetUtilInitializations.class); + + private NetUtilInitializations() { + } + + static Inet4Address createLocalhost4() { + byte[] LOCALHOST4_BYTES = {127, 0, 0, 1}; + + Inet4Address localhost4 = null; + try { + localhost4 = (Inet4Address) InetAddress.getByAddress("localhost", LOCALHOST4_BYTES); + } catch (Exception e) { + // We should not get here as long as the length of the address is correct. + PlatformDependent.throwException(e); + } + + return localhost4; + } + + static Inet6Address createLocalhost6() { + byte[] LOCALHOST6_BYTES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + + Inet6Address localhost6 = null; + try { + localhost6 = (Inet6Address) InetAddress.getByAddress("localhost", LOCALHOST6_BYTES); + } catch (Exception e) { + // We should not get here as long as the length of the address is correct. + PlatformDependent.throwException(e); + } + + return localhost6; + } + + static NetworkIfaceAndInetAddress determineLoopback(Inet4Address localhost4, Inet6Address localhost6) { + // Retrieve the list of available network interfaces. + List ifaces = new ArrayList(); + try { + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + if (interfaces != null) { + while (interfaces.hasMoreElements()) { + NetworkInterface iface = interfaces.nextElement(); + // Use the interface with proper INET addresses only. + if (SocketUtils.addressesFromNetworkInterface(iface).hasMoreElements()) { + ifaces.add(iface); + } + } + } + } catch (SocketException e) { + logger.warn("Failed to retrieve the list of available network interfaces", e); + } + + // Find the first loopback interface available from its INET address (127.0.0.1 or ::1) + // Note that we do not use NetworkInterface.isLoopback() in the first place because it takes long time + // on a certain environment. (e.g. Windows with -Djava.net.preferIPv4Stack=true) + NetworkInterface loopbackIface = null; + InetAddress loopbackAddr = null; + loop: for (NetworkInterface iface: ifaces) { + for (Enumeration i = SocketUtils.addressesFromNetworkInterface(iface); i.hasMoreElements();) { + InetAddress addr = i.nextElement(); + if (addr.isLoopbackAddress()) { + // Found + loopbackIface = iface; + loopbackAddr = addr; + break loop; + } + } + } + + // If failed to find the loopback interface from its INET address, fall back to isLoopback(). + if (loopbackIface == null) { + try { + for (NetworkInterface iface: ifaces) { + if (iface.isLoopback()) { + Enumeration i = SocketUtils.addressesFromNetworkInterface(iface); + if (i.hasMoreElements()) { + // Found the one with INET address. + loopbackIface = iface; + loopbackAddr = i.nextElement(); + break; + } + } + } + + if (loopbackIface == null) { + logger.warn("Failed to find the loopback interface"); + } + } catch (SocketException e) { + logger.warn("Failed to find the loopback interface", e); + } + } + + if (loopbackIface != null) { + // Found the loopback interface with an INET address. + logger.debug( + "Loopback interface: {} ({}, {})", + loopbackIface.getName(), loopbackIface.getDisplayName(), loopbackAddr.getHostAddress()); + } else { + // Could not find the loopback interface, but we can't leave LOCALHOST as null. + // Use LOCALHOST6 or LOCALHOST4, preferably the IPv6 one. + if (loopbackAddr == null) { + try { + if (NetworkInterface.getByInetAddress(localhost6) != null) { + logger.debug("Using hard-coded IPv6 localhost address: {}", localhost6); + loopbackAddr = localhost6; + } + } catch (Exception e) { + // Ignore + } finally { + if (loopbackAddr == null) { + logger.debug("Using hard-coded IPv4 localhost address: {}", localhost4); + loopbackAddr = localhost4; + } + } + } + } + + return new NetworkIfaceAndInetAddress(loopbackIface, loopbackAddr); + } + + static final class NetworkIfaceAndInetAddress { + private final NetworkInterface iface; + private final InetAddress address; + + NetworkIfaceAndInetAddress(NetworkInterface iface, InetAddress address) { + this.iface = iface; + this.address = address; + } + + public NetworkInterface iface() { + return iface; + } + + public InetAddress address() { + return address; + } + } +} diff --git a/common/src/main/java/io/netty/util/NetUtilSubstitutions.java b/common/src/main/java/io/netty/util/NetUtilSubstitutions.java new file mode 100644 index 0000000000..9d9740392c --- /dev/null +++ b/common/src/main/java/io/netty/util/NetUtilSubstitutions.java @@ -0,0 +1,78 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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.netty.util; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.TargetClass; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +@TargetClass(NetUtil.class) +final class NetUtilSubstitutions { + private NetUtilSubstitutions() { + } + + @Alias + @InjectAccessors(NetUtilLocalhost4Accessor.class) + public static Inet4Address LOCALHOST4; + + @Alias + @InjectAccessors(NetUtilLocalhost6Accessor.class) + public static Inet6Address LOCALHOST6; + + @Alias + @InjectAccessors(NetUtilLocalhostAccessor.class) + public static InetAddress LOCALHOST; + + private static final class NetUtilLocalhost4Accessor { + static Inet4Address get() { + // using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + return NetUtilLocalhost4LazyHolder.LOCALHOST4; + } + } + + private static final class NetUtilLocalhost4LazyHolder { + private static final Inet4Address LOCALHOST4 = NetUtilInitializations.createLocalhost4(); + } + + private static final class NetUtilLocalhost6Accessor { + static Inet6Address get() { + // using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + return NetUtilLocalhost6LazyHolder.LOCALHOST6; + } + } + + private static final class NetUtilLocalhost6LazyHolder { + private static final Inet6Address LOCALHOST6 = NetUtilInitializations.createLocalhost6(); + } + + private static final class NetUtilLocalhostAccessor { + static InetAddress get() { + // using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + return NetUtilLocalhostLazyHolder.LOCALHOST; + } + } + + private static final class NetUtilLocalhostLazyHolder { + private static final InetAddress LOCALHOST = NetUtilInitializations + .determineLoopback(NetUtilLocalhost4LazyHolder.LOCALHOST4, NetUtilLocalhost6LazyHolder.LOCALHOST6) + .address(); + } +} + diff --git a/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties b/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties index 1e4620fab2..d2f607f492 100644 --- a/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties +++ b/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties @@ -12,4 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -Args = --initialize-at-run-time=io.netty.util.AbstractReferenceCounted,io.netty.util.concurrent.GlobalEventExecutor,io.netty.util.concurrent.ImmediateEventExecutor,io.netty.util.concurrent.ScheduledFutureTask,io.netty.util.internal.ThreadLocalRandom +Args = --initialize-at-run-time=io.netty.util.AbstractReferenceCounted,io.netty.util.concurrent.GlobalEventExecutor,io.netty.util.concurrent.ImmediateEventExecutor,io.netty.util.concurrent.ScheduledFutureTask,io.netty.util.internal.ThreadLocalRandom \ + --initialize-at-run-time=io.netty.util.NetUtilSubstitutions$NetUtilLocalhost4LazyHolder \ + --initialize-at-run-time=io.netty.util.NetUtilSubstitutions$NetUtilLocalhost6LazyHolder \ + --initialize-at-run-time=io.netty.util.NetUtilSubstitutions$NetUtilLocalhostLazyHolder diff --git a/pom.xml b/pom.xml index 8d8f8c6774..9ee780edf0 100644 --- a/pom.xml +++ b/pom.xml @@ -442,6 +442,7 @@ testsuite-osgi testsuite-shading testsuite-native-image + testsuite-native-image-client transport-blockhound-tests microbench bom diff --git a/resolver-dns/src/main/resources/META-INF/native-image/io.netty/resolver-dns/native-image.properties b/resolver-dns/src/main/resources/META-INF/native-image/io.netty/resolver-dns/native-image.properties new file mode 100644 index 0000000000..43af984157 --- /dev/null +++ b/resolver-dns/src/main/resources/META-INF/native-image/io.netty/resolver-dns/native-image.properties @@ -0,0 +1,18 @@ +# Copyright 2020 The Netty Project +# +# The Netty Project licenses this file to you 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: +# +# https://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. + +Args = --initialize-at-run-time=io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider \ + --initialize-at-run-time=io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder \ + --initialize-at-run-time=io.netty.resolver.dns.DnsNameResolver \ + --initialize-at-run-time=io.netty.resolver.HostsFileEntriesResolver diff --git a/testsuite-native-image-client/pom.xml b/testsuite-native-image-client/pom.xml new file mode 100644 index 0000000000..678eb0d3d5 --- /dev/null +++ b/testsuite-native-image-client/pom.xml @@ -0,0 +1,106 @@ + + + + + 4.0.0 + + io.netty + netty-parent + 4.1.54.Final-SNAPSHOT + + + netty-testsuite-native-image-client + jar + + Netty/Testsuite/NativeImage/Client + + + true + + + + + ${project.groupId} + netty-transport + ${project.version} + + + ${project.groupId} + netty-resolver-dns + ${project.version} + + + + + + + skipTests + + + skipTests + + + + true + + + + + + + + com.oracle.substratevm + native-image-maven-plugin + ${graalvm.version} + + + + native-image + + package + + + + ${skipNativeImageTestsuite} + ${project.artifactId} + io.netty.testsuite.svm.client.DnsNativeClient + --report-unsupported-elements-at-runtime --allow-incomplete-classpath --no-fallback --initialize-at-build-time=io.netty -H:ReflectionConfigurationResources=reflection-config.json + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + + + verify-native-image + verify + + exec + + + + + ${skipNativeImageTestsuite} + ${project.build.directory}/${project.artifactId} + + + + + diff --git a/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/DnsNativeClient.java b/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/DnsNativeClient.java new file mode 100644 index 0000000000..afbbd61eb1 --- /dev/null +++ b/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/DnsNativeClient.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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.netty.testsuite.svm.client; + +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioDatagramChannel; +import io.netty.resolver.AddressResolver; +import io.netty.resolver.dns.DnsAddressResolverGroup; +import io.netty.resolver.dns.DnsServerAddressStreamProviders; +import io.netty.util.concurrent.DefaultThreadFactory; + +import java.net.InetSocketAddress; + +/** + * A client that uses netty-dns and gets compiled to a native image. + */ +public final class DnsNativeClient { + /** + * Main entry point (not instantiable) + */ + private DnsNativeClient() { + } + + public static void main(String[] args) throws Exception { + NioEventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory("netty")); + + DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(NioDatagramChannel.class, + DnsServerAddressStreamProviders.platformDefault()); + AddressResolver resolver = resolverGroup.getResolver(group.next()); + System.out.println(resolver); + + resolver.close(); + group.shutdownGracefully().get(); + } +} diff --git a/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/package-info.java b/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/package-info.java new file mode 100644 index 0000000000..0426d41985 --- /dev/null +++ b/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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. + */ + +/** + * A hello world server that should be compiled to native. + */ +package io.netty.testsuite.svm.client; diff --git a/testsuite-native-image-client/src/main/resources/reflection-config.json b/testsuite-native-image-client/src/main/resources/reflection-config.json new file mode 100644 index 0000000000..2d1c8edff5 --- /dev/null +++ b/testsuite-native-image-client/src/main/resources/reflection-config.json @@ -0,0 +1,8 @@ +[ + { + "name": "io.netty.channel.socket.nio.NioDatagramChannel", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + } +] diff --git a/transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java b/transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java index 369cdd28b5..57f7cf7d2c 100644 --- a/transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java +++ b/transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java @@ -25,17 +25,15 @@ import java.net.InetAddress; * Internet Protocol (IP) families used byte the {@link DatagramChannel} */ public enum InternetProtocolFamily { - IPv4(Inet4Address.class, 1, NetUtil.LOCALHOST4), - IPv6(Inet6Address.class, 2, NetUtil.LOCALHOST6); + IPv4(Inet4Address.class, 1), + IPv6(Inet6Address.class, 2); private final Class addressType; private final int addressNumber; - private final InetAddress localHost; - InternetProtocolFamily(Class addressType, int addressNumber, InetAddress localHost) { + InternetProtocolFamily(Class addressType, int addressNumber) { this.addressType = addressType; this.addressNumber = addressNumber; - this.localHost = localHost; } /** @@ -58,7 +56,14 @@ public enum InternetProtocolFamily { * Returns the {@link InetAddress} that represent the {@code LOCALHOST} for the family. */ public InetAddress localhost() { - return localHost; + switch (this) { + case IPv4: + return NetUtil.LOCALHOST4; + case IPv6: + return NetUtil.LOCALHOST6; + default: + throw new IllegalStateException("Unsupported family " + this); + } } /** diff --git a/transport/src/test/java/io/netty/channel/socket/InternetProtocolFamilyTest.java b/transport/src/test/java/io/netty/channel/socket/InternetProtocolFamilyTest.java new file mode 100644 index 0000000000..4a203057ed --- /dev/null +++ b/transport/src/test/java/io/netty/channel/socket/InternetProtocolFamilyTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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.netty.channel.socket; + +import io.netty.util.NetUtil; +import org.junit.Test; + +import java.net.InetAddress; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class InternetProtocolFamilyTest { + @Test + public void ipv4ShouldHaveLocalhostOfIpV4() { + assertThat(InternetProtocolFamily.IPv4.localhost(), is((InetAddress) NetUtil.LOCALHOST4)); + } + + @Test + public void ipv6ShouldHaveLocalhostOfIpV6() { + assertThat(InternetProtocolFamily.IPv6.localhost(), is((InetAddress) NetUtil.LOCALHOST6)); + } +}