mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
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
18 changed files with 1289 additions and 300 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue