Socket changes to support both NioSocketImpl and PlainSocketImpl switchable by net property
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Fri Feb 08 18:16:53 2019 +0000
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Fri Feb 08 19:29:14 2019 +0000
@@ -714,6 +714,34 @@
socketClose0(false);
}
+ void postCustomAccept() {
+ // defaults ok
+ }
+
+ void copyTo(SocketImpl dest) {
+ if (dest instanceof PlainSocketImpl) {
+ AbstractPlainSocketImpl psi = (AbstractPlainSocketImpl)dest;
+ try {
+ dest.close();
+ } catch (IOException e) {}
+ psi.fd = this.fd;
+ psi.socket = this.socket;
+ psi.serverSocket = this.serverSocket;
+ psi.address = this.address;
+ psi.port = this.port;
+ psi.localport = this.localport;
+ psi.trafficClass = this.trafficClass;
+ psi.socketInputStream = this.socketInputStream;
+ psi.socketOutputStream = this.socketOutputStream;
+ psi.fdUseCount = this.fdUseCount;
+ psi.stream = this.stream;
+ psi.connectionReset = this.connectionReset;
+ psi.closePending = this.closePending;
+ psi.shut_rd = this.shut_rd;
+ psi.shut_wr = this.shut_wr;
+ }
+ }
+
abstract void socketCreate(boolean isServer) throws IOException;
abstract void socketConnect(InetAddress address, int port, int timeout)
throws IOException;
--- a/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java Fri Feb 08 18:16:53 2019 +0000
+++ b/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java Fri Feb 08 19:29:14 2019 +0000
@@ -25,7 +25,10 @@
package java.net;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
@@ -43,7 +46,7 @@
* @since 1.8
*/
-/*package*/ class HttpConnectSocketImpl extends NioSocketImpl {
+/*package*/ class HttpConnectSocketImpl extends SocketImpl implements SocketImpl.DelegatingImpl {
private static final String httpURLClazzStr =
"sun.net.www.protocol.http.HttpURLConnection";
@@ -56,6 +59,7 @@
private final String server;
private InetSocketAddress external_address;
private HashMap<Integer, Object> optionsMap = new HashMap<>();
+ private final SocketImpl delegate;
static {
try {
@@ -77,9 +81,14 @@
throw new InternalError("Should not reach here", x);
}
}
+ HttpConnectSocketImpl(SocketImpl delegate) {
+ this.delegate = delegate;
+ this.server = null;
+ throw new InternalError();
+ }
- HttpConnectSocketImpl(Proxy proxy) {
- super(false);
+ HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate) {
+ this.delegate = delegate;
SocketAddress a = proxy.address();
if ( !(a instanceof InetSocketAddress) )
throw new IllegalArgumentException("Unsupported address type");
@@ -90,6 +99,11 @@
}
@Override
+ protected void create(boolean stream) throws IOException {
+ delegate.create(stream);
+ }
+
+ @Override
protected void connect(String host, int port) throws IOException {
connect(new InetSocketAddress(host, port), 0);
}
@@ -100,6 +114,18 @@
}
@Override
+ void setSocket(Socket socket) {
+ delegate.socket = socket;
+ super.setSocket(socket);
+ }
+
+ @Override
+ void setServerSocket(ServerSocket socket) {
+ delegate.serverSocket = socket;
+ super.setServerSocket(socket);
+ }
+
+ @Override
protected void connect(SocketAddress endpoint, int timeout)
throws IOException
{
@@ -138,8 +164,68 @@
}
@Override
+ protected void bind(InetAddress host, int port) throws IOException {
+ delegate.bind(host, port);
+ }
+
+ @Override
+ protected void listen(int backlog) throws IOException {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ protected void accept(SocketImpl s) throws IOException {
+ throw new IllegalStateException();
+ }
+
+ @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 <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ delegate.setOption(name, value);
+ }
+
+ @Override
+ protected <T> T getOption(SocketOption<T> name) throws IOException {
+ return delegate.getOption(name);
+ }
+
+ @Override
+ void reset() throws IOException {
+ delegate.reset();
+ }
+
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ return delegate.supportedOptions();
+ }
+
+ @Override
+ protected boolean supportsUrgentData () {
+ return delegate.supportsUrgentData();
+ }
+
+ @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
@@ -148,6 +234,11 @@
optionsMap.put(opt, val);
}
+ @Override
+ public Object getOption(int optID) throws SocketException {
+ return delegate.getOption(optID);
+ }
+
private Socket privilegedDoTunnel(final String urlString,
final int timeout)
throws IOException
@@ -198,7 +289,7 @@
if (external_address != null)
return external_address.getAddress();
else
- return super.getInetAddress();
+ return delegate.getInetAddress();
}
@Override
@@ -206,6 +297,51 @@
if (external_address != null)
return external_address.getPort();
else
- return super.getPort();
+ return delegate.getPort();
+ }
+
+
+ @Override
+ protected FileDescriptor getFileDescriptor() {
+ return delegate.getFileDescriptor();
+ }
+
+ @Override
+ protected void shutdownInput() throws IOException {
+ delegate.shutdownInput();
+ }
+
+ @Override
+ protected void shutdownOutput() throws IOException {
+ delegate.shutdownOutput();
+ }
+
+ @Override
+ protected int getLocalPort() {
+ return delegate.getLocalPort();
+ }
+
+ @Override
+ protected void sendUrgentData(int data) throws IOException {
+ delegate.sendUrgentData(data);
+ }
+
+ @Override
+ public SocketImpl delegate() {
+ return delegate;
+ }
+
+ @Override
+ public SocketImpl newInstance() {
+ if (delegate instanceof PlainSocketImpl)
+ return new HttpConnectSocketImpl(new PlainSocketImpl());
+ else if (delegate instanceof NioSocketImpl)
+ return new HttpConnectSocketImpl(new NioSocketImpl(false));
+ throw new InternalError();
+ }
+
+ @Override
+ public void postCustomAccept() {
+ // TODO
}
}
--- a/src/java.base/share/classes/java/net/ServerSocket.java Fri Feb 08 18:16:53 2019 +0000
+++ b/src/java.base/share/classes/java/net/ServerSocket.java Fri Feb 08 19:29:14 2019 +0000
@@ -27,7 +27,6 @@
import jdk.internal.access.JavaNetSocketAccess;
import jdk.internal.access.SharedSecrets;
-import sun.nio.ch.NioSocketImpl;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -38,6 +37,9 @@
import java.security.PrivilegedExceptionAction;
import java.util.Set;
import java.util.Collections;
+import sun.nio.ch.NioSocketImpl;
+import static java.net.SocketImpl.DelegatingImpl;
+import static java.net.SocketImpl.getDefaultSocketImpl;
/**
* This class implements server sockets. A server socket waits for
@@ -297,7 +299,7 @@
} else {
// No need to do a checkOldImpl() here, we know it's an up to date
// SocketImpl!
- impl = new NioSocketImpl(true);
+ impl = new SocksSocketImpl(getDefaultSocketImpl(true));
}
if (impl != null)
impl.setServerSocket(this);
@@ -526,6 +528,23 @@
return s;
}
+ static void prepareImpl(SocketImpl impl) {
+ if (!(impl instanceof DelegatingImpl))
+ return;
+ impl = ((DelegatingImpl)impl).delegate();
+ if (impl.address == null)
+ impl.address = new InetAddress();
+ if (impl.fd == null)
+ impl.fd = new FileDescriptor();
+ }
+
+ static SocketImpl newInstanceOfType(SocketImpl impl1, SocketImpl impl2) {
+ if (impl1 instanceof DelegatingImpl)
+ return ((DelegatingImpl)impl1).newInstance();
+ else
+ return ((DelegatingImpl)impl2).newInstance();
+ }
+
/**
* Subclasses of ServerSocket use this method to override accept()
* to return their own subclass of socket. So a FooServerSocket
@@ -550,12 +569,14 @@
if (si == null) {
// create a SocketImpl and accept the connection
si = Socket.createImpl();
+ prepareImpl(si);
+ s.setImpl(si);
impl.accept(si);
try {
- // a custom impl has accepted the connection with a NIO SocketImpl
- if (!(impl instanceof NioSocketImpl) && (si instanceof NioSocketImpl)) {
- ((NioSocketImpl) si).postCustomAccept();
+ // a custom impl has accepted the connection with a Platform SocketImpl
+ if (!(impl instanceof DelegatingImpl) && (si instanceof DelegatingImpl)) {
+ ((DelegatingImpl)si).postCustomAccept();
}
} finally {
securityCheckAccept(si); // closes si if permission check fails
@@ -567,19 +588,18 @@
return;
}
- // ServerSocket or Socket is using NIO SocketImpl
- if (impl instanceof NioSocketImpl || si instanceof NioSocketImpl) {
- // not implemented
- if (impl instanceof NioSocketImpl && impl.getClass() != NioSocketImpl.class)
- throw new UnsupportedOperationException();
+ // ServerSocket or Socket is using NIO or Plain SocketImpl
+ if (impl instanceof DelegatingImpl || si instanceof DelegatingImpl) {
// accept connection via new SocketImpl
- NioSocketImpl nsi = new NioSocketImpl(false);
+
+ SocketImpl nsi = newInstanceOfType(impl, si);
+ prepareImpl(nsi);
impl.accept(nsi);
securityCheckAccept(nsi); // closes si if permission check fails
// copy state to the existing SocketImpl and update socket state
- nsi.copyTo(si);
+ ((DelegatingImpl)nsi).copyTo(si);
s.postAccept();
return;
}
@@ -589,8 +609,7 @@
boolean completed = false;
try {
si.reset();
- si.fd = new FileDescriptor();
- si.address = new InetAddress();
+ prepareImpl(si);
impl.accept(si);
securityCheckAccept(si); // closes si if permission check fails
completed = true;
--- a/src/java.base/share/classes/java/net/Socket.java Fri Feb 08 18:16:53 2019 +0000
+++ b/src/java.base/share/classes/java/net/Socket.java Fri Feb 08 19:29:14 2019 +0000
@@ -27,6 +27,7 @@
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.security.AccessController;
@@ -35,6 +36,7 @@
import java.util.Set;
import java.util.Collections;
import sun.nio.ch.NioSocketImpl;
+import static java.net.SocketImpl.getDefaultSocketImpl;
/**
* This class implements client sockets (also called just
@@ -138,13 +140,14 @@
security.checkConnect(epoint.getAddress().getHostAddress(),
epoint.getPort());
}
- impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p)
- : new HttpConnectSocketImpl(p);
+ impl = type == Proxy.Type.SOCKS
+ ? new SocksSocketImpl(p, getDefaultSocketImpl(false))
+ : new HttpConnectSocketImpl(p, getDefaultSocketImpl(false));
impl.setSocket(this);
} else {
if (p == Proxy.NO_PROXY) {
if (factory == null) {
- impl = new NioSocketImpl(false);
+ impl = new SocksSocketImpl(getDefaultSocketImpl(false));
impl.setSocket(this);
} else
setImpl();
@@ -497,7 +500,7 @@
if (factory != null) {
return factory.createSocketImpl();
} else {
- return new SocksSocketImpl();
+ return new SocksSocketImpl(getDefaultSocketImpl(false));
}
}
@@ -517,7 +520,7 @@
} else {
// No need to do a checkOldImpl() here, we know it's an up to date
// SocketImpl!
- impl = new SocksSocketImpl();
+ impl = new SocksSocketImpl(getDefaultSocketImpl(false));
}
if (impl != null)
impl.setSocket(this);
@@ -922,7 +925,19 @@
if (isInputShutdown())
throw new SocketException("Socket input is shutdown");
// wrap the input stream so that the close method closes this socket
- return new SocketInputStream(this, impl.getInputStream());
+ InputStream is = null;
+ try {
+ is = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<InputStream>() {
+ public InputStream run() throws IOException {
+ return impl.getInputStream();
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+
+ return new SocketInputStream(this, is);
}
private static class SocketInputStream extends InputStream {
@@ -978,7 +993,18 @@
if (isOutputShutdown())
throw new SocketException("Socket output is shutdown");
// wrap the output stream so that the close method closes this socket
- return new SocketOutputStream(this, impl.getOutputStream());
+ OutputStream os = null;
+ try {
+ os = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<OutputStream>() {
+ public OutputStream run() throws IOException {
+ return impl.getOutputStream();
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ return new SocketOutputStream(this, os);
}
private static class SocketOutputStream extends OutputStream {
--- a/src/java.base/share/classes/java/net/SocketImpl.java Fri Feb 08 18:16:53 2019 +0000
+++ b/src/java.base/share/classes/java/net/SocketImpl.java Fri Feb 08 19:29:14 2019 +0000
@@ -25,12 +25,15 @@
package java.net;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.FileDescriptor;
+import java.io.*;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.util.Set;
+import sun.net.NetProperties;
+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
@@ -69,6 +72,33 @@
*/
protected int localport;
+ private static boolean useNioSocketImpl = getUseNioSocketImpl();
+
+ // A simple way to override the socketimpl by creating a file in $user.dir
+ private static boolean getUseNioSocketImpl() {
+ // temporary for testing only
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
+ @Override
+ public Boolean run() throws Exception {
+ String s = NetProperties.get("jdk.net.socketimpl.default");
+ return (s == null || !s.equalsIgnoreCase("classic"));
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ return false;
+ }
+ }
+
+ static SocketImpl getDefaultSocketImpl(boolean server) {
+ if (useNioSocketImpl) {
+ return new NioSocketImpl(server);
+ } else {
+ return new PlainSocketImpl();
+ }
+ }
+
+
/**
* Creates either a stream or a datagram socket.
*
@@ -297,6 +327,28 @@
}
/**
+ * Implemented by SocksSocketImpl and HttpConnectSocketImpl to show
+ * they delegate to a "real" impl. Accessible through delegate().
+ */
+ interface DelegatingImpl {
+ SocketImpl delegate();
+ SocketImpl newInstance();
+ void postCustomAccept();
+
+ default void copyTo(SocketImpl dest) {
+ SocketImpl src = delegate();
+ if (dest instanceof DelegatingImpl)
+ dest = ((DelegatingImpl)dest).delegate();
+ if (src instanceof NioSocketImpl) {
+ ((NioSocketImpl)src).copyTo(dest);
+ } else if (src instanceof PlainSocketImpl) {
+ ((PlainSocketImpl)src).copyTo(dest);
+ } else
+ throw new InternalError();
+ }
+ }
+
+ /**
* Returns the address and port of this socket as a {@code String}.
*
* @return a string representation of this socket.
--- a/src/java.base/share/classes/java/net/SocksSocketImpl.java Fri Feb 08 18:16:53 2019 +0000
+++ b/src/java.base/share/classes/java/net/SocksSocketImpl.java Fri Feb 08 19:29:14 2019 +0000
@@ -24,11 +24,14 @@
*/
package java.net;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedOutputStream;
import java.security.AccessController;
+import java.util.Objects;
+import java.util.Set;
import jdk.internal.util.StaticProperty;
import sun.net.SocksProxy;
@@ -41,7 +44,7 @@
* Note this class should <b>NOT</b> be public.
*/
-class SocksSocketImpl extends NioSocketImpl implements SocksConsts {
+class SocksSocketImpl extends SocketImpl implements SocksConsts, SocketImpl.DelegatingImpl {
private String server = null;
private int serverPort = DEFAULT_PORT;
private InetSocketAddress external_address;
@@ -49,13 +52,15 @@
private Socket cmdsock = null;
private InputStream cmdIn = null;
private OutputStream cmdOut = null;
+ final SocketImpl delegate;
- SocksSocketImpl() {
- super(false);
+ SocksSocketImpl(SocketImpl delegate) {
+ Objects.requireNonNull(delegate);
+ this.delegate = delegate;
}
- SocksSocketImpl(Proxy proxy) {
- super(false);
+ SocksSocketImpl(Proxy proxy, SocketImpl delegate) {
+ this.delegate = delegate;
SocketAddress a = proxy.address();
if (a instanceof InetSocketAddress) {
InetSocketAddress ad = (InetSocketAddress) a;
@@ -96,7 +101,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 {
@@ -248,6 +253,33 @@
}
}
+ @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
+ void setSocket(Socket soc) {
+ delegate.socket = soc;
+ super.setSocket(soc);
+ }
+
+ @Override
+ void setServerSocket(ServerSocket soc) {
+ delegate.serverSocket = soc;
+ super.setServerSocket(soc);
+ }
+
/**
* Connects the Socks Socket to the specified endpoint. It will first
* connect to the SOCKS proxy and negotiate the access. If the proxy
@@ -299,7 +331,7 @@
/*
* No default proxySelector --> direct connection
*/
- super.connect(epoint, remainingMillis(deadlineMillis));
+ delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
URI uri;
@@ -322,13 +354,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;
}
@@ -518,7 +550,43 @@
external_address = epoint;
}
+ @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);
+ }
+
+ /**
+ * Accept the connection onto the given SocketImpl
+ *
+ * @param s the accepted connection.
+ * @throws IOException
+ */
+ @Override
+ protected void accept(SocketImpl s) throws IOException {
+ if (s instanceof SocketImpl.DelegatingImpl)
+ s = ((SocketImpl.DelegatingImpl)s).delegate();
+ 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();
+ }
/**
* Returns the value of this socket's {@code address} field.
@@ -531,7 +599,17 @@
if (external_address != null)
return external_address.getAddress();
else
- return super.getInetAddress();
+ return delegate.getInetAddress();
+ }
+
+ @Override
+ protected void shutdownOutput() throws IOException {
+ delegate.shutdownOutput();
+ }
+
+ @Override
+ protected FileDescriptor getFileDescriptor() {
+ return delegate.getFileDescriptor();
}
/**
@@ -545,7 +623,17 @@
if (external_address != null)
return external_address.getPort();
else
- return super.getPort();
+ return delegate.getPort();
+ }
+
+ @Override
+ protected void sendUrgentData(int data) throws IOException {
+ delegate.sendUrgentData(data);
+ }
+
+ @Override
+ protected void shutdownInput() throws IOException {
+ delegate.shutdownInput();
}
@Override
@@ -553,10 +641,74 @@
if (cmdsock != null)
cmdsock.close();
cmdsock = null;
- super.close();
+ delegate.close();
}
private String getUserName() {
return StaticProperty.userName();
}
+
+ @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 boolean supportsUrgentData () {
+ return delegate.supportsUrgentData();
+ }
+
+ @Override
+ protected int getLocalPort() {
+ return delegate.getLocalPort();
+ }
+
+ @Override
+ protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ delegate.setOption(name, value);
+ }
+
+ @Override
+ protected <T> T getOption(SocketOption<T> name) throws IOException {
+ return delegate.getOption(name);
+ }
+
+ @Override
+ void reset() throws IOException {
+ delegate.reset();
+ }
+
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ return delegate.supportedOptions();
+ }
+
+ @Override
+ public SocketImpl delegate() {
+ return delegate;
+ }
+
+ @Override
+ public SocketImpl newInstance() {
+ if (delegate instanceof PlainSocketImpl)
+ return new SocksSocketImpl(new PlainSocketImpl());
+ else if (delegate instanceof NioSocketImpl)
+ return new SocksSocketImpl(new NioSocketImpl(false));
+ throw new InternalError();
+ }
+
+ @Override
+ public void postCustomAccept() {
+ if (delegate instanceof NioSocketImpl) {
+ // TODO ((NioSocketImpl)delegate).postCustomAccept();
+ } else if (delegate instanceof PlainSocketImpl) {
+ ((PlainSocketImpl)delegate).postCustomAccept();
+ } else
+ throw new InternalError();
+ }
}