8224477: java.net socket types new-style socket option methods - spec and impl mismatch

Reviewed-by: alanb
This commit is contained in:
Chris Hegarty 2019-05-29 13:58:05 +01:00
parent cf48689855
commit bc24d17e80
18 changed files with 1289 additions and 300 deletions

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,9 +28,11 @@ import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import sun.net.ResourceManager; import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
/** /**
@ -87,26 +89,6 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
return isReusePortAvailable; return isReusePortAvailable;
} }
/**
* 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 * Creates a datagram socket
*/ */
@ -400,6 +382,125 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
return result; 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 datagramSocketCreate() throws SocketException;
protected abstract void datagramSocketClose(); protected abstract void datagramSocketClose();
protected abstract void socketSetOption(int opt, Object val) protected abstract void socketSetOption(int opt, Object val)

View file

@ -35,12 +35,14 @@ import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import sun.net.ConnectionResetException; import sun.net.ConnectionResetException;
import sun.net.NetHooks; import sun.net.NetHooks;
import sun.net.PlatformSocketImpl; import sun.net.PlatformSocketImpl;
import sun.net.ResourceManager; import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.SocketExceptions; import sun.net.util.SocketExceptions;
/** /**
@ -84,6 +86,9 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
*/ */
protected boolean stream; protected boolean stream;
/* whether this is a server or not */
final boolean isServer;
/** /**
* Load net library into runtime. * Load net library into runtime.
*/ */
@ -112,27 +117,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
} }
AbstractPlainSocketImpl(boolean isServer) { AbstractPlainSocketImpl(boolean isServer) {
super(isServer); this.isServer = 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;
} }
/** /**
@ -394,6 +379,121 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
} }
} }
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 * The workhorse of the connection operation. Tries several times to
* establish a connection to the given <host, port>. If unsuccessful, * establish a connection to the given <host, port>. If unsuccessful,

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -29,6 +29,7 @@ import java.io.IOException;
import java.nio.channels.DatagramChannel; import java.nio.channels.DatagramChannel;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.Collections; import java.util.Collections;
@ -1343,6 +1344,9 @@ class DatagramSocket implements java.io.Closeable {
public <T> DatagramSocket setOption(SocketOption<T> name, T value) public <T> DatagramSocket setOption(SocketOption<T> name, T value)
throws IOException throws IOException
{ {
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setOption(name, value); getImpl().setOption(name, value);
return this; return this;
} }
@ -1371,6 +1375,9 @@ class DatagramSocket implements java.io.Closeable {
* @since 9 * @since 9
*/ */
public <T> T getOption(SocketOption<T> name) throws IOException { public <T> T getOption(SocketOption<T> name) throws IOException {
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
return getImpl().getOption(name); return getImpl().getOption(name);
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,6 +27,7 @@ package java.net;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@ -265,123 +266,69 @@ public abstract class DatagramSocketImpl implements SocketOptions {
/** /**
* Called to set a socket option. * 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 <T> The type of the socket option value
* @param name The socket option * @param name The socket option
*
* @param value The value of the socket option. A value of {@code null} * @param value The value of the socket option. A value of {@code null}
* may be valid for some options. * may be valid for some options.
* *
* @throws UnsupportedOperationException if the DatagramSocketImpl does not * @throws UnsupportedOperationException if the DatagramSocketImpl does not
* support the option * 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 NullPointerException if name is {@code null}
* @throws IOException if an I/O problem occurs while attempting to set the option *
* @since 9 * @since 9
*/ */
protected <T> void setOption(SocketOption<T> name, T value) throws IOException { protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (name == StandardSocketOptions.SO_SNDBUF) { Objects.requireNonNull(name);
setOption(SocketOptions.SO_SNDBUF, value); throw new UnsupportedOperationException("'" + name + "' not supported");
} 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");
}
} }
/** /**
* Called to get a socket option. * 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 <T> The type of the socket option value
* @param name The socket option * @param name The socket option
* @return the socket option
* *
* @throws UnsupportedOperationException if the DatagramSocketImpl does not * @throws UnsupportedOperationException if the DatagramSocketImpl does not
* support the option * 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 NullPointerException if name is {@code null}
* @throws IOException if an I/O problem occurs while attempting to set the option
* *
* @since 9 * @since 9
*/ */
@SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException { protected <T> T getOption(SocketOption<T> name) throws IOException {
if (name == StandardSocketOptions.SO_SNDBUF) { Objects.requireNonNull(name);
return (T) getOption(SocketOptions.SO_SNDBUF); throw new UnsupportedOperationException("'" + name + "' not supported");
} 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);
} }
/** /**
* Returns a set of SocketOptions supported by this impl * Returns a set of SocketOptions supported by this impl
* and by this impl's socket (DatagramSocket or MulticastSocket) * 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 * @return a Set of SocketOptions
* *
* @since 9 * @since 9
*/ */
protected Set<SocketOption<?>> supportedOptions() { protected Set<SocketOption<?>> supportedOptions() {
if (getDatagramSocket() instanceof MulticastSocket) { return Set.of();
return mcSocketOptions;
} else {
return dgSocketOptions;
}
} }
} }

