--- a/src/java.base/share/classes/java/lang/System.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/lang/System.java Sat Mar 09 12:52:30 2019 +0000
@@ -2172,6 +2172,12 @@
public void blockedOn(Interruptible b) {
Thread.blockedOn(b);
}
+ public void setNativeTid(long tid) {
+ Thread.setNativeTid(tid);
+ }
+ public long nativeTid() {
+ return Thread.nativeTid();
+ }
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
--- a/src/java.base/share/classes/java/lang/Thread.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/lang/Thread.java Sat Mar 09 12:52:30 2019 +0000
@@ -241,6 +241,18 @@
}
/**
+ * Native thread id, cached here for use for threads are blocked in I/O
+ * operations.
+ */
+ private long nativeTid;
+ static void setNativeTid(long tid) {
+ Thread.currentThread().nativeTid = tid;
+ }
+ static long nativeTid() {
+ return Thread.currentThread().nativeTid;
+ }
+
+ /**
* The minimum priority that a thread can have.
*/
public static final int MIN_PRIORITY = 1;
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -30,12 +30,16 @@
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import sun.net.ConnectionResetException;
import sun.net.NetHooks;
+import sun.net.PlatformSocketImpl;
import sun.net.ResourceManager;
import sun.net.util.SocketExceptions;
@@ -46,7 +50,7 @@
*
* @author Steven B. Byrne
*/
-abstract class AbstractPlainSocketImpl extends SocketImpl {
+abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSocketImpl {
/* instance variable for SO_TIMEOUT */
int timeout; // timeout in millisec
// traffic class
@@ -450,15 +454,17 @@
/**
* Accepts connections.
- * @param s the connection
+ * @param si the socket impl
*/
- protected void accept(SocketImpl s) throws IOException {
+ protected void accept(SocketImpl si) throws IOException {
+ si.fd = new FileDescriptor();
acquireFD();
try {
- socketAccept(s);
+ socketAccept(si);
} finally {
releaseFD();
}
+ SocketCleanable.register(si.fd);
}
/**
@@ -470,8 +476,14 @@
throw new IOException("Socket Closed");
if (shut_rd)
throw new IOException("Socket input is shutdown");
- if (socketInputStream == null)
- socketInputStream = new SocketInputStream(this);
+ if (socketInputStream == null) {
+ PrivilegedExceptionAction<SocketInputStream> pa = () -> new SocketInputStream(this);
+ try {
+ socketInputStream = AccessController.doPrivileged(pa);
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getCause();
+ }
+ }
}
return socketInputStream;
}
@@ -489,8 +501,14 @@
throw new IOException("Socket Closed");
if (shut_wr)
throw new IOException("Socket output is shutdown");
- if (socketOutputStream == null)
- socketOutputStream = new SocketOutputStream(this);
+ if (socketOutputStream == null) {
+ PrivilegedExceptionAction<SocketOutputStream> pa = () -> new SocketOutputStream(this);
+ try {
+ socketOutputStream = AccessController.doPrivileged(pa);
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getCause();
+ }
+ }
}
return socketOutputStream;
}
@@ -589,15 +607,10 @@
}
}
- void reset() throws IOException {
- if (fd != null) {
- socketClose();
- }
- fd = null;
- super.reset();
+ void reset() {
+ throw new InternalError("should not get here");
}
-
/**
* Shutdown read-half of the socket connection;
*/
@@ -714,6 +727,42 @@
socketClose0(false);
}
+ @Override
+ public void copyTo(SocketImpl si) {
+ // this SocketImpl should be connected
+ assert fd.valid() && localport != 0 && address != null && port != 0;
+
+ if (si instanceof AbstractPlainSocketImpl) {
+ AbstractPlainSocketImpl psi = (AbstractPlainSocketImpl) si;
+ try {
+ psi.close();
+ } catch (IOException ignore) { }
+
+ // copy fields
+ psi.stream = this.stream;
+ psi.fd = this.fd;
+ psi.localport = this.localport;
+ psi.address = this.address;
+ psi.port = this.port;
+
+ // reset fields; do not reset timeout
+ psi.closePending = false;
+ psi.connectionReset = false;
+ psi.shut_rd = false;
+ psi.shut_wr = false;
+ } else {
+ // copy fields
+ si.fd = this.fd;
+ si.localport = this.localport;
+ si.address = this.address;
+ si.port = this.port;
+ }
+
+ // this SocketImpl is now closed and should be discarded
+ this.closePending = true;
+ this.fd = null;
+ }
+
abstract void socketCreate(boolean isServer) throws IOException;
abstract void socketConnect(InetAddress address, int port, int timeout)
throws IOException;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/net/DelegatingSocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,171 @@
+/*
+ * 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 java.net;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Objects;
+import java.util.Set;
+
+import sun.net.PlatformSocketImpl;
+
+/**
+ * A SocketImpl that delegates all methods to another SocketImpl.
+ */
+
+class DelegatingSocketImpl extends SocketImpl {
+ protected final SocketImpl delegate;
+
+ DelegatingSocketImpl(SocketImpl delegate) {
+ assert delegate instanceof PlatformSocketImpl;
+ this.delegate = Objects.requireNonNull(delegate);
+ }
+
+ final SocketImpl delegate() {
+ return delegate;
+ }
+
+ @Override
+ protected FileDescriptor getFileDescriptor() {
+ return delegate.getFileDescriptor();
+ }
+
+ @Override
+ protected InetAddress getInetAddress() {
+ return delegate.getInetAddress();
+ }
+
+ @Override
+ protected int getPort() {
+ return delegate.getPort();
+ }
+
+ @Override
+ protected int getLocalPort() {
+ return delegate.getLocalPort();
+ }
+
+ @Override
+ protected void create(boolean stream) throws IOException {
+ delegate.create(stream);
+ }
+
+ @Override
+ protected void connect(String host, int port) throws IOException {
+ delegate.connect(host, port);
+ }
+
+ @Override
+ protected void connect(InetAddress address, int port) throws IOException {
+ delegate.connect(address, port);
+ }
+
+ @Override
+ protected void connect(SocketAddress address, int timeout) throws IOException {
+ delegate.connect(address, timeout);
+ }
+
+ @Override
+ protected void bind(InetAddress host, int port) throws IOException {
+ delegate.bind(host, port);
+ }
+
+ @Override
+ protected void listen(int backlog) throws IOException {
+ delegate.listen(backlog);
+ }
+
+ @Override
+ protected void accept(SocketImpl s) throws IOException {
+ delegate.accept(s);
+ }
+
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ return delegate.getInputStream();
+ }
+
+ @Override
+ protected OutputStream getOutputStream() throws IOException {
+ return delegate.getOutputStream();
+ }
+
+ @Override
+ protected int available() throws IOException {
+ return delegate.available();
+ }
+
+ @Override
+ protected void close() throws IOException {
+ delegate.close();
+ }
+
+ @Override
+ protected boolean supportsUrgentData() {
+ return delegate.supportsUrgentData();
+ }
+
+ @Override
+ protected void sendUrgentData(int data) throws IOException {
+ delegate.sendUrgentData(data);
+ }
+
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ return delegate.supportedOptions();
+ }
+
+ @Override
+ protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+ delegate.setOption(opt, value);
+ }
+
+ @Override
+ protected <T> T getOption(SocketOption<T> opt) throws IOException {
+ return delegate.getOption(opt);
+ }
+
+ @Override
+ public void setOption(int optID, Object value) throws SocketException {
+ delegate.setOption(optID, value);
+ }
+
+ @Override
+ public Object getOption(int optID) throws SocketException {
+ return delegate.getOption(optID);
+ }
+
+ @Override
+ protected void shutdownInput() throws IOException {
+ delegate.shutdownInput();
+ }
+
+ @Override
+ protected void shutdownOutput() throws IOException {
+ delegate.shutdownOutput();
+ }
+}
--- a/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -34,14 +34,13 @@
/**
* Basic SocketImpl that relies on the internal HTTP protocol handler
- * implementation to perform the HTTP tunneling and authentication. The
- * sockets impl is swapped out and replaced with the socket from the HTTP
- * handler after the tunnel is successfully setup.
+ * implementation to perform the HTTP tunneling and authentication. Once
+ * connected, all socket operations delegate to a platform SocketImpl.
*
* @since 1.8
*/
-/*package*/ class HttpConnectSocketImpl extends PlainSocketImpl {
+/*package*/ class HttpConnectSocketImpl extends DelegatingSocketImpl {
private static final String httpURLClazzStr =
"sun.net.www.protocol.http.HttpURLConnection";
@@ -76,12 +75,8 @@
}
}
- HttpConnectSocketImpl(String server, int port) {
- this.server = server;
- this.port = port;
- }
-
- HttpConnectSocketImpl(Proxy proxy) {
+ HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate) {
+ super(delegate);
SocketAddress a = proxy.address();
if ( !(a instanceof InetSocketAddress) )
throw new IllegalArgumentException("Unsupported address type");
@@ -92,6 +87,27 @@
}
@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
+ void setSocket(Socket socket) {
+ delegate.socket = socket;
+ super.setSocket(socket);
+ }
+
+ @Override
+ void setServerSocket(ServerSocket socket) {
+ throw new InternalError("should not get here");
+ }
+
+ @Override
protected void connect(SocketAddress endpoint, int timeout)
throws IOException
{
@@ -117,21 +133,37 @@
close();
// update the Sockets impl to the impl from the http Socket
- AbstractPlainSocketImpl psi = (AbstractPlainSocketImpl) httpSocket.impl;
- this.getSocket().impl = psi;
+ SocketImpl si = httpSocket.impl;
+ getSocket().setImpl(si);
// best effort is made to try and reset options previously set
Set<Map.Entry<Integer,Object>> options = optionsMap.entrySet();
try {
for(Map.Entry<Integer,Object> entry : options) {
- psi.setOption(entry.getKey(), entry.getValue());
+ si.setOption(entry.getKey(), entry.getValue());
}
} catch (IOException x) { /* gulp! */ }
}
+
+ @Override
+ protected void listen(int backlog) {
+ throw new InternalError("should not get here");
+ }
+
+ @Override
+ protected void accept(SocketImpl s) {
+ throw new InternalError("should not get here");
+ }
+
+ @Override
+ void reset() {
+ throw new InternalError("should not get here");
+ }
+
@Override
public void setOption(int opt, Object val) throws SocketException {
- super.setOption(opt, val);
+ delegate.setOption(opt, val);
if (external_address != null)
return; // we're connected, just return
@@ -163,7 +195,10 @@
URL destURL = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) destURL.openConnection(proxy);
conn.setConnectTimeout(connectTimeout);
- conn.setReadTimeout(this.timeout);
+ int timeout = (int) getOption(SocketOptions.SO_TIMEOUT);
+ if (timeout > 0) {
+ conn.setReadTimeout(timeout);
+ }
conn.connect();
doTunneling(conn);
try {
@@ -174,10 +209,14 @@
}
}
- private void doTunneling(HttpURLConnection conn) {
+ private void doTunneling(HttpURLConnection conn) throws IOException {
try {
doTunneling.invoke(conn);
} catch (ReflectiveOperationException x) {
+ Throwable cause = x.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ }
throw new InternalError("Should not reach here", x);
}
}
@@ -187,7 +226,7 @@
if (external_address != null)
return external_address.getAddress();
else
- return super.getInetAddress();
+ return delegate.getInetAddress();
}
@Override
@@ -195,6 +234,6 @@
if (external_address != null)
return external_address.getPort();
else
- return super.getPort();
+ return delegate.getPort();
}
}
--- a/src/java.base/share/classes/java/net/ServerSocket.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/net/ServerSocket.java Sat Mar 09 12:52:30 2019 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 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
@@ -25,9 +25,6 @@
package java.net;
-import jdk.internal.access.JavaNetSocketAccess;
-import jdk.internal.access.SharedSecrets;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Constructor;
@@ -38,6 +35,10 @@
import java.util.Set;
import java.util.Collections;
+import jdk.internal.access.JavaNetSocketAccess;
+import jdk.internal.access.SharedSecrets;
+import sun.net.PlatformSocketImpl;
+
/**
* This class implements server sockets. A server socket waits for
* requests to come in over the network. It performs some operation
@@ -290,13 +291,12 @@
}
private void setImpl() {
+ SocketImplFactory factory = ServerSocket.factory;
if (factory != null) {
impl = factory.createSocketImpl();
checkOldImpl();
} else {
- // No need to do a checkOldImpl() here, we know it's an up to date
- // SocketImpl!
- impl = new SocksSocketImpl();
+ impl = SocketImpl.createPlatformSocketImpl(true);
}
if (impl != null)
impl.setServerSocket(this);
@@ -542,38 +542,99 @@
* @spec JSR-51
*/
protected final void implAccept(Socket s) throws IOException {
- SocketImpl si = null;
- try {
- if (s.impl == null)
- s.setImpl();
- else {
- s.impl.reset();
+ SocketImpl si = s.impl;
+
+ // Socket has no SocketImpl
+ if (si == null) {
+ // create a platform or custom SocketImpl and accept the connection
+ SocketImplFactory factory = Socket.socketImplFactory();
+ if (factory == null) {
+ si = SocketImpl.createPlatformSocketImpl(false);
+ } else {
+ si = factory.createSocketImpl();
}
- si = s.impl;
- s.impl = null;
- si.address = new InetAddress();
- si.fd = new FileDescriptor();
- getImpl().accept(si);
- SocketCleanable.register(si.fd); // raw fd has been set
+ implAccept(si);
+ // bind Socket to the SocketImpl and update socket state
+ s.setImpl(si);
+ s.postAccept();
+ return;
+ }
+
+ // Socket has a SOCKS or HTTP SocketImpl, need delegate
+ if (si instanceof DelegatingSocketImpl) {
+ si = ((DelegatingSocketImpl) si).delegate();
+ assert si instanceof PlatformSocketImpl;
+ }
+
+ // ServerSocket or Socket (or both) have, or delegate to, a platform SocketImpl
+ if (impl instanceof PlatformSocketImpl || si instanceof PlatformSocketImpl) {
+ // create a new platform SocketImpl and accept the connection
+ var psi = SocketImpl.createPlatformSocketImpl(false);
+ implAccept(psi);
+ // copy connection/state to the existing SocketImpl and update socket state
+ psi.copyTo(si);
+ s.postAccept();
+ return;
+ }
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkAccept(si.getInetAddress().getHostAddress(),
- si.getPort());
+ // ServerSocket and Socket bound to custom SocketImpls
+ s.impl = null; // break connection to impl
+ si.reset();
+ try {
+ implAccept(si);
+ } catch (Exception e) {
+ si.reset();
+ throw e;
+ } finally {
+ s.impl = si; // restore connection to impl
+ }
+ s.postAccept();
+ }
+
+ /**
+ * Accepts a new connection so that the given SocketImpl is connected to
+ * the peer. The SocketImpl and connection are closed if the connection is
+ * denied by the security manager.
+ * @throws IOException if an I/O error occurs
+ * @throws SecurityException if the security manager's checkAccept method fails
+ */
+ private void implAccept(SocketImpl si) throws IOException {
+ assert !(si instanceof DelegatingSocketImpl);
+
+ // A non-platform SocketImpl cannot accept a connection with a platform SocketImpl
+ if (!(impl instanceof PlatformSocketImpl) && (si instanceof PlatformSocketImpl)) {
+ throw new IOException("An instance of " + impl.getClass() +
+ " cannot accept a connection with an instance of " + si.getClass());
+ }
+
+ // custom SocketImpl may expect fd/address objects to be created
+ if (!(si instanceof PlatformSocketImpl)) {
+ si.fd = new FileDescriptor();
+ si.address = new InetAddress();
+ }
+
+ // accept a connection
+ impl.accept(si);
+
+ // sanity check that the fields defined by SocketImpl have been set
+ if (si instanceof PlatformSocketImpl) {
+ var fd = si.fd;
+ if (fd == null || !fd.valid() || si.localport <= 0
+ || si.address == null || si.port <= 0) {
+ throw new IOException("Invalid accepted state:" + si);
}
- } catch (IOException e) {
- if (si != null)
- si.reset();
- s.impl = si;
- throw e;
- } catch (SecurityException e) {
- if (si != null)
- si.reset();
- s.impl = si;
- throw e;
}
- s.impl = si;
- s.postAccept();
+
+ // check permission, close SocketImpl/connection if denied
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ sm.checkAccept(si.getInetAddress().getHostAddress(), si.getPort());
+ } catch (SecurityException se) {
+ si.close();
+ throw se;
+ }
+ }
}
/**
@@ -661,6 +722,8 @@
public synchronized void setSoTimeout(int timeout) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
+ if (timeout < 0)
+ throw new IllegalArgumentException("timeout can't be negative");
getImpl().setOption(SocketOptions.SO_TIMEOUT, timeout);
}
@@ -778,7 +841,7 @@
/**
* The factory for all server sockets.
*/
- private static SocketImplFactory factory = null;
+ private static volatile SocketImplFactory factory;
/**
* Sets the server socket implementation factory for the
--- a/src/java.base/share/classes/java/net/Socket.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/net/Socket.java Sat Mar 09 12:52:30 2019 +0000
@@ -28,9 +28,10 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.nio.channels.SocketChannel;
import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedAction;
import java.util.Set;
import java.util.Collections;
@@ -76,6 +77,22 @@
private boolean oldImpl = false;
/**
+ * Socket input/output streams
+ */
+ private volatile InputStream in;
+ private volatile OutputStream out;
+ private static final VarHandle IN, OUT;
+ static {
+ try {
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ IN = l.findVarHandle(Socket.class, "in", InputStream.class);
+ OUT = l.findVarHandle(Socket.class, "out", OutputStream.class);
+ } catch (Exception e) {
+ throw new InternalError(e);
+ }
+ }
+
+ /**
* Creates an unconnected socket, with the
* system-default type of SocketImpl.
*
@@ -137,16 +154,22 @@
security.checkConnect(epoint.getAddress().getHostAddress(),
epoint.getPort());
}
- impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p)
- : new HttpConnectSocketImpl(p);
+
+ // create a SOCKS or HTTP SocketImpl that delegates to a platform SocketImpl
+ SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
+ impl = (type == Proxy.Type.SOCKS) ? new SocksSocketImpl(p, delegate)
+ : new HttpConnectSocketImpl(p, delegate);
impl.setSocket(this);
} else {
if (p == Proxy.NO_PROXY) {
+ // create a platform or custom SocketImpl for the DIRECT case
+ SocketImplFactory factory = Socket.factory;
if (factory == null) {
- impl = new PlainSocketImpl();
- impl.setSocket(this);
- } else
- setImpl();
+ impl = SocketImpl.createPlatformSocketImpl(false);
+ } else {
+ impl = factory.createSocketImpl();
+ }
+ impl.setSocket(this);
} else
throw new IllegalArgumentException("Invalid Proxy");
}
@@ -491,24 +514,29 @@
});
}
+ void setImpl(SocketImpl si) {
+ impl = si;
+ impl.setSocket(this);
+ }
+
/**
* Sets impl to the system-default type of SocketImpl.
* @since 1.4
*/
void setImpl() {
+ SocketImplFactory factory = Socket.factory;
if (factory != null) {
impl = factory.createSocketImpl();
checkOldImpl();
} else {
- // No need to do a checkOldImpl() here, we know it's an up to date
- // SocketImpl!
- impl = new SocksSocketImpl();
+ // create a SOCKS SocketImpl that delegates to a platform SocketImpl
+ SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
+ impl = new SocksSocketImpl(delegate);
}
if (impl != null)
impl.setSocket(this);
}
-
/**
* Get the {@code SocketImpl} attached to this socket, creating
* it if necessary.
@@ -907,18 +935,49 @@
throw new SocketException("Socket is not connected");
if (isInputShutdown())
throw new SocketException("Socket input is shutdown");
- InputStream is = null;
- try {
- is = AccessController.doPrivileged(
- new PrivilegedExceptionAction<>() {
- public InputStream run() throws IOException {
- return impl.getInputStream();
- }
- });
- } catch (java.security.PrivilegedActionException e) {
- throw (IOException) e.getException();
+ InputStream in = this.in;
+ if (in == null) {
+ // wrap the input stream so that the close method closes this socket
+ in = new SocketInputStream(this, impl.getInputStream());
+ if (!IN.compareAndSet(this, null, in)) {
+ in = this.in;
+ }
}
- return is;
+ return in;
+ }
+
+ /**
+ * An InputStream that delegates read/available operations to an underlying
+ * input stream. The close method is overridden to close the Socket.
+ *
+ * This class is instrumented by Java Flight Recorder (JFR) to get socket
+ * I/O events.
+ */
+ private static class SocketInputStream extends InputStream {
+ private final Socket parent;
+ private final InputStream in;
+ SocketInputStream(Socket parent, InputStream in) {
+ this.parent = parent;
+ this.in = in;
+ }
+ @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 in.read(b, off, len);
+ }
+ @Override
+ public int available() throws IOException {
+ return in.available();
+ }
+ @Override
+ public void close() throws IOException {
+ parent.close();
+ }
}
/**
@@ -946,18 +1005,44 @@
throw new SocketException("Socket is not connected");
if (isOutputShutdown())
throw new SocketException("Socket output is shutdown");
- OutputStream os = null;
- try {
- os = AccessController.doPrivileged(
- new PrivilegedExceptionAction<>() {
- public OutputStream run() throws IOException {
- return impl.getOutputStream();
- }
- });
- } catch (java.security.PrivilegedActionException e) {
- throw (IOException) e.getException();
+ OutputStream out = this.out;
+ if (out == null) {
+ // wrap the output stream so that the close method closes this socket
+ out = new SocketOutputStream(this, impl.getOutputStream());
+ if (!OUT.compareAndSet(this, null, out)) {
+ out = this.out;
+ }
}
- return os;
+ return out;
+ }
+
+ /**
+ * An OutputStream that delegates write operations to an underlying output
+ * stream. The close method is overridden to close the Socket.
+ *
+ * This class is instrumented by Java Flight Recorder (JFR) to get socket
+ * I/O events.
+ */
+ private static class SocketOutputStream extends OutputStream {
+ private final Socket parent;
+ private final OutputStream out;
+ SocketOutputStream(Socket parent, OutputStream out) {
+ this.parent = parent;
+ this.out = out;
+ }
+ @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 {
+ out.write(b, off, len);
+ }
+ @Override
+ public void close() throws IOException {
+ parent.close();
+ }
}
/**
@@ -1381,9 +1466,8 @@
try {
getImpl().setOption(SocketOptions.IP_TOS, tc);
} catch (SocketException se) {
- // not supported if socket already connected
- // Solaris returns error in such cases
- if(!isConnected())
+ // may not be supported to change when socket is connected
+ if (!isConnected())
throw se;
}
}
@@ -1644,7 +1728,11 @@
/**
* The factory for all client sockets.
*/
- private static SocketImplFactory factory = null;
+ private static volatile SocketImplFactory factory;
+
+ static SocketImplFactory socketImplFactory() {
+ return factory;
+ }
/**
* Sets the client socket implementation factory for the
@@ -1757,7 +1845,14 @@
* @since 9
*/
public <T> Socket setOption(SocketOption<T> name, T value) throws IOException {
- getImpl().setOption(name, value);
+ try {
+ getImpl().setOption(name, value);
+ } catch (SocketException se) {
+ // may not be supported to change when socket is connected
+ if (name != StandardSocketOptions.IP_TOS || !isConnected()) {
+ throw se;
+ }
+ }
return this;
}
--- a/src/java.base/share/classes/java/net/SocketImpl.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/net/SocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -25,12 +25,18 @@
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.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
@@ -43,6 +49,26 @@
* @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) {
+ if (USE_PLAINSOCKETIMPL) {
+ return (S) new PlainSocketImpl();
+ } else {
+ return (S) new NioSocketImpl(server);
+ }
+ }
+
/**
* The actual Socket object.
*/
@@ -306,7 +332,8 @@
",port=" + getPort() + ",localport=" + getLocalPort() + "]";
}
- void reset() throws IOException {
+ void reset() {
+ fd = null;
address = null;
port = 0;
localport = 0;
--- a/src/java.base/share/classes/java/net/SocksSocketImpl.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/java/net/SocksSocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -23,6 +23,7 @@
* questions.
*/
package java.net;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -36,11 +37,9 @@
/**
* SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
- * This is a subclass of PlainSocketImpl.
- * Note this class should <b>NOT</b> be public.
*/
-class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
+class SocksSocketImpl extends DelegatingSocketImpl implements SocksConsts {
private String server = null;
private int serverPort = DEFAULT_PORT;
private InetSocketAddress external_address;
@@ -49,11 +48,12 @@
private InputStream cmdIn = null;
private OutputStream cmdOut = null;
- SocksSocketImpl() {
- // Nothing needed
+ SocksSocketImpl(SocketImpl delegate) {
+ super(delegate);
}
- SocksSocketImpl(Proxy proxy) {
+ SocksSocketImpl(Proxy proxy, SocketImpl delegate) {
+ super(delegate);
SocketAddress a = proxy.address();
if (a instanceof InetSocketAddress) {
InetSocketAddress ad = (InetSocketAddress) a;
@@ -75,7 +75,7 @@
private synchronized void privilegedConnect(final String host,
final int port,
final int timeout)
- throws IOException
+ throws IOException
{
try {
AccessController.doPrivileged(
@@ -94,7 +94,7 @@
private void superConnectServer(String host, int port,
int timeout) throws IOException {
- super.connect(new InetSocketAddress(host, port), timeout);
+ delegate.connect(new InetSocketAddress(host, port), timeout);
}
private static int remainingMillis(long deadlineMillis) throws IOException {
@@ -111,16 +111,23 @@
private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
int len = data.length;
int received = 0;
- while (received < len) {
- int count;
- try {
- count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
- } catch (SocketTimeoutException e) {
- throw new SocketTimeoutException("Connect timed out");
+ int originalTimeout = (int) getOption(SocketOptions.SO_TIMEOUT);
+ try {
+ while (received < len) {
+ int count;
+ int remaining = remainingMillis(deadlineMillis);
+ setOption(SocketOptions.SO_TIMEOUT, remaining);
+ try {
+ count = in.read(data, received, len - received);
+ } catch (SocketTimeoutException e) {
+ throw new SocketTimeoutException("Connect timed out");
+ }
+ if (count < 0)
+ throw new SocketException("Malformed reply from SOCKS server");
+ received += count;
}
- if (count < 0)
- throw new SocketException("Malformed reply from SOCKS server");
- received += count;
+ } finally {
+ setOption(SocketOptions.SO_TIMEOUT, originalTimeout);
}
return received;
}
@@ -239,6 +246,27 @@
}
}
+ @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
+ void setSocket(Socket soc) {
+ delegate.socket = soc;
+ super.setSocket(soc);
+ }
+
+ @Override
+ void setServerSocket(ServerSocket soc) {
+ throw new InternalError("should not get here");
+ }
+
/**
* Connects the Socks Socket to the specified endpoint. It will first
* connect to the SOCKS proxy and negotiate the access. If the proxy
@@ -290,7 +318,7 @@
/*
* No default proxySelector --> direct connection
*/
- super.connect(epoint, remainingMillis(deadlineMillis));
+ delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
URI uri;
@@ -313,13 +341,13 @@
java.util.Iterator<Proxy> iProxy = null;
iProxy = sel.select(uri).iterator();
if (iProxy == null || !(iProxy.hasNext())) {
- super.connect(epoint, remainingMillis(deadlineMillis));
+ delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
while (iProxy.hasNext()) {
p = iProxy.next();
if (p == null || p.type() != Proxy.Type.SOCKS) {
- super.connect(epoint, remainingMillis(deadlineMillis));
+ delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
@@ -509,7 +537,15 @@
external_address = epoint;
}
+ @Override
+ protected void listen(int backlog) {
+ throw new InternalError("should not get here");
+ }
+ @Override
+ protected void accept(SocketImpl s) {
+ throw new InternalError("should not get here");
+ }
/**
* Returns the value of this socket's {@code address} field.
@@ -522,7 +558,7 @@
if (external_address != null)
return external_address.getAddress();
else
- return super.getInetAddress();
+ return delegate.getInetAddress();
}
/**
@@ -536,7 +572,7 @@
if (external_address != null)
return external_address.getPort();
else
- return super.getPort();
+ return delegate.getPort();
}
@Override
@@ -544,10 +580,15 @@
if (cmdsock != null)
cmdsock.close();
cmdsock = null;
- super.close();
+ delegate.close();
}
private String getUserName() {
return StaticProperty.userName();
}
+
+ @Override
+ void reset() {
+ throw new InternalError("should not get here");
+ }
}
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java Sat Mar 09 12:52:30 2019 +0000
@@ -107,6 +107,16 @@
void blockedOn(Interruptible b);
/**
+ * Set the current thread's native ID
+ */
+ void setNativeTid(long tid);
+
+ /**
+ * Returns the current thread's native ID
+ */
+ long nativeTid();
+
+ /**
* Registers a shutdown hook.
*
* It is expected that this method with registerShutdownInProgress=true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/net/PlatformSocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,43 @@
+/*
+ * 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.net;
+
+import java.net.SocketImpl;
+
+/**
+ * Implemented by the platform's SocketImpl implementations.
+ */
+
+public interface PlatformSocketImpl {
+
+ /**
+ * Copy the state from this connected SocketImpl to a target SocketImpl. If
+ * the target SocketImpl is not a newly created SocketImpl then it is first
+ * closed to release any resources. The target SocketImpl becomes the owner
+ * of the file descriptor, this SocketImpl is marked as closed and should
+ * be discarded.
+ */
+ void copyTo(SocketImpl si);
+}
--- a/src/java.base/share/classes/sun/nio/ch/IOStatus.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/sun/nio/ch/IOStatus.java Sat Mar 09 12:52:30 2019 +0000
@@ -81,4 +81,12 @@
return ((n > EOF) || (n < UNSUPPORTED_CASE));
}
+ /**
+ * Returns true if the error code is UNAVAILABLE or INTERRUPTED, the
+ * error codes to indicate that an I/O operation can be retried.
+ */
+ static boolean okayToRetry(long n) {
+ return (n == IOStatus.UNAVAILABLE) || (n == IOStatus.INTERRUPTED);
+ }
+
}
--- a/src/java.base/share/classes/sun/nio/ch/Net.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/sun/nio/ch/Net.java Sat Mar 09 12:52:30 2019 +0000
@@ -310,6 +310,12 @@
static final ExtendedSocketOptions extendedOptions =
ExtendedSocketOptions.getInstance();
+ static void setSocketOption(FileDescriptor fd, SocketOption<?> name, Object value)
+ throws IOException
+ {
+ setSocketOption(fd, Net.UNSPEC, name, value);
+ }
+
static void setSocketOption(FileDescriptor fd, ProtocolFamily family,
SocketOption<?> name, Object value)
throws IOException
@@ -372,8 +378,13 @@
setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6);
}
- static Object getSocketOption(FileDescriptor fd, ProtocolFamily family,
- SocketOption<?> name)
+ static Object getSocketOption(FileDescriptor fd, SocketOption<?> name)
+ throws IOException
+ {
+ return getSocketOption(fd, Net.UNSPEC, name);
+ }
+
+ static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, SocketOption<?> name)
throws IOException
{
Class<?> type = name.type();
@@ -426,8 +437,7 @@
return socket(UNSPEC, stream);
}
- static FileDescriptor socket(ProtocolFamily family, boolean stream)
- throws IOException {
+ static FileDescriptor socket(ProtocolFamily family, boolean stream) throws IOException {
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
return IOUtil.newFD(socket0(preferIPv6, stream, false, fastLoopback));
@@ -524,6 +534,10 @@
static native int poll(FileDescriptor fd, int events, long timeout)
throws IOException;
+ static int pollNow(FileDescriptor fd, int events) throws IOException {
+ return poll(fd, events, 0);
+ }
+
/**
* Polls a connecting socket to test if the connection has been established.
*
@@ -535,6 +549,10 @@
public static native int pollConnect(FileDescriptor fd, long timeout)
throws IOException;
+ static int pollConnectNow(FileDescriptor fd) throws IOException {
+ return pollConnect(fd, 0);
+ }
+
/**
* Return the number of bytes in the socket input buffer.
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,1324 @@
+/*
+ * Copyright (c) 2018, 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.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Field;
+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.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+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.access.SharedSecrets;
+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 a connect, accept or read is attempted with a timeout then the
+ * socket is changed to non-blocking mode. When in non-blocking mode, operations
+ * that don't complete immediately will poll the socket.
+ */
+
+public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl {
+ private static final NativeDispatcher nd = new SocketDispatcher(true);
+
+ // 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;
+
+ // lazily set to true when the socket is configured non-blocking
+ 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;
+
+ // cached value of IPV6_TCLASS or IP_TOS socket option, protected by stateLock
+ private int trafficClass;
+
+ // 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 = MILLISECONDS.convert(nanos, NANOSECONDS);
+ }
+ 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);
+ }
+
+ /**
+ * Ensures that the socket is configured non-blocking when a timeout is specified.
+ * @throws IOException if there is an I/O error changing the blocking mode
+ */
+ private void configureNonBlockingIfNeeded(FileDescriptor fd, int timeout)
+ throws IOException
+ {
+ if (timeout > 0 && !nonBlocking) {
+ assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+ 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)
+ stateLock.notifyAll();
+ 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, int millis)
+ throws IOException
+ {
+ assert nonBlocking;
+ long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+ long remainingNanos = nanos;
+ long startNanos = System.nanoTime();
+ int n;
+ do {
+ park(fd, Net.POLLIN, remainingNanos);
+ n = tryRead(fd, b, off, len);
+ if (n == IOStatus.UNAVAILABLE) {
+ remainingNanos = nanos - (System.nanoTime() - startNanos);
+ if (remainingNanos <= 0) {
+ throw new SocketTimeoutException("Read timed out");
+ }
+ }
+ } while (n == IOStatus.UNAVAILABLE && isOpen());
+ 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 {
+ readLock.lock();
+ try {
+ // emulate legacy behavior to return -1, even if socket is closed
+ if (readEOF)
+ return -1;
+ int n = 0;
+ FileDescriptor fd = beginRead();
+ try {
+ if (connectionReset)
+ throw new SocketException("Connection reset");
+ if (isInputClosed)
+ return -1;
+ int timeout = this.timeout;
+ configureNonBlockingIfNeeded(fd, timeout);
+ n = tryRead(fd, b, off, len);
+ if (IOStatus.okayToRetry(n) && isOpen()) {
+ if (timeout > 0) {
+ // read with timeout
+ n = timedRead(fd, b, off, len, timeout);
+ } else {
+ // read, no timeout
+ do {
+ park(fd, Net.POLLIN);
+ n = tryRead(fd, b, off, len);
+ } while (IOStatus.okayToRetry(n) && isOpen());
+ }
+ }
+ if (n == -1)
+ readEOF = true;
+ 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);
+ }
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * 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 {
+ // read up to MAX_BUFFER_SIZE bytes
+ int size = Math.min(len, MAX_BUFFER_SIZE);
+ return implRead(b, off, size);
+ }
+ }
+
+ /**
+ * 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)
+ stateLock.notifyAll();
+ 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 an socket I/O error occurs
+ */
+ private int implWrite(byte[] b, int off, int len) throws IOException {
+ writeLock.lock();
+ try {
+ 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);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Writes a sequence of bytes to the socket from the given byte array.
+ * @throws SocketException if the socket is closed or an 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) {
+ 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;
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * For use by ServerSocket to copy the state from this connected SocketImpl
+ * to a target SocketImpl. If the target SocketImpl is not a newly created
+ * SocketImpl then it is first closed to release any resources. The target
+ * SocketImpl becomes the owner of the file descriptor, this SocketImpl
+ * is marked as closed and should be discarded.
+ */
+ @Override
+ public void copyTo(SocketImpl si) {
+ if (si instanceof NioSocketImpl) {
+ NioSocketImpl nsi = (NioSocketImpl) si;
+ if (nsi.state != ST_NEW) {
+ try {
+ nsi.close();
+ } catch (IOException ignore) { }
+ }
+ // copy/reset fields protected by stateLock
+ synchronized (nsi.stateLock) {
+ guarantee(nsi.state == ST_NEW || nsi.state == ST_CLOSED);
+ synchronized (this.stateLock) {
+ // this SocketImpl should be connected
+ guarantee(state == ST_CONNECTED && fd != null && fd.valid()
+ && localport > 0 && address != null && port > 0);
+
+ // copy fields
+ nsi.stream = this.stream;
+ nsi.fd = this.fd;
+ nsi.localport = this.localport;
+ nsi.address = this.address;
+ nsi.port = this.port;
+
+ // reset fields; do not reset timeout
+ nsi.nonBlocking = false;
+ nsi.isReuseAddress = false;
+ nsi.isInputClosed = false;
+ nsi.isOutputClosed = false;
+ nsi.state = ST_CONNECTED;
+
+ // GC'ing of this impl should not close the file descriptor
+ this.closer.disable();
+ this.state = ST_CLOSED;
+
+ // create new closer to execute when nsi is GC'ed
+ nsi.closer = FileDescriptorCloser.create(nsi);
+ }
+ }
+ // reset fields protected by readLock
+ nsi.readLock.lock();
+ try {
+ nsi.readEOF = false;
+ nsi.connectionReset = false;
+ } finally {
+ nsi.readLock.unlock();
+ }
+ } else {
+ synchronized (this.stateLock) {
+ // this SocketImpl should be connected
+ guarantee(state == ST_CONNECTED && fd != null && fd.valid()
+ && localport > 0 && address != null && port > 0);
+
+ // set fields in foreign impl
+ setSocketImplFields(si, fd, localport, address, port);
+
+ // disable closer to prevent GC'ing of this impl from
+ // closing the file descriptor
+ this.closer.disable();
+ this.state = ST_CLOSED;
+ }
+ }
+ }
+
+ /**
+ * 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_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(boolean completed) throws IOException {
+ synchronized (stateLock) {
+ readerThread = 0;
+ int state = this.state;
+ if (state == ST_CLOSING)
+ stateLock.notifyAll();
+ 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 int timedFinishConnect(FileDescriptor fd, int millis) throws IOException {
+ long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+ long remainingNanos = nanos;
+ long startNanos = System.nanoTime();
+ int n;
+ do {
+ park(fd, Net.POLLOUT, remainingNanos);
+ n = Net.pollConnectNow(fd);
+ if (n == 0) {
+ remainingNanos = nanos - (System.nanoTime() - startNanos);
+ if (remainingNanos <= 0) {
+ throw new SocketTimeoutException("Connect timed out");
+ }
+ }
+ } while (n == 0 && isOpen());
+ return n;
+ }
+
+ /**
+ * Attempts to establish a connection to the given socket address with a
+ * timeout. Closes the socket if connection cannot be established.
+ * @throws IllegalArgumentException if the address is not an InetSocketAddress
+ * @throws UnknownHostException if the InetSocketAddress is not resolved
+ * @throws IOException if the connection cannot be established
+ */
+ private void implConnect(SocketAddress remote, int millis) throws IOException {
+ if (!(remote instanceof InetSocketAddress))
+ throw new IllegalArgumentException("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 {
+ configureNonBlockingIfNeeded(fd, millis);
+ int n = Net.connect(fd, address, port);
+ if (IOStatus.okayToRetry(n) && isOpen()) {
+ if (millis > 0) {
+ // finish connect with timeout
+ n = timedFinishConnect(fd, millis);
+ } else {
+ // finish connect, no timeout
+ do {
+ park(fd, Net.POLLOUT);
+ n = Net.pollConnectNow(fd);
+ } while (n == 0 && isOpen());
+ }
+ }
+ connected = (n > 0) && isOpen();
+ } finally {
+ endConnect(connected);
+ }
+ } finally {
+ connectLock.unlock();
+ }
+ } catch (IOException ioe) {
+ close();
+ throw SocketExceptions.of(ioe, isa);
+ }
+ }
+
+ @Override
+ protected void connect(String host, int port) throws IOException {
+ implConnect(new InetSocketAddress(host, port), timeout);
+ }
+
+ @Override
+ protected void connect(InetAddress address, int port) throws IOException {
+ implConnect(new InetSocketAddress(address, port), timeout);
+ }
+
+ @Override
+ protected void connect(SocketAddress address, int timeout) throws IOException {
+ implConnect(address, timeout);
+ }
+
+ @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)
+ stateLock.notifyAll();
+ 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,
+ int millis)
+ throws IOException
+ {
+ assert nonBlocking;
+ long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+ long remainingNanos = nanos;
+ long startNanos = System.nanoTime();
+ int n;
+ do {
+ park(fd, Net.POLLIN, remainingNanos);
+ n = ServerSocketChannelImpl.accept0(fd, newfd, isaa);
+ if (n == IOStatus.UNAVAILABLE) {
+ remainingNanos = nanos - (System.nanoTime() - startNanos);
+ if (remainingNanos <= 0) {
+ throw new SocketTimeoutException("Accept timed out");
+ }
+ }
+ } while (n == IOStatus.UNAVAILABLE && isOpen());
+ return n;
+ }
+
+ /**
+ * Accepts a new connection so that the given SocketImpl is connected to
+ * the peer.
+ */
+ @Override
+ protected void accept(SocketImpl si) throws IOException {
+ // accept a connection
+ FileDescriptor newfd = new FileDescriptor();
+ InetSocketAddress[] isaa = new InetSocketAddress[1];
+
+ ReentrantLock acceptLock = readLock;
+ acceptLock.lock();
+ try {
+ int n = 0;
+ FileDescriptor fd = beginAccept();
+ try {
+ int timeout = this.timeout;
+ configureNonBlockingIfNeeded(fd, timeout);
+ n = ServerSocketChannelImpl.accept0(fd, newfd, isaa);
+ if (IOStatus.okayToRetry(n) && isOpen()) {
+ if (timeout > 0) {
+ // accept with timeout
+ n = timedAccept(fd, newfd, isaa, timeout);
+ } else {
+ // accept, no timeout
+ do {
+ park(fd, Net.POLLIN);
+ n = ServerSocketChannelImpl.accept0(fd, newfd, isaa);
+ } while (IOStatus.okayToRetry(n) && isOpen());
+ }
+ }
+ } 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
+ InetSocketAddress remoteAddress = isaa[0];
+ if (si instanceof NioSocketImpl) {
+ NioSocketImpl nsi = (NioSocketImpl) si;
+ synchronized (nsi.stateLock) {
+ nsi.fd = newfd;
+ nsi.stream = true;
+ nsi.closer = FileDescriptorCloser.create(nsi);
+ nsi.localport = localAddress.getPort();
+ nsi.address = remoteAddress.getAddress();
+ nsi.port = remoteAddress.getPort();
+ nsi.state = ST_CONNECTED;
+ }
+ } else {
+ // set fields in foreign impl
+ setSocketImplFields(si, newfd,
+ localAddress.getPort(),
+ remoteAddress.getAddress(),
+ remoteAddress.getPort());
+ }
+ }
+
+ @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 {
+ readLock.lock();
+ try {
+ ensureOpenAndConnected();
+ if (isInputClosed) {
+ return 0;
+ } else {
+ return Net.available(fd);
+ }
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Closes the socket, signalling and waiting for blocking I/O operations
+ * to complete.
+ */
+ @Override
+ protected void close() throws IOException {
+ boolean interrupted = false;
+
+ 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;
+ assert fd != null && closer != null;
+
+ // shutdown output when linger interval not set
+ try {
+ var SO_LINGER = StandardSocketOptions.SO_LINGER;
+ if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
+ Net.shutdown(fd, Net.SHUT_WR);
+ }
+ } catch (IOException ignore) { }
+
+ // interrupt and wait for kernel threads to complete I/O operations
+ long reader = readerThread;
+ long writer = writerThread;
+ if (reader != 0 || writer != 0) {
+ nd.preClose(fd);
+
+ if (reader != 0)
+ NativeThread.signal(reader);
+ if (writer != 0)
+ NativeThread.signal(writer);
+
+ // wait for blocking I/O operations to end
+ while (readerThread != 0 || writerThread != 0) {
+ try {
+ stateLock.wait();
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ }
+
+ // close file descriptor
+ try {
+ closer.run();
+ } finally {
+ this.state = ST_CLOSED;
+ }
+ }
+
+ // restore interrupt status
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ Set<SocketOption<?>> options = new HashSet<>();
+ options.addAll(super.supportedOptions());
+ if (server) {
+ options.addAll(ExtendedSocketOptions.serverSocketOptions());
+ } else {
+ options.addAll(ExtendedSocketOptions.clientSocketOptions());
+ }
+ if (Net.isReusePortAvailable())
+ options.add(StandardSocketOptions.SO_REUSEPORT);
+ return Collections.unmodifiableSet(options);
+ }
+
+ @Override
+ protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+ if (!supportedOptions().contains(opt))
+ throw new UnsupportedOperationException("'" + opt + "' not supported");
+ synchronized (stateLock) {
+ ensureOpen();
+ if (opt == StandardSocketOptions.IP_TOS) {
+ // maps to IP_TOS or IPV6_TCLASS
+ int i = (int) value;
+ Net.setSocketOption(fd, family(), opt, i);
+ trafficClass = i;
+ } 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) Integer.valueOf(trafficClass);
+ } 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);
+ trafficClass = 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 trafficClass;
+ 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 RuntimeException(ioe);
+ } finally {
+ if (!stream) {
+ // decrement
+ ResourceManager.afterUdpClose();
+ }
+ }
+ }
+ }
+
+ boolean disable() {
+ return CLOSED.compareAndSet(this, false, true);
+ }
+ }
+
+ /**
+ * Returns the socket protocol family.
+ */
+ private static ProtocolFamily family() {
+ if (Net.isIPv6Available()) {
+ return StandardProtocolFamily.INET6;
+ } else {
+ return StandardProtocolFamily.INET;
+ }
+ }
+
+ /**
+ * Sets the SocketImpl fields to the given values.
+ */
+ private static void setSocketImplFields(SocketImpl si,
+ FileDescriptor fd,
+ int localport,
+ InetAddress address,
+ int port)
+ {
+ PrivilegedExceptionAction<Void> pa = () -> {
+ setSocketImplField(si, "fd", fd);
+ setSocketImplField(si, "localport", localport);
+ setSocketImplField(si, "address", address);
+ setSocketImplField(si, "port", port);
+ return null;
+ };
+ try {
+ AccessController.doPrivileged(pa);
+ } catch (PrivilegedActionException pae) {
+ throw new InternalError(pae);
+ }
+ }
+
+ private static void setSocketImplField(SocketImpl si, String name, Object value)
+ throws Exception
+ {
+ Field field = SocketImpl.class.getDeclaredField(name);
+ field.setAccessible(true);
+ field.set(si, value);
+ }
+
+ /**
+ * Throws InternalError if the given expression is not true.
+ */
+ private static void guarantee(boolean expr) {
+ if (!expr) throw new InternalError();
+ }
+}
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Sat Mar 09 12:52:30 2019 +0000
@@ -529,7 +529,7 @@
// Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
// connections are pending) or IOStatus.INTERRUPTED.
//
- private native int accept0(FileDescriptor ssfd,
+ static native int accept0(FileDescriptor ssfd,
FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException;
--- a/src/java.base/unix/classes/sun/nio/ch/NativeThread.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/unix/classes/sun/nio/ch/NativeThread.java Sat Mar 09 12:52:30 2019 +0000
@@ -25,7 +25,6 @@
package sun.nio.ch;
-
// Signalling operations on native threads
//
// On some operating systems (e.g., Linux), closing a channel while another
@@ -33,23 +32,35 @@
// thread to be released. This class provides access to the native threads
// upon which Java threads are built, and defines a simple signal mechanism
// that can be used to release a native thread from a blocking I/O operation.
-// On systems that do not require this type of signalling, the current() method
-// always returns -1 and the signal(long) method has no effect.
+import jdk.internal.access.JavaLangAccess;
+import jdk.internal.access.SharedSecrets;
public class NativeThread {
+ private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
- // Returns an opaque token representing the native thread underlying the
- // invoking Java thread. On systems that do not require signalling, this
- // method always returns -1.
- //
- public static native long current();
+ /**
+ * Returns the current thread's ID.
+ */
+ public static long current() {
+ long tid = JLA.nativeTid();
+ if (tid == 0) {
+ tid = current0();
+ JLA.setNativeTid(tid);
+ }
+ return tid;
+ }
- // Signals the given native thread so as to release it from a blocking I/O
- // operation. On systems that do not require signalling, this method has
- // no effect.
- //
- public static native void signal(long nt);
+ /**
+ * Signals the given thread.
+ */
+ public static void signal(long tid) {
+ signal0(tid);
+ }
+
+ private static native long current0();
+
+ private static native void signal0(long tid);
private static native void init();
--- a/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java Sat Mar 09 12:52:30 2019 +0000
@@ -34,9 +34,22 @@
*/
class SocketDispatcher extends NativeDispatcher {
+ private final boolean detectConnectionReset;
+
+ SocketDispatcher(boolean detectConnectionReset) {
+ this.detectConnectionReset = detectConnectionReset;
+ }
+
+ SocketDispatcher() {
+ this(false);
+ }
int read(FileDescriptor fd, long address, int len) throws IOException {
- return FileDispatcherImpl.read0(fd, address, len);
+ if (detectConnectionReset) {
+ return read0(fd, address, len);
+ } else {
+ return FileDispatcherImpl.read0(fd, address, len);
+ }
}
long readv(FileDescriptor fd, long address, int len) throws IOException {
@@ -58,4 +71,20 @@
void preClose(FileDescriptor fd) throws IOException {
FileDispatcherImpl.preClose0(fd);
}
+
+ // -- Native methods --
+
+ /**
+ * Reads up to len bytes from a socket with special handling for "connection
+ * reset".
+ *
+ * @throws sun.net.ConnectionResetException if connection reset is detected
+ * @throws IOException if another I/O error occurs
+ */
+ private static native int read0(FileDescriptor fd, long address, int len)
+ throws IOException;
+
+ static {
+ IOUtil.load();
+ }
}
--- a/src/java.base/unix/native/libnio/ch/NativeThread.c Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/unix/native/libnio/ch/NativeThread.c Sat Mar 09 12:52:30 2019 +0000
@@ -77,7 +77,7 @@
}
JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_NativeThread_current(JNIEnv *env, jclass cl)
+Java_sun_nio_ch_NativeThread_current0(JNIEnv *env, jclass cl)
{
#ifdef __solaris__
return (jlong)thr_self();
@@ -87,7 +87,7 @@
}
JNIEXPORT void JNICALL
-Java_sun_nio_ch_NativeThread_signal(JNIEnv *env, jclass cl, jlong thread)
+Java_sun_nio_ch_NativeThread_signal0(JNIEnv *env, jclass cl, jlong thread)
{
int ret;
#ifdef __solaris__
--- a/src/java.base/unix/native/libnio/ch/Net.c Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/unix/native/libnio/ch/Net.c Sat Mar 09 12:52:30 2019 +0000
@@ -830,6 +830,7 @@
break;
case EADDRINUSE: /* Fall through */
case EADDRNOTAVAIL:
+ case EACCES:
xn = JNU_JAVANETPKG "BindException";
break;
default:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/native/libnio/ch/SocketDispatcher.c Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+ #include <sys/types.h>
+ #include <unistd.h>
+
+ #include "jni.h"
+ #include "jni_util.h"
+ #include "jlong.h"
+ #include "nio.h"
+ #include "nio_util.h"
+ #include "sun_nio_ch_SocketDispatcher.h"
+
+ JNIEXPORT jint JNICALL
+ Java_sun_nio_ch_SocketDispatcher_read0(JNIEnv *env, jclass clazz,
+ jobject fdo, jlong address, jint len)
+ {
+ jint fd = fdval(env, fdo);
+ void *buf = (void *)jlong_to_ptr(address);
+ jint n = read(fd, buf, len);
+ if ((n == -1) && (errno == ECONNRESET || errno == EPIPE)) {
+ JNU_ThrowByName(env, "sun/net/ConnectionResetException", "Connection reset");
+ return IOS_THROWN;
+ } else {
+ return convertReturnVal(env, n, JNI_TRUE);
+ }
+ }
--- a/src/java.base/windows/classes/sun/nio/ch/SocketDispatcher.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/windows/classes/sun/nio/ch/SocketDispatcher.java Sat Mar 09 12:52:30 2019 +0000
@@ -32,12 +32,11 @@
* for read and write operations.
*/
-class SocketDispatcher extends NativeDispatcher
-{
+class SocketDispatcher extends NativeDispatcher {
- static {
- IOUtil.load();
- }
+ SocketDispatcher() { }
+
+ SocketDispatcher(boolean ignore) { }
int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len);
@@ -63,7 +62,8 @@
close0(fd);
}
- //-- Native methods
+ // -- Native methods --
+
static native int read0(FileDescriptor fd, long address, int len)
throws IOException;
@@ -79,4 +79,8 @@
static native void preClose0(FileDescriptor fd) throws IOException;
static native void close0(FileDescriptor fd) throws IOException;
+
+ static {
+ IOUtil.load();
+ }
}
--- a/src/java.base/windows/native/libnio/ch/SocketDispatcher.c Thu Feb 28 16:37:28 2019 +0800
+++ b/src/java.base/windows/native/libnio/ch/SocketDispatcher.c Sat Mar 09 12:52:30 2019 +0000
@@ -72,7 +72,11 @@
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
- JNU_ThrowIOExceptionWithLastError(env, "Read failed");
+ if (theErr == WSAECONNRESET) {
+ JNU_ThrowIOException(env, "Connection reset by peer");
+ } else {
+ JNU_ThrowIOExceptionWithLastError(env, "Read failed");
+ }
return IOS_THROWN;
}
@@ -128,7 +132,11 @@
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
- JNU_ThrowIOExceptionWithLastError(env, "Vector read failed");
+ if (theErr == WSAECONNRESET) {
+ JNU_ThrowIOException(env, "Connection reset by peer");
+ } else {
+ JNU_ThrowIOExceptionWithLastError(env, "Vector read failed");
+ }
return IOS_THROWN;
}
@@ -174,7 +182,11 @@
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
- JNU_ThrowIOExceptionWithLastError(env, "Write failed");
+ if (theErr == WSAECONNRESET) {
+ JNU_ThrowIOException(env, "Connection reset by peer");
+ } else {
+ JNU_ThrowIOExceptionWithLastError(env, "Write failed");
+ }
return IOS_THROWN;
}
}
@@ -256,7 +268,11 @@
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
- JNU_ThrowIOExceptionWithLastError(env, "Vector write failed");
+ if (theErr == WSAECONNRESET) {
+ JNU_ThrowIOException(env, "Connection reset by peer");
+ } else {
+ JNU_ThrowIOExceptionWithLastError(env, "Vector write failed");
+ }
return IOS_THROWN;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java Sat Mar 09 12:52:30 2019 +0000
@@ -27,15 +27,14 @@
import java.io.IOException;
import java.net.InetAddress;
+import java.net.Socket;
import jdk.jfr.events.SocketReadEvent;
/**
* See {@link JITracer} for an explanation of this code.
*/
-@JIInstrumentationTarget("java.net.SocketInputStream")
-@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketInputStreamInstrumentor$AbstractPlainSocketImpl",
- to = "java.net.AbstractPlainSocketImpl")
+@JIInstrumentationTarget("java.net.Socket$SocketInputStream")
final class SocketInputStreamInstrumentor {
private SocketInputStreamInstrumentor() {
@@ -43,30 +42,28 @@
@SuppressWarnings("deprecation")
@JIInstrumentationMethod
- int read(byte b[], int off, int length, int timeout) throws IOException {
+ public int read(byte b[], int off, int length) throws IOException {
SocketReadEvent event = SocketReadEvent.EVENT.get();
if (!event.isEnabled()) {
- return read(b, off, length, timeout);
+ return read(b, off, length);
}
int bytesRead = 0;
try {
event.begin();
- bytesRead = read(b, off, length, timeout);
+ bytesRead = read(b, off, length);
} finally {
event.end();
if (event.shouldCommit()) {
- String hostString = impl.address.toString();
- int delimiterIndex = hostString.lastIndexOf('/');
-
- event.host = hostString.substring(0, delimiterIndex);
- event.address = hostString.substring(delimiterIndex + 1);
- event.port = impl.port;
+ InetAddress remote = parent.getInetAddress();
+ event.host = remote.getHostName();
+ event.address = remote.getHostAddress();
+ event.port = parent.getPort();
if (bytesRead < 0) {
event.endOfStream = true;
} else {
event.bytesRead = bytesRead;
}
- event.timeout = timeout;
+ event.timeout = parent.getSoTimeout();
event.commit();
event.reset();
@@ -75,14 +72,6 @@
return bytesRead;
}
- private AbstractPlainSocketImpl impl = null;
-
- void silenceFindBugsUnwrittenField(InetAddress dummy) {
- impl.address = dummy;
- }
-
- static class AbstractPlainSocketImpl {
- InetAddress address;
- int port;
- }
+ // private field in java.net.Socket$SocketInputStream
+ private Socket parent;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java Thu Feb 28 16:37:28 2019 +0800
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java Sat Mar 09 12:52:30 2019 +0000
@@ -27,15 +27,14 @@
import java.io.IOException;
import java.net.InetAddress;
+import java.net.Socket;
import jdk.jfr.events.SocketWriteEvent;
/**
* See {@link JITracer} for an explanation of this code.
*/
-@JIInstrumentationTarget("java.net.SocketOutputStream")
-@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketOutputStreamInstrumentor$AbstractPlainSocketImpl",
- to = "java.net.AbstractPlainSocketImpl")
+@JIInstrumentationTarget("java.net.Socket$SocketOutputStream")
final class SocketOutputStreamInstrumentor {
private SocketOutputStreamInstrumentor() {
@@ -43,27 +42,25 @@
@SuppressWarnings("deprecation")
@JIInstrumentationMethod
- private void socketWrite(byte b[], int off, int len) throws IOException {
+ public void write(byte b[], int off, int len) throws IOException {
SocketWriteEvent event = SocketWriteEvent.EVENT.get();
if (!event.isEnabled()) {
- socketWrite(b, off, len);
+ write(b, off, len);
return;
}
int bytesWritten = 0;
try {
event.begin();
- socketWrite(b, off, len);
+ write(b, off, len);
bytesWritten = len;
} finally {
event.end() ;
if (event.shouldCommit()) {
- String hostString = impl.address.toString();
- int delimiterIndex = hostString.lastIndexOf('/');
-
- event.host = hostString.substring(0, delimiterIndex);
- event.address = hostString.substring(delimiterIndex + 1);
- event.port = impl.port;
- event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten;
+ InetAddress remote = parent.getInetAddress();
+ event.host = remote.getHostName();
+ event.address = remote.getHostAddress();
+ event.port = parent.getPort();
+ event.bytesWritten = bytesWritten;
event.commit();
event.reset();
@@ -71,14 +68,6 @@
}
}
- private AbstractPlainSocketImpl impl = null;
-
- void silenceFindBugsUnwrittenField(InetAddress dummy) {
- impl.address = dummy;
- }
-
- static class AbstractPlainSocketImpl {
- InetAddress address;
- int port;
- }
+ // private field in java.net.Socket$SocketOutputStream
+ private Socket parent;
}
--- a/test/jdk/ProblemList.txt Thu Feb 28 16:37:28 2019 +0800
+++ b/test/jdk/ProblemList.txt Sat Mar 09 12:52:30 2019 +0000
@@ -562,6 +562,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
@@ -871,3 +874,5 @@
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 0000000 generic-all
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/AsyncShutdown.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,138 @@
+/*
+ * 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
+ * @requires (os.family == "linux" | os.family == "mac")
+ * @run testng AsyncShutdown
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl AsyncShutdown
+ * @summary Test shutdownInput/shutdownOutput with threads blocked in read/write
+ */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class AsyncShutdown {
+
+ public void testShutdownInput1() throws IOException {
+ withConnection((s1, s2) -> {
+ scheduleShutdownInput(s1, 2000);
+ int n = s1.getInputStream().read();
+ assertTrue(n == -1);
+ });
+ }
+
+ public void testShutdownInput2() throws IOException {
+ withConnection((s1, s2) -> {
+ scheduleShutdownInput(s1, 2000);
+ s1.setSoTimeout(30*1000);
+ int n = s1.getInputStream().read();
+ assertTrue(n == -1);
+ });
+ }
+
+ public void testShutdownOutput1() throws IOException {
+ withConnection((s1, s2) -> {
+ scheduleShutdownOutput(s1, 2000);
+ byte[] data = new byte[128*1024];
+ try {
+ while (true) {
+ s1.getOutputStream().write(data);
+ }
+ } catch (IOException expected) { }
+ });
+ }
+
+ public void testShutdownOutput2() throws IOException {
+ withConnection((s1, s2) -> {
+ s1.setSoTimeout(100);
+ try {
+ s1.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketTimeoutException e) { }
+
+ scheduleShutdownOutput(s1, 2000);
+ byte[] data = new byte[128*1024];
+ try {
+ while (true) {
+ s1.getOutputStream().write(data);
+ }
+ } catch (IOException expected) { }
+ });
+ }
+
+ static void scheduleShutdownInput(Socket s, long delay) {
+ schedule(() -> {
+ try {
+ s.shutdownInput();
+ } catch (IOException ioe) { }
+ }, delay);
+ }
+
+ static void scheduleShutdownOutput(Socket s, long delay) {
+ schedule(() -> {
+ try {
+ s.shutdownOutput();
+ } catch (IOException ioe) { }
+ }, delay);
+ }
+
+ static void schedule(Runnable task, long delay) {
+ ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+ try {
+ executor.schedule(task, delay, TimeUnit.MILLISECONDS);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ interface ThrowingBiConsumer<T, U> {
+ void accept(T t, U u) throws IOException;
+ }
+
+ static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer)
+ throws IOException
+ {
+ Socket s1 = null;
+ Socket s2 = null;
+ try (ServerSocket ss = new ServerSocket(0)) {
+ s1 = new Socket();
+ s1.connect(ss.getLocalSocketAddress());
+ s2 = ss.accept();
+ consumer.accept(s1, s2);
+ } finally {
+ if (s1 != null) s1.close();
+ if (s2 != null) s2.close();
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/ConnectionReset.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,219 @@
+/*
+ * 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
+ * @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
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class ConnectionReset {
+
+ static final int REPEAT_COUNT = 5;
+
+ /**
+ * Tests available before read when there are no bytes to read
+ */
+ public void testAvailableBeforeRead1() throws IOException {
+ System.out.println("testAvailableBeforeRead1");
+ withResetConnection(null, s -> {
+ InputStream in = s.getInputStream();
+ for (int i=0; i<REPEAT_COUNT; i++) {
+ int bytesAvailable = in.available();
+ System.out.format("available => %d%n", bytesAvailable);
+ assertTrue(bytesAvailable == 0);
+ try {
+ int bytesRead = in.read();
+ if (bytesRead == -1) {
+ System.out.println("read => EOF");
+ } else {
+ System.out.println("read => 1 byte");
+ }
+ assertTrue(false);
+ } catch (IOException ioe) {
+ System.out.format("read => %s (expected)%n", ioe);
+ }
+ }
+ });
+ }
+
+ /**
+ * Tests available before read when there are bytes to read
+ */
+ public void testAvailableBeforeRead2() throws IOException {
+ System.out.println("testAvailableBeforeRead2");
+ byte[] data = { 1, 2, 3 };
+ withResetConnection(data, s -> {
+ InputStream in = s.getInputStream();
+ int remaining = data.length;
+ for (int i=0; i<REPEAT_COUNT; i++) {
+ int bytesAvailable = in.available();
+ System.out.format("available => %d%n", bytesAvailable);
+ assertTrue(bytesAvailable <= remaining);
+ try {
+ int bytesRead = in.read();
+ if (bytesRead == -1) {
+ System.out.println("read => EOF");
+ assertTrue(false);
+ } else {
+ System.out.println("read => 1 byte");
+ assertTrue(remaining > 0);
+ remaining--;
+ }
+ } catch (IOException ioe) {
+ System.out.format("read => %s%n", ioe);
+ remaining = 0;
+ }
+ }
+ });
+ }
+
+ /**
+ * Tests read before available when there are no bytes to read
+ */
+ public void testReadBeforeAvailable1() throws IOException {
+ System.out.println("testReadBeforeAvailable1");
+ withResetConnection(null, s -> {
+ InputStream in = s.getInputStream();
+ for (int i=0; i<REPEAT_COUNT; i++) {
+ try {
+ int bytesRead = in.read();
+ if (bytesRead == -1) {
+ System.out.println("read => EOF");
+ } else {
+ System.out.println("read => 1 byte");
+ }
+ assertTrue(false);
+ } catch (IOException ioe) {
+ System.out.format("read => %s (expected)%n", ioe);
+ }
+ int bytesAvailable = in.available();
+ System.out.format("available => %d%n", bytesAvailable);
+ assertTrue(bytesAvailable == 0);
+ }
+ });
+ }
+
+ /**
+ * Tests read before available when there are bytes to read
+ */
+ public void testReadBeforeAvailable2() throws IOException {
+ System.out.println("testReadBeforeAvailable2");
+ byte[] data = { 1, 2, 3 };
+ withResetConnection(data, s -> {
+ InputStream in = s.getInputStream();
+ int remaining = data.length;
+ for (int i=0; i<REPEAT_COUNT; i++) {
+ try {
+ int bytesRead = in.read();
+ if (bytesRead == -1) {
+ System.out.println("read => EOF");
+ assertTrue(false);
+ } else {
+ System.out.println("read => 1 byte");
+ assertTrue(remaining > 0);
+ remaining--;
+ }
+ } catch (IOException ioe) {
+ System.out.format("read => %s%n", ioe);
+ remaining = 0;
+ }
+ int bytesAvailable = in.available();
+ System.out.format("available => %d%n", bytesAvailable);
+ assertTrue(bytesAvailable <= remaining);
+ }
+ });
+ }
+
+ /**
+ * Tests available and read on a socket closed after connection reset
+ */
+ public void testAfterClose() throws IOException {
+ System.out.println("testAfterClose");
+ withResetConnection(null, s -> {
+ InputStream in = s.getInputStream();
+ try {
+ in.read();
+ assertTrue(false);
+ } catch (IOException ioe) {
+ // expected
+ }
+ s.close();
+ try {
+ int bytesAvailable = in.available();
+ System.out.format("available => %d%n", bytesAvailable);
+ assertTrue(false);
+ } catch (IOException ioe) {
+ System.out.format("available => %s (expected)%n", ioe);
+ }
+ try {
+ int n = in.read();
+ System.out.format("read => %d%n", n);
+ assertTrue(false);
+ } catch (IOException ioe) {
+ System.out.format("read => %s (expected)%n", ioe);
+ }
+ });
+ }
+
+ interface ThrowingConsumer<T> {
+ void accept(T t) throws IOException;
+ }
+
+ /**
+ * Invokes a consumer with a Socket connected to a peer that has closed the
+ * connection with a "connection reset". The peer sends the given data bytes
+ * before closing (when data is not null).
+ */
+ static void withResetConnection(byte[] data, ThrowingConsumer<Socket> consumer)
+ throws IOException
+ {
+ var loopback = InetAddress.getLoopbackAddress();
+ try (var listener = new ServerSocket()) {
+ listener.bind(new InetSocketAddress(loopback, 0));
+ try (var socket = new Socket()) {
+ socket.connect(listener.getLocalSocketAddress());
+ try (Socket peer = listener.accept()) {
+ if (data != null) {
+ peer.getOutputStream().write(data);
+ }
+ peer.setSoLinger(true, 0);
+ }
+ consumer.accept(socket);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/Timeouts.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,500 @@
+/*
+ * 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
+ * @library /test/lib
+ * @build jdk.test.lib.Utils
+ * @run testng Timeouts
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl Timeouts
+ * @summary Test Socket timeouts
+ */
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ConnectException;
+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.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+import jdk.test.lib.Utils;
+
+@Test
+public class Timeouts {
+
+ /**
+ * Test timed connect where connection is established
+ */
+ public void testTimedConnect1() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ try (Socket s = new Socket()) {
+ s.connect(ss.getLocalSocketAddress(), 2000);
+ }
+ }
+ }
+
+ /**
+ * Test timed connect where connection is refused
+ */
+ public void testTimedConnect2() throws IOException {
+ try (Socket s = new Socket()) {
+ SocketAddress remote = Utils.refusingEndpoint();
+ try {
+ s.connect(remote, 2000);
+ } catch (ConnectException expected) { }
+ }
+ }
+
+ /**
+ * Test connect with a timeout of Integer.MAX_VALUE
+ */
+ public void testTimedConnect3() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ try (Socket s = new Socket()) {
+ s.connect(ss.getLocalSocketAddress(), Integer.MAX_VALUE);
+ }
+ }
+ }
+
+ /**
+ * Test connect with a negative timeout. This case is not currently specified
+ * but the long standing behavior is to throw IllegalArgumentException.
+ */
+ public void testTimedConnect4() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ try (Socket s = new Socket()) {
+ try {
+ s.connect(ss.getLocalSocketAddress(), -1);
+ assertTrue(false);
+ } catch (IllegalArgumentException expected) { }
+ }
+ }
+ }
+
+ /**
+ * Test timed read where the read succeeds immediately
+ */
+ public void testTimedRead1() throws IOException {
+ withConnection((s1, s2) -> {
+ s1.getOutputStream().write(99);
+ s2.setSoTimeout(30*1000);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test timed read where the read succeeds after a delay
+ */
+ public void testTimedRead2() throws IOException {
+ withConnection((s1, s2) -> {
+ scheduleWrite(s1.getOutputStream(), 99, 2000);
+ s2.setSoTimeout(30*1000);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test timed read where the read times out
+ */
+ public void testTimedRead3() throws IOException {
+ withConnection((s1, s2) -> {
+ s2.setSoTimeout(2000);
+ try {
+ s2.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketTimeoutException expected) { }
+ });
+ }
+
+ /**
+ * Test timed read that succeeds after a previous read has timed out
+ */
+ public void testTimedRead4() throws IOException {
+ withConnection((s1, s2) -> {
+ s2.setSoTimeout(2000);
+ try {
+ s2.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketTimeoutException e) { }
+ s1.getOutputStream().write(99);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test timed read that succeeds after a previous read has timed out and
+ * after a short delay
+ */
+ public void testTimedRead5() throws IOException {
+ withConnection((s1, s2) -> {
+ s2.setSoTimeout(2000);
+ try {
+ s2.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketTimeoutException e) { }
+ s2.setSoTimeout(30*3000);
+ scheduleWrite(s1.getOutputStream(), 99, 2000);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test untimed read that succeeds after a previous read has timed out
+ */
+ public void testTimedRead6() throws IOException {
+ withConnection((s1, s2) -> {
+ s2.setSoTimeout(2000);
+ try {
+ s2.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketTimeoutException e) { }
+ s1.getOutputStream().write(99);
+ s2.setSoTimeout(0);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test untimed read that succeeds after a previous read has timed out and
+ * after a short delay
+ */
+ public void testTimedRead7() throws IOException {
+ withConnection((s1, s2) -> {
+ s2.setSoTimeout(2000);
+ try {
+ s2.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketTimeoutException e) { }
+ scheduleWrite(s1.getOutputStream(), 99, 2000);
+ s2.setSoTimeout(0);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test async close of timed read
+ */
+ public void testTimedRead8() throws IOException {
+ withConnection((s1, s2) -> {
+ s2.setSoTimeout(30*1000);
+ scheduleClose(s2, 2000);
+ try {
+ s2.getInputStream().read();
+ assertTrue(false);
+ } catch (SocketException expected) { }
+ });
+ }
+
+ /**
+ * Test read with a timeout of Integer.MAX_VALUE
+ */
+ public void testTimedRead9() throws IOException {
+ withConnection((s1, s2) -> {
+ scheduleWrite(s1.getOutputStream(), 99, 2000);
+ s2.setSoTimeout(Integer.MAX_VALUE);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+ });
+ }
+
+ /**
+ * Test writing after a timed read. The timed read changes the underlying
+ * socket to non-blocking.
+ */
+ public void testTimedWrite1() throws IOException {
+ withConnection((s1, s2) -> {
+ s1.getOutputStream().write(99);
+ s2.setSoTimeout(3000);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+
+ // schedule thread to read s1 to EOF
+ scheduleReadToEOF(s1.getInputStream(), 3000);
+
+ // write a lot so that write blocks
+ byte[] data = new byte[128*1024];
+ for (int i = 0; i < 100; i++) {
+ s2.getOutputStream().write(data);
+ }
+ });
+ }
+
+ /**
+ * Test async close of writer (after a timed read). The timed read changes
+ * the underlying socket to non-blocking.
+ */
+ public void testTimedWrite2() throws IOException {
+ withConnection((s1, s2) -> {
+ s1.getOutputStream().write(99);
+ s2.setSoTimeout(3000);
+ int b = s2.getInputStream().read();
+ assertTrue(b == 99);
+
+ // schedule s2 to be be closed
+ scheduleClose(s2, 3000);
+
+ // write a lot so that write blocks
+ byte[] data = new byte[128*1024];
+ try {
+ while (true) {
+ s2.getOutputStream().write(data);
+ }
+ } catch (SocketException expected) { }
+ });
+ }
+
+ /**
+ * Test timed accept where a connection is established immediately
+ */
+ public void testTimedAccept1() throws IOException {
+ Socket s1 = null;
+ Socket s2 = null;
+ try (ServerSocket ss = new ServerSocket(0)) {
+ s1 = new Socket();
+ s1.connect(ss.getLocalSocketAddress());
+ ss.setSoTimeout(30*1000);
+ s2 = ss.accept();
+ } finally {
+ if (s1 != null) s1.close();
+ if (s2 != null) s2.close();
+ }
+ }
+
+ /**
+ * Test timed accept where a connection is established after a short delay
+ */
+ public void testTimedAccept2() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ ss.setSoTimeout(30*1000);
+ scheduleConnect(ss.getLocalSocketAddress(), 2000);
+ Socket s = ss.accept();
+ s.close();
+ }
+ }
+
+ /**
+ * Test timed accept where the accept times out
+ */
+ public void testTimedAccept3() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ ss.setSoTimeout(2000);
+ try {
+ Socket s = ss.accept();
+ s.close();
+ assertTrue(false);
+ } catch (SocketTimeoutException expected) { }
+ }
+ }
+
+ /**
+ * Test timed accept where a connection is established immediately after a
+ * previous accept timed out.
+ */
+ public void testTimedAccept4() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ ss.setSoTimeout(2000);
+ try {
+ Socket s = ss.accept();
+ s.close();
+ assertTrue(false);
+ } catch (SocketTimeoutException expected) { }
+ try (Socket s1 = new Socket()) {
+ s1.connect(ss.getLocalSocketAddress());
+ Socket s2 = ss.accept();
+ s2.close();
+ }
+ }
+ }
+
+ /**
+ * Test untimed accept where a connection is established after a previous
+ * accept timed out
+ */
+ public void testTimedAccept5() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ ss.setSoTimeout(2000);
+ try {
+ Socket s = ss.accept();
+ s.close();
+ assertTrue(false);
+ } catch (SocketTimeoutException expected) { }
+ ss.setSoTimeout(0);
+ try (Socket s1 = new Socket()) {
+ s1.connect(ss.getLocalSocketAddress());
+ Socket s2 = ss.accept();
+ s2.close();
+ }
+ }
+ }
+
+ /**
+ * Test untimed accept where a connection is established after a previous
+ * accept timed out and after a short delay
+ */
+ public void testTimedAccept6() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ ss.setSoTimeout(2000);
+ try {
+ Socket s = ss.accept();
+ s.close();
+ assertTrue(false);
+ } catch (SocketTimeoutException expected) { }
+ ss.setSoTimeout(0);
+ scheduleConnect(ss.getLocalSocketAddress(), 2000);
+ Socket s = ss.accept();
+ s.close();
+ }
+ }
+
+ /**
+ * Test async close of a timed accept
+ */
+ public void testTimedAccept7() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ ss.setSoTimeout(30*1000);
+ scheduleClose(ss, 2000);
+ try {
+ ss.accept().close();
+ assertTrue(false);
+ } catch (SocketException expected) { }
+ }
+ }
+
+ /**
+ * Test Socket setSoTimeout with a negative timeout. This case is not currently
+ * specified but the long standing behavior is to throw IllegalArgumentException.
+ */
+ @Test(expectedExceptions = { IllegalArgumentException.class })
+ public void testBadTimeout1() throws IOException {
+ try (Socket s = new Socket()) {
+ s.setSoTimeout(-1);
+ }
+ }
+
+ /**
+ * Test ServerSocket setSoTimeout with a negative timeout. This case is not
+ * currently specified but the long standing behavior is to throw
+ * IllegalArgumentException.
+ */
+ @Test(expectedExceptions = { IllegalArgumentException.class })
+ public void testBadTimeout2() throws IOException {
+ try (ServerSocket ss = new ServerSocket()) {
+ ss.setSoTimeout(-1);
+ }
+ }
+
+ interface ThrowingBiConsumer<T, U> {
+ void accept(T t, U u) throws IOException;
+ }
+
+ /**
+ * Invokes the consumer with a connected pair of sockets
+ */
+ static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer)
+ throws IOException
+ {
+ Socket s1 = null;
+ Socket s2 = null;
+ try (ServerSocket ss = new ServerSocket(0)) {
+ s1 = new Socket();
+ s1.connect(ss.getLocalSocketAddress());
+ s2 = ss.accept();
+ consumer.accept(s1, s2);
+ } finally {
+ if (s1 != null) s1.close();
+ if (s2 != null) s2.close();
+ }
+ }
+
+ /**
+ * Schedule c to be closed after a delay
+ */
+ static void scheduleClose(Closeable c, long delay) {
+ schedule(() -> {
+ try {
+ c.close();
+ } catch (IOException ioe) { }
+ }, delay);
+ }
+
+ /**
+ * Schedule a thread to connect to the given end point after a delay
+ */
+ static void scheduleConnect(SocketAddress remote, long delay) {
+ schedule(() -> {
+ try (Socket s = new Socket()) {
+ s.connect(remote);
+ } catch (IOException ioe) { }
+ }, delay);
+ }
+
+ /**
+ * Schedule a thread to read to EOF after a delay
+ */
+ static void scheduleReadToEOF(InputStream in, long delay) {
+ schedule(() -> {
+ byte[] bytes = new byte[8192];
+ try {
+ while (in.read(bytes) != -1) { }
+ } catch (IOException ioe) { }
+ }, delay);
+ }
+
+ /**
+ * Schedule a thread to write after a delay
+ */
+ static void scheduleWrite(OutputStream out, byte[] data, long delay) {
+ schedule(() -> {
+ try {
+ out.write(data);
+ } catch (IOException ioe) { }
+ }, delay);
+ }
+ static void scheduleWrite(OutputStream out, int b, long delay) {
+ scheduleWrite(out, new byte[] { (byte)b }, delay);
+ }
+
+ static void schedule(Runnable task, long delay) {
+ ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+ try {
+ executor.schedule(task, delay, TimeUnit.MILLISECONDS);
+ } finally {
+ executor.shutdown();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/UdpSocket.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,76 @@
+/*
+ * 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
+ * @run main UdpSocket
+ * @run main/othervm -Djdk.net.usePlainSocketImpl UdpSocket
+ * @summary Basic test for a Socket to a UDP socket
+ */
+
+import java.io.IOException;
+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.util.Arrays;
+
+public class UdpSocket {
+
+ static 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));
+
+ int port = ((InetSocketAddress) dc.getLocalAddress()).getPort();
+ try (Socket s = new Socket(loopback, port, false)) {
+
+ // send datagram with socket output stream
+ byte[] array1 = MESSAGE.getBytes("UTF-8");
+ s.getOutputStream().write(array1);
+
+ // receive the datagram
+ var buf = ByteBuffer.allocate(100);
+ SocketAddress remote = dc.receive(buf);
+ buf.flip();
+ if (buf.remaining() != MESSAGE.length())
+ throw new RuntimeException("Unexpected size");
+
+ // echo the datagram
+ dc.send(buf, remote);
+
+ // 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");
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/BadSocketImpls.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,147 @@
+/*
+ * 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
+ * @summary Combinations of bad SocketImpls
+ * @run testng/othervm BadSocketImpls
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl BadSocketImpls
+ */
+
+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.Socket;
+import java.net.SocketAddress;
+import java.net.SocketImpl;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static java.lang.String.format;
+import static java.lang.System.out;
+import static org.testng.Assert.fail;
+
+public class BadSocketImpls {
+
+ Class<IOException> IOE = IOException.class;
+
+ /**
+ * Tests that a server-side custom socket impl, whose accept is a no-op,
+ * does not have any adverse negative effects. Default accept impl.
+ */
+ @Test
+ public void testNoOpAccept() throws IOException {
+ ServerSocket ss = new ServerSocket(new NoOpSocketImpl()) { };
+ try (ss) {
+ ss.bind(new InetSocketAddress(0));
+ assertThrows(IOE, ss::accept);
+ }
+ }
+
+ /**
+ * Tests that a server-side custom socket impl, whose accept is a no-op,
+ * does not have any adverse negative effects. Custom accept, client has
+ * an explicit null impl.
+ */
+ @Test
+ public void testNoOpAcceptWithNullClientImpl() throws IOException {
+ ServerSocket ss = new ServerSocket(new NoOpSocketImpl()) {
+ @Override
+ public Socket accept() throws IOException {
+ Socket s = new Socket((SocketImpl)null) { };
+ implAccept(s);
+ return s;
+ }
+ };
+ try (ss) {
+ ss.bind(new InetSocketAddress(0));
+ assertThrows(IOE, ss::accept);
+ }
+ }
+
+ /**
+ * Tests that a server-side custom socket impl, whose accept is a no-op,
+ * does not have any adverse negative effects. Custom accept, client has
+ * a platform impl.
+ */
+ @Test
+ public void testNoOpAcceptWithPlatformClientImpl() throws IOException {
+ ServerSocket ss = new ServerSocket(new NoOpSocketImpl()) {
+ @Override
+ public Socket accept() throws IOException {
+ Socket s = new Socket();
+ implAccept(s);
+ return s;
+ }
+ };
+ try (ss) {
+ ss.bind(new InetSocketAddress(0));
+ assertThrows(IOE, ss::accept);
+ }
+ }
+
+ static class NoOpSocketImpl extends SocketImpl {
+ @Override protected void create(boolean b) { }
+ @Override protected void connect(String s, int i) { }
+ @Override protected void connect(InetAddress inetAddress, int i) { }
+ @Override protected void connect(SocketAddress socketAddress, int i) { }
+ @Override protected void bind(InetAddress inetAddress, int i) { }
+ @Override protected void listen(int i) { }
+ @Override protected void accept(SocketImpl socket) { }
+ @Override protected InputStream getInputStream() { return null; }
+ @Override protected OutputStream getOutputStream() { return null; }
+ @Override protected int available() { return 0; }
+ @Override protected void close() { }
+ @Override protected void sendUrgentData(int i) { }
+ @Override public void setOption(int i, Object o) { }
+ @Override public Object getOption(int i) { return null; }
+ }
+
+ static <T extends Throwable> void assertThrows(Class<T> throwableClass,
+ ThrowingRunnable runnable) {
+ try {
+ runnable.run();
+ fail(format("Expected %s to be thrown, but nothing was thrown",
+ throwableClass.getSimpleName()));
+ } catch (Throwable t) {
+ if (!throwableClass.isInstance(t)) {
+ fail(format("Expected %s to be thrown, but %s was thrown",
+ throwableClass.getSimpleName(),
+ t.getClass().getSimpleName()),
+ t);
+ }
+ out.println("caught expected exception: " + t);
+ }
+ }
+
+ interface ThrowingRunnable {
+ void run() throws Throwable;
+ }
+
+ @BeforeMethod
+ public void breakBetweenTests() {
+ System.out.println("\n-------\n");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/SocketImplCombinations.java Sat Mar 09 12:52:30 2019 +0000
@@ -0,0 +1,844 @@
+/*
+ * 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
+ * @modules java.base/java.net:+open java.base/sun.nio.ch:+open
+ * @run testng/othervm SocketImplCombinations
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl SocketImplCombinations
+ * @summary Test Socket and ServerSocket with combinations of SocketImpls
+ */
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketImpl;
+import java.net.SocketImplFactory;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.function.BiConsumer;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class SocketImplCombinations {
+
+ /**
+ * Test creating an unconnected Socket, it should be created with a platform SocketImpl.
+ */
+ public void testNewSocket1() throws IOException {
+ try (Socket s = new Socket()) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ }
+ }
+
+ /**
+ * Test creating a connected Socket, it should be created with a platform SocketImpl.
+ */
+ public void testNewSocket2() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ try (Socket s = new Socket(ss.getInetAddress(), ss.getLocalPort())) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ }
+ }
+ }
+
+ /**
+ * Test creating a Socket for a DIRECT connection, it should be created with a
+ * platform SocketImpl.
+ */
+ public void testNewSocket3() throws IOException {
+ try (Socket s = new Socket(Proxy.NO_PROXY)) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isPlatformSocketImpl(si));
+ }
+ }
+
+ /**
+ * Test creating a Socket for a SOCKS connection, it should be created with a
+ * SOCKS SocketImpl.
+ */
+ public void testNewSocket4() throws IOException {
+ var address = new InetSocketAddress("127.0.0.1", 1080);
+ var socksProxy = new Proxy(Proxy.Type.SOCKS, address);
+ try (Socket s = new Socket(socksProxy)) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ }
+ }
+
+ /**
+ * Test creating a Socket for a HTTP proxy connection, it should be created with
+ * a HTTP proxy SocketImpl.
+ */
+ public void testNewSocket5() throws IOException {
+ var address = new InetSocketAddress("127.0.0.1", 8080);
+ var httpProxy = new Proxy(Proxy.Type.HTTP, address);
+ try (Socket s = new Socket(httpProxy)) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isHttpConnectSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ }
+ }
+
+ /**
+ * Test creating a Socket no SocketImpl. A platform SocketImpl should be
+ * created lazily.
+ */
+ public void testNewSocket6() throws IOException {
+ Socket s = new Socket((SocketImpl) null) { };
+ try (s) {
+ assertTrue(getSocketImpl(s) == null);
+ s.bind(new InetSocketAddress(0)); // force SocketImpl to be created
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ }
+ }
+
+ /**
+ * Test creating a Socket with a custom SocketImpl.
+ */
+ public void testNewSocket7() throws IOException {
+ Socket s = new Socket(new CustomSocketImpl(false)) { };
+ try (s) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(si instanceof CustomSocketImpl);
+ }
+ }
+
+ /**
+ * Test creating a Socket when there is a SocketImplFactory set.
+ */
+ public void testNewSocket8() throws IOException {
+ setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+ try (Socket s = new Socket()) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(si instanceof CustomSocketImpl);
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test creating a Socket for a DIRECT connection when there is a
+ * SocketImplFactory set.
+ */
+ public void testNewSocket9() throws IOException {
+ setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+ try (Socket s = new Socket(Proxy.NO_PROXY)) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(si instanceof CustomSocketImpl);
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test creating a Socket for a SOCKS connection when there is a
+ * SocketImplFactory set.
+ */
+ public void testNewSocket10() throws IOException {
+ var address = new InetSocketAddress("127.0.0.1", 1080);
+ var socksProxy = new Proxy(Proxy.Type.SOCKS, address);
+ setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+ try (Socket s = new Socket(socksProxy)) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test creating a Socket for a HTTP proxy connection when there is a
+ * SocketImplFactory set.
+ */
+ public void testNewSocket11() throws IOException {
+ var address = new InetSocketAddress("127.0.0.1", 8080);
+ var httpProxy = new Proxy(Proxy.Type.HTTP, address);
+ setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+ try (Socket s = new Socket(httpProxy)) {
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isHttpConnectSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test creating a Socket no SocketImpl when there is a SocketImplFactory set.
+ */
+ public void testNewSocket12() throws IOException {
+ setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+ try {
+ Socket s = new Socket((SocketImpl) null) { };
+ try (s) {
+ assertTrue(getSocketImpl(s) == null);
+ s.bind(new InetSocketAddress(0)); // force SocketImpl to be created
+ assertTrue(getSocketImpl(s) instanceof CustomSocketImpl);
+ }
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test creating an unbound ServerSocket, it should be created with a platform
+ * SocketImpl.
+ */
+ public void testNewServerSocket1() throws IOException {
+ try (ServerSocket ss = new ServerSocket()) {
+ SocketImpl si = getSocketImpl(ss);
+ assertTrue(isPlatformSocketImpl(si));
+ }
+ }
+
+ /**
+ * Test creating a bound ServerSocket, it should be created with a platform
+ * SocketImpl.
+ */
+ public void testNewServerSocket2() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ SocketImpl si = getSocketImpl(ss);
+ assertTrue(isPlatformSocketImpl(si));
+ }
+ }
+
+ /**
+ * Test creating a ServerSocket with a custom SocketImpl.
+ */
+ public void testNewServerSocket3() throws IOException {
+ ServerSocket ss = new ServerSocket(new CustomSocketImpl(true)) { };
+ try (ss) {
+ SocketImpl si = getSocketImpl(ss);
+ assertTrue(si instanceof CustomSocketImpl);
+ }
+ }
+
+ /**
+ * Test creating an unbound ServerSocket when there is a SocketImplFactory set.
+ */
+ public void testNewServerSocket4() throws IOException {
+ setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+ try (ServerSocket ss = new ServerSocket()) {
+ SocketImpl si = getSocketImpl(ss);
+ assertTrue(si instanceof CustomSocketImpl);
+ } finally {
+ setServerSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test creating a bound ServerSocket when there is a SocketImplFactory set.
+ */
+ public void testNewServerSocket5() throws IOException {
+ setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+ try (ServerSocket ss = new ServerSocket(0)) {
+ SocketImpl si = getSocketImpl(ss);
+ assertTrue(si instanceof CustomSocketImpl);
+ } finally {
+ setServerSocketImplFactory(null);
+ }
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+ * the Socket to accept is created with no SocketImpl.
+ */
+ public void testServerSocketAccept1() throws IOException {
+ var socket = new Socket((SocketImpl) null) { };
+ assertTrue(getSocketImpl(socket) == null);
+
+ serverSocketAccept(socket, (ss, s) -> {
+ assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+ assertTrue(s == socket);
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(isPlatformSocketImpl(si));
+ checkFields(si);
+ });
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+ * the Socket to accept is created with no SocketImpl, and there is a custom
+ * client SocketImplFactory set.
+ */
+ public void testServerSocketAccept2() throws IOException {
+ var socket = new Socket((SocketImpl) null) { };
+ assertTrue(getSocketImpl(socket) == null);
+
+ serverSocketAccept(socket, () -> new CustomSocketImpl(false), (ss, s) -> {
+ assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+ assertTrue(s == socket);
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(si instanceof CustomSocketImpl);
+ checkFields(si);
+ });
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+ * the Socket to accept is created with a SocketImpl that delegates to a
+ * platform SocketImpl.
+ */
+ public void testServerSocketAccept3() throws IOException {
+ var socket = new Socket();
+ SocketImpl si = getSocketImpl(socket);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+
+ serverSocketAccept(socket, (ss, s) -> {
+ assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+ assertTrue(s == socket);
+ assertTrue(getSocketImpl(s) == si);
+ assertTrue(getDelegate(si) == delegate);
+ checkFields(delegate);
+ });
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+ * the Socket to accept is created with a custom SocketImpl.
+ */
+ public void testServerSocketAccept4a() throws IOException {
+ SocketImpl clientImpl = new CustomSocketImpl(false);
+ Socket socket = new Socket(clientImpl) { };
+ assertTrue(getSocketImpl(socket) == clientImpl);
+
+ serverSocketAccept(socket, (ss, s) -> {
+ assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+ assertTrue(s == socket);
+ assertTrue(getSocketImpl(s) == clientImpl);
+ checkFields(clientImpl);
+ });
+ }
+
+ public void testServerSocketAccept4b() throws IOException {
+ SocketImpl clientImpl = new CustomSocketImpl(false);
+ Socket socket = new Socket(clientImpl) { };
+ assertTrue(getSocketImpl(socket) == clientImpl);
+
+ serverSocketAccept(socket, () -> new CustomSocketImpl(false), (ss, s) -> {
+ assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+ assertTrue(s == socket);
+ assertTrue(getSocketImpl(s) == clientImpl);
+ checkFields(clientImpl);
+ });
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+ * the Socket to accept is created no SocketImpl.
+ */
+ public void testServerSocketAccept5a() throws IOException {
+ SocketImpl serverImpl = new CustomSocketImpl(true);
+ try (ServerSocket ss = new ServerSocket(serverImpl) { }) {
+ ss.bind(new InetSocketAddress(0));
+ expectThrows(IOException.class, ss::accept);
+ }
+ }
+
+ public void testServerSocketAccept5b() throws IOException {
+ var socket = new Socket((SocketImpl) null) { };
+ assertTrue(getSocketImpl(socket) == null);
+
+ SocketImpl serverImpl = new CustomSocketImpl(true);
+ try (ServerSocket ss = serverSocketToAccept(serverImpl, socket)) {
+ expectThrows(IOException.class, ss::accept);
+ } finally {
+ socket.close();
+ }
+ }
+
+ public void testServerSocketAccept5c() throws IOException {
+ setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+ try (ServerSocket ss = new ServerSocket(0)) {
+ expectThrows(IOException.class, ss::accept);
+ } finally {
+ setServerSocketImplFactory(null);
+ }
+ }
+
+ public void testServerSocketAccept5d() throws IOException {
+ var socket = new Socket((SocketImpl) null) { };
+ assertTrue(getSocketImpl(socket) == null);
+
+ setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+ try (ServerSocket ss = serverSocketToAccept(socket)) {
+ expectThrows(IOException.class, ss::accept);
+ } finally {
+ setServerSocketImplFactory(null);
+ socket.close();
+ }
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+ * the Socket to accept is created with no SocketImpl, and there is a custom
+ * client SocketImplFactory set.
+ */
+ public void testServerSocketAccept6() throws Exception {
+ var socket = new Socket((SocketImpl) null) { };
+ assertTrue(getSocketImpl(socket) == null);
+
+ SocketImpl serverImpl = new CustomSocketImpl(true);
+ SocketImplFactory clientFactory = () -> new CustomSocketImpl(false);
+ serverSocketAccept(serverImpl, socket, clientFactory, (ss, s) -> {
+ assertTrue(getSocketImpl(ss) == serverImpl);
+ SocketImpl si = getSocketImpl(s);
+ assertTrue(si instanceof CustomSocketImpl);
+ checkFields(si);
+ });
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+ * the Socket to accept is created with a SocketImpl that delegates to a
+ * platform SocketImpl.
+ */
+ public void testServerSocketAccept7a() throws IOException {
+ var socket = new Socket();
+ SocketImpl si = getSocketImpl(socket);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+
+ SocketImpl serverImpl = new CustomSocketImpl(true);
+ try (ServerSocket ss = serverSocketToAccept(serverImpl, socket)) {
+ expectThrows(IOException.class, ss::accept);
+ } finally {
+ socket.close();
+ }
+ }
+
+ public void testServerSocketAccept7b() throws IOException {
+ var socket = new Socket();
+ SocketImpl si = getSocketImpl(socket);
+ assertTrue(isSocksSocketImpl(si));
+ SocketImpl delegate = getDelegate(si);
+ assertTrue(isPlatformSocketImpl(delegate));
+
+ setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+ try (ServerSocket ss = serverSocketToAccept(socket)) {
+ expectThrows(IOException.class, ss::accept);
+ } finally {
+ setServerSocketImplFactory(null);
+ socket.close();
+ }
+ }
+
+ /**
+ * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+ * the Socket to accept is created with a custom SocketImpl.
+ */
+ public void testServerSocketAccept8() throws Exception {
+ SocketImpl clientImpl = new CustomSocketImpl(false);
+ Socket socket = new Socket(clientImpl) { };
+ assertTrue(getSocketImpl(socket) == clientImpl);
+
+ SocketImpl serverImpl = new CustomSocketImpl(true);
+ SocketImplFactory clientFactory = () -> new CustomSocketImpl(false);
+ serverSocketAccept(serverImpl, socket, clientFactory, (ss, s) -> {
+ assertTrue(getSocketImpl(ss) == serverImpl);
+ assertTrue(getSocketImpl(s) == clientImpl);
+ checkFields(clientImpl);
+ });
+ }
+
+ /**
+ * Creates a ServerSocket that returns the given Socket from accept.
+ * The consumer is invoked with the server socket and the accepted socket.
+ */
+ static void serverSocketAccept(Socket socket,
+ BiConsumer<ServerSocket, Socket> consumer)
+ throws IOException
+ {
+ Socket s1 = null;
+ Socket s2 = null;
+ try (ServerSocket ss = serverSocketToAccept(socket)) {
+ s1 = new Socket(ss.getInetAddress(), ss.getLocalPort());
+ s2 = ss.accept();
+ consumer.accept(ss, s2);
+ } finally {
+ if (s1 != null) s1.close();
+ if (s2 != null) s2.close();
+ }
+ }
+
+ /**
+ * Creates a ServerSocket that returns the given Socket from accept. The
+ * given SocketImplFactory is set during the accept and the consumer is
+ * invoked when the server socket and the accepted socket.
+ */
+ static void serverSocketAccept(Socket socket,
+ SocketImplFactory factory,
+ BiConsumer<ServerSocket, Socket> consumer)
+ throws IOException
+ {
+ Socket s1 = null;
+ Socket s2 = null;
+ try (ServerSocket ss = serverSocketToAccept(socket)) {
+ s1 = new Socket(ss.getInetAddress(), ss.getLocalPort());
+ setSocketSocketImplFactory(factory);
+ try {
+ s2 = ss.accept();
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ consumer.accept(ss, s2);
+ } finally {
+ if (s1 != null) s1.close();
+ if (s2 != null) s2.close();
+ }
+ }
+
+ /**
+ * Creates a ServerSocket with a SocketImpl returns the given Socket from
+ * accept. The given SocketImplFactory is set during the accept and the
+ * consumer is invoked when the server socket and the accepted socket.
+ */
+ static void serverSocketAccept(SocketImpl impl,
+ Socket socket,
+ SocketImplFactory factory,
+ BiConsumer<ServerSocket, Socket> consumer)
+ throws IOException
+ {
+ Socket s1 = null;
+ Socket s2 = null;
+ try (ServerSocket ss = serverSocketToAccept(impl, socket)) {
+ s1 = new Socket(ss.getInetAddress(), ss.getLocalPort());
+ setSocketSocketImplFactory(factory);
+ try {
+ s2 = ss.accept();
+ } finally {
+ setSocketSocketImplFactory(null);
+ }
+ consumer.accept(ss, s2);
+ } finally {
+ if (s1 != null) s1.close();
+ if (s2 != null) s2.close();
+ }
+ }
+
+ /**
+ * Creates a ServerSocket that returns the given Socket from accept.
+ */
+ static ServerSocket serverSocketToAccept(Socket s) throws IOException {
+ return new ServerSocket(0) {
+ @Override
+ public Socket accept() throws IOException {
+ implAccept(s);
+ return s;
+ }
+ };
+ }
+
+ /**
+ * Creates a ServerSocket with a SocketImpl that returns the given Socket
+ * from accept.
+ */
+ static ServerSocket serverSocketToAccept(SocketImpl impl, Socket s) throws IOException {
+ ServerSocket ss = new ServerSocket(impl) {
+ @Override
+ public Socket accept() throws IOException {
+ implAccept(s);
+ return s;
+ }
+ };
+ ss.bind(new InetSocketAddress(0));
+ return ss;
+ }
+
+ /**
+ * Returns the socket's SocketImpl
+ */
+ static SocketImpl getSocketImpl(Socket s) {
+ try {
+ Field f = Socket.class.getDeclaredField("impl");
+ f.setAccessible(true);
+ return (SocketImpl) f.get(s);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the server socket's SocketImpl
+ */
+ static SocketImpl getSocketImpl(ServerSocket ss) {
+ try {
+ Field f = ServerSocket.class.getDeclaredField("impl");
+ f.setAccessible(true);
+ return (SocketImpl) f.get(ss);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the SocketImpl that the given SocketImpl delegates to
+ */
+ static SocketImpl getDelegate(SocketImpl si) {
+ try {
+ Class<?> clazz = Class.forName("java.net.DelegatingSocketImpl");
+ Field f = clazz.getDeclaredField("delegate");
+ f.setAccessible(true);
+ return (SocketImpl) f.get(si);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the value of a SocketImpl field
+ */
+ static <T> T get(SocketImpl si, String name) {
+ try {
+ Field f = SocketImpl.class.getDeclaredField(name);
+ f.setAccessible(true);
+ return (T) f.get(si);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Sets the value of SocketImpl field
+ */
+ static void set(SocketImpl si, String name, Object value) {
+ try {
+ Field f = SocketImpl.class.getDeclaredField(name);
+ f.setAccessible(true);
+ f.set(si, value);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns true if the SocketImpl is a PlatformSocketImpl
+ */
+ static boolean isPlatformSocketImpl(SocketImpl si) {
+ try {
+ Class<?> clazz = Class.forName("sun.net.PlatformSocketImpl");
+ return clazz.isInstance(si);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns true if the SocketImpl is a SocksSocketImpl
+ */
+ static boolean isSocksSocketImpl(SocketImpl si) {
+ try {
+ Class<?> clazz = Class.forName("java.net.SocksSocketImpl");
+ return clazz.isInstance(si);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns true if the SocketImpl is a HttpConnectSocketImpl
+ */
+ static boolean isHttpConnectSocketImpl(SocketImpl si) {
+ try {
+ Class<?> clazz = Class.forName("java.net.HttpConnectSocketImpl");
+ return clazz.isInstance(si);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Socket.setSocketImplFactory(SocketImplFactory)
+ */
+ static void setSocketSocketImplFactory(SocketImplFactory factory) {
+ try {
+ Field f = Socket.class.getDeclaredField("factory");
+ f.setAccessible(true);
+ f.set(null, factory);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * ServerSocket.setSocketFactory(SocketImplFactory)
+ */
+ static void setServerSocketImplFactory(SocketImplFactory factory) {
+ try {
+ Field f = ServerSocket.class.getDeclaredField("factory");
+ f.setAccessible(true);
+ f.set(null, factory);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Checks the 4 protected fields of a SocketImpl to make sure that they
+ * have been initialized.
+ */
+ static void checkFields(SocketImpl si) {
+ FileDescriptor fd = get(si, "fd");
+ InetAddress address = get(si, "address");
+ int port = get(si, "port");
+ int localport = get(si, "localport");
+ assertTrue(fd.valid() && address != null && port != 0 && localport != 0);
+ }
+
+ /**
+ * Custom SocketImpl that is layed on a SocketChannel or ServerSocketChannel
+ */
+ static class CustomSocketImpl extends SocketImpl {
+ private final boolean server;
+ private ServerSocketChannel ssc;
+ private SocketChannel sc;
+
+ CustomSocketImpl(boolean server) {
+ this.server = server;
+ }
+
+ @Override
+ protected void create(boolean stream) throws IOException {
+ if (server) {
+ ssc = ServerSocketChannel.open();
+ } else {
+ sc = SocketChannel.open();
+ }
+ }
+
+ @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 connect(SocketAddress remote, int timeout) throws IOException {
+ sc.connect(remote);
+ super.address = ((InetSocketAddress) remote).getAddress();
+ super.port = ((InetSocketAddress) remote).getPort();
+ }
+
+ @Override
+ protected void bind(InetAddress address, int port) throws IOException {
+ if (server) {
+ ssc.bind(new InetSocketAddress(address, port));
+ super.localport = ssc.socket().getLocalPort();
+ } else {
+ sc.bind(new InetSocketAddress(address, port));
+ super.localport = sc.socket().getLocalPort();
+ }
+ super.address = address;
+ }
+
+ @Override
+ protected void listen(int backlog) {
+ // do nothing
+ }
+
+ @Override
+ protected void accept(SocketImpl si) throws IOException {
+ SocketChannel peer = ssc.accept();
+ FileDescriptor fd;
+ try {
+ Class<?> clazz = Class.forName("sun.nio.ch.SocketChannelImpl");
+ Field f = clazz.getDeclaredField("fd");
+ f.setAccessible(true);
+ fd = (FileDescriptor) f.get(peer);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ set(si, "fd", fd);
+ set(si, "address", peer.socket().getInetAddress());
+ set(si, "port", peer.socket().getPort());
+ set(si, "localport", peer.socket().getLocalPort());
+ }
+
+ @Override
+ protected InputStream getInputStream() {
+ throw new RuntimeException();
+ }
+
+ @Override
+ protected OutputStream getOutputStream() {
+ throw new RuntimeException();
+ }
+
+ @Override
+ protected int available() {
+ return 0;
+ }
+
+ @Override
+ protected void close() {
+ }
+
+ @Override
+ protected void sendUrgentData(int data) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void setOption(int option, Object value) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public Object getOption(int option) {
+ throw new RuntimeException();
+ }
+ }
+}
--- a/test/jdk/jdk/jfr/event/io/TestInstrumentation.java Thu Feb 28 16:37:28 2019 +0800
+++ b/test/jdk/jdk/jfr/event/io/TestInstrumentation.java Sat Mar 09 12:52:30 2019 +0000
@@ -105,14 +105,14 @@
"java/io/FileOutputStream::write::(I)V",
"java/io/FileOutputStream::write::([B)V",
"java/io/FileOutputStream::write::([BII)V",
- "java/net/SocketInputStream::read::()I",
- "java/net/SocketInputStream::read::([B)I",
- "java/net/SocketInputStream::read::([BII)I",
- "java/net/SocketInputStream::close::()V",
- "java/net/SocketOutputStream::write::(I)V",
- "java/net/SocketOutputStream::write::([B)V",
- "java/net/SocketOutputStream::write::([BII)V",
- "java/net/SocketOutputStream::close::()V",
+ "java/net/Socket$SocketInputStream::read::()I",
+ "java/net/Socket$SocketInputStream::read::([B)I",
+ "java/net/Socket$SocketInputStream::read::([BII)I",
+ "java/net/Socket$SocketInputStream::close::()V",
+ "java/net/Socket$SocketOutputStream::write::(I)V",
+ "java/net/Socket$SocketOutputStream::write::([B)V",
+ "java/net/Socket$SocketOutputStream::write::([BII)V",
+ "java/net/Socket$SocketOutputStream::close::()V",
"java/nio/channels/FileChannel::read::([Ljava/nio/ByteBuffer;)J",
"java/nio/channels/FileChannel::write::([Ljava/nio/ByteBuffer;)J",
"java/nio/channels/SocketChannel::open::()Ljava/nio/channels/SocketChannel;",
--- a/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java Thu Feb 28 16:37:28 2019 +0800
+++ b/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java Sat Mar 09 12:52:30 2019 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -22,86 +22,206 @@
*/
package org.openjdk.bench.java.net;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import org.openjdk.jmh.annotations.BenchmarkMode;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OutputTimeUnit;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.TearDown;
+/**
+ * Tests socket read/write.
+ */
-/**
- * Tests the overheads of I/O API.
- * This test is known to depend heavily on network conditions and paltform.
- */
@BenchmarkMode(Mode.Throughput)
-@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class SocketReadWrite {
- private OutputStream os;
- private InputStream is;
- private ServerSocket ss;
- private Socket s1, s2;
- private ReadThread rt;
+ static final InetAddress address = InetAddress.getLoopbackAddress();
+ public static final int TIMEOUT = 10000;
+
+ static class EchoServer implements Runnable {
+ final ServerSocket ss;
+ final int port;
+ final CountDownLatch startedLatch;
+ final int size;
+ final boolean timeout;
+ List<ServerThread> threads = new ArrayList<>();
+ volatile boolean isDone = false;
+
+ public EchoServer(CountDownLatch await, int size, boolean timeout) throws IOException {
+ this.size = size;
+ this.timeout = timeout;
+ ss = new ServerSocket(0);
+ port = ss.getLocalPort();
+ this.startedLatch = await;
+ }
+
+ @Override
+ public void run() {
+ startedLatch.countDown();
+ while (!isDone) {
+ try {
+ Socket s = ss.accept();
+ s.setTcpNoDelay(true);
+ if (timeout) {
+ s.setSoTimeout(TIMEOUT);
+ }
+ ServerThread st = new ServerThread(s, size);
+ threads.add(st);
+ new Thread(st).start();
+ } catch (IOException e) {
+ if (!isDone) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ synchronized void close() throws IOException {
+ if (!isDone) {
+ isDone = true;
+ ss.close();
+ for (ServerThread st : threads) {
+ st.close();
+ }
+ }
+ }
+
+ static EchoServer instance = null;
+
+ static synchronized EchoServer startServer(int size, boolean timeout) throws IOException {
+ if (instance == null) {
+ CountDownLatch started = new CountDownLatch(1);
+ EchoServer s = new EchoServer(started, size, timeout);
+ new Thread(s).start();
+ try {
+ started.await(); // wait until server thread started
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ instance = s;
+ }
+ return instance;
+ }
+
+ static class ServerThread implements Runnable {
+
+ final Socket s;
+ final InputStream in;
+ final OutputStream out;
+ final int size;
+ volatile boolean isDone = false;
+
+ ServerThread(Socket s, int size) throws IOException {
+ this.s = s;
+ this.size = size;
+ in = s.getInputStream();
+ out = s.getOutputStream();
+ }
+
+ @Override
+ public void run() {
+ if (size == 1) {
+ while (!isDone) {
+ try {
+ int b = this.in.read();
+ out.write(b);
+ } catch (IOException e) {
+ if (!isDone) {
+ e.printStackTrace();
+ }
+ }
+ }
+ } else {
+ byte[] a = new byte[size];
+ while (!isDone) {
+ try {
+ readN(a, size, this.in);
+ out.write(a);
+ } catch (IOException e) {
+ if (!isDone) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ public void close() throws IOException {
+ isDone = true;
+ s.close();
+ }
+
+ }
+ }
+
+ static void readN(byte[] array, int size, InputStream in) throws IOException {
+ int nread = 0;
+ while (size > 0) {
+ int n = in.read(array, nread, size);
+ if (n < 0) throw new RuntimeException();
+ nread += n;
+ size -= n;
+ }
+ }
+
+ EchoServer server;
+
+ @Param({"1", "1024", "8192", "64000", "128000"})
+ public int size;
+
+ @Param({"false", "true"})
+ public boolean timeout;
+
+ Socket s;
+ InputStream in;
+ OutputStream out;
+ byte[] array;
@Setup
- public void beforeRun() throws IOException {
- InetAddress iaddr = InetAddress.getLocalHost();
-
- ss = new ServerSocket(0);
- s1 = new Socket(iaddr, ss.getLocalPort());
- s2 = ss.accept();
-
- os = s1.getOutputStream();
- is = s2.getInputStream();
-
- rt = new ReadThread(is);
- rt.start();
+ public void setup() throws IOException {
+ server = EchoServer.startServer(size, timeout);
+ int port = server.port;
+ s = new Socket(address, port);
+ s.setTcpNoDelay(true);
+ if (timeout) {
+ s.setSoTimeout(TIMEOUT);
+ }
+ in = s.getInputStream();
+ out = s.getOutputStream();
+ array = new byte[size];
}
@TearDown
- public void afterRun() throws IOException, InterruptedException {
- os.write(0);
- os.close();
- is.close();
- s1.close();
- s2.close();
- ss.close();
- rt.join();
+ public void tearDown() throws IOException {
+ server.close();
+ s.close();
}
@Benchmark
- public void test() throws IOException {
- os.write((byte) 4711);
- }
-
- static class ReadThread extends Thread {
- private InputStream is;
-
- public ReadThread(InputStream is) {
- this.is = is;
+ public void echo() throws IOException {
+ if (size == 1) {
+ out.write((byte) 47);
+ int c = in.read();
+ } else {
+ out.write(array);
+ readN(array, size, in);
}
- public void run() {
- try {
- while (is.read() > 0);
- } catch (SocketException ex) {
- // ignore - most likely "socket closed", which means shutdown
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
}
-
}