8224477: java.net socket types new-style socket option methods - spec and impl mismatch
Reviewed-by: alanb
This commit is contained in:
parent
cf48689855
commit
bc24d17e80
@ -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.FileDescriptor;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -87,26 +89,6 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
||||
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
|
||||
*/
|
||||
@ -400,6 +382,125 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
||||
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)
|
||||
|
@ -35,12 +35,14 @@ import java.security.PrivilegedActionException;
|
||||
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 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
||||
*/
|
||||
protected boolean stream;
|
||||
|
||||
/* whether this is a server or not */
|
||||
final boolean isServer;
|
||||
|
||||
/**
|
||||
* Load net library into runtime.
|
||||
*/
|
||||
@ -112,27 +117,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
||||
}
|
||||
|
||||
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 @@ 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
|
||||
* establish a connection to the given <host, port>. If unsuccessful,
|
||||
|
@ -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.io.IOException;
|
||||
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 @@ class DatagramSocket implements java.io.Closeable {
|
||||
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 @@ class DatagramSocket implements java.io.Closeable {
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -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 @@ package java.net;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -265,123 +266,69 @@ public abstract class DatagramSocketImpl implements SocketOptions {
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
@ -1025,6 +1025,9 @@ class ServerSocket implements java.io.Closeable {
|
||||
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 @@ class ServerSocket implements java.io.Closeable {
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ import java.lang.invoke.VarHandle;
|
||||
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 @@ class Socket implements java.io.Closeable {
|
||||
* @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 @@ class Socket implements java.io.Closeable {
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.net.PlatformSocketImpl;
|
||||
|
||||
/**
|
||||
@ -74,22 +74,10 @@ public abstract class SocketImpl implements SocketOptions {
|
||||
*/
|
||||
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 @@ public abstract class SocketImpl implements SocketOptions {
|
||||
/**
|
||||
* 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 @@ public abstract class SocketImpl implements SocketOptions {
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -222,6 +222,8 @@ class DatagramChannelImpl
|
||||
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 @@ class DatagramChannelImpl
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -147,6 +147,9 @@ class ServerSocketChannelImpl
|
||||
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();
|
||||
|
||||
|
@ -218,6 +218,8 @@ class SocketChannelImpl
|
||||
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();
|
||||
|
@ -41,46 +41,6 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
|
||||
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)) {
|
||||
|
@ -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 @@ class PlainSocketImpl extends AbstractPlainSocketImpl
|
||||
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)) {
|
||||
|
114
test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java
Normal file
114
test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java
Normal 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; }
|
||||
}
|
||||
}
|
112
test/jdk/java/net/SocketImpl/TestDefaultBehavior.java
Normal file
112
test/jdk/java/net/SocketImpl/TestDefaultBehavior.java
Normal 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; }
|
||||
}
|
||||
}
|
384
test/jdk/java/net/SocketOption/AfterClose.java
Normal file
384
test/jdk/java/net/SocketOption/AfterClose.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
331
test/jdk/java/net/SocketOption/NullsAndBadValues.java
Normal file
331
test/jdk/java/net/SocketOption/NullsAndBadValues.java
Normal 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 } }) );
|
||||
}
|
||||
}
|
@ -61,14 +61,18 @@ public class OptionsTest {
|
||||
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 @@ public class OptionsTest {
|
||||
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)
|
||||
};
|
||||
|
||||
|
@ -64,6 +64,13 @@ public class UnsupportedOptionsTest {
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user