8224477: java.net socket types new-style socket option methods - spec and impl mismatch
Reviewed-by: alanb
--- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Wed May 29 13:58:05 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -28,9 +28,11 @@
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
import sun.net.ResourceManager;
+import sun.net.ext.ExtendedSocketOptions;
import sun.security.action.GetPropertyAction;
/**
@@ -88,26 +90,6 @@
}
/**
- * 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;
- }
-
- /**
* Creates a datagram socket
*/
protected synchronized void create() throws SocketException {
@@ -400,6 +382,125 @@
return result;
}
+ static final ExtendedSocketOptions extendedOptions =
+ ExtendedSocketOptions.getInstance();
+
+ private static final Set<SocketOption<?>> datagramSocketOptions = datagramSocketOptions();
+ private static final Set<SocketOption<?>> multicastSocketOptions = multicastSocketOptions();
+
+ private static Set<SocketOption<?>> datagramSocketOptions() {
+ HashSet<SocketOption<?>> options = new HashSet<>();
+ options.add(StandardSocketOptions.SO_SNDBUF);
+ 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.datagramSocketOptions());
+ return Collections.unmodifiableSet(options);
+ }
+
+ private static Set<SocketOption<?>> multicastSocketOptions() {
+ HashSet<SocketOption<?>> options = new HashSet<>();
+ options.add(StandardSocketOptions.SO_SNDBUF);
+ options.add(StandardSocketOptions.SO_RCVBUF);
+ options.add(StandardSocketOptions.SO_REUSEADDR);
+ options.add(StandardSocketOptions.IP_TOS);
+ options.add(StandardSocketOptions.IP_MULTICAST_IF);
+ options.add(StandardSocketOptions.IP_MULTICAST_TTL);
+ options.add(StandardSocketOptions.IP_MULTICAST_LOOP);
+ if (isReusePortAvailable())
+ options.add(StandardSocketOptions.SO_REUSEPORT);
+ options.addAll(ExtendedSocketOptions.datagramSocketOptions());
+ return Collections.unmodifiableSet(options);
+ }
+
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ if (getDatagramSocket() instanceof MulticastSocket)
+ return multicastSocketOptions;
+ else
+ return datagramSocketOptions;
+ }
+
+ @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 (isClosed())
+ throw new SocketException("Socket closed");
+
+ 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.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.IP_MULTICAST_IF ) {
+ setOption(SocketOptions.IP_MULTICAST_IF2, value);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) {
+ int i = ((Integer)value).intValue();
+ if (i < 0 || i > 255)
+ throw new IllegalArgumentException("Invalid TTL/hop value: " + value);
+ setTimeToLive((Integer)value);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) {
+ setOption(SocketOptions.IP_MULTICAST_LOOP, 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 (isClosed())
+ throw new SocketException("Socket closed");
+
+ 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.IP_TOS) {
+ return (T) getOption(SocketOptions.IP_TOS);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_IF) {
+ return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) {
+ return (T) ((Integer) getTimeToLive());
+ } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) {
+ return (T) getOption(SocketOptions.IP_MULTICAST_LOOP);
+ } else if (extendedOptions.isOptionSupported(name)) {
+ return (T) extendedOptions.getOption(fd, name);
+ } else {
+ throw new AssertionError("unknown option: " + name);
+ }
+ }
+
protected abstract void datagramSocketCreate() throws SocketException;
protected abstract void datagramSocketClose();
protected abstract void socketSetOption(int opt, Object val)
--- 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,
--- a/src/java.base/share/classes/java/net/DatagramSocket.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java Wed May 29 13:58:05 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2015, 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
@@ -29,6 +29,7 @@
import java.nio.channels.DatagramChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
+import java.util.Objects;
import java.util.Set;
import java.util.Collections;
@@ -1343,6 +1344,9 @@
public <T> DatagramSocket setOption(SocketOption<T> name, T value)
throws IOException
{
+ Objects.requireNonNull(name);
+ if (isClosed())
+ throw new SocketException("Socket is closed");
getImpl().setOption(name, value);
return this;
}
@@ -1371,6 +1375,9 @@
* @since 9
*/
public <T> T getOption(SocketOption<T> name) throws IOException {
+ Objects.requireNonNull(name);
+ if (isClosed())
+ throw new SocketException("Socket is closed");
return getImpl().getOption(name);
}
--- a/src/java.base/share/classes/java/net/DatagramSocketImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocketImpl.java Wed May 29 13:58:05 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -27,6 +27,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.Objects;
import java.util.Set;
/**
@@ -265,123 +266,69 @@
/**
* Called to set a socket option.
*
+ * @implSpec
+ * The default implementation of this method first checks that the given
+ * socket option {code name} is not null, then throws {@code
+ * UnsupportedOperationException}. Subclasses should override this method
+ * with an appropriate implementation.
+ *
* @param <T> The type of the socket option value
* @param name The socket option
- *
* @param value The value of the socket option. A value of {@code null}
* may be valid for some options.
*
* @throws UnsupportedOperationException if the DatagramSocketImpl does not
* support the option
- *
+ * @throws IllegalArgumentException if the value is not valid for
+ * the option
+ * @throws IOException if an I/O error occurs, or if the socket is closed
* @throws NullPointerException if name is {@code null}
- * @throws IOException if an I/O problem occurs while attempting to set the option
+ *
* @since 9
*/
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
- if (name == StandardSocketOptions.SO_SNDBUF) {
- setOption(SocketOptions.SO_SNDBUF, value);
- } else if (name == StandardSocketOptions.SO_RCVBUF) {
- setOption(SocketOptions.SO_RCVBUF, value);
- } else if (name == StandardSocketOptions.SO_REUSEADDR) {
- setOption(SocketOptions.SO_REUSEADDR, value);
- } else if (name == StandardSocketOptions.SO_REUSEPORT &&
- supportedOptions().contains(name)) {
- setOption(SocketOptions.SO_REUSEPORT, value);
- } else if (name == StandardSocketOptions.IP_TOS) {
- setOption(SocketOptions.IP_TOS, value);
- } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
- (getDatagramSocket() instanceof MulticastSocket)) {
- setOption(SocketOptions.IP_MULTICAST_IF2, value);
- } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
- (getDatagramSocket() instanceof MulticastSocket)) {
- if (! (value instanceof Integer)) {
- throw new IllegalArgumentException("not an integer");
- }
- setTimeToLive((Integer)value);
- } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
- (getDatagramSocket() instanceof MulticastSocket)) {
- setOption(SocketOptions.IP_MULTICAST_LOOP, value);
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
+ Objects.requireNonNull(name);
+ throw new UnsupportedOperationException("'" + name + "' not supported");
}
/**
* Called to get a socket option.
*
- * @return the socket option
+ * @implSpec
+ * The default implementation of this method first checks that the given
+ * socket option {code name} is not null, then throws {@code
+ * UnsupportedOperationException}. Subclasses should override this method
+ * with an appropriate implementation.
+ *
* @param <T> The type of the socket option value
* @param name The socket option
+ * @return the socket option
*
* @throws UnsupportedOperationException if the DatagramSocketImpl does not
* support the option
- *
+ * @throws IOException if an I/O error occurs, or if the socket is closed
* @throws NullPointerException if name is {@code null}
- * @throws IOException if an I/O problem occurs while attempting to set the option
*
* @since 9
*/
- @SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException {
- 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 &&
- supportedOptions().contains(name)) {
- return (T) getOption(SocketOptions.SO_REUSEPORT);
- } else if (name == StandardSocketOptions.IP_TOS) {
- return (T) getOption(SocketOptions.IP_TOS);
- } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
- (getDatagramSocket() instanceof MulticastSocket)) {
- return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
- } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
- (getDatagramSocket() instanceof MulticastSocket)) {
- Integer ttl = getTimeToLive();
- return (T)ttl;
- } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
- (getDatagramSocket() instanceof MulticastSocket)) {
- return (T) getOption(SocketOptions.IP_MULTICAST_LOOP);
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
- }
-
- private static final Set<SocketOption<?>> dgSocketOptions;
-
- private static final Set<SocketOption<?>> mcSocketOptions;
-
- static {
- dgSocketOptions = Set.of(StandardSocketOptions.SO_SNDBUF,
- StandardSocketOptions.SO_RCVBUF,
- StandardSocketOptions.SO_REUSEADDR,
- StandardSocketOptions.IP_TOS);
-
- mcSocketOptions = Set.of(StandardSocketOptions.SO_SNDBUF,
- StandardSocketOptions.SO_RCVBUF,
- StandardSocketOptions.SO_REUSEADDR,
- StandardSocketOptions.IP_TOS,
- StandardSocketOptions.IP_MULTICAST_IF,
- StandardSocketOptions.IP_MULTICAST_TTL,
- StandardSocketOptions.IP_MULTICAST_LOOP);
+ Objects.requireNonNull(name);
+ throw new UnsupportedOperationException("'" + name + "' not supported");
}
/**
* Returns a set of SocketOptions supported by this impl
* and by this impl's socket (DatagramSocket or MulticastSocket)
*
+ * @implSpec
+ * The default implementation of this method returns an empty set.
+ * Subclasses should override this method with an appropriate implementation.
+ *
* @return a Set of SocketOptions
*
* @since 9
*/
protected Set<SocketOption<?>> supportedOptions() {
- if (getDatagramSocket() instanceof MulticastSocket) {
- return mcSocketOptions;
- } else {
- return dgSocketOptions;
- }
+ return Set.of();
}
}
--- a/src/java.base/share/classes/java/net/ServerSocket.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/ServerSocket.java Wed May 29 13:58:05 2019 +0100
@@ -1025,6 +1025,9 @@
public <T> ServerSocket setOption(SocketOption<T> name, T value)
throws IOException
{
+ Objects.requireNonNull(name);
+ if (isClosed())
+ throw new SocketException("Socket is closed");
getImpl().setOption(name, value);
return this;
}
@@ -1053,6 +1056,9 @@
* @since 9
*/
public <T> T getOption(SocketOption<T> name) throws IOException {
+ Objects.requireNonNull(name);
+ if (isClosed())
+ throw new SocketException("Socket is closed");
return getImpl().getOption(name);
}
--- a/src/java.base/share/classes/java/net/Socket.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/Socket.java Wed May 29 13:58:05 2019 +0100
@@ -33,6 +33,7 @@
import java.nio.channels.SocketChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.Objects;
import java.util.Set;
import java.util.Collections;
@@ -1786,6 +1787,9 @@
* @since 9
*/
public <T> Socket setOption(SocketOption<T> name, T value) throws IOException {
+ Objects.requireNonNull(name);
+ if (isClosed())
+ throw new SocketException("Socket is closed");
getImpl().setOption(name, value);
return this;
}
@@ -1815,6 +1819,9 @@
*/
@SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name) throws IOException {
+ Objects.requireNonNull(name);
+ if (isClosed())
+ throw new SocketException("Socket is closed");
return getImpl().getOption(name);
}
--- a/src/java.base/share/classes/java/net/SocketImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/SocketImpl.java Wed May 29 13:58:05 2019 +0100
@@ -29,8 +29,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileDescriptor;
+import java.util.Objects;
import java.util.Set;
-
import sun.net.PlatformSocketImpl;
/**
@@ -75,21 +75,9 @@
protected int localport;
/**
- * Whether this is a server or not.
- */
- final boolean isServer;
-
-
- SocketImpl(boolean isServer) {
- this.isServer = isServer;
- }
-
- /**
* Initialize a new instance of this class
*/
- public SocketImpl() {
- this.isServer = false;
- }
+ public SocketImpl() { }
/**
* Creates either a stream or a datagram socket.
@@ -376,79 +364,54 @@
/**
* Called to set a socket option.
*
+ * @implSpec
+ * The default implementation of this method first checks that the given
+ * socket option {code name} is not null, then throws {@code
+ * UnsupportedOperationException}. Subclasses should override this method
+ * with an appropriate implementation.
+ *
* @param <T> The type of the socket option value
* @param name The socket option
- *
* @param value The value of the socket option. A value of {@code null}
* may be valid for some options.
*
* @throws UnsupportedOperationException if the SocketImpl does not
* support the option
- *
- * @throws IOException if an I/O error occurs, or if the socket is closed.
+ * @throws IllegalArgumentException if the value is not valid for
+ * the option
+ * @throws IOException if an I/O error occurs, or if the socket is closed
+ * @throws NullPointerException if name is {@code null}
*
* @since 9
*/
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
- if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) {
- setOption(SocketOptions.SO_KEEPALIVE, value);
- } else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) {
- setOption(SocketOptions.SO_SNDBUF, value);
- } else if (name == StandardSocketOptions.SO_RCVBUF) {
- setOption(SocketOptions.SO_RCVBUF, value);
- } else if (name == StandardSocketOptions.SO_REUSEADDR) {
- setOption(SocketOptions.SO_REUSEADDR, value);
- } else if (name == StandardSocketOptions.SO_REUSEPORT &&
- supportedOptions().contains(name)) {
- setOption(SocketOptions.SO_REUSEPORT, value);
- } else if (name == StandardSocketOptions.SO_LINGER && !isServer) {
- setOption(SocketOptions.SO_LINGER, value);
- } else if (name == StandardSocketOptions.IP_TOS) {
- setOption(SocketOptions.IP_TOS, value);
- } else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) {
- setOption(SocketOptions.TCP_NODELAY, value);
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
+ Objects.requireNonNull(name);
+ throw new UnsupportedOperationException("'" + name + "' not supported");
}
/**
* Called to get a socket option.
*
+ * @implSpec
+ * The default implementation of this method first checks that the given
+ * socket option {code name} is not null, then throws {@code
+ * UnsupportedOperationException}. Subclasses should override this method
+ * with an appropriate implementation.
+ *
* @param <T> The type of the socket option value
* @param name The socket option
- *
* @return the value of the named option
*
* @throws UnsupportedOperationException if the SocketImpl does not
- * support the option.
- *
- * @throws IOException if an I/O error occurs, or if the socket is closed.
+ * support the option
+ * @throws IOException if an I/O error occurs, or if the socket is closed
+ * @throws NullPointerException if name is {@code null}
*
* @since 9
*/
- @SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException {
- if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) {
- return (T)getOption(SocketOptions.SO_KEEPALIVE);
- } else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) {
- 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 &&
- supportedOptions().contains(name)) {
- return (T)getOption(SocketOptions.SO_REUSEPORT);
- } else if (name == StandardSocketOptions.SO_LINGER && !isServer) {
- return (T)getOption(SocketOptions.SO_LINGER);
- } else if (name == StandardSocketOptions.IP_TOS) {
- return (T)getOption(SocketOptions.IP_TOS);
- } else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) {
- return (T)getOption(SocketOptions.TCP_NODELAY);
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
+ Objects.requireNonNull(name);
+ throw new UnsupportedOperationException("'" + name + "' not supported");
}
/**
@@ -464,37 +427,19 @@
} catch (IOException ignore) { }
}
- private static final Set<SocketOption<?>> socketOptions;
-
- private static final Set<SocketOption<?>> serverSocketOptions;
-
- static {
- socketOptions = Set.of(StandardSocketOptions.SO_KEEPALIVE,
- StandardSocketOptions.SO_SNDBUF,
- StandardSocketOptions.SO_RCVBUF,
- StandardSocketOptions.SO_REUSEADDR,
- StandardSocketOptions.SO_LINGER,
- StandardSocketOptions.IP_TOS,
- StandardSocketOptions.TCP_NODELAY);
-
- serverSocketOptions = Set.of(StandardSocketOptions.SO_RCVBUF,
- StandardSocketOptions.SO_REUSEADDR,
- StandardSocketOptions.IP_TOS);
- }
-
/**
* Returns a set of SocketOptions supported by this impl
* and by this impl's socket (Socket or ServerSocket)
*
+ * @implSpec
+ * The default implementation of this method returns an empty set.
+ * Subclasses should override this method with an appropriate implementation.
+ *
* @return a Set of SocketOptions
*
* @since 9
*/
protected Set<SocketOption<?>> supportedOptions() {
- if (!isServer) {
- return socketOptions;
- } else {
- return serverSocketOptions;
- }
+ return Set.of();
}
}
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed May 29 13:58:05 2019 +0100
@@ -222,6 +222,8 @@
Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
+ if (!name.type().isInstance(value))
+ throw new IllegalArgumentException("Invalid value '" + value + "'");
synchronized (stateLock) {
ensureOpen();
@@ -236,8 +238,6 @@
}
if (name == StandardSocketOptions.IP_MULTICAST_IF) {
- if (value == null)
- throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'");
NetworkInterface interf = (NetworkInterface)value;
if (family == StandardProtocolFamily.INET6) {
int index = interf.getIndex();
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Wed May 29 13:58:05 2019 +0100
@@ -147,6 +147,9 @@
Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
+ if (!name.type().isInstance(value))
+ throw new IllegalArgumentException("Invalid value '" + value + "'");
+
synchronized (stateLock) {
ensureOpen();
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Wed May 29 13:58:05 2019 +0100
@@ -218,6 +218,8 @@
Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
+ if (!name.type().isInstance(value))
+ throw new IllegalArgumentException("Invalid value '" + value + "'");
synchronized (stateLock) {
ensureOpen();
--- a/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Wed May 29 13:58:05 2019 +0100
@@ -41,46 +41,6 @@
init();
}
- static final ExtendedSocketOptions extendedOptions =
- ExtendedSocketOptions.getInstance();
-
- protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
- if (isClosed()) {
- throw new SocketException("Socket closed");
- }
- if (supportedOptions().contains(name)) {
- if (extendedOptions.isOptionSupported(name)) {
- extendedOptions.setOption(fd, name, value);
- } else {
- super.setOption(name, value);
- }
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
- }
-
- @SuppressWarnings("unchecked")
- protected <T> T getOption(SocketOption<T> name) throws IOException {
- if (isClosed()) {
- throw new SocketException("Socket closed");
- }
- if (supportedOptions().contains(name)) {
- if (extendedOptions.isOptionSupported(name)) {
- return (T) extendedOptions.getOption(fd, name);
- } else {
- return super.getOption(name);
- }
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
- }
-
- protected Set<SocketOption<?>> supportedOptions() {
- HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
- options.addAll(ExtendedSocketOptions.datagramSocketOptions());
- return options;
- }
-
protected void socketSetOption(int opt, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
--- a/src/java.base/unix/classes/java/net/PlainSocketImpl.java Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/unix/classes/java/net/PlainSocketImpl.java Wed May 29 13:58:05 2019 +0100
@@ -25,7 +25,6 @@
package java.net;
import java.io.IOException;
-import java.io.FileDescriptor;
import java.util.Set;
import java.util.HashSet;
import sun.net.ext.ExtendedSocketOptions;
@@ -49,50 +48,6 @@
super(isServer);
}
- static final ExtendedSocketOptions extendedOptions =
- ExtendedSocketOptions.getInstance();
-
- protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
- if (isClosedOrPending()) {
- throw new SocketException("Socket closed");
- }
- if (supportedOptions().contains(name)) {
- if (extendedOptions.isOptionSupported(name)) {
- extendedOptions.setOption(fd, name, value);
- } else {
- super.setOption(name, value);
- }
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
- }
-
- @SuppressWarnings("unchecked")
- protected <T> T getOption(SocketOption<T> name) throws IOException {
- if (isClosedOrPending()) {
- throw new SocketException("Socket closed");
- }
- if (supportedOptions().contains(name)) {
- if (extendedOptions.isOptionSupported(name)) {
- return (T) extendedOptions.getOption(fd, name);
- } else {
- return super.getOption(name);
- }
- } else {
- throw new UnsupportedOperationException("unsupported option");
- }
- }
-
- protected Set<SocketOption<?>> supportedOptions() {
- HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
- if (isServer) {
- options.addAll(ExtendedSocketOptions.serverSocketOptions());
- } else {
- options.addAll(ExtendedSocketOptions.clientSocketOptions());
- }
- return options;
- }
-
protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,114 @@
+/*
+ * 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
+ * @bug 8224477
+ * @summary Basic test for java.net.DatagramSocketImpl default behavior
+ * @run testng TestDefaultBehavior
+ */
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocketImpl;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.util.Set;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.expectThrows;
+
+public class TestDefaultBehavior {
+
+ static final Class<NullPointerException> NPE = NullPointerException.class;
+ static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+ @Test
+ public void datagramSocketImpl() {
+ CustomDatagramSocketImpl dsi = new CustomDatagramSocketImpl();
+
+ assertEquals(dsi.supportedOptions().size(), 0);
+
+ expectThrows(NPE, () -> dsi.setOption(null, null));
+ expectThrows(NPE, () -> dsi.setOption(null, 1));
+ expectThrows(UOE, () -> dsi.setOption(SO_RCVBUF, 100));
+ expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE));
+ expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, FALSE));
+ expectThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE));
+
+ expectThrows(NPE, () -> dsi.getOption(null));
+ expectThrows(UOE, () -> dsi.getOption(SO_RCVBUF));
+ expectThrows(UOE, () -> dsi.getOption(SO_KEEPALIVE));
+ expectThrows(UOE, () -> dsi.getOption(FAKE_SOCK_OPT));
+ }
+
+ static final SocketOption<Boolean> FAKE_SOCK_OPT = new SocketOption<>() {
+ @Override public String name() { return "FAKE_SOCK_OPT"; }
+ @Override public Class<Boolean> type() { return Boolean.class; }
+ };
+
+ // A DatagramSocketImpl that delegates the three new-style socket option
+ // methods to the default java.net.DatagramSocketImpl implementation.
+ static class CustomDatagramSocketImpl extends DatagramSocketImpl {
+
+ @Override
+ public <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ super.setOption(name, value);
+ }
+
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return super.supportedOptions();
+ }
+
+ @Override
+ public <T> T getOption(SocketOption<T> name) throws IOException {
+ return super.getOption(name);
+ }
+
+ // --
+ @Override protected void create() { }
+ @Override protected void bind(int lport, InetAddress laddr) { }
+ @Override protected void send(DatagramPacket p) { }
+ @Override protected int peek(InetAddress i) { return 0; }
+ @Override protected int peekData(DatagramPacket p) { return 0; }
+ @Override protected void receive(DatagramPacket p) { }
+ @Override protected void setTTL(byte ttl) { }
+ @Override protected byte getTTL() { return 0; }
+ @Override protected void setTimeToLive(int ttl) { }
+ @Override protected int getTimeToLive() { return 0; }
+ @Override protected void join(InetAddress inetaddr) { }
+ @Override protected void leave(InetAddress inetaddr) { }
+ @Override protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) { }
+ @Override protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) { }
+ @Override protected void close() { }
+ @Override public void setOption(int optID, Object value) { }
+ @Override public Object getOption(int optID) { return null; }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,112 @@
+/*
+ * 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
+ * @bug 8224477
+ * @summary Basic test for java.net.SocketImpl default behavior
+ * @run testng TestDefaultBehavior
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketAddress;
+import java.net.SocketImpl;
+import java.net.SocketOption;
+import java.util.Set;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.expectThrows;
+
+public class TestDefaultBehavior {
+
+ static final Class<NullPointerException> NPE = NullPointerException.class;
+ static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+ @Test
+ public void socketImpl() {
+ CustomSocketImpl csi = new CustomSocketImpl();
+
+ assertEquals(csi.supportedOptions().size(), 0);
+
+ expectThrows(NPE, () -> csi.setOption(null, null));
+ expectThrows(NPE, () -> csi.setOption(null, 1));
+ expectThrows(UOE, () -> csi.setOption(SO_RCVBUF, 100));
+ expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE));
+ expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, FALSE));
+ expectThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE));
+
+ expectThrows(NPE, () -> csi.getOption(null));
+ expectThrows(UOE, () -> csi.getOption(SO_RCVBUF));
+ expectThrows(UOE, () -> csi.getOption(SO_KEEPALIVE));
+ expectThrows(UOE, () -> csi.getOption(FAKE_SOCK_OPT));
+ }
+
+ static final SocketOption<Boolean> FAKE_SOCK_OPT = new SocketOption<>() {
+ @Override public String name() { return "FAKE_SOCK_OPT"; }
+ @Override public Class<Boolean> type() { return Boolean.class; }
+ };
+
+ // A SocketImpl that delegates the three new-style socket option
+ // methods to the default java.net.SocketImpl implementation.
+ static class CustomSocketImpl extends SocketImpl {
+
+ @Override
+ public <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ super.setOption(name, value);
+ }
+
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return super.supportedOptions();
+ }
+
+ @Override
+ public <T> T getOption(SocketOption<T> name) throws IOException {
+ return super.getOption(name);
+ }
+
+ // --
+
+ @Override protected void create(boolean stream) { }
+ @Override protected void connect(String host, int port) { }
+ @Override protected void connect(InetAddress address, int port) { }
+ @Override protected void connect(SocketAddress address, int timeout) { }
+ @Override protected void bind(InetAddress host, int port) { }
+ @Override protected void listen(int backlog) { }
+ @Override protected void accept(SocketImpl s) { }
+ @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 data) { }
+ @Override public void setOption(int optID, Object value) { }
+ @Override public Object getOption(int optID) { return null; }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketOption/AfterClose.java Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,384 @@
+/*
+ * 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
+ * @bug 8224477
+ * @summary Ensures that IOException is thrown after the socket is closed
+ * @run testng AfterClose
+ */
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.DatagramSocket;
+import java.net.MulticastSocket;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketOption;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.expectThrows;
+
+public class AfterClose {
+
+ static final Class<IOException> IOE = IOException.class;
+
+ static Map<SocketOption<?>,List<Object>> OPTION_VALUES_MAP = optionValueMap();
+
+ static Map<SocketOption<?>,List<Object>> optionValueMap() {
+ Map<SocketOption<?>,List<Object>> map = new HashMap<>();
+ map.put(IP_MULTICAST_IF, listOf(TRUE, FALSE) );
+ map.put(IP_MULTICAST_LOOP, listOf(TRUE, FALSE) );
+ map.put(IP_MULTICAST_TTL, listOf(0, 100, 255) );
+ map.put(IP_TOS, listOf(0, 101, 255) );
+ map.put(SO_BROADCAST, listOf(TRUE, FALSE) );
+ map.put(SO_KEEPALIVE, listOf(TRUE, FALSE) );
+ map.put(SO_LINGER, listOf(0, 5, 15) );
+ map.put(SO_RCVBUF, listOf(1, 100, 1000));
+ map.put(SO_REUSEADDR, listOf(TRUE, FALSE) );
+ map.put(SO_REUSEPORT, listOf(TRUE, FALSE) );
+ map.put(SO_SNDBUF, listOf(1, 100, 1000));
+ map.put(TCP_NODELAY, listOf(TRUE, FALSE) );
+ // extended options
+ try {
+ Class<?> c = Class.forName("jdk.net.ExtendedSocketOptions");
+ Field field = c.getField("SO_FLOW_SLA");
+ map.put((SocketOption<?>)field.get(null), listOf(createSocketFlow()));
+ field = c.getField("TCP_QUICKACK");
+ map.put((SocketOption<?>)field.get(null), listOf(TRUE, FALSE));
+ field = c.getField("TCP_KEEPIDLE");
+ map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+ field = c.getField("TCP_KEEPINTERVAL");
+ map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+ field = c.getField("TCP_KEEPCOUNT");
+ map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+ } catch (ClassNotFoundException e) {
+ // ignore, jdk.net module not present
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ return map;
+ }
+
+ // -- Socket
+
+ @DataProvider(name = "socketOptionValues")
+ public Object[][] socketOptionValues() throws Exception {
+ try (Socket s = new Socket()) {
+ return s.supportedOptions().stream()
+ .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "socketOptionValues")
+ public <T> void closedSocketImplUncreated(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ Socket socket = createClosedSocketImplUncreated();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> socket.setOption(option, value));
+ expectThrows(IOE, () -> socket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "socketOptionValues")
+ public <T> void closedSocketImplCreated(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ Socket socket = createClosedSocketImplCreated();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> socket.setOption(option, value));
+ expectThrows(IOE, () -> socket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "socketOptionValues")
+ public <T> void closedSocketAdapter(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ Socket socket = createClosedSocketFromAdapter();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> socket.setOption(option, value));
+ expectThrows(IOE, () -> socket.getOption(option));
+ }
+ }
+ }
+
+ // -- ServerSocket
+
+ @DataProvider(name = "serverSocketOptionValues")
+ public Object[][] serverSocketOptionValues() throws Exception {
+ try (ServerSocket ss = new ServerSocket()) {
+ return ss.supportedOptions().stream()
+ .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "serverSocketOptionValues")
+ public <T> void closedServerSocketImplUncreated(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ ServerSocket serverSocket = createClosedServerSocketImplUncreated();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> serverSocket.setOption(option, value));
+ expectThrows(IOE, () -> serverSocket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "serverSocketOptionValues")
+ public <T> void closedServerSocketImplCreated(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ ServerSocket serverSocket = createClosedServerSocketImplCreated();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> serverSocket.setOption(option, value));
+ expectThrows(IOE, () -> serverSocket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "serverSocketOptionValues")
+ public <T> void closedServerSocketAdapter(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ if (option == IP_TOS)
+ return; // SSC does not support IP_TOS
+
+ ServerSocket serverSocket = createClosedServerSocketFromAdapter();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> serverSocket.setOption(option, value));
+ expectThrows(IOE, () -> serverSocket.getOption(option));
+ }
+ }
+ }
+
+ // -- DatagramSocket
+
+ @DataProvider(name = "datagramSocketOptionValues")
+ public Object[][] datagramSocketOptionValues() throws Exception {
+ try (DatagramSocket ds = new DatagramSocket()) {
+ return ds.supportedOptions().stream()
+ .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "datagramSocketOptionValues")
+ public <T> void closedUnboundDatagramSocket(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ DatagramSocket datagramSocket = createClosedUnboundDatagramSocket();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> datagramSocket.setOption(option, value));
+ expectThrows(IOE, () -> datagramSocket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "datagramSocketOptionValues")
+ public <T> void closedBoundDatagramSocket(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ DatagramSocket datagramSocket = createClosedBoundDatagramSocket();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> datagramSocket.setOption(option, value));
+ expectThrows(IOE, () -> datagramSocket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "datagramSocketOptionValues")
+ public <T> void closedDatagramAdapter(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ DatagramSocket datagramSocket = createClosedBoundDatagramSocket();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> datagramSocket.setOption(option, value));
+ expectThrows(IOE, () -> datagramSocket.getOption(option));
+ }
+ }
+ }
+
+ // -- MulticastSocket
+
+ @DataProvider(name = "multicastSocketOptionValues")
+ public Object[][] multicastSocketOptionValues() throws Exception {
+ try (MulticastSocket ms = new MulticastSocket()) {
+ return ms.supportedOptions().stream()
+ .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "multicastSocketOptionValues")
+ public <T> void closedUnboundMulticastSocket(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ MulticastSocket multicastSocket = createClosedUnboundMulticastSocket();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> multicastSocket.setOption(option, value));
+ expectThrows(IOE, () -> multicastSocket.getOption(option));
+ }
+ }
+ }
+
+ @Test(dataProvider = "multicastSocketOptionValues")
+ public <T> void closedBoundMulticastSocket(SocketOption<T> option, List<T> values)
+ throws IOException
+ {
+ MulticastSocket multicastSocket = createClosedBoundMulticastSocket();
+ for (int i=0; i<3; i++); {
+ for (T value : values) {
+ expectThrows(IOE, () -> multicastSocket.setOption(option, value));
+ expectThrows(IOE, () -> multicastSocket.getOption(option));
+ }
+ }
+ }
+
+ // --
+
+ static List<Object> listOf(Object... objs) {
+ List<Object> l = new ArrayList<>();
+ Arrays.stream(objs).forEachOrdered(l::add);
+ return l;
+ }
+
+ // Returns a closed Socket that has an impl whose `create` method has NOT been invoked.
+ static Socket createClosedSocketImplUncreated() throws IOException {
+ Socket s = new Socket();
+ s.close();
+ return s;
+ }
+
+ // Returns a closed Socket that has an impl whose `create` method has been invoked.
+ static Socket createClosedSocketImplCreated() throws IOException {
+ Socket s = new Socket();
+ s.bind(null); // binding causes impl::create to be invoked
+ s.close();
+ return s;
+ }
+
+ // Returns a closed Socket created from a SocketChannel's adapter.
+ static Socket createClosedSocketFromAdapter() throws IOException {
+ SocketChannel sc = SocketChannel.open();
+ sc.close();
+ return sc.socket();
+ }
+
+ // Returns a closed ServerSocket that has an impl whose `create` method has NOT been invoked.
+ static ServerSocket createClosedServerSocketImplUncreated() throws IOException {
+ ServerSocket ss = new ServerSocket();
+ ss.close();
+ return ss;
+ }
+
+ // Returns a closed ServerSocket that has an impl whose `create` method has been invoked.
+ static ServerSocket createClosedServerSocketImplCreated() throws IOException {
+ ServerSocket ss = new ServerSocket();
+ ss.bind(null); // binding causes impl::create to be invoked
+ ss.close();
+ return ss;
+ }
+
+ // Returns a closed ServerSocket created from a ServerSocketChannel's adapter.
+ static ServerSocket createClosedServerSocketFromAdapter() throws IOException {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.close();
+ return ssc.socket();
+ }
+
+ // Returns a closed unbound DatagramSocket.
+ static DatagramSocket createClosedUnboundDatagramSocket() throws IOException {
+ DatagramSocket ds = new DatagramSocket(null);
+ assert ds.isBound() == false;
+ ds.close();
+ return ds;
+ }
+
+ // Returns a closed bound DatagramSocket.
+ static DatagramSocket createClosedBoundDatagramSocket() throws IOException {
+ DatagramSocket ds = new DatagramSocket();
+ assert ds.isBound() == true;
+ ds.close();
+ return ds;
+ }
+
+ // Returns a closed DatagramSocket that created from a DatagramChannel's adapter.
+ static DatagramSocket createClosedDatagramSocketFromAdapter() throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.close();
+ return dc.socket();
+ }
+
+ // Returns a closed unbound MulticastSocket.
+ static MulticastSocket createClosedUnboundMulticastSocket() throws IOException {
+ MulticastSocket ms = new MulticastSocket(null);
+ assert ms.isBound() == false;
+ ms.close();
+ return ms;
+ }
+
+ // Returns a closed bound MulticastSocket.
+ static MulticastSocket createClosedBoundMulticastSocket() throws IOException {
+ MulticastSocket ms = new MulticastSocket();
+ assert ms.isBound() == true;
+ ms.close();
+ return ms;
+ }
+
+ static Object createSocketFlow() {
+ try {
+ Class<?> c = Class.forName("jdk.net.SocketFlow");
+ Method method = c.getDeclaredMethod("create");
+ return method.invoke(null);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketOption/NullsAndBadValues.java Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,331 @@
+/*
+ * 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
+ * @bug 8224477
+ * @summary Basic test for NPE, UOE, and IAE for get/setOption
+ * @run testng NullsAndBadValues
+ * @run testng/othervm -Dsun.net.useExclusiveBind=false NullsAndBadValues
+ */
+
+import java.net.DatagramSocket;
+import java.net.MulticastSocket;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketOption;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.expectThrows;
+
+public class NullsAndBadValues {
+
+ static final Class<NullPointerException> NPE = NullPointerException.class;
+ static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
+ static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+ @Test
+ public void nulls() throws Exception {
+ try (Socket s = new Socket()) {
+ expectThrows(NPE, () -> s.setOption(null, null));
+ expectThrows(NPE, () -> s.setOption(null, ""));
+ expectThrows(NPE, () -> s.setOption(null, 1));
+ expectThrows(NPE, () -> s.getOption(null));
+ }
+ try (ServerSocket ss = new ServerSocket()) {
+ expectThrows(NPE, () -> ss.setOption(null, null));
+ expectThrows(NPE, () -> ss.setOption(null, ""));
+ expectThrows(NPE, () -> ss.setOption(null, 1));
+ expectThrows(NPE, () -> ss.getOption(null));
+ }
+ try (DatagramSocket ds = new DatagramSocket()) {
+ expectThrows(NPE, () -> ds.setOption(null, null));
+ expectThrows(NPE, () -> ds.setOption(null, ""));
+ expectThrows(NPE, () -> ds.setOption(null, 1));
+ expectThrows(NPE, () -> ds.getOption(null));
+ }
+ try (MulticastSocket ms = new MulticastSocket()) {
+ expectThrows(NPE, () -> ms.setOption(null, null));
+ expectThrows(NPE, () -> ms.setOption(null, ""));
+ expectThrows(NPE, () -> ms.setOption(null, 1));
+ expectThrows(NPE, () -> ms.getOption(null));
+ }
+ try (Socket sa = SocketChannel.open().socket()) {
+ expectThrows(NPE, () -> sa.setOption(null, null));
+ expectThrows(NPE, () -> sa.setOption(null, ""));
+ expectThrows(NPE, () -> sa.setOption(null, 1));
+ expectThrows(NPE, () -> sa.getOption(null));
+ }
+ try (ServerSocket ssa = ServerSocketChannel.open().socket()) {
+ expectThrows(NPE, () -> ssa.setOption(null, null));
+ expectThrows(NPE, () -> ssa.setOption(null, ""));
+ expectThrows(NPE, () -> ssa.setOption(null, 1));
+ expectThrows(NPE, () -> ssa.getOption(null));
+ }
+ try (DatagramSocket dsa = DatagramChannel.open().socket()) {
+ expectThrows(NPE, () -> dsa.setOption(null, null));
+ expectThrows(NPE, () -> dsa.setOption(null, ""));
+ expectThrows(NPE, () -> dsa.setOption(null, 1));
+ expectThrows(NPE, () -> dsa.getOption(null));
+ }
+ }
+
+ static final SocketOption<Boolean> FAKE_SOCK_OPT = new SocketOption<>() {
+ @Override public String name() { return "FAKE_SOCK_OPT"; }
+ @Override public Class<Boolean> type() { return Boolean.class; }
+ };
+
+ static final SocketOption RAW_SOCK_OPT = new SocketOption() {
+ @Override public String name() { return "RAW_SOCK_OPT"; }
+ @Override public Class type() { return Boolean.class; }
+ };
+
+ @Test
+ public void uoe() throws Exception {
+ try (Socket s = new Socket()) {
+ expectThrows(UOE, () -> s.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> s.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> s.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> s.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> s.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> s.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> s.getOption(RAW_SOCK_OPT));
+ }
+ try (ServerSocket ss = new ServerSocket()) {
+ expectThrows(UOE, () -> ss.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> ss.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> ss.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> ss.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> ss.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> ss.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> ss.getOption(RAW_SOCK_OPT));
+ }
+ try (DatagramSocket ds = new DatagramSocket()) {
+ expectThrows(UOE, () -> ds.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> ds.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> ds.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> ds.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> ds.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> ds.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> ds.getOption(RAW_SOCK_OPT));
+ }
+ try (MulticastSocket ms = new MulticastSocket()) {
+ expectThrows(UOE, () -> ms.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> ms.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> ms.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> ms.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> ms.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> ms.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> ms.getOption(RAW_SOCK_OPT));
+ }
+ try (Socket sa = SocketChannel.open().socket()) {
+ expectThrows(UOE, () -> sa.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> sa.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> sa.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> sa.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> sa.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> sa.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> sa.getOption(RAW_SOCK_OPT));
+ }
+ try (ServerSocket ssa = ServerSocketChannel.open().socket()) {
+ expectThrows(UOE, () -> ssa.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> ssa.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> ssa.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> ssa.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> ssa.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> ssa.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> ssa.getOption(RAW_SOCK_OPT));
+ }
+ try (DatagramSocket dsa = DatagramChannel.open().socket()) {
+ expectThrows(UOE, () -> dsa.setOption(FAKE_SOCK_OPT, null));
+ expectThrows(UOE, () -> dsa.setOption(FAKE_SOCK_OPT, TRUE));
+ expectThrows(UOE, () -> dsa.setOption(FAKE_SOCK_OPT, FALSE));
+ expectThrows(UOE, () -> dsa.setOption(RAW_SOCK_OPT, ""));
+ expectThrows(UOE, () -> dsa.setOption(RAW_SOCK_OPT, 1));
+ expectThrows(UOE, () -> dsa.getOption(FAKE_SOCK_OPT));
+ expectThrows(UOE, () -> dsa.getOption(RAW_SOCK_OPT));
+ }
+ }
+
+ static Map<SocketOption<?>,List<Object>> BAD_OPTION_VALUES = badOptionValues();
+
+ static Map<SocketOption<?>,List<Object>> badOptionValues() {
+ Map<SocketOption<?>,List<Object>> map = new HashMap<>();
+ map.put(IP_MULTICAST_IF, listOf(null) );
+ map.put(IP_MULTICAST_LOOP, listOf(null) );
+ map.put(IP_MULTICAST_TTL, listOf(null, -1, 256));
+ map.put(IP_TOS, listOf(null, -1, 256));
+ map.put(SO_BROADCAST, listOf(null) );
+ map.put(SO_KEEPALIVE, listOf(null) );
+ map.put(SO_LINGER, listOf(null) );
+ map.put(SO_RCVBUF, listOf(null, -1) );
+ map.put(SO_REUSEADDR, listOf(null) );
+ map.put(SO_REUSEPORT, listOf(null) );
+ map.put(SO_SNDBUF, listOf(null, -1) );
+ map.put(TCP_NODELAY, listOf(null) );
+ // extended options, not in the map, will get a null value
+ return map;
+ }
+
+ // -- Socket
+
+ @DataProvider(name = "socketBadOptionValues")
+ public Object[][] socketBadOptionValues() throws Exception {
+ try (Socket s = new Socket()) {
+ return s.supportedOptions().stream()
+ .flatMap(NullsAndBadValues::socketOptionToBadValues)
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "socketBadOptionValues")
+ public <T> void socket(SocketOption<T> option, T value)
+ throws Exception
+ {
+ try (Socket s = new Socket()) {
+ expectThrows(IAE, () -> s.setOption(option, value));
+ }
+ }
+
+ @Test(dataProvider = "socketBadOptionValues")
+ public <T> void socketAdapter(SocketOption<T> option, T value)
+ throws Exception
+ {
+ try (Socket s = SocketChannel.open().socket()) {
+ expectThrows(IAE, () -> s.setOption(option, value));
+ }
+ }
+
+ // -- ServerSocket
+
+ @DataProvider(name = "serverSocketBadOptionValues")
+ public Object[][] serverSocketBadOptionValues() throws Exception {
+ try (ServerSocket ss = new ServerSocket()) {
+ return ss.supportedOptions().stream()
+ .flatMap(NullsAndBadValues::socketOptionToBadValues)
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "serverSocketBadOptionValues")
+ public <T> void serverSocket(SocketOption<T> option, T value)
+ throws Exception
+ {
+ try (ServerSocket ss = new ServerSocket()) {
+ expectThrows(IAE, () -> ss.setOption(option, value));
+ }
+ }
+
+ @Test(dataProvider = "serverSocketBadOptionValues")
+ public <T> void serverSocketAdapter(SocketOption<T> option, T value)
+ throws Exception
+ {
+ if (option == IP_TOS)
+ return; // SSC does not support IP_TOS
+
+ try (ServerSocket ss = ServerSocketChannel.open().socket()) {
+ expectThrows(IAE, () -> ss.setOption(option, value));
+ }
+ }
+
+ // -- DatagramSocket
+
+ @DataProvider(name = "datagramSocketBadOptionValues")
+ public Object[][] datagramSocketBadOptionValues() throws Exception {
+ try (DatagramSocket ds = new DatagramSocket()) {
+ return ds.supportedOptions().stream()
+ .flatMap(NullsAndBadValues::socketOptionToBadValues)
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "datagramSocketBadOptionValues")
+ public <T> void datagramSocket(SocketOption<T> option, T value)
+ throws Exception
+ {
+ try (DatagramSocket ds = new DatagramSocket()) {
+ expectThrows(IAE, () -> ds.setOption(option, value));
+ }
+ }
+
+ @Test(dataProvider = "datagramSocketBadOptionValues")
+ public <T> void datagramSocketAdapter(SocketOption<T> option, T value)
+ throws Exception
+ {
+ try (DatagramSocket ds = DatagramChannel.open().socket()) {
+ expectThrows(IAE, () -> ds.setOption(option, value));
+ }
+ }
+
+ // -- MulticastSocket
+
+ @DataProvider(name = "multicastSocketBadOptionValues")
+ public Object[][] multicastSocketBadOptionValues() throws Exception {
+ try (MulticastSocket ms = new MulticastSocket()) {
+ return ms.supportedOptions().stream()
+ .flatMap(NullsAndBadValues::socketOptionToBadValues)
+ .toArray(Object[][]::new);
+ }
+ }
+
+ @Test(dataProvider = "multicastSocketBadOptionValues")
+ public <T> void multicastSocket(SocketOption<T> option, T value)
+ throws Exception
+ {
+ try (MulticastSocket ms = new MulticastSocket()) {
+ expectThrows(IAE, () -> ms.setOption(option, value));
+ }
+ }
+
+ // --
+
+ static List<Object> listOf(Object... objs) {
+ List<Object> l = new ArrayList<>();
+ if (objs == null)
+ l.add(null);
+ else
+ Arrays.stream(objs).forEachOrdered(l::add);
+ return l;
+ }
+
+ static Stream<Object[]> socketOptionToBadValues(SocketOption<?> socketOption) {
+ List<Object> values = BAD_OPTION_VALUES.get(socketOption);
+ if (values == null) {
+ Object[][] a = new Object[][] { new Object[] { socketOption, null } };
+ return Stream.of(a);
+ }
+ return values.stream()
+ .flatMap(v -> Stream.of(new Object[][] { new Object[] { socketOption, v } }) );
+ }
+}
--- a/test/jdk/java/net/SocketOption/OptionsTest.java Wed May 29 08:21:33 2019 -0400
+++ b/test/jdk/java/net/SocketOption/OptionsTest.java Wed May 29 13:58:05 2019 +0100
@@ -61,14 +61,18 @@
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)),
- Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound
};
static Test[] serverSocketTests = new Test[] {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
- Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound
};
static Test[] dgSocketTests = new Test[] {
@@ -76,12 +80,16 @@
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
- Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound
};
static Test[] mcSocketTests = new Test[] {
Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()),
+ Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)), // lower-bound
Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)),
+ Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound
Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE)
};
--- a/test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java Wed May 29 08:21:33 2019 -0400
+++ b/test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java Wed May 29 13:58:05 2019 +0100
@@ -64,6 +64,13 @@
socketOptions.add((SocketOption<?>)field.get(null));
field = c.getField("TCP_QUICKACK");
socketOptions.add((SocketOption<?>)field.get(null));
+ field = c.getField("TCP_KEEPIDLE");
+ socketOptions.add((SocketOption<?>)field.get(null));
+ field = c.getField("TCP_KEEPINTERVAL");
+ socketOptions.add((SocketOption<?>)field.get(null));
+ field = c.getField("TCP_KEEPCOUNT");
+ socketOptions.add((SocketOption<?>)field.get(null));
+
} catch (ClassNotFoundException e) {
// ignore, jdk.net module not present
} catch (ReflectiveOperationException e) {