--- a/src/java.base/share/classes/java/net/SocketImpl.java Wed May 29 22:17:48 2019 -0400
+++ b/src/java.base/share/classes/java/net/SocketImpl.java Thu May 30 07:19:19 2019 +0100
@@ -25,33 +25,63 @@
package java.net;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.FileDescriptor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.Set;
+
+import sun.net.NetProperties;
import sun.net.PlatformSocketImpl;
+import sun.nio.ch.NioSocketImpl;
/**
* The abstract class {@code SocketImpl} is a common superclass
* of all classes that actually implement sockets. It is used to
* create both client and server sockets.
- * <p>
- * A "plain" socket implements these methods exactly as
- * described, without attempting to go through a firewall or proxy.
+ *
+ * @implNote Client and server sockets created with the {@code Socket} and
+ * {@code SocketServer} public constructors create a system-default
+ * {@code SocketImpl}. The JDK historically used a {@code SocketImpl}
+ * implementation type named "PlainSocketImpl" that has since been replaced by a
+ * newer implementation. The JDK continues to ship with the older implementation
+ * to allow code to run that depends on unspecified behavior that differs between
+ * the old and new implementations. The old implementation will be used if the
+ * Java virtual machine is started with the system property {@systemProperty
+ * jdk.net.usePlainSocketImpl} set to use the old implementation. It may also be
+ * set in the JDK's network configuration file, located in {@code
+ * ${java.home}/conf/net.properties}. The value of the property is the string
+ * representation of a boolean. If set without a value then it defaults to {@code
+ * true}, hence running with {@code -Djdk.net.usePlainSocketImpl} or {@code
+ * -Djdk.net.usePlainSocketImpl=true} will configure the Java virtual machine
+ * to use the old implementation. The property and old implementation will be
+ * removed in a future version.
*
* @author unascribed
* @since 1.0
*/
public abstract class SocketImpl implements SocketOptions {
+ private static final boolean USE_PLAINSOCKETIMPL = usePlainSocketImpl();
+
+ private static boolean usePlainSocketImpl() {
+ PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainSocketImpl");
+ String s = AccessController.doPrivileged(pa);
+ return (s != null) && !s.equalsIgnoreCase("false");
+ }
/**
* Creates an instance of platform's SocketImpl
*/
@SuppressWarnings("unchecked")
static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
- return (S) new PlainSocketImpl(server);
+ if (USE_PLAINSOCKETIMPL) {
+ return (S) new PlainSocketImpl(server);
+ } else {
+ return (S) new NioSocketImpl(server);
+ }
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Thu May 30 07:19:19 2019 +0100
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2019, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ProtocolFamily;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketImpl;
+import java.net.SocketOption;
+import java.net.SocketTimeoutException;
+import java.net.StandardProtocolFamily;
+import java.net.StandardSocketOptions;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jdk.internal.ref.CleanerFactory;
+import sun.net.ConnectionResetException;
+import sun.net.NetHooks;
+import sun.net.PlatformSocketImpl;
+import sun.net.ResourceManager;
+import sun.net.ext.ExtendedSocketOptions;
+import sun.net.util.SocketExceptions;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+/**
+ * NIO based SocketImpl.
+ *
+ * This implementation attempts to be compatible with legacy PlainSocketImpl,
+ * including behavior and exceptions that are not specified by SocketImpl.
+ *
+ * The underlying socket used by this SocketImpl is initially configured
+ * blocking. If the connect method is used to establish a connection with a
+ * timeout then the socket is configured non-blocking for the connect attempt,
+ * and then restored to blocking mode when the connection is established.
+ * If the accept or read methods are used with a timeout then the socket is
+ * configured non-blocking and is never restored. When in non-blocking mode,
+ * operations that don't complete immediately will poll the socket and preserve
+ * the semantics of blocking operations.
+ */
+
+public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl {
+ private static final NativeDispatcher nd = new SocketDispatcher();
+
+ // The maximum number of bytes to read/write per syscall to avoid needing
+ // a huge buffer from the temporary buffer cache
+ private static final int MAX_BUFFER_SIZE = 128 * 1024;
+
+ // true if this is a SocketImpl for a ServerSocket
+ private final boolean server;
+
+ // Lock held when reading (also used when accepting or connecting)
+ private final ReentrantLock readLock = new ReentrantLock();
+
+ // Lock held when writing
+ private final ReentrantLock writeLock = new ReentrantLock();
+
+ // The stateLock for read/changing state
+ private final Object stateLock = new Object();
+ private static final int ST_NEW = 0;
+ private static final int ST_UNCONNECTED = 1;
+ private static final int ST_CONNECTING = 2;
+ private static final int ST_CONNECTED = 3;
+ private static final int ST_CLOSING = 4;
+ private static final int ST_CLOSED = 5;
+ private volatile int state; // need stateLock to change
+
+ // set by SocketImpl.create, protected by stateLock
+ private boolean stream;
+ private FileDescriptorCloser closer;
+
+ // set to true when the socket is in non-blocking mode
+ private volatile boolean nonBlocking;
+
+ // used by connect/read/write/accept, protected by stateLock
+ private long readerThread;
+ private long writerThread;
+
+ // used when SO_REUSEADDR is emulated, protected by stateLock
+ private boolean isReuseAddress;
+
+ // read or accept timeout in millis
+ private volatile int timeout;
+
+ // flags to indicate if the connection is shutdown for input and output
+ private volatile boolean isInputClosed;
+ private volatile boolean isOutputClosed;
+
+ // used by read to emulate legacy behavior, protected by readLock
+ private boolean readEOF;
+ private boolean connectionReset;
+
+ /**
+ * Creates an instance of this SocketImpl.
+ * @param server true if this is a SocketImpl for a ServerSocket
+ */
+ public NioSocketImpl(boolean server) {
+ this.server = server;
+ }
+
+ /**
+ * Returns true if the socket is open.
+ */
+ private boolean isOpen() {
+ return state < ST_CLOSING;
+ }
+
+ /**
+ * Throws SocketException if the socket is not open.
+ */
+ private void ensureOpen() throws SocketException {
+ int state = this.state;
+ if (state == ST_NEW)
+ throw new SocketException("Socket not created");
+ if (state >= ST_CLOSING)
+ throw new SocketException("Socket closed");
+ }
+
+ /**
+ * Throws SocketException if the socket is not open and connected.
+ */
+ private void ensureOpenAndConnected() throws SocketException {
+ int state = this.state;
+ if (state < ST_CONNECTED)
+ throw new SocketException("Not connected");
+ if (state > ST_CONNECTED)
+ throw new SocketException("Socket closed");
+ }
+
+ /**
+ * Disables the current thread for scheduling purposes until the socket is
+ * ready for I/O, or is asynchronously closed, for up to the specified
+ * waiting time.
+ * @throws IOException if an I/O error occurs
+ */
+ private void park(FileDescriptor fd, int event, long nanos) throws IOException {
+ long millis;
+ if (nanos == 0) {
+ millis = -1;
+ } else {
+ millis = NANOSECONDS.toMillis(nanos);
+ }
+ Net.poll(fd, event, millis);
+ }
+
+ /**
+ * Disables the current thread for scheduling purposes until the socket is
+ * ready for I/O or is asynchronously closed.
+ * @throws IOException if an I/O error occurs
+ */
+ private void park(FileDescriptor fd, int event) throws IOException {
+ park(fd, event, 0);
+ }
+
+ /**
+ * Configures the socket to blocking mode. This method is a no-op if the
+ * socket is already in blocking mode.
+ * @throws IOException if closed or there is an I/O error changing the mode
+ */
+ private void configureBlocking(FileDescriptor fd) throws IOException {
+ assert readLock.isHeldByCurrentThread();
+ if (nonBlocking) {
+ synchronized (stateLock) {
+ ensureOpen();
+ IOUtil.configureBlocking(fd, true);
+ nonBlocking = false;
+ }
+ }
+ }
+
+ /**
+ * Configures the socket to non-blocking mode. This method is a no-op if the
+ * socket is already in non-blocking mode.
+ * @throws IOException if closed or there is an I/O error changing the mode
+ */
+ private void configureNonBlocking(FileDescriptor fd) throws IOException {
+ assert readLock.isHeldByCurrentThread();
+ if (!nonBlocking) {
+ synchronized (stateLock) {
+ ensureOpen();
+ IOUtil.configureBlocking(fd, false);
+ nonBlocking = true;
+ }
+ }
+ }
+
+ /**
+ * Marks the beginning of a read operation that might block.
+ * @throws SocketException if the socket is closed or not connected
+ */
+ private FileDescriptor beginRead() throws SocketException {
+ synchronized (stateLock) {
+ ensureOpenAndConnected();
+ readerThread = NativeThread.current();
+ return fd;
+ }
+ }
+
+ /**
+ * Marks the end of a read operation that may have blocked.
+ * @throws SocketException is the socket is closed
+ */
+ private void endRead(boolean completed) throws SocketException {
+ synchronized (stateLock) {
+ readerThread = 0;
+ int state = this.state;
+ if (state == ST_CLOSING)
+ tryFinishClose();
+ if (!completed && state >= ST_CLOSING)
+ throw new SocketException("Socket closed");
+ }
+ }
+
+ /**
+ * Attempts to read bytes from the socket into the given byte array.
+ */
+ private int tryRead(FileDescriptor fd, byte[] b, int off, int len)
+ throws IOException
+ {
+ ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
+ assert dst.position() == 0;
+ try {
+ int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
+ if (n > 0) {
+ dst.get(b, off, n);
+ }
+ return n;
+ } finally {
+ Util.offerFirstTemporaryDirectBuffer(dst);
+ }
+ }
+
+ /**
+ * Reads bytes from the socket into the given byte array with a timeout.
+ * @throws SocketTimeoutException if the read timeout elapses
+ */
+ private int timedRead(FileDescriptor fd, byte[] b, int off, int len, long nanos)
+ throws IOException
+ {
+ long startNanos = System.nanoTime();
+ int n = tryRead(fd, b, off, len);
+ while (n == IOStatus.UNAVAILABLE && isOpen()) {
+ long remainingNanos = nanos - (System.nanoTime() - startNanos);
+ if (remainingNanos <= 0) {
+ throw new SocketTimeoutException("Read timed out");
+ }
+ park(fd, Net.POLLIN, remainingNanos);
+ n = tryRead(fd, b, off, len);
+ }
+ return n;
+ }
+
+ /**
+ * Reads bytes from the socket into the given byte array.
+ * @return the number of bytes read or -1 at EOF
+ * @throws SocketException if the socket is closed or a socket I/O error occurs
+ * @throws SocketTimeoutException if the read timeout elapses
+ */
+ private int implRead(byte[] b, int off, int len) throws IOException {
+ int n = 0;
+ FileDescriptor fd = beginRead();
+ try {
+ if (connectionReset)
+ throw new SocketException("Connection reset");
+ if (isInputClosed)
+ return -1;
+ int timeout = this.timeout;
+ if (timeout > 0) {
+ // read with timeout
+ configureNonBlocking(fd);
+ n = timedRead(fd, b, off, len, MILLISECONDS.toNanos(timeout));
+ } else {
+ // read, no timeout
+ n = tryRead(fd, b, off, len);
+ while (IOStatus.okayToRetry(n) && isOpen()) {
+ park(fd, Net.POLLIN);
+ n = tryRead(fd, b, off, len);
+ }
+ }
+ return n;
+ } catch (SocketTimeoutException e) {
+ throw e;
+ } catch (ConnectionResetException e) {
+ connectionReset = true;
+ throw new SocketException("Connection reset");
+ } catch (IOException ioe) {
+ throw new SocketException(ioe.getMessage());
+ } finally {
+ endRead(n > 0);
+ }
+ }
+
+ /**
+ * Reads bytes from the socket into the given byte array.
+ * @return the number of bytes read or -1 at EOF
+ * @throws IndexOutOfBoundsException if the bound checks fail
+ * @throws SocketException if the socket is closed or a socket I/O error occurs
+ * @throws SocketTimeoutException if the read timeout elapses
+ */
+ private int read(byte[] b, int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ if (len == 0) {
+ return 0;
+ } else {
+ readLock.lock();
+ try {
+ // emulate legacy behavior to return -1, even if socket is closed
+ if (readEOF)
+ return -1;
+ // read up to MAX_BUFFER_SIZE bytes
+ int size = Math.min(len, MAX_BUFFER_SIZE);
+ int n = implRead(b, off, size);
+ if (n == -1)
+ readEOF = true;
+ return n;
+ } finally {
+ readLock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Marks the beginning of a write operation that might block.
+ * @throws SocketException if the socket is closed or not connected
+ */
+ private FileDescriptor beginWrite() throws SocketException {
+ synchronized (stateLock) {
+ ensureOpenAndConnected();
+ writerThread = NativeThread.current();
+ return fd;
+ }
+ }
+
+ /**
+ * Marks the end of a write operation that may have blocked.
+ * @throws SocketException is the socket is closed
+ */
+ private void endWrite(boolean completed) throws SocketException {
+ synchronized (stateLock) {
+ writerThread = 0;
+ int state = this.state;
+ if (state == ST_CLOSING)
+ tryFinishClose();
+ if (!completed && state >= ST_CLOSING)
+ throw new SocketException("Socket closed");
+ }
+ }
+
+ /**
+ * Attempts to write a sequence of bytes to the socket from the given
+ * byte array.
+ */
+ private int tryWrite(FileDescriptor fd, byte[] b, int off, int len)
+ throws IOException
+ {
+ ByteBuffer src = Util.getTemporaryDirectBuffer(len);
+ assert src.position() == 0;
+ try {
+ src.put(b, off, len);
+ return nd.write(fd, ((DirectBuffer)src).address(), len);
+ } finally {
+ Util.offerFirstTemporaryDirectBuffer(src);
+ }
+ }
+
+ /**
+ * Writes a sequence of bytes to the socket from the given byte array.
+ * @return the number of bytes written
+ * @throws SocketException if the socket is closed or a socket I/O error occurs
+ */
+ private int implWrite(byte[] b, int off, int len) throws IOException {
+ int n = 0;
+ FileDescriptor fd = beginWrite();
+ try {
+ n = tryWrite(fd, b, off, len);
+ while (IOStatus.okayToRetry(n) && isOpen()) {
+ park(fd, Net.POLLOUT);
+ n = tryWrite(fd, b, off, len);
+ }
+ return n;
+ } catch (IOException ioe) {
+ throw new SocketException(ioe.getMessage());
+ } finally {
+ endWrite(n > 0);
+ }
+ }
+
+ /**
+ * Writes a sequence of bytes to the socket from the given byte array.
+ * @throws SocketException if the socket is closed or a socket I/O error occurs
+ */
+ private void write(byte[] b, int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ if (len > 0) {
+ writeLock.lock();
+ try {
+ int pos = off;
+ int end = off + len;
+ while (pos < end) {
+ // write up to MAX_BUFFER_SIZE bytes
+ int size = Math.min((end - pos), MAX_BUFFER_SIZE);
+ int n = implWrite(b, pos, size);
+ pos += n;
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Creates the socket.
+ * @param stream {@code true} for a streams socket
+ */
+ @Override
+ protected void create(boolean stream) throws IOException {
+ synchronized (stateLock) {
+ if (state != ST_NEW)
+ throw new IOException("Already created");
+ if (!stream)
+ ResourceManager.beforeUdpCreate();
+ FileDescriptor fd;
+ try {
+ if (server) {
+ assert stream;
+ fd = Net.serverSocket(true);
+ } else {
+ fd = Net.socket(stream);
+ }
+ } catch (IOException ioe) {
+ if (!stream)
+ ResourceManager.afterUdpClose();
+ throw ioe;
+ }
+ this.fd = fd;
+ this.stream = stream;
+ this.closer = FileDescriptorCloser.create(this);
+ this.state = ST_UNCONNECTED;
+ }
+ }
+
+ /**
+ * Marks the beginning of a connect operation that might block.
+ * @throws SocketException if the socket is closed or already connected
+ */
+ private FileDescriptor beginConnect(InetAddress address, int port)
+ throws IOException
+ {
+ synchronized (stateLock) {
+ int state = this.state;
+ if (state != ST_UNCONNECTED) {
+ if (state == ST_NEW)
+ throw new SocketException("Not created");
+ if (state == ST_CONNECTING)
+ throw new SocketException("Connection in progress");
+ if (state == ST_CONNECTED)
+ throw new SocketException("Already connected");
+ if (state >= ST_CLOSING)
+ throw new SocketException("Socket closed");
+ assert false;
+ }
+ this.state = ST_CONNECTING;
+
+ // invoke beforeTcpConnect hook if not already bound
+ if (localport == 0) {
+ NetHooks.beforeTcpConnect(fd, address, port);
+ }
+
+ // save the remote address/port
+ this.address = address;
+ this.port = port;
+
+ readerThread = NativeThread.current();
+ return fd;
+ }
+ }
+
+ /**
+ * Marks the end of a connect operation that may have blocked.
+ * @throws SocketException is the socket is closed
+ */
+ private void endConnect(FileDescriptor fd, boolean completed) throws IOException {
+ synchronized (stateLock) {
+ readerThread = 0;
+ int state = this.state;
+ if (state == ST_CLOSING)
+ tryFinishClose();
+ if (completed && state == ST_CONNECTING) {
+ this.state = ST_CONNECTED;
+ localport = Net.localAddress(fd).getPort();
+ } else if (!completed && state >= ST_CLOSING) {
+ throw new SocketException("Socket closed");
+ }
+ }
+ }
+
+ /**
+ * Waits for a connection attempt to finish with a timeout
+ * @throws SocketTimeoutException if the connect timeout elapses
+ */
+ private boolean timedFinishConnect(FileDescriptor fd, long nanos) throws IOException {
+ long startNanos = System.nanoTime();
+ boolean polled = Net.pollConnectNow(fd);
+ while (!polled && isOpen()) {
+ long remainingNanos = nanos - (System.nanoTime() - startNanos);
+ if (remainingNanos <= 0) {
+ throw new SocketTimeoutException("Connect timed out");
+ }
+ park(fd, Net.POLLOUT, remainingNanos);
+ polled = Net.pollConnectNow(fd);
+ }
+ return polled && isOpen();
+ }
+
+ /**
+ * Attempts to establish a connection to the given socket address with a
+ * timeout. Closes the socket if connection cannot be established.
+ * @throws IOException if the address is not a resolved InetSocketAddress or
+ * the connection cannot be established
+ */
+ @Override
+ protected void connect(SocketAddress remote, int millis) throws IOException {
+ // SocketImpl connect only specifies IOException
+ if (!(remote instanceof InetSocketAddress))
+ throw new IOException("Unsupported address type");
+ InetSocketAddress isa = (InetSocketAddress) remote;
+ if (isa.isUnresolved()) {
+ throw new UnknownHostException(isa.getHostName());
+ }
+
+ InetAddress address = isa.getAddress();
+ if (address.isAnyLocalAddress())
+ address = InetAddress.getLocalHost();
+ int port = isa.getPort();
+
+ ReentrantLock connectLock = readLock;
+ try {
+ connectLock.lock();
+ try {
+ boolean connected = false;
+ FileDescriptor fd = beginConnect(address, port);
+ try {
+
+ // configure socket to non-blocking mode when there is a timeout
+ if (millis > 0) {
+ configureNonBlocking(fd);
+ }
+
+ int n = Net.connect(fd, address, port);
+ if (n > 0) {
+ // connection established
+ connected = true;
+ } else {
+ assert IOStatus.okayToRetry(n);
+ if (millis > 0) {
+ // finish connect with timeout
+ long nanos = MILLISECONDS.toNanos(millis);
+ connected = timedFinishConnect(fd, nanos);
+ } else {
+ // finish connect, no timeout
+ boolean polled = false;
+ while (!polled && isOpen()) {
+ park(fd, Net.POLLOUT);
+ polled = Net.pollConnectNow(fd);
+ }
+ connected = polled && isOpen();
+ }
+ }
+
+ // restore socket to blocking mode
+ if (connected && millis > 0) {
+ configureBlocking(fd);
+ }
+
+ } finally {
+ endConnect(fd, connected);
+ }
+ } finally {
+ connectLock.unlock();
+ }
+ } catch (IOException ioe) {
+ close();
+ throw SocketExceptions.of(ioe, isa);
+ }
+ }
+
+ @Override
+ protected void connect(String host, int port) throws IOException {
+ connect(new InetSocketAddress(host, port), 0);
+ }
+
+ @Override
+ protected void connect(InetAddress address, int port) throws IOException {
+ connect(new InetSocketAddress(address, port), 0);
+ }
+
+ @Override
+ protected void bind(InetAddress host, int port) throws IOException {
+ synchronized (stateLock) {
+ ensureOpen();
+ if (localport != 0)
+ throw new SocketException("Already bound");
+ NetHooks.beforeTcpBind(fd, host, port);
+ Net.bind(fd, host, port);
+ // set the address field to the given host address to keep
+ // compatibility with PlainSocketImpl. When binding to 0.0.0.0
+ // then the actual local address will be ::0 when IPv6 is enabled.
+ address = host;
+ localport = Net.localAddress(fd).getPort();
+ }
+ }
+
+ @Override
+ protected void listen(int backlog) throws IOException {
+ synchronized (stateLock) {
+ ensureOpen();
+ if (localport == 0)
+ throw new SocketException("Not bound");
+ Net.listen(fd, backlog < 1 ? 50 : backlog);
+ }
+ }
+
+ /**
+ * Marks the beginning of an accept operation that might block.
+ * @throws SocketException if the socket is closed
+ */
+ private FileDescriptor beginAccept() throws SocketException {
+ synchronized (stateLock) {
+ ensureOpen();
+ if (!stream)
+ throw new SocketException("Not a stream socket");
+ if (localport == 0)
+ throw new SocketException("Not bound");
+ readerThread = NativeThread.current();
+ return fd;
+ }
+ }
+
+ /**
+ * Marks the end of an accept operation that may have blocked.
+ * @throws SocketException is the socket is closed
+ */
+ private void endAccept(boolean completed) throws SocketException {
+ synchronized (stateLock) {
+ int state = this.state;
+ readerThread = 0;
+ if (state == ST_CLOSING)
+ tryFinishClose();
+ if (!completed && state >= ST_CLOSING)
+ throw new SocketException("Socket closed");
+ }
+ }
+
+ /**
+ * Accepts a new connection with a timeout.
+ * @throws SocketTimeoutException if the accept timeout elapses
+ */
+ private int timedAccept(FileDescriptor fd,
+ FileDescriptor newfd,
+ InetSocketAddress[] isaa,
+ long nanos)
+ throws IOException
+ {
+ long startNanos = System.nanoTime();
+ int n = Net.accept(fd, newfd, isaa);
+ while (n == IOStatus.UNAVAILABLE && isOpen()) {
+ long remainingNanos = nanos - (System.nanoTime() - startNanos);
+ if (remainingNanos <= 0) {
+ throw new SocketTimeoutException("Accept timed out");
+ }
+ park(fd, Net.POLLIN, remainingNanos);
+ n = Net.accept(fd, newfd, isaa);
+ }
+ return n;
+ }
+
+ /**
+ * Accepts a new connection so that the given SocketImpl is connected to
+ * the peer. The SocketImpl must be a newly created NioSocketImpl.
+ */
+ @Override
+ protected void accept(SocketImpl si) throws IOException {
+ NioSocketImpl nsi = (NioSocketImpl) si;
+ if (nsi.state != ST_NEW)
+ throw new SocketException("Not a newly created SocketImpl");
+
+ FileDescriptor newfd = new FileDescriptor();
+ InetSocketAddress[] isaa = new InetSocketAddress[1];
+
+ // acquire the lock, adjusting the timeout for cases where several
+ // threads are accepting connections and there is a timeout set
+ ReentrantLock acceptLock = readLock;
+ int timeout = this.timeout;
+ long remainingNanos = 0;
+ if (timeout > 0) {
+ remainingNanos = tryLock(acceptLock, timeout, MILLISECONDS);
+ if (remainingNanos <= 0) {
+ assert !acceptLock.isHeldByCurrentThread();
+ throw new SocketTimeoutException("Accept timed out");
+ }
+ } else {
+ acceptLock.lock();
+ }
+
+ // accept a connection
+ try {
+ int n = 0;
+ FileDescriptor fd = beginAccept();
+ try {
+ if (remainingNanos > 0) {
+ // accept with timeout
+ configureNonBlocking(fd);
+ n = timedAccept(fd, newfd, isaa, remainingNanos);
+ } else {
+ // accept, no timeout
+ n = Net.accept(fd, newfd, isaa);
+ while (IOStatus.okayToRetry(n) && isOpen()) {
+ park(fd, Net.POLLIN);
+ n = Net.accept(fd, newfd, isaa);
+ }
+ }
+ } finally {
+ endAccept(n > 0);
+ assert IOStatus.check(n);
+ }
+ } finally {
+ acceptLock.unlock();
+ }
+
+ // get local address and configure accepted socket to blocking mode
+ InetSocketAddress localAddress;
+ try {
+ localAddress = Net.localAddress(newfd);
+ IOUtil.configureBlocking(newfd, true);
+ } catch (IOException ioe) {
+ nd.close(newfd);
+ throw ioe;
+ }
+
+ // set the fields
+ synchronized (nsi.stateLock) {
+ nsi.fd = newfd;
+ nsi.stream = true;
+ nsi.closer = FileDescriptorCloser.create(nsi);
+ nsi.localport = localAddress.getPort();
+ nsi.address = isaa[0].getAddress();
+ nsi.port = isaa[0].getPort();
+ nsi.state = ST_CONNECTED;
+ }
+ }
+
+ @Override
+ protected InputStream getInputStream() {
+ return new InputStream() {
+ @Override
+ public int read() throws IOException {
+ byte[] a = new byte[1];
+ int n = read(a, 0, 1);
+ return (n > 0) ? (a[0] & 0xff) : -1;
+ }
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return NioSocketImpl.this.read(b, off, len);
+ }
+ @Override
+ public int available() throws IOException {
+ return NioSocketImpl.this.available();
+ }
+ @Override
+ public void close() throws IOException {
+ NioSocketImpl.this.close();
+ }
+ };
+ }
+
+ @Override
+ protected OutputStream getOutputStream() {
+ return new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ byte[] a = new byte[]{(byte) b};
+ write(a, 0, 1);
+ }
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ NioSocketImpl.this.write(b, off, len);
+ }
+ @Override
+ public void close() throws IOException {
+ NioSocketImpl.this.close();
+ }
+ };
+ }
+
+ @Override
+ protected int available() throws IOException {
+ synchronized (stateLock) {
+ ensureOpenAndConnected();
+ if (isInputClosed) {
+ return 0;
+ } else {
+ return Net.available(fd);
+ }
+ }
+ }
+
+ /**
+ * Closes the socket if there are no I/O operations in progress.
+ */
+ private boolean tryClose() throws IOException {
+ assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+ if (readerThread == 0 && writerThread == 0) {
+ try {
+ closer.run();
+ } catch (UncheckedIOException ioe) {
+ throw ioe.getCause();
+ } finally {
+ state = ST_CLOSED;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes tryClose to attempt to close the socket.
+ *
+ * This method is used for deferred closing by I/O operations.
+ */
+ private void tryFinishClose() {
+ try {
+ tryClose();
+ } catch (IOException ignore) { }
+ }
+
+ /**
+ * Closes the socket. If there are I/O operations in progress then the
+ * socket is pre-closed and the threads are signalled. The socket will be
+ * closed when the last I/O operation aborts.
+ */
+ @Override
+ protected void close() throws IOException {
+ synchronized (stateLock) {
+ int state = this.state;
+ if (state >= ST_CLOSING)
+ return;
+ if (state == ST_NEW) {
+ // stillborn
+ this.state = ST_CLOSED;
+ return;
+ }
+ this.state = ST_CLOSING;
+
+ // shutdown output when linger interval not set to 0
+ try {
+ var SO_LINGER = StandardSocketOptions.SO_LINGER;
+ if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
+ Net.shutdown(fd, Net.SHUT_WR);
+ }
+ } catch (IOException ignore) { }
+
+ // attempt to close the socket. If there are I/O operations in progress
+ // then the socket is pre-closed and the thread(s) signalled. The
+ // last thread will close the file descriptor.
+ if (!tryClose()) {
+ nd.preClose(fd);
+ long reader = readerThread;
+ if (reader != 0)
+ NativeThread.signal(reader);
+ long writer = writerThread;
+ if (writer != 0)
+ NativeThread.signal(writer);
+ }
+ }
+ }
+
+ // the socket options supported by client and server sockets
+ private static volatile Set<SocketOption<?>> clientSocketOptions;
+ private static volatile Set<SocketOption<?>> serverSocketOptions;
+
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ Set<SocketOption<?>> options = (server) ? serverSocketOptions : clientSocketOptions;
+ if (options == null) {
+ options = new HashSet<>();
+ options.add(StandardSocketOptions.SO_RCVBUF);
+ options.add(StandardSocketOptions.SO_REUSEADDR);
+ if (server) {
+ // IP_TOS added for server socket to maintain compatibility
+ options.add(StandardSocketOptions.IP_TOS);
+ options.addAll(ExtendedSocketOptions.serverSocketOptions());
+ } else {
+ options.add(StandardSocketOptions.IP_TOS);
+ options.add(StandardSocketOptions.SO_KEEPALIVE);
+ options.add(StandardSocketOptions.SO_SNDBUF);
+ options.add(StandardSocketOptions.SO_LINGER);
+ options.add(StandardSocketOptions.TCP_NODELAY);
+ options.addAll(ExtendedSocketOptions.clientSocketOptions());
+ }
+ if (Net.isReusePortAvailable())
+ options.add(StandardSocketOptions.SO_REUSEPORT);
+ options = Collections.unmodifiableSet(options);
+ if (server) {
+ serverSocketOptions = options;
+ } else {
+ clientSocketOptions = options;
+ }
+ }
+ return options;
+ }
+
+ @Override
+ protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+ if (!supportedOptions().contains(opt))
+ throw new UnsupportedOperationException("'" + opt + "' not supported");
+ if (!opt.type().isInstance(value))
+ throw new IllegalArgumentException("Invalid value '" + value + "'");
+ synchronized (stateLock) {
+ ensureOpen();
+ if (opt == StandardSocketOptions.IP_TOS) {
+ // maps to IP_TOS or IPV6_TCLASS
+ Net.setSocketOption(fd, family(), opt, value);
+ } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
+ boolean b = (boolean) value;
+ if (Net.useExclusiveBind()) {
+ isReuseAddress = b;
+ } else {
+ Net.setSocketOption(fd, opt, b);
+ }
+ } else {
+ // option does not need special handling
+ Net.setSocketOption(fd, opt, value);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <T> T getOption(SocketOption<T> opt) throws IOException {
+ if (!supportedOptions().contains(opt))
+ throw new UnsupportedOperationException("'" + opt + "' not supported");
+ synchronized (stateLock) {
+ ensureOpen();
+ if (opt == StandardSocketOptions.IP_TOS) {
+ return (T) Net.getSocketOption(fd, family(), opt);
+ } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
+ if (Net.useExclusiveBind()) {
+ return (T) Boolean.valueOf(isReuseAddress);
+ } else {
+ return (T) Net.getSocketOption(fd, opt);
+ }
+ } else {
+ // option does not need special handling
+ return (T) Net.getSocketOption(fd, opt);
+ }
+ }
+ }
+
+ private boolean booleanValue(Object value, String desc) throws SocketException {
+ if (!(value instanceof Boolean))
+ throw new SocketException("Bad value for " + desc);
+ return (boolean) value;
+ }
+
+ private int intValue(Object value, String desc) throws SocketException {
+ if (!(value instanceof Integer))
+ throw new SocketException("Bad value for " + desc);
+ return (int) value;
+ }
+
+ @Override
+ public void setOption(int opt, Object value) throws SocketException {
+ synchronized (stateLock) {
+ ensureOpen();
+ try {
+ switch (opt) {
+ case SO_LINGER: {
+ // the value is "false" to disable, or linger interval to enable
+ int i;
+ if (value instanceof Boolean && ((boolean) value) == false) {
+ i = -1;
+ } else {
+ i = intValue(value, "SO_LINGER");
+ }
+ Net.setSocketOption(fd, StandardSocketOptions.SO_LINGER, i);
+ break;
+ }
+ case SO_TIMEOUT: {
+ int i = intValue(value, "SO_TIMEOUT");
+ if (i < 0)
+ throw new IllegalArgumentException("timeout < 0");
+ timeout = i;
+ break;
+ }
+ case IP_TOS: {
+ int i = intValue(value, "IP_TOS");
+ Net.setSocketOption(fd, family(), StandardSocketOptions.IP_TOS, i);
+ break;
+ }
+ case TCP_NODELAY: {
+ boolean b = booleanValue(value, "TCP_NODELAY");
+ Net.setSocketOption(fd, StandardSocketOptions.TCP_NODELAY, b);
+ break;
+ }
+ case SO_SNDBUF: {
+ int i = intValue(value, "SO_SNDBUF");
+ if (i <= 0)
+ throw new SocketException("SO_SNDBUF <= 0");
+ Net.setSocketOption(fd, StandardSocketOptions.SO_SNDBUF, i);
+ break;
+ }
+ case SO_RCVBUF: {
+ int i = intValue(value, "SO_RCVBUF");
+ if (i <= 0)
+ throw new SocketException("SO_RCVBUF <= 0");
+ Net.setSocketOption(fd, StandardSocketOptions.SO_RCVBUF, i);
+ break;
+ }
+ case SO_KEEPALIVE: {
+ boolean b = booleanValue(value, "SO_KEEPALIVE");
+ Net.setSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE, b);
+ break;
+ }
+ case SO_OOBINLINE: {
+ boolean b = booleanValue(value, "SO_OOBINLINE");
+ Net.setSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE, b);
+ break;
+ }
+ case SO_REUSEADDR: {
+ boolean b = booleanValue(value, "SO_REUSEADDR");
+ if (Net.useExclusiveBind()) {
+ isReuseAddress = b;
+ } else {
+ Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEADDR, b);
+ }
+ break;
+ }
+ case SO_REUSEPORT: {
+ if (!Net.isReusePortAvailable())
+ throw new SocketException("SO_REUSEPORT not supported");
+ boolean b = booleanValue(value, "SO_REUSEPORT");
+ Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEPORT, b);
+ break;
+ }
+ default:
+ throw new SocketException("Unknown option " + opt);
+ }
+ } catch (SocketException e) {
+ throw e;
+ } catch (IllegalArgumentException | IOException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public Object getOption(int opt) throws SocketException {
+ synchronized (stateLock) {
+ ensureOpen();
+ try {
+ switch (opt) {
+ case SO_TIMEOUT:
+ return timeout;
+ case TCP_NODELAY:
+ return Net.getSocketOption(fd, StandardSocketOptions.TCP_NODELAY);
+ case SO_OOBINLINE:
+ return Net.getSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE);
+ case SO_LINGER: {
+ // return "false" when disabled, linger interval when enabled
+ int i = (int) Net.getSocketOption(fd, StandardSocketOptions.SO_LINGER);
+ if (i == -1) {
+ return Boolean.FALSE;
+ } else {
+ return i;
+ }
+ }
+ case SO_REUSEADDR:
+ if (Net.useExclusiveBind()) {
+ return isReuseAddress;
+ } else {
+ return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEADDR);
+ }
+ case SO_BINDADDR:
+ return Net.localAddress(fd).getAddress();
+ case SO_SNDBUF:
+ return Net.getSocketOption(fd, StandardSocketOptions.SO_SNDBUF);
+ case SO_RCVBUF:
+ return Net.getSocketOption(fd, StandardSocketOptions.SO_RCVBUF);
+ case IP_TOS:
+ return Net.getSocketOption(fd, family(), StandardSocketOptions.IP_TOS);
+ case SO_KEEPALIVE:
+ return Net.getSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE);
+ case SO_REUSEPORT:
+ if (!Net.isReusePortAvailable())
+ throw new SocketException("SO_REUSEPORT not supported");
+ return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEPORT);
+ default:
+ throw new SocketException("Unknown option " + opt);
+ }
+ } catch (SocketException e) {
+ throw e;
+ } catch (IllegalArgumentException | IOException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ protected void shutdownInput() throws IOException {
+ synchronized (stateLock) {
+ ensureOpenAndConnected();
+ if (!isInputClosed) {
+ Net.shutdown(fd, Net.SHUT_RD);
+ isInputClosed = true;
+ }
+ }
+ }
+
+ @Override
+ protected void shutdownOutput() throws IOException {
+ synchronized (stateLock) {
+ ensureOpenAndConnected();
+ if (!isOutputClosed) {
+ Net.shutdown(fd, Net.SHUT_WR);
+ isOutputClosed = true;
+ }
+ }
+ }
+
+ @Override
+ protected boolean supportsUrgentData() {
+ return true;
+ }
+
+ @Override
+ protected void sendUrgentData(int data) throws IOException {
+ writeLock.lock();
+ try {
+ int n = 0;
+ FileDescriptor fd = beginWrite();
+ try {
+ do {
+ n = Net.sendOOB(fd, (byte) data);
+ } while (n == IOStatus.INTERRUPTED && isOpen());
+ if (n == IOStatus.UNAVAILABLE) {
+ throw new SocketException("No buffer space available");
+ }
+ } finally {
+ endWrite(n > 0);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * A task that closes a SocketImpl's file descriptor. The task runs when the
+ * SocketImpl is explicitly closed and when the SocketImpl becomes phantom
+ * reachable.
+ */
+ private static class FileDescriptorCloser implements Runnable {
+ private static final VarHandle CLOSED;
+ static {
+ try {
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ CLOSED = l.findVarHandle(FileDescriptorCloser.class,
+ "closed",
+ boolean.class);
+ } catch (Exception e) {
+ throw new InternalError(e);
+ }
+ }
+
+ private final FileDescriptor fd;
+ private final boolean stream;
+ private volatile boolean closed;
+
+ FileDescriptorCloser(FileDescriptor fd, boolean stream) {
+ this.fd = fd;
+ this.stream = stream;
+ }
+
+ static FileDescriptorCloser create(NioSocketImpl impl) {
+ assert Thread.holdsLock(impl.stateLock);
+ var closer = new FileDescriptorCloser(impl.fd, impl.stream);
+ CleanerFactory.cleaner().register(impl, closer);
+ return closer;
+ }
+
+ @Override
+ public void run() {
+ if (CLOSED.compareAndSet(this, false, true)) {
+ try {
+ nd.close(fd);
+ } catch (IOException ioe) {
+ throw new UncheckedIOException(ioe);
+ } finally {
+ if (!stream) {
+ // decrement
+ ResourceManager.afterUdpClose();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Attempts to acquire the given lock within the given waiting time.
+ * @return the remaining time in nanoseconds when the lock is acquired, zero
+ * or less if the lock was not acquired before the timeout expired
+ */
+ private static long tryLock(ReentrantLock lock, long timeout, TimeUnit unit) {
+ assert timeout > 0;
+ boolean interrupted = false;
+ long nanos = NANOSECONDS.convert(timeout, unit);
+ long remainingNanos = nanos;
+ long startNanos = System.nanoTime();
+ boolean acquired = false;
+ while (!acquired && (remainingNanos > 0)) {
+ try {
+ acquired = lock.tryLock(remainingNanos, NANOSECONDS);
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ remainingNanos = nanos - (System.nanoTime() - startNanos);
+ }
+ if (acquired && remainingNanos <= 0L)
+ lock.unlock(); // release lock if timeout has expired
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ return remainingNanos;
+ }
+
+ /**
+ * Returns the socket protocol family.
+ */
+ private static ProtocolFamily family() {
+ if (Net.isIPv6Available()) {
+ return StandardProtocolFamily.INET6;
+ } else {
+ return StandardProtocolFamily.INET;
+ }
+ }
+}
--- a/test/jdk/ProblemList.txt Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/ProblemList.txt Thu May 30 07:19:19 2019 +0100
@@ -600,6 +600,9 @@
java/net/ServerSocket/AcceptInheritHandle.java 8211854 aix-ppc64
+java/net/Inet6Address/B6206527.java 8216417 macosx-all
+java/net/ipv6tests/B6521014.java 8216417 macosx-all
+
############################################################################
# jdk_nio
@@ -862,6 +865,7 @@
jdk/jfr/event/io/TestInstrumentation.java 8202142 generic-all
jdk/jfr/api/recording/event/TestPeriod.java 8215890 generic-all
+jdk/jfr/event/io/EvilInstrument.java 8221331 generic-all
############################################################################
--- a/test/jdk/com/sun/net/httpserver/Test1.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/com/sun/net/httpserver/Test1.java Thu May 30 07:19:19 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -27,6 +27,7 @@
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext
* @run main/othervm Test1
+ * @run main/othervm -Djdk.net.usePlainSocketImpl Test1
* @run main/othervm -Dsun.net.httpserver.maxReqTime=10 Test1
* @run main/othervm -Dsun.net.httpserver.nodelay=true Test1
* @summary Light weight HTTP server
--- a/test/jdk/java/net/ServerSocket/AcceptCauseFileDescriptorLeak.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/ServerSocket/AcceptCauseFileDescriptorLeak.java Thu May 30 07:19:19 2019 +0100
@@ -38,6 +38,7 @@
* jdk.test.lib.process.*
* AcceptCauseFileDescriptorLeak
* @run main/othervm AcceptCauseFileDescriptorLeak root
+ * @run main/othervm -Djdk.net.usePlainSocketImpl AcceptCauseFileDescriptorLeak root
*/
import java.io.IOException;
--- a/test/jdk/java/net/ServerSocket/UnreferencedSockets.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/ServerSocket/UnreferencedSockets.java Thu May 30 07:19:19 2019 +0100
@@ -27,6 +27,7 @@
* @modules java.management java.base/java.io:+open java.base/java.net:+open
* @run main/othervm UnreferencedSockets
* @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedSockets
+ * @run main/othervm -Djdk.net.usePlainSocketImpl UnreferencedSockets
* @summary Check that unreferenced sockets are closed
*/
--- a/test/jdk/java/net/Socket/ConnectionReset.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/Socket/ConnectionReset.java Thu May 30 07:19:19 2019 +0100
@@ -25,6 +25,7 @@
* @test
* @requires os.family != "solaris"
* @run testng ConnectionReset
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl ConnectionReset
* @summary Test behavior of read and available when a connection is reset
*/
--- a/test/jdk/java/net/Socket/Timeouts.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/Socket/Timeouts.java Thu May 30 07:19:19 2019 +0100
@@ -23,9 +23,10 @@
/*
* @test
+ * @bug 8221481
* @library /test/lib
* @build jdk.test.lib.Utils
- * @run testng Timeouts
+ * @run testng/timeout=180 Timeouts
* @summary Test Socket timeouts
*/
@@ -34,12 +35,17 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -54,7 +60,7 @@
* Test timed connect where connection is established
*/
public void testTimedConnect1() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
try (Socket s = new Socket()) {
s.connect(ss.getLocalSocketAddress(), 2000);
}
@@ -77,7 +83,7 @@
* Test connect with a timeout of Integer.MAX_VALUE
*/
public void testTimedConnect3() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
try (Socket s = new Socket()) {
s.connect(ss.getLocalSocketAddress(), Integer.MAX_VALUE);
}
@@ -88,12 +94,10 @@
* Test connect with a negative timeout.
*/
public void testTimedConnect4() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
try (Socket s = new Socket()) {
- try {
- s.connect(ss.getLocalSocketAddress(), -1);
- assertTrue(false);
- } catch (IllegalArgumentException expected) { }
+ expectThrows(IllegalArgumentException.class,
+ () -> s.connect(ss.getLocalSocketAddress(), -1));
}
}
}
@@ -128,10 +132,10 @@
public void testTimedRead3() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException expected) { }
+ long startMillis = millisTime();
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
+ int timeout = s2.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
});
}
@@ -141,10 +145,7 @@
public void testTimedRead4() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
s1.getOutputStream().write(99);
int b = s2.getInputStream().read();
assertTrue(b == 99);
@@ -158,10 +159,7 @@
public void testTimedRead5() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
s2.setSoTimeout(30*3000);
scheduleWrite(s1.getOutputStream(), 99, 2000);
int b = s2.getInputStream().read();
@@ -175,10 +173,7 @@
public void testTimedRead6() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
s1.getOutputStream().write(99);
s2.setSoTimeout(0);
int b = s2.getInputStream().read();
@@ -193,10 +188,7 @@
public void testTimedRead7() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
scheduleWrite(s1.getOutputStream(), 99, 2000);
s2.setSoTimeout(0);
int b = s2.getInputStream().read();
@@ -211,10 +203,7 @@
withConnection((s1, s2) -> {
s2.setSoTimeout(30*1000);
scheduleClose(s2, 2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketException expected) { }
+ expectThrows(SocketException.class, () -> s2.getInputStream().read());
});
}
@@ -280,7 +269,7 @@
public void testTimedAccept1() throws IOException {
Socket s1 = null;
Socket s2 = null;
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
s1 = new Socket();
s1.connect(ss.getLocalSocketAddress());
ss.setSoTimeout(30*1000);
@@ -295,7 +284,7 @@
* Test timed accept where a connection is established after a short delay
*/
public void testTimedAccept2() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
ss.setSoTimeout(30*1000);
scheduleConnect(ss.getLocalSocketAddress(), 2000);
Socket s = ss.accept();
@@ -307,13 +296,17 @@
* Test timed accept where the accept times out
*/
public void testTimedAccept3() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
ss.setSoTimeout(2000);
+ long startMillis = millisTime();
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
- } catch (SocketTimeoutException expected) { }
+ fail();
+ } catch (SocketTimeoutException expected) {
+ int timeout = ss.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
+ }
}
}
@@ -322,12 +315,12 @@
* previous accept timed out.
*/
public void testTimedAccept4() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
ss.setSoTimeout(2000);
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) { }
try (Socket s1 = new Socket()) {
s1.connect(ss.getLocalSocketAddress());
@@ -342,12 +335,12 @@
* accept timed out
*/
public void testTimedAccept5() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
ss.setSoTimeout(2000);
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) { }
ss.setSoTimeout(0);
try (Socket s1 = new Socket()) {
@@ -363,12 +356,12 @@
* accept timed out and after a short delay
*/
public void testTimedAccept6() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
ss.setSoTimeout(2000);
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) { }
ss.setSoTimeout(0);
scheduleConnect(ss.getLocalSocketAddress(), 2000);
@@ -381,13 +374,134 @@
* Test async close of a timed accept
*/
public void testTimedAccept7() throws IOException {
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
ss.setSoTimeout(30*1000);
- scheduleClose(ss, 2000);
+ long delay = 2000;
+ scheduleClose(ss, delay);
+ long startMillis = millisTime();
try {
ss.accept().close();
- assertTrue(false);
- } catch (SocketException expected) { }
+ fail();
+ } catch (SocketException expected) {
+ checkDuration(startMillis, delay-100, delay+2000);
+ }
+ }
+ }
+
+ /**
+ * Test timed accept with the thread interrupt status set.
+ */
+ public void testTimedAccept8() throws IOException {
+ try (ServerSocket ss = boundServerSocket()) {
+ ss.setSoTimeout(2000);
+ Thread.currentThread().interrupt();
+ long startMillis = millisTime();
+ try {
+ Socket s = ss.accept();
+ s.close();
+ fail();
+ } catch (SocketTimeoutException expected) {
+ // accept should have blocked for 2 seconds
+ int timeout = ss.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
+ assertTrue(Thread.currentThread().isInterrupted());
+ } finally {
+ Thread.interrupted(); // clear interrupt status
+ }
+ }
+ }
+
+ /**
+ * Test interrupt of thread blocked in timed accept.
+ */
+ public void testTimedAccept9() throws IOException {
+ try (ServerSocket ss = boundServerSocket()) {
+ ss.setSoTimeout(4000);
+ // interrupt thread after 1 second
+ Future<?> interrupter = scheduleInterrupt(Thread.currentThread(), 1000);
+ long startMillis = millisTime();
+ try {
+ Socket s = ss.accept(); // should block for 4 seconds
+ s.close();
+ fail();
+ } catch (SocketTimeoutException expected) {
+ // accept should have blocked for 4 seconds
+ int timeout = ss.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
+ assertTrue(Thread.currentThread().isInterrupted());
+ } finally {
+ interrupter.cancel(true);
+ Thread.interrupted(); // clear interrupt status
+ }
+ }
+ }
+
+ /**
+ * Test two threads blocked in timed accept where no connection is established.
+ */
+ public void testTimedAccept10() throws Exception {
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try (ServerSocket ss = boundServerSocket()) {
+ ss.setSoTimeout(4000);
+
+ long startMillis = millisTime();
+
+ Future<Socket> result1 = pool.submit(ss::accept);
+ Future<Socket> result2 = pool.submit(ss::accept);
+
+ // both tasks should complete with SocketTimeoutException
+ Throwable e = expectThrows(ExecutionException.class, result1::get);
+ assertTrue(e.getCause() instanceof SocketTimeoutException);
+ e = expectThrows(ExecutionException.class, result2::get);
+ assertTrue(e.getCause() instanceof SocketTimeoutException);
+
+ // should get here in 4 seconds, not 8 seconds
+ int timeout = ss.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
+ } finally {
+ pool.shutdown();
+ }
+ }
+
+ /**
+ * Test two threads blocked in timed accept where one connection is established.
+ */
+ public void testTimedAccept11() throws Exception {
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try (ServerSocket ss = boundServerSocket()) {
+ ss.setSoTimeout(4000);
+
+ long startMillis = millisTime();
+
+ Future<Socket> result1 = pool.submit(ss::accept);
+ Future<Socket> result2 = pool.submit(ss::accept);
+
+ // establish connection after 2 seconds
+ scheduleConnect(ss.getLocalSocketAddress(), 2000);
+
+ // one task should have accepted the connection, the other should
+ // have completed with SocketTimeoutException
+ Socket s1 = null;
+ try {
+ s1 = result1.get();
+ s1.close();
+ } catch (ExecutionException e) {
+ assertTrue(e.getCause() instanceof SocketTimeoutException);
+ }
+ Socket s2 = null;
+ try {
+ s2 = result2.get();
+ s2.close();
+ } catch (ExecutionException e) {
+ assertTrue(e.getCause() instanceof SocketTimeoutException);
+ }
+ assertTrue((s1 != null) ^ (s2 != null));
+
+ // should get here in 4 seconds, not 8 seconds
+ int timeout = ss.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
+ } finally {
+ pool.shutdown();
}
}
@@ -411,6 +525,19 @@
}
}
+ /**
+ * Returns a ServerSocket bound to a port on the loopback address
+ */
+ static ServerSocket boundServerSocket() throws IOException {
+ var loopback = InetAddress.getLoopbackAddress();
+ ServerSocket ss = new ServerSocket();
+ ss.bind(new InetSocketAddress(loopback, 0));
+ return ss;
+ }
+
+ /**
+ * An operation that accepts two arguments and may throw IOException
+ */
interface ThrowingBiConsumer<T, U> {
void accept(T t, U u) throws IOException;
}
@@ -423,7 +550,7 @@
{
Socket s1 = null;
Socket s2 = null;
- try (ServerSocket ss = new ServerSocket(0)) {
+ try (ServerSocket ss = boundServerSocket()) {
s1 = new Socket();
s1.connect(ss.getLocalSocketAddress());
s2 = ss.accept();
@@ -446,6 +573,13 @@
}
/**
+ * Schedule thread to be interrupted after a delay
+ */
+ static Future<?> scheduleInterrupt(Thread thread, long delay) {
+ return schedule(() -> thread.interrupt(), delay);
+ }
+
+ /**
* Schedule a thread to connect to the given end point after a delay
*/
static void scheduleConnect(SocketAddress remote, long delay) {
@@ -482,12 +616,36 @@
scheduleWrite(out, new byte[] { (byte)b }, delay);
}
- static void schedule(Runnable task, long delay) {
+ static Future<?> schedule(Runnable task, long delay) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
try {
- executor.schedule(task, delay, TimeUnit.MILLISECONDS);
+ return executor.schedule(task, delay, TimeUnit.MILLISECONDS);
} finally {
executor.shutdown();
}
}
+
+ /**
+ * Returns the current time in milliseconds.
+ */
+ private static long millisTime() {
+ long now = System.nanoTime();
+ return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS);
+ }
+
+ /**
+ * Check the duration of a task
+ * @param start start time, in milliseconds
+ * @param min minimum expected duration, in milliseconds
+ * @param max maximum expected duration, in milliseconds
+ * @return the duration (now - start), in milliseconds
+ */
+ private static long checkDuration(long start, long min, long max) {
+ long duration = millisTime() - start;
+ assertTrue(duration >= min,
+ "Duration " + duration + "ms, expected >= " + min + "ms");
+ assertTrue(duration <= max,
+ "Duration " + duration + "ms, expected <= " + max + "ms");
+ return duration;
+ }
}
--- a/test/jdk/java/net/Socket/UdpSocket.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/Socket/UdpSocket.java Thu May 30 07:19:19 2019 +0100
@@ -23,24 +23,36 @@
/**
* @test
- * @run main UdpSocket
+ * @run testng/othervm -Dsun.net.maxDatagramSockets=32 UdpSocket
* @summary Basic test for a Socket to a UDP socket
*/
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
+import java.security.Permission;
import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
public class UdpSocket {
- static final String MESSAGE = "hello";
+ /**
+ * Test using the Socket API to send/receive datagrams
+ */
+ public void testSendReceive() throws IOException {
+ final String MESSAGE = "hello";
- public static void main(String[] args) throws IOException {
try (DatagramChannel dc = DatagramChannel.open()) {
var loopback = InetAddress.getLoopbackAddress();
dc.bind(new InetSocketAddress(loopback, 0));
@@ -56,8 +68,7 @@
var buf = ByteBuffer.allocate(100);
SocketAddress remote = dc.receive(buf);
buf.flip();
- if (buf.remaining() != MESSAGE.length())
- throw new RuntimeException("Unexpected size");
+ assertTrue(buf.remaining() == MESSAGE.length(), "Unexpected size");
// echo the datagram
dc.send(buf, remote);
@@ -65,11 +76,71 @@
// receive datagram with the socket input stream
byte[] array2 = new byte[100];
int n = s.getInputStream().read(array2);
- if (n != MESSAGE.length())
- throw new RuntimeException("Unexpected size");
- if (!Arrays.equals(array1, 0, n, array2, 0, n))
- throw new RuntimeException("Unexpected contents");
+ assertTrue(n == MESSAGE.length(), "Unexpected size");
+ assertEquals(Arrays.copyOf(array1, n), Arrays.copyOf(array2, n),
+ "Unexpected contents");
}
}
}
+
+ /**
+ * Test that the number of UDP sockets is limited when running with a
+ * security manager.
+ */
+ public void testMaxSockets() throws Exception {
+ int limit = Integer.getInteger("sun.net.maxDatagramSockets");
+
+ // security manager grants all permissions
+ var securityManager = new SecurityManager() {
+ @Override public void checkPermission(Permission perm) { }
+ };
+
+ System.setSecurityManager(securityManager);
+ Deque<Socket> sockets = new ArrayDeque<>();
+ try {
+ // create the maximum number of sockets
+ for (int i=0; i<limit; i++) {
+ sockets.offer(newUdpSocket());
+ }
+
+ // try to create another socket - should fail
+ try {
+ Socket s = newUdpSocket();
+ s.close();
+ assertTrue(false);
+ } catch (IOException expected) { }
+
+ // close one socket
+ sockets.pop().close();
+
+ // try to create another socket - should succeed
+ Socket s = newUdpSocket();
+
+ // unreference the socket and wait for it to be closed by the cleaner
+ var ref = new WeakReference<>(s);
+ s = null;
+ while (ref.get() != null) {
+ System.gc();
+ Thread.sleep(100);
+ }
+
+ // try to create another socket - should succeed
+ s = newUdpSocket();
+ s.close();
+ } finally {
+ closeAll(sockets);
+ System.setSecurityManager(null);
+ }
+ }
+
+ private Socket newUdpSocket() throws IOException {
+ return new Socket(InetAddress.getLoopbackAddress(), 8000, false);
+ }
+
+ private void closeAll(Deque<Socket> sockets) throws IOException {
+ Socket s;
+ while ((s = sockets.poll()) != null) {
+ s.close();
+ }
+ }
}
--- a/test/jdk/java/net/Socket/asyncClose/AsyncClose.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/Socket/asyncClose/AsyncClose.java Thu May 30 07:19:19 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -35,6 +35,7 @@
* cause any thread blocked on the socket to throw a SocketException.
* @run main AsyncClose
* @run main/othervm -Djava.net.preferIPv4Stack=true AsyncClose
+ * @run main/othervm -Djdk.net.usePlainSocketImpl AsyncClose
*/
public class AsyncClose {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/BadUsages.java Thu May 30 07:19:19 2019 +0100
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2019, 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
+ * @bug 8221481
+ * @compile/module=java.base java/net/PlatformSocketImpl.java
+ * @run testng/othervm BadUsages
+ * @summary Test the platform SocketImpl when used in unintended ways
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketImpl;
+import java.net.SocketOption;
+import java.net.SocketOptions;
+import java.net.StandardSocketOptions;
+import java.util.Set;
+
+import java.net.PlatformSocketImpl; // test helper
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/**
+ * SocketImpl does not specify how the SocketImpl behaves when used in ways
+ * that are not intended, e.g. invoking socket operations before the socket is
+ * created or trying to establish a connection after the socket is connected or
+ * closed.
+ *
+ * This test exercises the platform SocketImpl to test that it is reliable, and
+ * throws reasonable exceptions, for these scenarios.
+ */
+
+@Test
+public class BadUsages {
+
+ /**
+ * Test create when already created.
+ */
+ public void testCreate1() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(IOException.class, () -> impl.create(true));
+ }
+ }
+
+ /**
+ * Test create when closed.
+ */
+ public void testCreate2() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class, () -> impl.create(true));
+ }
+
+ /**
+ * Test connect when not created.
+ */
+ public void testConnect1() throws IOException {
+ try (var ss = new ServerSocket(0)) {
+ var impl = new PlatformSocketImpl(false);
+ var address = ss.getInetAddress();
+ int port = ss.getLocalPort();
+ expectThrows(IOException.class, () -> impl.connect(address, port));
+ }
+ }
+
+ /**
+ * Test connect with unsupported address type.
+ */
+ public void testConnect2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ var remote = new SocketAddress() { };
+ expectThrows(IOException.class, () -> impl.connect(remote, 0));
+ }
+ }
+
+ /**
+ * Test connect with an unresolved address.
+ */
+ public void testConnect3() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ var remote = new InetSocketAddress("blah-blah.blah-blah", 80);
+ expectThrows(IOException.class, () -> impl.connect(remote, 0));
+ }
+ }
+
+ /**
+ * Test connect when already connected.
+ */
+ public void testConnect4() throws IOException {
+ try (var ss = new ServerSocket();
+ var impl = new PlatformSocketImpl(false)) {
+ var loopback = InetAddress.getLoopbackAddress();
+ ss.bind(new InetSocketAddress(loopback, 0));
+ impl.create(true);
+ int port = ss.getLocalPort();
+ impl.connect(loopback, port);
+ expectThrows(IOException.class, () -> impl.connect(loopback, port));
+ }
+ }
+
+ /**
+ * Test connect when closed.
+ */
+ public void testConnect5() throws IOException {
+ try (var ss = new ServerSocket(0)) {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ String host = ss.getInetAddress().getHostAddress();
+ int port = ss.getLocalPort();
+ expectThrows(IOException.class, () -> impl.connect(host, port));
+ }
+ }
+
+ /**
+ * Test bind when not created.
+ */
+ public void testBind1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ var loopback = InetAddress.getLoopbackAddress();
+ expectThrows(IOException.class, () -> impl.bind(loopback, 0));
+ }
+
+ /**
+ * Test bind when already bound.
+ */
+ public void testBind2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ var loopback = InetAddress.getLoopbackAddress();
+ impl.bind(loopback, 0);
+ expectThrows(IOException.class, () -> impl.bind(loopback, 0));
+ }
+ }
+
+ /**
+ * Test bind when connected.
+ */
+ public void testBind3() throws IOException {
+ try (var ss = new ServerSocket();
+ var impl = new PlatformSocketImpl(false)) {
+ var loopback = InetAddress.getLoopbackAddress();
+ ss.bind(new InetSocketAddress(loopback, 0));
+ impl.create(true);
+ impl.connect(ss.getLocalSocketAddress(), 0);
+ expectThrows(IOException.class, () -> impl.bind(loopback, 0));
+ }
+ }
+
+ /**
+ * Test bind when closed.
+ */
+ public void testBind4() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ var loopback = InetAddress.getLoopbackAddress();
+ expectThrows(IOException.class, () -> impl.bind(loopback, 0));
+ }
+
+
+ /**
+ * Test listen when not created.
+ */
+ public void testListen1() {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.listen(16));
+ }
+
+ /**
+ * Test listen when not bound.
+ */
+ public void testListen2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(IOException.class, () -> impl.listen(16));
+ }
+ }
+
+ /**
+ * Test listen when closed.
+ */
+ public void testListen3() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class, () -> impl.listen(16));
+ }
+
+ /**
+ * Test accept when not created.
+ */
+ public void testAccept1() throws IOException {
+ var impl = new PlatformSocketImpl(true);
+ var si = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.accept(si));
+ }
+
+ /**
+ * Test accept when not bound.
+ */
+ public void testAccept2() throws IOException {
+ try (var impl = new PlatformSocketImpl(true)) {
+ impl.create(true);
+ var si = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.accept(si));
+ }
+ }
+
+ /**
+ * Test accept when not a stream socket.
+ */
+ public void testAccept3() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(false);
+ impl.bind(InetAddress.getLoopbackAddress(), 0);
+ var si = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.accept(si));
+ }
+ }
+
+ /**
+ * Test accept when closed.
+ */
+ public void testAccept4() throws IOException {
+ var impl = new PlatformSocketImpl(true);
+ impl.close();
+ var si = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.accept(si));
+ }
+
+ /**
+ * Test accept with SocketImpl that is already created.
+ */
+ public void testAccept5() throws IOException {
+ try (var impl = new PlatformSocketImpl(true);
+ var si = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ impl.bind(InetAddress.getLoopbackAddress(), 0);
+ si.create(true);
+ expectThrows(IOException.class, () -> impl.accept(si));
+ }
+ }
+
+ /**
+ * Test accept with SocketImpl that is closed.
+ */
+ public void testAccept6() throws IOException {
+ try (var impl = new PlatformSocketImpl(true);
+ var si = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ impl.bind(InetAddress.getLoopbackAddress(), 0);
+ si.create(true);
+ si.close();
+ expectThrows(IOException.class, () -> impl.accept(si));
+ }
+ }
+
+ /**
+ * Test available when not created.
+ */
+ public void testAvailable1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.available());
+ }
+
+ /**
+ * Test available when created but not connected.
+ */
+ public void testAvailable2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(IOException.class, () -> impl.available());
+ }
+ }
+
+ /**
+ * Test available when closed.
+ */
+ public void testAvailable3() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class, () -> impl.available());
+ }
+
+ /**
+ * Test setOption when not created.
+ */
+ public void testSetOption1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class,
+ () -> impl.setOption(StandardSocketOptions.SO_REUSEADDR, true));
+ // legacy
+ expectThrows(SocketException.class,
+ () -> impl.setOption(SocketOptions.SO_REUSEADDR, true));
+ }
+
+ /**
+ * Test setOption when closed.
+ */
+ public void testSetOption2() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class,
+ () -> impl.setOption(StandardSocketOptions.SO_REUSEADDR, true));
+ // legacy
+ expectThrows(SocketException.class,
+ () -> impl.setOption(SocketOptions.SO_REUSEADDR, true));
+ }
+
+ /**
+ * Test setOption with unsupported option.
+ */
+ public void testSetOption3() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ var opt = new SocketOption<String>() {
+ @Override public String name() { return "birthday"; }
+ @Override public Class<String> type() { return String.class; }
+ };
+ expectThrows(UnsupportedOperationException.class, () -> impl.setOption(opt, ""));
+ // legacy
+ expectThrows(SocketException.class, () -> impl.setOption(-1, ""));
+ }
+ }
+
+ /**
+ * Test setOption(int, Object) with invalid values.
+ */
+ public void testSetOption4() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(SocketException.class,
+ () -> impl.setOption(SocketOptions.SO_REUSEADDR, -1));
+ expectThrows(SocketException.class,
+ () -> impl.setOption(SocketOptions.SO_TIMEOUT, -1));
+ expectThrows(SocketException.class,
+ () -> impl.setOption(SocketOptions.SO_SNDBUF, -1));
+ expectThrows(SocketException.class,
+ () -> impl.setOption(SocketOptions.SO_RCVBUF, -1));
+ }
+ }
+
+ /**
+ * Test getOption when not created.
+ */
+ public void testGetOption1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class,
+ () -> impl.getOption(StandardSocketOptions.SO_REUSEADDR));
+ expectThrows(SocketException.class,
+ () -> impl.getOption(-1));
+ }
+
+ /**
+ * Test getOption when closed.
+ */
+ public void testGetOption2() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class,
+ () -> impl.getOption(StandardSocketOptions.SO_REUSEADDR));
+ expectThrows(SocketException.class,
+ () -> impl.getOption(SocketOptions.SO_REUSEADDR));
+ }
+
+ /**
+ * Test getOption with unsupported option.
+ */
+ public void testGetOption3() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ var opt = new SocketOption<String>() {
+ @Override public String name() { return "birthday"; }
+ @Override public Class<String> type() { return String.class; }
+ };
+ expectThrows(UnsupportedOperationException.class, () -> impl.getOption(opt));
+ expectThrows(SocketException.class, () -> impl.getOption(-1));
+ }
+ }
+
+ /**
+ * Test shutdownInput when not created.
+ */
+ public void testShutdownInput1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.shutdownInput());
+ }
+
+ /**
+ * Test shutdownInput when not connected.
+ */
+ public void testShutdownInput2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(IOException.class, () -> impl.shutdownInput());
+ }
+ }
+
+ /**
+ * Test shutdownInput when closed.
+ */
+ public void testShutdownInput3() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class, () -> impl.shutdownInput());
+ }
+
+ /**
+ * Test shutdownOutput when not created.
+ */
+ public void testShutdownOutput1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.shutdownOutput());
+ }
+
+ /**
+ * Test shutdownOutput when not connected.
+ */
+ public void testShutdownOutput2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(IOException.class, () -> impl.shutdownOutput());
+ }
+ }
+
+ /**
+ * Test shutdownOutput when closed.
+ */
+ public void testShutdownOutput3() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class, () -> impl.shutdownOutput());
+ }
+
+ /**
+ * Test sendUrgentData when not created.
+ */
+ public void testSendUrgentData1() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ expectThrows(IOException.class, () -> impl.sendUrgentData(0));
+ }
+
+ /**
+ * Test sendUrgentData when not connected.
+ */
+ public void testSendUrgentData2() throws IOException {
+ try (var impl = new PlatformSocketImpl(false)) {
+ impl.create(true);
+ expectThrows(IOException.class, () -> impl.sendUrgentData(0));
+ }
+ }
+
+ /**
+ * Test sendUrgentData when closed.
+ */
+ public void testSendUrgentData3() throws IOException {
+ var impl = new PlatformSocketImpl(false);
+ impl.close();
+ expectThrows(IOException.class, () -> impl.sendUrgentData(0));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/CompareSocketOptions.java Thu May 30 07:19:19 2019 +0100
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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
+ * @bug 8221481
+ * @modules java.base/java.net:+open java.base/sun.nio.ch:+open
+ * @run testng CompareSocketOptions
+ * @summary Compare the set of socket options supported by the old and new SocketImpls
+ */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketImpl;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class CompareSocketOptions {
+
+ /**
+ * Test that the old and new platform SocketImpl support the same set of
+ * client socket options.
+ */
+ public void testClientSocketSupportedOptions() throws IOException {
+ Socket s1 = new Socket(createOldSocketImpl(false)) { };
+ Socket s2 = new Socket(createNewSocketImpl(false)) { };
+ assertEquals(s1.supportedOptions(), s2.supportedOptions());
+ }
+
+ /**
+ * Test that the old and new platform SocketImpl support the same set of
+ * server socket options.
+ */
+ public void testServerSocketSupportedOptions() throws IOException {
+ ServerSocket ss1 = new ServerSocket(createOldSocketImpl(true)) { };
+ ServerSocket ss2 = new ServerSocket(createNewSocketImpl(true)) { };
+ assertEquals(ss1.supportedOptions(), ss2.supportedOptions());
+ }
+
+ private static SocketImpl createOldSocketImpl(boolean server) {
+ return newPlatformSocketImpl("java.net.PlainSocketImpl", server);
+ }
+
+ private static SocketImpl createNewSocketImpl(boolean server) {
+ return newPlatformSocketImpl("sun.nio.ch.NioSocketImpl", server);
+ }
+
+ private static SocketImpl newPlatformSocketImpl(String name, boolean server) {
+ try {
+ var ctor = Class.forName(name).getDeclaredConstructor(boolean.class);
+ ctor.setAccessible(true);
+ return (SocketImpl) ctor.newInstance(server);
+ } catch (Exception e) {
+ fail("Should not get here", e);
+ return null;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/java.base/java/net/PlatformSocketImpl.java Thu May 30 07:19:19 2019 +0100
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+package java.net;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * A SocketImpl that delegates all operations to a platform SocketImpl. It also
+ * overrides all methods with public methods and implements AutoCloseable to make
+ * it easy to write tests.
+ */
+
+public class PlatformSocketImpl extends SocketImpl implements AutoCloseable {
+ private final SocketImpl impl;
+
+ public PlatformSocketImpl(boolean server) {
+ impl = new sun.nio.ch.NioSocketImpl(server);
+ }
+
+ @Override
+ public void close() throws IOException {
+ impl.close();
+ }
+
+ @Override
+ public void create(boolean stream) throws IOException {
+ impl.create(stream);
+ }
+
+ @Override
+ public void connect(SocketAddress remote, int millis) throws IOException {
+ impl.connect(remote, millis);
+ }
+
+ @Override
+ public void connect(String host, int port) throws IOException {
+ impl.connect(host, port);
+ }
+
+ @Override
+ public void connect(InetAddress address, int port) throws IOException {
+ impl.connect(address, port);
+ }
+
+ @Override
+ public void bind(InetAddress address, int port) throws IOException {
+ impl.bind(address, port);
+ }
+
+ @Override
+ public void listen(int backlog) throws IOException {
+ impl.listen(backlog);
+ }
+
+ @Override
+ public void accept(SocketImpl si) throws IOException {
+ impl.accept(((PlatformSocketImpl) si).impl);
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return impl.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ return impl.getOutputStream();
+ }
+
+ @Override
+ public int available() throws IOException {
+ return impl.available();
+ }
+
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return impl.supportedOptions();
+ }
+
+ @Override
+ public <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+ impl.setOption(opt, value);
+ }
+
+ @Override
+ public <T> T getOption(SocketOption<T> opt) throws IOException {
+ return impl.getOption(opt);
+ }
+
+ @Override
+ public void setOption(int opt, Object value) throws SocketException {
+ impl.setOption(opt, value);
+ }
+
+ @Override
+ public Object getOption(int opt) throws SocketException {
+ return impl.getOption(opt);
+ }
+
+ @Override
+ public void shutdownInput() throws IOException {
+ impl.shutdownInput();
+ }
+
+ @Override
+ public void shutdownOutput() throws IOException {
+ impl.shutdownOutput();
+ }
+
+ @Override
+ public boolean supportsUrgentData() {
+ return impl.supportsUrgentData();
+ }
+
+ @Override
+ public void sendUrgentData(int data) throws IOException {
+ impl.sendUrgentData(data);
+ }
+}
--- a/test/jdk/java/net/SocketOption/OptionsTest.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/SocketOption/OptionsTest.java Thu May 30 07:19:19 2019 +0100
@@ -38,23 +38,24 @@
public class OptionsTest {
- static class Test {
- Test(SocketOption<?> option, Object testValue) {
+ static class Test<T> {
+ final SocketOption<T> option;
+ final T value;
+ Test(SocketOption<T> option, T value) {
this.option = option;
- this.testValue = testValue;
+ this.value = value;
}
- static Test create (SocketOption<?> option, Object testValue) {
- return new Test(option, testValue);
+ static <T> Test<T> create(SocketOption<T> option, T value) {
+ return new Test<T>(option, value);
}
- Object option;
- Object testValue;
+
}
// The tests set the option using the new API, read back the set value
// which could be diferent, and then use the legacy get API to check
// these values are the same
- static Test[] socketTests = new Test[] {
+ static Test<?>[] socketTests = new Test<?>[] {
Test.create(StandardSocketOptions.SO_KEEPALIVE, Boolean.TRUE),
Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
@@ -66,7 +67,7 @@
Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound
};
- static Test[] serverSocketTests = new Test[] {
+ static Test<?>[] serverSocketTests = new Test<?>[] {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
@@ -75,7 +76,7 @@
Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound
};
- static Test[] dgSocketTests = new Test[] {
+ static Test<?>[] datagramSocketTests = new Test<?>[] {
Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
@@ -85,7 +86,7 @@
Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound
};
- static Test[] mcSocketTests = new Test[] {
+ static Test<?>[] multicastSocketTests = new Test<?>[] {
Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()),
Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)), // lower-bound
Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)),
@@ -97,7 +98,7 @@
try {
Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
while (nifs.hasMoreElements()) {
- NetworkInterface ni = (NetworkInterface)nifs.nextElement();
+ NetworkInterface ni = nifs.nextElement();
if (ni.supportsMulticast()) {
return ni;
}
@@ -107,99 +108,110 @@
return null;
}
+ static boolean okayToTest(Socket s, SocketOption<?> option) {
+ if (option == StandardSocketOptions.SO_REUSEPORT) {
+ // skip SO_REUSEPORT if option is not supported
+ return s.supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT);
+ }
+ if (option == StandardSocketOptions.IP_TOS && s.isConnected()) {
+ // skip IP_TOS if connected
+ return false;
+ }
+ return true;
+ }
+
+ static <T> void testEqual(SocketOption<T> option, T value1, T value2) {
+ if (!value1.equals(value2)) {
+ throw new RuntimeException("Test of " + option.name() + " failed: "
+ + value1 + " != " + value2);
+ }
+ }
+
+ static <T> void test(Socket s, Test<T> test) throws Exception {
+ SocketOption<T> option = test.option;
+ s.setOption(option, test.value);
+ T value1 = s.getOption(test.option);
+ T value2 = (T) legacyGetOption(Socket.class, s, test.option);
+ testEqual(option, value1, value2);
+ }
+
+ static <T> void test(ServerSocket ss, Test<T> test) throws Exception {
+ SocketOption<T> option = test.option;
+ ss.setOption(option, test.value);
+ T value1 = ss.getOption(test.option);
+ T value2 = (T) legacyGetOption(ServerSocket.class, ss, test.option);
+ testEqual(option, value1, value2);
+ }
+
+ static <T> void test(DatagramSocket ds, Test<T> test) throws Exception {
+ SocketOption<T> option = test.option;
+ ds.setOption(option, test.value);
+ T value1 = ds.getOption(test.option);
+ T value2 = (T) legacyGetOption(ds.getClass(), ds, test.option);
+ testEqual(option, value1, value2);
+ }
+
+ @SuppressWarnings("try")
static void doSocketTests() throws Exception {
- try (
- ServerSocket srv = new ServerSocket(0, 50, InetAddress.getLoopbackAddress());
- Socket c = new Socket(InetAddress.getLoopbackAddress(), srv.getLocalPort());
- Socket s = srv.accept();
- ) {
- Set<SocketOption<?>> options = c.supportedOptions();
- boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
- for (int i=0; i<socketTests.length; i++) {
- Test test = socketTests[i];
- if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(Socket.class, c,test.option);
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("S Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
- }
+ // unconnected socket
+ try (Socket s = new Socket()) {
+ for (Test<?> test : socketTests) {
+ if (okayToTest(s, test.option)) {
+ test(s, test);
}
}
}
- }
- static void doDgSocketTests() throws Exception {
- try (
- DatagramSocket c = new DatagramSocket(0);
- ) {
- Set<SocketOption<?>> options = c.supportedOptions();
- boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
- for (int i=0; i<dgSocketTests.length; i++) {
- Test test = dgSocketTests[i];
- if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(DatagramSocket.class, c,test.option);
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("DG Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
+ // connected socket
+ try (ServerSocket ss = new ServerSocket()) {
+ var loopback = InetAddress.getLoopbackAddress();
+ ss.bind(new InetSocketAddress(loopback, 0));
+ try (Socket s1 = new Socket()) {
+ s1.connect(ss.getLocalSocketAddress());
+ try (Socket s2 = ss.accept()) {
+ for (Test<?> test : socketTests) {
+ if (okayToTest(s1, test.option)) {
+ test(s1, test);
+ }
}
}
}
}
}
- static void doMcSocketTests() throws Exception {
- try (
- MulticastSocket c = new MulticastSocket(0);
- ) {
- for (int i=0; i<mcSocketTests.length; i++) {
- Test test = mcSocketTests[i];
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(MulticastSocket.class, c,test.option);
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("MC Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
+ static void doServerSocketTests() throws Exception {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ Set<SocketOption<?>> options = ss.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
+ for (Test<?> test : serverSocketTests) {
+ if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
+ test(ss, test);
}
}
}
}
- static void doServerSocketTests() throws Exception {
- try (
- ServerSocket c = new ServerSocket(0);
- ) {
- Set<SocketOption<?>> options = c.supportedOptions();
+ static void doDatagramSocketTests() throws Exception {
+ try (DatagramSocket ds = new DatagramSocket(0)) {
+ Set<SocketOption<?>> options = ds.supportedOptions();
boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
- for (int i=0; i<serverSocketTests.length; i++) {
- Test test = serverSocketTests[i];
+ for (Test<?> test : datagramSocketTests) {
if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(
- ServerSocket.class, c, test.option
- );
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("SS Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
- }
+ test(ds, test);
}
}
}
}
- static Object legacyGetOption(
- Class<?> type, Object s, Object option)
+ static void doMulticastSocketTests() throws Exception {
+ try (MulticastSocket ms = new MulticastSocket(0)) {
+ for (Test<?> test : multicastSocketTests) {
+ test(ms, test);
+ }
+ }
+ }
- throws Exception
- {
+ static Object legacyGetOption(Class<?> type, Object s, Object option) throws Exception {
if (type.equals(Socket.class)) {
Socket socket = (Socket)s;
Set<SocketOption<?>> options = socket.supportedOptions();
@@ -291,8 +303,8 @@
IPSupport.throwSkippedExceptionIfNonOperational();
doSocketTests();
doServerSocketTests();
- doDgSocketTests();
- doMcSocketTests();
+ doDatagramSocketTests();
+ doMulticastSocketTests();
}
// Reflectively access jdk.net.Sockets.getOption so that the test can run
--- a/test/jdk/java/net/ipv6tests/TcpTest.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/java/net/ipv6tests/TcpTest.java Thu May 30 07:19:19 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -32,6 +32,7 @@
* @build jdk.test.lib.NetworkConfiguration
* jdk.test.lib.Platform
* @run main TcpTest -d
+ * @run main/othervm -Djdk.net.usePlainSocketImpl TcpTest -d
*/
import java.net.*;
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/NewSocketMethods.java Wed May 29 22:17:48 2019 -0400
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NewSocketMethods.java Thu May 30 07:19:19 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -197,14 +197,19 @@
/**
* test some new methods of java.net.Socket added to merlin.
*/
- socket.setTrafficClass(8);
- socket.setReuseAddress(true);
- System.out.println("Client getTrafficClass(): "
- + socket.getTrafficClass());
System.out.println("Client isInputShutdown() "
+ socket.isInputShutdown());
+ socket.setReuseAddress(true);
System.out.println("Client getReuseAddress(): "
+ socket.getReuseAddress());
+
+ // Solaris does not support set/get of IPV6_TCLASS when connected
+ if (!"SunOS".equals(System.getProperty("os.name"))) {
+ socket.setTrafficClass(8);
+ System.out.println("Client getTrafficClass(): "
+ + socket.getTrafficClass());
+ }
+
os.write(237);
os.flush();
System.out.println("Client read: " + is.read());