OpenJDK / jdk / jdk
changeset 59319:d652d9aaf286
8241305: Add protocol specific factory creation methods to SocketChannel and ServerSocketChannel
Reviewed-by: alanb, chegar, dfuchs
line wrap: on
line diff
--- a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java Sun May 17 21:15:33 2020 +0100 @@ -33,6 +33,7 @@ import java.nio.ByteBuffer; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; +import static java.util.Objects.requireNonNull; /** * A selectable channel for datagram-oriented sockets. @@ -184,7 +185,7 @@ * @since 1.7 */ public static DatagramChannel open(ProtocolFamily family) throws IOException { - return SelectorProvider.provider().openDatagramChannel(family); + return SelectorProvider.provider().openDatagramChannel(requireNonNull(family)); } /**
--- a/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java Sun May 17 21:15:33 2020 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,13 @@ package java.nio.channels; import java.io.IOException; +import java.net.ProtocolFamily; import java.net.ServerSocket; import java.net.SocketOption; import java.net.SocketAddress; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; +import static java.util.Objects.requireNonNull; /** * A selectable channel for stream-oriented listening sockets. @@ -114,6 +116,34 @@ } /** + * Opens a server-socket channel.The {@code family} parameter specifies the + * {@link ProtocolFamily protocol family} of the channel's socket. + * + * <p> The new channel is created by invoking the {@link + * java.nio.channels.spi.SelectorProvider#openServerSocketChannel(ProtocolFamily) + * openServerSocketChannel(ProtocolFamily)} method of the system-wide default {@link + * java.nio.channels.spi.SelectorProvider} object. </p> + * + * @param family + * The protocol family + * + * @return A new socket channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported. For example, + * suppose the parameter is specified as {@link + * java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6} + * but IPv6 is not enabled on the platform. + * @throws IOException + * If an I/O error occurs + * + * @since 15 + */ + public static ServerSocketChannel open(ProtocolFamily family) throws IOException { + return SelectorProvider.provider().openServerSocketChannel(requireNonNull(family)); + } + + /** * Returns an operation set identifying this channel's supported * operations. *
--- a/src/java.base/share/classes/java/nio/channels/SocketChannel.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/java/nio/channels/SocketChannel.java Sun May 17 21:15:33 2020 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,14 @@ package java.nio.channels; import java.io.IOException; +import java.net.ProtocolFamily; import java.net.Socket; import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; +import static java.util.Objects.requireNonNull; /** * A selectable channel for stream-oriented connecting sockets. @@ -151,6 +153,34 @@ } /** + * Opens a socket channel. The {@code family} parameter specifies the + * {@link ProtocolFamily protocol family} of the channel's socket. + * + * <p> The new channel is created by invoking the {@link + * java.nio.channels.spi.SelectorProvider#openSocketChannel(ProtocolFamily) + * openSocketChannel(ProtocolFamily)} method of the system-wide default. + * {@link java.nio.channels.spi.SelectorProvider} object.</p> + * + * @param family + * The protocol family + * + * @return A new socket channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported. For example, + * suppose the parameter is specified as {@link + * java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6} + * but IPv6 is not enabled on the platform. + * @throws IOException + * If an I/O error occurs + * + * @since 15 + */ + public static SocketChannel open(ProtocolFamily family) throws IOException { + return SelectorProvider.provider().openSocketChannel(requireNonNull(family)); + } + + /** * Opens a socket channel and connects it to a remote address. * * <p> This convenience method works as if by invoking the {@link #open()}
--- a/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java Sun May 17 21:15:33 2020 +0100 @@ -36,6 +36,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Iterator; +import java.util.Objects; import java.util.ServiceLoader; import java.util.ServiceConfigurationError; @@ -315,4 +316,51 @@ return null; } + /** + * Opens a socket channel. + * + * @implSpec The default implementation of this method first checks that + * the given protocol {@code family} is not {@code null}, + * then throws {@link UnsupportedOperationException}. + * + * @param family + * The protocol family + * + * @return The new channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported + * @throws IOException + * If an I/O error occurs + * + * @since 15 + */ + public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException { + Objects.requireNonNull(family); + throw new UnsupportedOperationException("Protocol family not supported"); + } + + /** + * Opens a server-socket channel. + * + * @implSpec The default implementation of this method first checks that + * the given protocol {@code family} is not {@code null}, + * then throws {@link UnsupportedOperationException}. + * + * @param family + * The protocol family + * + * @return The new channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported + * @throws IOException + * If an I/O error occurs + * + * @since 15 + */ + public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) throws IOException { + Objects.requireNonNull(family); + throw new UnsupportedOperationException("Protocol family not supported"); + } }
--- a/src/java.base/share/classes/sun/nio/ch/Net.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/sun/nio/ch/Net.java Sun May 17 21:15:33 2020 +0100 @@ -249,6 +249,57 @@ port); } + private static final InetAddress anyLocalInet4Address; + private static final InetAddress anyLocalInet6Address; + private static final InetAddress inet4LoopbackAddress; + private static final InetAddress inet6LoopbackAddress; + static { + try { + anyLocalInet4Address = inet4FromInt(0); + assert anyLocalInet4Address instanceof Inet4Address + && anyLocalInet4Address.isAnyLocalAddress(); + + anyLocalInet6Address = InetAddress.getByAddress(new byte[16]); + assert anyLocalInet6Address instanceof Inet6Address + && anyLocalInet6Address.isAnyLocalAddress(); + + inet4LoopbackAddress = inet4FromInt(0x7f000001); + assert inet4LoopbackAddress instanceof Inet4Address + && inet4LoopbackAddress.isLoopbackAddress(); + + byte[] bytes = new byte[16]; + bytes[15] = 0x01; + inet6LoopbackAddress = InetAddress.getByAddress(bytes); + assert inet6LoopbackAddress instanceof Inet6Address + && inet6LoopbackAddress.isLoopbackAddress(); + } catch (Exception e) { + throw new InternalError(e); + } + } + + static InetAddress inet4LoopbackAddress() { + return inet4LoopbackAddress; + } + + static InetAddress inet6LoopbackAddress() { + return inet6LoopbackAddress; + } + + /** + * Returns the wildcard address that corresponds to the given protocol family. + * + * @see InetAddress#isAnyLocalAddress() + */ + static InetAddress anyLocalAddress(ProtocolFamily family) { + if (family == StandardProtocolFamily.INET) { + return anyLocalInet4Address; + } else if (family == StandardProtocolFamily.INET6) { + return anyLocalInet6Address; + } else { + throw new IllegalArgumentException(); + } + } + /** * Returns any IPv4 address of the given network interface, or * null if the interface does not have any IPv4 addresses. @@ -467,7 +518,13 @@ } static FileDescriptor serverSocket(boolean stream) { - return IOUtil.newFD(socket0(isIPv6Available(), stream, true, fastLoopback)); + return serverSocket(UNSPEC, stream); + } + + static FileDescriptor serverSocket(ProtocolFamily family, boolean stream) { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return IOUtil.newFD(socket0(preferIPv6, stream, true, fastLoopback)); } // Due to oddities SO_REUSEADDR on windows reuse is ignored
--- a/src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java Sun May 17 21:15:33 2020 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,4 +72,14 @@ public SocketChannel openSocketChannel() throws IOException { return new SocketChannelImpl(this); } + + @Override + public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException { + return new SocketChannelImpl(this, family); + } + + @Override + public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) { + return new ServerSocketChannelImpl(this, family); + } }
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Sun May 17 21:15:33 2020 +0100 @@ -28,10 +28,12 @@ import java.io.FileDescriptor; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.ProtocolFamily; import java.net.ServerSocket; import java.net.SocketAddress; import java.net.SocketOption; import java.net.SocketTimeoutException; +import java.net.StandardProtocolFamily; import java.net.StandardSocketOptions; import java.nio.channels.AlreadyBoundException; import java.nio.channels.AsynchronousCloseException; @@ -62,6 +64,9 @@ // Used to make native close and configure calls private static final NativeDispatcher nd = new SocketDispatcher(); + // The protocol family of the socket + private final ProtocolFamily family; + // Our file descriptor private final FileDescriptor fd; private final int fdVal; @@ -95,10 +100,26 @@ // -- End of fields protected by stateLock + ServerSocketChannelImpl(SelectorProvider sp) { + this(sp, Net.isIPv6Available() + ? StandardProtocolFamily.INET6 + : StandardProtocolFamily.INET); + } - ServerSocketChannelImpl(SelectorProvider sp) { + ServerSocketChannelImpl(SelectorProvider sp, ProtocolFamily family) { super(sp); - this.fd = Net.serverSocket(true); + Objects.requireNonNull(family, "'family' is null"); + + if ((family != StandardProtocolFamily.INET) && + (family != StandardProtocolFamily.INET6)) { + throw new UnsupportedOperationException("Protocol family not supported"); + } + if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) { + throw new UnsupportedOperationException("IPv6 not available"); + } + + this.family = family; + this.fd = Net.serverSocket(family, true); this.fdVal = IOUtil.fdVal(fd); } @@ -106,8 +127,13 @@ throws IOException { super(sp); + + this.family = Net.isIPv6Available() + ? StandardProtocolFamily.INET6 + : StandardProtocolFamily.INET; this.fd = fd; this.fdVal = IOUtil.fdVal(fd); + if (bound) { synchronized (stateLock) { localAddress = Net.localAddress(fd); @@ -210,14 +236,17 @@ ensureOpen(); if (localAddress != null) throw new AlreadyBoundException(); - InetSocketAddress isa = (local == null) - ? new InetSocketAddress(0) - : Net.checkAddress(local); + InetSocketAddress isa; + if (local == null) { + isa = new InetSocketAddress(Net.anyLocalAddress(family), 0); + } else { + isa = Net.checkAddress(local, family); + } SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkListen(isa.getPort()); NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); - Net.bind(fd, isa.getAddress(), isa.getPort()); + Net.bind(family, fd, isa.getAddress(), isa.getPort()); Net.listen(fd, backlog < 1 ? 50 : backlog); localAddress = Net.localAddress(fd); } @@ -358,7 +387,7 @@ if (sm != null) { sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort()); } - return new SocketChannelImpl(provider(), newfd, isa); + return new SocketChannelImpl(provider(), family, newfd, isa); } catch (Exception e) { nd.close(newfd); throw e;
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Sun May 17 21:15:33 2020 +0100 @@ -28,6 +28,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.net.InetAddress; +import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.ProtocolFamily; import java.net.Socket; @@ -71,6 +72,9 @@ // Used to make native read and write calls private static final NativeDispatcher nd = new SocketDispatcher(); + // The protocol family of the socket + private final ProtocolFamily family; + // Our file descriptor object private final FileDescriptor fd; private final int fdVal; @@ -118,12 +122,26 @@ // -- End of fields protected by stateLock - // Constructor for normal connecting sockets // SocketChannelImpl(SelectorProvider sp) throws IOException { + this(sp, Net.isIPv6Available() + ? StandardProtocolFamily.INET6 + : StandardProtocolFamily.INET); + } + + SocketChannelImpl(SelectorProvider sp, ProtocolFamily family) throws IOException { super(sp); - this.fd = Net.socket(true); + Objects.requireNonNull(family, "'family' is null"); + if ((family != StandardProtocolFamily.INET) && + (family != StandardProtocolFamily.INET6)) { + throw new UnsupportedOperationException("Protocol family not supported"); + } + if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) { + throw new UnsupportedOperationException("IPv6 not available"); + } + this.family = family; + this.fd = Net.socket(family, true); this.fdVal = IOUtil.fdVal(fd); } @@ -131,8 +149,12 @@ throws IOException { super(sp); + this.family = Net.isIPv6Available() + ? StandardProtocolFamily.INET6 + : StandardProtocolFamily.INET; this.fd = fd; this.fdVal = IOUtil.fdVal(fd); + if (bound) { synchronized (stateLock) { this.localAddress = Net.localAddress(fd); @@ -142,10 +164,14 @@ // Constructor for sockets obtained from server sockets // - SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa) + SocketChannelImpl(SelectorProvider sp, + ProtocolFamily family, + FileDescriptor fd, + InetSocketAddress isa) throws IOException { super(sp); + this.family = family; this.fd = fd; this.fdVal = IOUtil.fdVal(fd); synchronized (stateLock) { @@ -225,8 +251,6 @@ ensureOpen(); if (name == StandardSocketOptions.IP_TOS) { - ProtocolFamily family = Net.isIPv6Available() ? - StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; Net.setSocketOption(fd, family, name, value); return this; } @@ -260,10 +284,8 @@ return (T)Boolean.valueOf(isReuseAddress); } - // special handling for IP_TOS: always return 0 when IPv6 + // special handling for IP_TOS if (name == StandardSocketOptions.IP_TOS) { - ProtocolFamily family = Net.isIPv6Available() ? - StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; return (T) Net.getSocketOption(fd, family, name); } @@ -632,14 +654,18 @@ throw new ConnectionPendingException(); if (localAddress != null) throw new AlreadyBoundException(); - InetSocketAddress isa = (local == null) ? - new InetSocketAddress(0) : Net.checkAddress(local); + InetSocketAddress isa; + if (local == null) { + isa = new InetSocketAddress(Net.anyLocalAddress(family), 0); + } else { + isa = Net.checkAddress(local, family); + } SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkListen(isa.getPort()); } NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); - Net.bind(fd, isa.getAddress(), isa.getPort()); + Net.bind(family, fd, isa.getAddress(), isa.getPort()); localAddress = Net.localAddress(fd); } } finally { @@ -723,14 +749,21 @@ /** * Checks the remote address to which this channel is to be connected. */ - private InetSocketAddress checkRemote(SocketAddress sa) throws IOException { - InetSocketAddress isa = Net.checkAddress(sa); + private InetSocketAddress checkRemote(SocketAddress sa) { + InetSocketAddress isa = Net.checkAddress(sa, family); SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); } - if (isa.getAddress().isAnyLocalAddress()) { - return new InetSocketAddress(InetAddress.getLocalHost(), isa.getPort()); + InetAddress address = isa.getAddress(); + if (address.isAnyLocalAddress()) { + int port = isa.getPort(); + if (address instanceof Inet4Address) { + return new InetSocketAddress(Net.inet4LoopbackAddress(), port); + } else { + assert family == StandardProtocolFamily.INET6; + return new InetSocketAddress(Net.inet6LoopbackAddress(), port); + } } else { return isa; } @@ -748,7 +781,10 @@ boolean connected = false; try { beginConnect(blocking, isa); - int n = Net.connect(fd, isa.getAddress(), isa.getPort()); + int n = Net.connect(family, + fd, + isa.getAddress(), + isa.getPort()); if (n > 0) { connected = true; } else if (blocking) {
--- a/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java Sun May 17 11:34:32 2020 -0700 +++ b/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java Sun May 17 21:15:33 2020 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,12 +29,13 @@ import java.io.FileDescriptor; import java.io.IOException; import java.net.InetAddress; +import java.net.Inet6Address; import java.net.InetSocketAddress; +import java.net.ProtocolFamily; import java.nio.channels.Channel; -import java.nio.channels.SocketChannel; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.DatagramChannel; import java.nio.channels.spi.SelectorProvider; +import static java.net.StandardProtocolFamily.INET6; +import static java.net.StandardProtocolFamily.INET; class InheritedChannel { @@ -81,12 +82,16 @@ */ public static class InheritedSocketChannelImpl extends SocketChannelImpl { + static ProtocolFamily family(InetSocketAddress isa) { + return (isa.getAddress() instanceof Inet6Address) ? INET6 : INET; + } + InheritedSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress remote) throws IOException { - super(sp, fd, remote); + super(sp, family(remote), fd, remote); } protected void implCloseSelectableChannel() throws IOException {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java Sun May 17 21:15:33 2020 +0100 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test local address type + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * @run testng/othervm LocalSocketAddressType + * @run testng/othervm -Djava.net.preferIPv4Stack=true LocalSocketAddressType + */ + +import jdk.test.lib.NetworkConfiguration; +import jdk.test.lib.net.IPSupport; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.net.*; +import java.nio.channels.DatagramChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.lang.Boolean.parseBoolean; +import static java.lang.System.getProperty; +import static java.lang.System.out; +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; +import static jdk.test.lib.net.IPSupport.*; + +public class LocalSocketAddressType { + + @BeforeTest() + public void setup() { + IPSupport.printPlatformSupport(out); + throwSkippedExceptionIfNonOperational(); + } + + @DataProvider(name = "addresses") + public static Iterator<Object[]> addresses() throws Exception { + NetworkConfiguration nc = NetworkConfiguration.probe(); + return Stream.concat(nc.ip4Addresses(), nc.ip6Addresses()) + .map(ia -> new Object[] { new InetSocketAddress(ia, 0) }) + .iterator(); + } + + @Test(dataProvider = "addresses") + public static void testSocketChannel(InetSocketAddress addr) throws Exception { + try (var c = SocketChannel.open()) { + Class<? extends InetAddress> cls = addr.getAddress().getClass(); + InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress(); + assertEquals(ia.getClass(), cls); + ia = c.socket().getLocalAddress(); + assertEquals(ia.getClass(), cls); + } + } + + @Test(dataProvider = "addresses") + public static void testServerSocketChannel(InetSocketAddress addr) throws Exception { + try (var c = ServerSocketChannel.open()) { + Class<? extends InetAddress> cls = addr.getAddress().getClass(); + InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress(); + assertEquals(ia.getClass(), cls); + ia = c.socket().getInetAddress(); + assertEquals(ia.getClass(), cls); + } + } + + @Test(dataProvider = "addresses") + public static void testDatagramChannel(InetSocketAddress addr) throws Exception { + try (var c = DatagramChannel.open()) { + Class<? extends InetAddress> cls = addr.getAddress().getClass(); + InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress(); + assertEquals(ia.getClass(), cls); + ia = c.socket().getLocalAddress(); + assertEquals(ia.getClass(), cls); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/nio/channels/etc/OpenAndConnect.java Sun May 17 21:15:33 2020 +0100 @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.NetworkConfiguration; +import jdk.test.lib.net.IPSupport; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.*; +import java.util.Arrays; +import java.util.List; +import java.util.LinkedList; + +import static java.lang.System.getProperty; +import static java.lang.System.out; +import static java.net.StandardProtocolFamily.INET; +import static java.net.StandardProtocolFamily.INET6; +import static jdk.test.lib.net.IPSupport.*; + +/* + * @test + * @summary Test SocketChannel, ServerSocketChannel and DatagramChannel + * open() and connect(), taking into consideration combinations of + * protocol families (INET, INET6, default), + * addresses (Inet4Address, Inet6Address). + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * @run testng/othervm OpenAndConnect + */ + + +public class OpenAndConnect { + static final Inet4Address IA4ANYLOCAL; + static final Inet6Address IA6ANYLOCAL; + static final Inet4Address IA4LOOPBACK; + static final Inet6Address IA6LOOPBACK; + static Inet4Address IA4LOCAL = null; + static Inet6Address IA6LOCAL = null; + static InetAddress DONT_BIND; + + static { + try { + IA4ANYLOCAL = (Inet4Address) InetAddress.getByName("0.0.0.0"); + IA6ANYLOCAL = (Inet6Address) InetAddress.getByName("::0"); + IA4LOOPBACK = (Inet4Address) InetAddress.getByName("127.0.0.1"); + IA6LOOPBACK = (Inet6Address) InetAddress.getByName("::1"); + + // Special value to tell test not to call bind (address is not used) + DONT_BIND = (Inet4Address) InetAddress.getByName("127.0.0.3"); + + initAddrs(); + } catch (Exception e) { + throw new RuntimeException("Could not initialize addresses", e); + } + } + + @BeforeTest() + public void setup() { + NetworkConfiguration.printSystemConfiguration(out); + IPSupport.printPlatformSupport(out); + throwSkippedExceptionIfNonOperational(); + + out.println("IA4LOCAL: " + IA4LOCAL); + out.println("IA6LOCAL: " + IA6LOCAL); + out.println("IA4ANYLOCAL: " + IA4ANYLOCAL); + out.println("IA6ANYLOCAL: " + IA6ANYLOCAL); + out.println("IA4LOOPBACK: " + IA4LOOPBACK); + out.println("IA6LOOPBACK: " + IA6LOOPBACK); + } + + @DataProvider(name = "openConnect") + public Object[][] openConnect() { + LinkedList<Object[]> l = new LinkedList<>(); + l.addAll(openConnectGenTests); + if (IA4LOCAL != null) { + l.addAll(openConnectV4LocalTests); + } + if (IA6LOCAL != null) { + l.addAll(openConnectV6LocalTests); + } + return l.toArray(new Object[][]{}); + } + + // +----- sfam is server/first socket family + // | + // | +------ saddr is bind address for server/first socket + // | | + // | | +---- cfam is family for client/second socket + // | | | + // | | | +---- caddr is address client/second + // | | | | socket binds to. When the server + // | | | | has bound to a wildcard address + // | | | | this is address used for connect + // | | | | also. + // | | | | + // | | | | + // | | | | + // | | | | + // + + + + + // { sfam, saddr, cfam, caddr, } + + public static List<Object[]> openConnectGenTests = + Arrays.asList(new Object[][] { + { INET, IA4LOOPBACK, INET, IA4LOOPBACK }, + { INET, IA4LOOPBACK, null, IA4LOOPBACK }, + { INET, IA4ANYLOCAL, null, IA4LOOPBACK }, + { INET, IA4ANYLOCAL, INET, IA4LOOPBACK }, + { INET6, IA6ANYLOCAL, null, IA6LOOPBACK }, + { INET6, IA6ANYLOCAL, INET6, IA6LOOPBACK }, + { INET6, IA6LOOPBACK, INET6, IA6LOOPBACK }, + { null, IA4LOOPBACK, INET, IA4ANYLOCAL }, + { null, IA4LOOPBACK, INET, IA4LOOPBACK }, + { null, IA4LOOPBACK, INET, null }, + { null, IA4LOOPBACK, INET6, IA6ANYLOCAL }, + { null, IA6LOOPBACK, INET6, IA6ANYLOCAL }, + { null, IA6LOOPBACK, INET6, IA6LOOPBACK }, + { null, IA6LOOPBACK, INET6, DONT_BIND }, + { null, IA4LOOPBACK, INET6, DONT_BIND }, + { null, IA4LOOPBACK, INET6, null }, + { null, IA6LOOPBACK, INET6, null }, + { null, IA4LOOPBACK, null, IA6ANYLOCAL }, + { null, IA6LOOPBACK, null, IA6ANYLOCAL }, + { null, IA6LOOPBACK, null, IA6LOOPBACK }, + { null, IA4LOOPBACK, null, null }, + { null, IA6LOOPBACK, null, null }, + { null, IA6ANYLOCAL, null, IA6LOCAL }, + { null, IA6ANYLOCAL, null, IA6LOOPBACK }, + { null, IA6ANYLOCAL, INET6, IA6LOCAL }, + { null, IA6ANYLOCAL, INET6, IA6LOOPBACK }, + { INET6, IA6LOOPBACK, INET6, IA6LOOPBACK } + }); + + // Additional tests for when an IPv4 local address or V6 + // local address is available + + public List<Object[]> openConnectV4LocalTests = + Arrays.asList(new Object[][] { + { INET, IA4LOCAL, INET, IA4LOCAL }, + { INET, IA4LOCAL, null, IA4LOCAL }, + { INET, IA4LOCAL, null, DONT_BIND }, + { INET, IA4ANYLOCAL, INET, IA4LOCAL }, + { INET, IA4ANYLOCAL, null, IA4LOCAL }, + { null, IA4LOCAL, INET, IA4ANYLOCAL }, + { null, IA4LOCAL, INET, IA4LOCAL }, + { null, IA4LOCAL, INET, null }, + { null, IA4LOCAL, INET6, IA6ANYLOCAL }, + { null, IA4LOCAL, INET6, null }, + { null, IA4LOCAL, null, IA6ANYLOCAL } + }); + + public List<Object[]> openConnectV6LocalTests = + Arrays.asList(new Object[][] { + { INET6, IA6ANYLOCAL, null, IA6LOCAL }, + { INET6, IA6ANYLOCAL, INET6, IA6LOCAL }, + { INET6, IA6LOCAL, INET6, IA6LOCAL }, + { INET6, IA6LOCAL, null, IA6LOCAL }, + { INET6, IA6LOCAL, null, DONT_BIND }, + { null, IA6LOCAL, INET6, IA6LOCAL }, + { null, IA6LOCAL, INET6, IA6ANYLOCAL }, + { null, IA6LOCAL, null, IA6ANYLOCAL }, + { null, IA6LOCAL, null, IA6LOCAL }, + { null, IA6LOCAL, INET6, null }, + { null, IA6LOCAL, null, null }, + { null, IA4LOCAL, null, null }, + { INET6, IA6LOCAL, INET6, IA6LOCAL } + }); + + + /** + * If the destination address is the wildcard, it is replaced by the alternate + * using the port number from destination. Otherwise destination is returned. + * Only used by dcOpenAndConnect + */ + static InetSocketAddress getDestinationAddress(SocketAddress destination, InetAddress alternate) { + InetSocketAddress isa = (InetSocketAddress)destination; + if (isa.getAddress().isAnyLocalAddress()) + return new InetSocketAddress(alternate, isa.getPort()); + else + return isa; + } + + @Test(dataProvider = "openConnect") + public void scOpenAndConnect(ProtocolFamily sfam, + InetAddress saddr, + ProtocolFamily cfam, + InetAddress caddr) throws IOException + { + out.printf("scOpenAndConnect: server bind: %s client bind: %s\n", saddr, caddr); + try (ServerSocketChannel ssc = openSSC(sfam)) { + ssc.bind(getSocketAddress(saddr)); + InetSocketAddress ssa = (InetSocketAddress)ssc.getLocalAddress(); + ssa = getDestinationAddress(ssa, caddr); + out.println(ssa); + try (SocketChannel csc = openSC(cfam)) { + if (caddr != DONT_BIND) { + csc.bind(getSocketAddress(caddr)); + } + csc.connect(ssa); + } + } + } + + @Test(dataProvider = "openConnect") + public void dcOpenAndConnect(ProtocolFamily sfam, + InetAddress saddr, + ProtocolFamily cfam, + InetAddress caddr) throws IOException + { + try (DatagramChannel sdc = openDC(sfam)) { + sdc.bind(getSocketAddress(saddr)); + SocketAddress ssa = sdc.socket().getLocalSocketAddress(); + ssa = getDestinationAddress(ssa, caddr); + out.println(ssa); + try (DatagramChannel dc = openDC(cfam)) { + if (caddr != DONT_BIND) { + dc.bind(getSocketAddress(caddr)); + } + dc.connect(ssa); + } + } + } + + // Helper methods + + private static SocketChannel openSC(ProtocolFamily fam) throws IOException { + return fam == null ? SocketChannel.open() : SocketChannel.open(fam); + } + + private static ServerSocketChannel openSSC(ProtocolFamily fam) + throws IOException { + return fam == null ? ServerSocketChannel.open() + : ServerSocketChannel.open(fam); + } + + private static DatagramChannel openDC(ProtocolFamily fam) + throws IOException { + return fam == null ? DatagramChannel.open() + : DatagramChannel.open(fam); + } + + private static SocketAddress getSocketAddress(InetAddress ia) { + return ia == null ? null : new InetSocketAddress(ia, 0); + } + + private static void initAddrs() throws IOException { + + NetworkConfiguration cfg = NetworkConfiguration.probe(); + + IA4LOCAL = cfg.ip4Addresses() + .filter(a -> !a.isLoopbackAddress()) + .findFirst() + .orElse(null); + + IA6LOCAL = cfg.ip6Addresses() + .filter(a -> !a.isLoopbackAddress()) + .findFirst() + .orElse(null); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/nio/channels/etc/ProtocolFamilies.java Sun May 17 21:15:33 2020 +0100 @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.NetworkConfiguration; +import jdk.test.lib.Platform; +import jdk.test.lib.net.IPSupport; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.Assert.ThrowingRunnable; +import java.io.IOException; +import java.net.*; +import java.nio.channels.*; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import static java.lang.System.out; +import static java.lang.System.getProperty; +import static java.lang.Boolean.parseBoolean; +import static java.net.StandardProtocolFamily.INET; +import static java.net.StandardProtocolFamily.INET6; +import static jdk.test.lib.net.IPSupport.*; +import static org.testng.Assert.assertThrows; + +/* + * @test + * @summary Test SocketChannel, ServerSocketChannel and DatagramChannel + * with various ProtocolFamily combinations + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * @run testng ProtocolFamilies + * @run testng/othervm -Djava.net.preferIPv4Stack=true ProtocolFamilies + */ + + +public class ProtocolFamilies { + static final boolean hasIPv6 = hasIPv6(); + static final boolean preferIPv4 = preferIPv4Stack(); + static Inet4Address ia4; + static Inet6Address ia6; + + @BeforeTest() + public void setup() throws Exception { + NetworkConfiguration.printSystemConfiguration(out); + IPSupport.printPlatformSupport(out); + throwSkippedExceptionIfNonOperational(); + + ia4 = getLocalIPv4Address(); + ia6 = getLocalIPv6Address(); + out.println("ia4: " + ia4); + out.println("ia6: " + ia6 + "\n"); + } + + static final Class<UnsupportedAddressTypeException> UATE = UnsupportedAddressTypeException.class; + static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class; + + @DataProvider(name = "open") + public Object[][] open() { + if (hasIPv6 && !preferIPv4) { + return new Object[][]{ + { INET, null }, + { INET6, null } + }; + } else { + return new Object[][]{ + { INET, null }, + { INET6, UOE } + }; + } + } + + @Test(dataProvider = "open") + public void scOpen(StandardProtocolFamily family, + Class<? extends Exception> expectedException) + throws Throwable + { + SocketChannel sc = null; + try { + if (expectedException == UOE) { + try { + sc = openSC(family); + } catch (UnsupportedOperationException e) {} + } else { + sc = openSC(family); + } + } finally { + if (sc != null) + sc.close(); + } + } + + @Test(dataProvider = "open") + public void sscOpen(StandardProtocolFamily family, + Class<? extends Exception> expectedException) + throws Throwable + { + ServerSocketChannel ssc = null; + try { + if (expectedException == UOE) { + try { + ssc = openSSC(family); + } catch (UnsupportedOperationException e) {} + } else { + openSSC(family); + } + } finally { + if (ssc != null) + ssc.close(); + } + } + + @Test(dataProvider = "open") + public void dcOpen(StandardProtocolFamily family, + Class<? extends Exception> expectedException) + throws Throwable + { + DatagramChannel dc = null; + try { + if (expectedException == UOE) { + try { + dc = openDC(family); + } catch (UnsupportedOperationException e) {} + } else { + openDC(family); + } + } finally { + if (dc != null) + dc.close(); + } + } + + @DataProvider(name = "openBind") + public Object[][] openBind() { + if (hasIPv6 && !preferIPv4) { + return new Object[][]{ + { INET, INET, null }, + { INET, INET6, UATE }, + { INET, null, null }, + { INET6, INET, null }, + { INET6, INET6, null }, + { INET6, null, null }, + { null, INET, null }, + { null, INET6, null }, + { null, null, null } + }; + } else { + return new Object[][]{ + { INET, INET, null }, + { INET, INET6, UATE }, + { INET, null, null }, + { null, INET, null }, + { null, INET6, UATE }, + { null, null, null } + }; + } + } + + // SocketChannel open - INET, INET6, default + // SocketChannel bind - INET, INET6, null + + @Test(dataProvider = "openBind") + public void scOpenBind(StandardProtocolFamily ofamily, + StandardProtocolFamily bfamily, + Class<? extends Exception> expectedException) + throws Throwable + { + try (SocketChannel sc = openSC(ofamily)) { + SocketAddress addr = getSocketAddress(bfamily); + ThrowingRunnable bindOp = () -> sc.bind(addr); + if (expectedException == null) + bindOp.run(); + else + assertThrows(expectedException, bindOp); + } + } + + // ServerSocketChannel open - INET, INET6, default + // ServerSocketChannel bind - INET, INET6, null + + @Test(dataProvider = "openBind") + public void sscOpenBind(StandardProtocolFamily ofamily, + StandardProtocolFamily bfamily, + Class<? extends Exception> expectedException) + throws Throwable + { + try (ServerSocketChannel ssc = openSSC(ofamily)) { + SocketAddress addr = getSocketAddress(bfamily); + ThrowingRunnable bindOp = () -> ssc.bind(addr); + if (expectedException == null) + bindOp.run(); + else + assertThrows(expectedException, bindOp); + } + } + + // DatagramChannel open - INET, INET6, default + // DatagramChannel bind - INET, INET6, null + + @Test(dataProvider = "openBind") + public void dcOpenBind(StandardProtocolFamily ofamily, + StandardProtocolFamily bfamily, + Class<? extends Exception> expectedException) + throws Throwable + { + try (DatagramChannel dc = openDC(ofamily)) { + SocketAddress addr = getSocketAddress(bfamily); + ThrowingRunnable bindOp = () -> dc.bind(addr); + if (expectedException == null) + bindOp.run(); + else + assertThrows(expectedException, bindOp); + } + } + + // SocketChannel open - INET, INET6, default + // SocketChannel connect - INET, INET6, default + + @DataProvider(name = "openConnect") + public Object[][] openConnect() { + if (hasIPv6 && !preferIPv4) { + return new Object[][]{ + { INET, INET, null }, + { INET, INET6, null }, + { INET, null, null }, + { INET6, INET, UATE }, + { INET6, INET6, null }, + { INET6, null, null }, + { null, INET, UATE }, + { null, INET6, null }, + { null, null, null } + }; + } else { + // INET6 channels cannot be created - UOE - tested elsewhere + return new Object[][]{ + { INET, INET, null }, + { INET, null, null }, + { null, INET, null }, + { null, null, null } + }; + } + } + + @Test(dataProvider = "openConnect") + public void scOpenConnect(StandardProtocolFamily sfamily, + StandardProtocolFamily cfamily, + Class<? extends Exception> expectedException) + throws Throwable + { + try (ServerSocketChannel ssc = openSSC(sfamily)) { + ssc.bind(null); + SocketAddress saddr = ssc.getLocalAddress(); + try (SocketChannel sc = openSC(cfamily)) { + if (expectedException == null) + sc.connect(saddr); + else + assertThrows(expectedException, () -> sc.connect(saddr)); + } + } + } + + static final Class<NullPointerException> NPE = NullPointerException.class; + + // Tests null handling + @Test + public void testNulls() { + assertThrows(NPE, () -> SocketChannel.open((ProtocolFamily)null)); + assertThrows(NPE, () -> ServerSocketChannel.open(null)); + assertThrows(NPE, () -> DatagramChannel.open(null)); + + assertThrows(NPE, () -> SelectorProvider.provider().openSocketChannel(null)); + assertThrows(NPE, () -> SelectorProvider.provider().openServerSocketChannel(null)); + assertThrows(NPE, () -> SelectorProvider.provider().openDatagramChannel(null)); + } + + static final ProtocolFamily BAD_PF = () -> "BAD_PROTOCOL_FAMILY"; + + // Tests UOE handling + @Test + public void testUoe() { + assertThrows(UOE, () -> SocketChannel.open(BAD_PF)); + assertThrows(UOE, () -> ServerSocketChannel.open(BAD_PF)); + assertThrows(UOE, () -> DatagramChannel.open(BAD_PF)); + + assertThrows(UOE, () -> SelectorProvider.provider().openSocketChannel(BAD_PF)); + assertThrows(UOE, () -> SelectorProvider.provider().openServerSocketChannel(BAD_PF)); + assertThrows(UOE, () -> SelectorProvider.provider().openDatagramChannel(BAD_PF)); + } + + // A concrete subclass of SelectorProvider, in order to test implSpec + static final SelectorProvider customerSelectorProvider = new SelectorProvider() { + @Override public DatagramChannel openDatagramChannel() { return null; } + @Override public DatagramChannel openDatagramChannel(ProtocolFamily family) { return null; } + @Override public Pipe openPipe() { return null; } + @Override public AbstractSelector openSelector() { return null; } + @Override public ServerSocketChannel openServerSocketChannel() { return null; } + @Override public SocketChannel openSocketChannel() { return null; } + }; + + // Tests the specified default implementation of SelectorProvider + @Test + public void testCustomProvider() { + assertThrows(NPE, () -> customerSelectorProvider.openSocketChannel(null)); + assertThrows(NPE, () -> customerSelectorProvider.openServerSocketChannel(null)); + + assertThrows(UOE, () -> customerSelectorProvider.openSocketChannel(BAD_PF)); + assertThrows(UOE, () -> customerSelectorProvider.openServerSocketChannel(BAD_PF)); + } + + // Helper methods + + private static SocketChannel openSC(StandardProtocolFamily family) + throws IOException { + return family == null ? SocketChannel.open() + : SocketChannel.open(family); + } + + private static ServerSocketChannel openSSC(StandardProtocolFamily family) + throws IOException { + return family == null ? ServerSocketChannel.open() + : ServerSocketChannel.open(family); + } + + private static DatagramChannel openDC(StandardProtocolFamily family) + throws IOException { + return family == null ? DatagramChannel.open() + : DatagramChannel.open(family); + } + + private static SocketAddress getSocketAddress(StandardProtocolFamily family) { + return family == null ? null : switch (family) { + case INET -> new InetSocketAddress(ia4, 0); + case INET6 -> new InetSocketAddress(ia6, 0); + }; + } + + private static SocketAddress getLoopback(StandardProtocolFamily family, int port) + throws UnknownHostException { + if ((family == null || family == INET6) && hasIPv6) { + return new InetSocketAddress(InetAddress.getByName("::1"), port); + } else { + return new InetSocketAddress(InetAddress.getByName("127.0.0.1"), port); + } + } + + private static Inet4Address getLocalIPv4Address() + throws Exception { + return NetworkConfiguration.probe() + .ip4Addresses() + .filter(a -> !a.isLoopbackAddress()) + .findFirst() + .orElse((Inet4Address)InetAddress.getByName("0.0.0.0")); + } + + private static Inet6Address getLocalIPv6Address() + throws Exception { + return NetworkConfiguration.probe() + .ip6Addresses() + .filter(a -> !a.isLoopbackAddress()) + .findFirst() + .orElse((Inet6Address) InetAddress.getByName("::0")); + } +}