# HG changeset patch # User michaelm # Date 1549654154 0 # Node ID 82874527373ea30048607b851c48675748926eda # Parent b3e971413a7b44c00550592eeb2101b0a7f1687b Socket changes to support both NioSocketImpl and PlainSocketImpl switchable by net property diff -r b3e971413a7b -r 82874527373e src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java --- 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; diff -r b3e971413a7b -r 82874527373e src/java.base/share/classes/java/net/HttpConnectSocketImpl.java --- 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 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 void setOption(SocketOption name, T value) throws IOException { + delegate.setOption(name, value); + } + + @Override + protected T getOption(SocketOption name) throws IOException { + return delegate.getOption(name); + } + + @Override + void reset() throws IOException { + delegate.reset(); + } + + @Override + protected Set> 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 } } diff -r b3e971413a7b -r 82874527373e src/java.base/share/classes/java/net/ServerSocket.java --- 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; diff -r b3e971413a7b -r 82874527373e src/java.base/share/classes/java/net/Socket.java --- 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() { + 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() { + 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 { diff -r b3e971413a7b -r 82874527373e src/java.base/share/classes/java/net/SocketImpl.java --- 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() { + @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. diff -r b3e971413a7b -r 82874527373e src/java.base/share/classes/java/net/SocksSocketImpl.java --- 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 NOT 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 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 void setOption(SocketOption name, T value) throws IOException { + delegate.setOption(name, value); + } + + @Override + protected T getOption(SocketOption name) throws IOException { + return delegate.getOption(name); + } + + @Override + void reset() throws IOException { + delegate.reset(); + } + + @Override + protected Set> 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(); + } }