Socket changes to support both NioSocketImpl and PlainSocketImpl switchable by net property niosocketimpl-branch
authormichaelm
Fri, 08 Feb 2019 19:29:14 +0000
branchniosocketimpl-branch
changeset 57167 82874527373e
parent 57166 b3e971413a7b
child 57169 1d423d38840d
Socket changes to support both NioSocketImpl and PlainSocketImpl switchable by net property
src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java
src/java.base/share/classes/java/net/HttpConnectSocketImpl.java
src/java.base/share/classes/java/net/ServerSocket.java
src/java.base/share/classes/java/net/Socket.java
src/java.base/share/classes/java/net/SocketImpl.java
src/java.base/share/classes/java/net/SocksSocketImpl.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;
--- 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();
+    }
 }