8241305: Add protocol specific factory creation methods to SocketChannel and ServerSocketChannel

Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
Michael McMahon 2020-05-17 21:15:33 +01:00
parent 42bad03de8
commit 0f7aeed416
12 changed files with 1044 additions and 33 deletions

View File

@ -33,6 +33,7 @@ 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 datagram-oriented sockets.
@ -184,7 +185,7 @@ public abstract class DatagramChannel
* @since 1.7
*/
public static DatagramChannel open(ProtocolFamily family) throws IOException {
return SelectorProvider.provider().openDatagramChannel(family);
return SelectorProvider.provider().openDatagramChannel(requireNonNull(family));
}
/**

View File

@ -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.
@ -113,6 +115,34 @@ public abstract class ServerSocketChannel
return SelectorProvider.provider().openServerSocketChannel();
}
/**
* 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.

View File

@ -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.
@ -150,6 +152,34 @@ public abstract class SocketChannel
return SelectorProvider.provider().openSocketChannel();
}
/**
* 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.
*

View File

@ -36,6 +36,7 @@ import java.nio.channels.SocketChannel;
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 @@ public abstract class SelectorProvider {
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");
}
}

View File

@ -249,6 +249,57 @@ public class Net {
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 @@ public class Net {
}
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

View File

@ -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 abstract class SelectorProviderImpl
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);
}
}

View File

@ -28,10 +28,12 @@ package sun.nio.ch;
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 @@ class ServerSocketChannelImpl
// 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 @@ class ServerSocketChannelImpl
// -- End of fields protected by stateLock
ServerSocketChannelImpl(SelectorProvider sp) {
this(sp, Net.isIPv6Available()
? StandardProtocolFamily.INET6
: StandardProtocolFamily.INET);
}
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 @@ class ServerSocketChannelImpl
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 @@ class ServerSocketChannelImpl
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 @@ class ServerSocketChannelImpl
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;

View File

@ -28,6 +28,7 @@ package sun.nio.ch;
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 @@ class SocketChannelImpl
// 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 @@ class SocketChannelImpl
// -- 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 @@ class SocketChannelImpl
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 @@ class SocketChannelImpl
// 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 @@ class SocketChannelImpl
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 @@ class SocketChannelImpl
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 @@ class SocketChannelImpl
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 @@ class SocketChannelImpl
/**
* 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 @@ class SocketChannelImpl
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) {

View File

@ -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.lang.reflect.Constructor;
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 @@ class InheritedChannel {
*/
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 {

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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"));
}
}