View file

@ -1025,6 +1025,9 @@ class ServerSocket implements java.io.Closeable {
public <T> ServerSocket setOption(SocketOption<T> name, T value) public <T> ServerSocket setOption(SocketOption<T> name, T value)
throws IOException throws IOException
{ {
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setOption(name, value); getImpl().setOption(name, value);
return this; return this;
} }
@ -1053,6 +1056,9 @@ class ServerSocket implements java.io.Closeable {
* @since 9 * @since 9
*/ */
public <T> T getOption(SocketOption<T> name) throws IOException { public <T> T getOption(SocketOption<T> name) throws IOException {
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
return getImpl().getOption(name); return getImpl().getOption(name);
} }

View file

@ -33,6 +33,7 @@ import java.lang.invoke.VarHandle;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.Collections; import java.util.Collections;
@ -1786,6 +1787,9 @@ class Socket implements java.io.Closeable {
* @since 9 * @since 9
*/ */
public <T> Socket setOption(SocketOption<T> name, T value) throws IOException { 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); getImpl().setOption(name, value);
return this; return this;
} }
@ -1815,6 +1819,9 @@ class Socket implements java.io.Closeable {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name) throws IOException { public <T> T getOption(SocketOption<T> name) throws IOException {
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
return getImpl().getOption(name); return getImpl().getOption(name);
} }

View file

@ -29,8 +29,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import sun.net.PlatformSocketImpl; import sun.net.PlatformSocketImpl;
/** /**
@ -74,22 +74,10 @@ public abstract class SocketImpl implements SocketOptions {
*/ */
protected int localport; 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 * Initialize a new instance of this class
*/ */
public SocketImpl() { public SocketImpl() { }
this.isServer = false;
}
/** /**
* Creates either a stream or a datagram socket. * Creates either a stream or a datagram socket.
@ -376,79 +364,54 @@ public abstract class SocketImpl implements SocketOptions {
/** /**
* Called to set a socket option. * 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 <T> The type of the socket option value
* @param name The socket option * @param name The socket option
*
* @param value The value of the socket option. A value of {@code null} * @param value The value of the socket option. A value of {@code null}
* may be valid for some options. * may be valid for some options.
* *
* @throws UnsupportedOperationException if the SocketImpl does not * @throws UnsupportedOperationException if the SocketImpl does not
* support the option * support the option
* * @throws IllegalArgumentException if the value is not valid for
* @throws IOException if an I/O error occurs, or if the socket is closed. * 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 * @since 9
*/ */
protected <T> void setOption(SocketOption<T> name, T value) throws IOException { protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) { Objects.requireNonNull(name);
setOption(SocketOptions.SO_KEEPALIVE, value); throw new UnsupportedOperationException("'" + name + "' not supported");
} 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");
}
} }
/** /**
* Called to get a socket option. * 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 <T> The type of the socket option value
* @param name The socket option * @param name The socket option
*
* @return the value of the named option * @return the value of the named option
* *
* @throws UnsupportedOperationException if the SocketImpl does not * @throws UnsupportedOperationException if the SocketImpl does not
* support the option. * support the option
* * @throws IOException if an I/O error occurs, or if the socket is closed
* @throws IOException if an I/O error occurs, or if the socket is closed. * @throws NullPointerException if name is {@code null}
* *
* @since 9 * @since 9
*/ */
@SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException { protected <T> T getOption(SocketOption<T> name) throws IOException {
if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) { Objects.requireNonNull(name);
return (T)getOption(SocketOptions.SO_KEEPALIVE); throw new UnsupportedOperationException("'" + name + "' not supported");
} 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");
}
} }
/** /**
@ -464,37 +427,19 @@ public abstract class SocketImpl implements SocketOptions {
} catch (IOException ignore) { } } 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 * Returns a set of SocketOptions supported by this impl
* and by this impl's socket (Socket or ServerSocket) * 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 * @return a Set of SocketOptions
* *
* @since 9 * @since 9
*/ */
protected Set<SocketOption<?>> supportedOptions() { protected Set<SocketOption<?>> supportedOptions() {
if (!isServer) { return Set.of();
return socketOptions;
} else {
return serverSocketOptions;
}
} }
} }

