src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java
changeset 55081 dd321e3596c0
parent 54689 b28b7f631301
child 55226 ce8bab2c4185
child 57382 14e01d39c01a
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -35,12 +35,14 @@
 import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 
 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;
 
 /**
@@ -84,6 +86,9 @@
     */
     protected boolean stream;
 
+    /* whether this is a server or not */
+    final boolean isServer;
+
     /**
      * Load net library into runtime.
      */
@@ -112,27 +117,7 @@
     }
 
     AbstractPlainSocketImpl(boolean isServer) {
-        super(isServer);
-    }
-
-    /**
-     * Returns a set of SocketOptions supported by this impl and by this impl's
-     * socket (Socket or ServerSocket)
-     *
-     * @return a Set of SocketOptions
-     */
-    @Override
-    protected Set<SocketOption<?>> supportedOptions() {
-        Set<SocketOption<?>> options;
-        if (isReusePortAvailable()) {
-            options = new HashSet<>();
-            options.addAll(super.supportedOptions());
-            options.add(StandardSocketOptions.SO_REUSEPORT);
-            options = Collections.unmodifiableSet(options);
-        } else {
-            options = super.supportedOptions();
-        }
-        return options;
+        this.isServer = isServer;
     }
 
     /**
@@ -394,6 +379,121 @@
         }
     }
 
+    static final ExtendedSocketOptions extendedOptions =
+            ExtendedSocketOptions.getInstance();
+
+    private static final Set<SocketOption<?>> clientSocketOptions = clientSocketOptions();
+    private static final Set<SocketOption<?>> serverSocketOptions = serverSocketOptions();
+
+    private static Set<SocketOption<?>> clientSocketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_KEEPALIVE);
+        options.add(StandardSocketOptions.SO_SNDBUF);
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.SO_LINGER);
+        options.add(StandardSocketOptions.IP_TOS);
+        options.add(StandardSocketOptions.TCP_NODELAY);
+        if (isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.clientSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    private static Set<SocketOption<?>> serverSocketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.IP_TOS);
+        if (isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.serverSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    @Override
+    protected Set<SocketOption<?>> supportedOptions() {
+        if (isServer)
+            return serverSocketOptions;
+        else
+            return clientSocketOptions;
+    }
+
+    @Override
+    protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
+        Objects.requireNonNull(name);
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        if (!name.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
+
+        if (isClosedOrPending())
+            throw new SocketException("Socket closed");
+
+        if (name == StandardSocketOptions.SO_KEEPALIVE) {
+            setOption(SocketOptions.SO_KEEPALIVE, value);
+        } else if (name == StandardSocketOptions.SO_SNDBUF) {
+            if (((Integer)value).intValue() < 0)
+                throw new IllegalArgumentException("Invalid send buffer size:" + value);
+            setOption(SocketOptions.SO_SNDBUF, value);
+        } else if (name == StandardSocketOptions.SO_RCVBUF) {
+            if (((Integer)value).intValue() < 0)
+                throw new IllegalArgumentException("Invalid recv buffer size:" + value);
+            setOption(SocketOptions.SO_RCVBUF, value);
+        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+            setOption(SocketOptions.SO_REUSEADDR, value);
+        } else if (name == StandardSocketOptions.SO_REUSEPORT) {
+            setOption(SocketOptions.SO_REUSEPORT, value);
+        } else if (name == StandardSocketOptions.SO_LINGER ) {
+            setOption(SocketOptions.SO_LINGER, value);
+        } else if (name == StandardSocketOptions.IP_TOS) {
+            int i = ((Integer)value).intValue();
+            if (i < 0 || i > 255)
+                throw new IllegalArgumentException("Invalid IP_TOS value: " + value);
+            setOption(SocketOptions.IP_TOS, value);
+        } else if (name == StandardSocketOptions.TCP_NODELAY) {
+            setOption(SocketOptions.TCP_NODELAY, value);
+        } else if (extendedOptions.isOptionSupported(name)) {
+            extendedOptions.setOption(fd, name, value);
+        } else {
+            throw new AssertionError("unknown option: " + name);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T> T getOption(SocketOption<T> name) throws IOException {
+        Objects.requireNonNull(name);
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        if (isClosedOrPending())
+            throw new SocketException("Socket closed");
+
+        if (name == StandardSocketOptions.SO_KEEPALIVE) {
+            return (T)getOption(SocketOptions.SO_KEEPALIVE);
+        } else if (name == StandardSocketOptions.SO_SNDBUF) {
+            return (T)getOption(SocketOptions.SO_SNDBUF);
+        } else if (name == StandardSocketOptions.SO_RCVBUF) {
+            return (T)getOption(SocketOptions.SO_RCVBUF);
+        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+            return (T)getOption(SocketOptions.SO_REUSEADDR);
+        } else if (name == StandardSocketOptions.SO_REUSEPORT) {
+            return (T)getOption(SocketOptions.SO_REUSEPORT);
+        } else if (name == StandardSocketOptions.SO_LINGER) {
+            return (T)getOption(SocketOptions.SO_LINGER);
+        } else if (name == StandardSocketOptions.IP_TOS) {
+            return (T)getOption(SocketOptions.IP_TOS);
+        } else if (name == StandardSocketOptions.TCP_NODELAY) {
+            return (T)getOption(SocketOptions.TCP_NODELAY);
+        } else if (extendedOptions.isOptionSupported(name)) {
+            return (T) extendedOptions.getOption(fd, name);
+        } else {
+            throw new AssertionError("unknown option: " + name);
+        }
+    }
+
     /**
      * The workhorse of the connection operation.  Tries several times to
      * establish a connection to the given <host, port>.  If unsuccessful,