View file

@ -222,6 +222,8 @@ class DatagramChannelImpl
Objects.requireNonNull(name); Objects.requireNonNull(name);
if (!supportedOptions().contains(name)) if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported"); throw new UnsupportedOperationException("'" + name + "' not supported");
if (!name.type().isInstance(value))
throw new IllegalArgumentException("Invalid value '" + value + "'");
synchronized (stateLock) { synchronized (stateLock) {
ensureOpen(); ensureOpen();
@ -236,8 +238,6 @@ class DatagramChannelImpl
} }
if (name == StandardSocketOptions.IP_MULTICAST_IF) { if (name == StandardSocketOptions.IP_MULTICAST_IF) {
if (value == null)
throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'");
NetworkInterface interf = (NetworkInterface)value; NetworkInterface interf = (NetworkInterface)value;
if (family == StandardProtocolFamily.INET6) { if (family == StandardProtocolFamily.INET6) {
int index = interf.getIndex(); int index = interf.getIndex();

View file

@ -147,6 +147,9 @@ class ServerSocketChannelImpl
Objects.requireNonNull(name); Objects.requireNonNull(name);
if (!supportedOptions().contains(name)) if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported"); throw new UnsupportedOperationException("'" + name + "' not supported");
if (!name.type().isInstance(value))
throw new IllegalArgumentException("Invalid value '" + value + "'");
synchronized (stateLock) { synchronized (stateLock) {
ensureOpen(); ensureOpen();

View file

@ -218,6 +218,8 @@ class SocketChannelImpl
Objects.requireNonNull(name); Objects.requireNonNull(name);
if (!supportedOptions().contains(name)) if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported"); throw new UnsupportedOperationException("'" + name + "' not supported");
if (!name.type().isInstance(value))
throw new IllegalArgumentException("Invalid value '" + value + "'");
synchronized (stateLock) { synchronized (stateLock) {
ensureOpen(); ensureOpen();

View file

@ -41,46 +41,6 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
init(); 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 { protected void socketSetOption(int opt, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT && if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {

View file

@ -25,7 +25,6 @@
package java.net; package java.net;
import java.io.IOException; import java.io.IOException;
import java.io.FileDescriptor;
import java.util.Set; import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import sun.net.ext.ExtendedSocketOptions; import sun.net.ext.ExtendedSocketOptions;
@ -49,50 +48,6 @@ class PlainSocketImpl extends AbstractPlainSocketImpl
super(isServer); 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 { protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT && if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {

View file

@ -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; }
}
}

View file

@ -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; }
}
}

View file

@ -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);
}
}
}

View file

@ -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 } }) );
}
}

View file

@ -61,14 +61,18 @@ public class OptionsTest {
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)), 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[] { static Test[] serverSocketTests = new Test[] {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, 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[] { static Test[] dgSocketTests = new Test[] {
@ -76,12 +80,16 @@ public class OptionsTest {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_REUSEPORT, 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[] { static Test[] mcSocketTests = new Test[] {
Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()), 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(10)),
Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound
Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE) Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE)
}; };

View file

@ -64,6 +64,13 @@ public class UnsupportedOptionsTest {
socketOptions.add((SocketOption<?>)field.get(null)); socketOptions.add((SocketOption<?>)field.get(null));
field = c.getField("TCP_QUICKACK"); field = c.getField("TCP_QUICKACK");
socketOptions.add((SocketOption<?>)field.get(null)); 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) { } catch (ClassNotFoundException e) {
// ignore, jdk.net module not present // ignore, jdk.net module not present
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {