# HG changeset patch # User chegar # Date 1559134685 -3600 # Node ID dd321e3596c0d7a6cb02bf473086436e4d6e36f7 # Parent ef713640430e1334f759f982e5093212ddbd5473 8224477: java.net socket types new-style socket option methods - spec and impl mismatch Reviewed-by: alanb diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java --- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Wed May 29 13:58:05 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,9 +28,11 @@ import java.io.IOException; import java.util.Collections; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import sun.net.ResourceManager; +import sun.net.ext.ExtendedSocketOptions; import sun.security.action.GetPropertyAction; /** @@ -88,26 +90,6 @@ } /** - * Returns a set of SocketOptions supported by this impl and by this impl's - * socket (Socket or ServerSocket) - * - * @return a Set of SocketOptions - */ - @Override - protected Set> supportedOptions() { - Set> options; - if (isReusePortAvailable()) { - options = new HashSet<>(); - options.addAll(super.supportedOptions()); - options.add(StandardSocketOptions.SO_REUSEPORT); - options = Collections.unmodifiableSet(options); - } else { - options = super.supportedOptions(); - } - return options; - } - - /** * Creates a datagram socket */ protected synchronized void create() throws SocketException { @@ -400,6 +382,125 @@ return result; } + static final ExtendedSocketOptions extendedOptions = + ExtendedSocketOptions.getInstance(); + + private static final Set> datagramSocketOptions = datagramSocketOptions(); + private static final Set> multicastSocketOptions = multicastSocketOptions(); + + private static Set> datagramSocketOptions() { + HashSet> 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> multicastSocketOptions() { + HashSet> 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> supportedOptions() { + if (getDatagramSocket() instanceof MulticastSocket) + return multicastSocketOptions; + else + return datagramSocketOptions; + } + + @Override + protected void setOption(SocketOption 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 getOption(SocketOption 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) diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java --- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Wed May 29 13:58:05 2019 +0100 @@ -35,12 +35,14 @@ import java.security.PrivilegedExceptionAction; import java.util.Collections; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import sun.net.ConnectionResetException; import sun.net.NetHooks; import sun.net.PlatformSocketImpl; import sun.net.ResourceManager; +import sun.net.ext.ExtendedSocketOptions; import sun.net.util.SocketExceptions; /** @@ -84,6 +86,9 @@ */ protected boolean stream; + /* whether this is a server or not */ + final boolean isServer; + /** * Load net library into runtime. */ @@ -112,27 +117,7 @@ } AbstractPlainSocketImpl(boolean isServer) { - super(isServer); - } - - /** - * Returns a set of SocketOptions supported by this impl and by this impl's - * socket (Socket or ServerSocket) - * - * @return a Set of SocketOptions - */ - @Override - protected Set> supportedOptions() { - Set> options; - if (isReusePortAvailable()) { - options = new HashSet<>(); - options.addAll(super.supportedOptions()); - options.add(StandardSocketOptions.SO_REUSEPORT); - options = Collections.unmodifiableSet(options); - } else { - options = super.supportedOptions(); - } - return options; + this.isServer = isServer; } /** @@ -394,6 +379,121 @@ } } + static final ExtendedSocketOptions extendedOptions = + ExtendedSocketOptions.getInstance(); + + private static final Set> clientSocketOptions = clientSocketOptions(); + private static final Set> serverSocketOptions = serverSocketOptions(); + + private static Set> clientSocketOptions() { + HashSet> 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> serverSocketOptions() { + HashSet> 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> supportedOptions() { + if (isServer) + return serverSocketOptions; + else + return clientSocketOptions; + } + + @Override + protected void setOption(SocketOption 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 getOption(SocketOption 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 . If unsuccessful, diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/DatagramSocket.java --- a/src/java.base/share/classes/java/net/DatagramSocket.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/DatagramSocket.java Wed May 29 13:58:05 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.nio.channels.DatagramChannel; import java.security.AccessController; import java.security.PrivilegedExceptionAction; +import java.util.Objects; import java.util.Set; import java.util.Collections; @@ -1343,6 +1344,9 @@ public DatagramSocket setOption(SocketOption name, T value) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) + throw new SocketException("Socket is closed"); getImpl().setOption(name, value); return this; } @@ -1371,6 +1375,9 @@ * @since 9 */ public T getOption(SocketOption name) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) + throw new SocketException("Socket is closed"); return getImpl().getOption(name); } diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/DatagramSocketImpl.java --- a/src/java.base/share/classes/java/net/DatagramSocketImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/DatagramSocketImpl.java Wed May 29 13:58:05 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.io.FileDescriptor; import java.io.IOException; +import java.util.Objects; import java.util.Set; /** @@ -265,123 +266,69 @@ /** * Called to set a socket option. * + * @implSpec + * The default implementation of this method first checks that the given + * socket option {code name} is not null, then throws {@code + * UnsupportedOperationException}. Subclasses should override this method + * with an appropriate implementation. + * * @param 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 void setOption(SocketOption 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 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 getOption(SocketOption 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> dgSocketOptions; - - private static final Set> 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> supportedOptions() { - if (getDatagramSocket() instanceof MulticastSocket) { - return mcSocketOptions; - } else { - return dgSocketOptions; - } + return Set.of(); } } diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/ServerSocket.java --- a/src/java.base/share/classes/java/net/ServerSocket.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/ServerSocket.java Wed May 29 13:58:05 2019 +0100 @@ -1025,6 +1025,9 @@ public ServerSocket setOption(SocketOption name, T value) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) + throw new SocketException("Socket is closed"); getImpl().setOption(name, value); return this; } @@ -1053,6 +1056,9 @@ * @since 9 */ public T getOption(SocketOption name) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) + throw new SocketException("Socket is closed"); return getImpl().getOption(name); } diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/Socket.java --- a/src/java.base/share/classes/java/net/Socket.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/Socket.java Wed May 29 13:58:05 2019 +0100 @@ -33,6 +33,7 @@ import java.nio.channels.SocketChannel; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Objects; import java.util.Set; import java.util.Collections; @@ -1786,6 +1787,9 @@ * @since 9 */ public Socket setOption(SocketOption name, T value) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) + throw new SocketException("Socket is closed"); getImpl().setOption(name, value); return this; } @@ -1815,6 +1819,9 @@ */ @SuppressWarnings("unchecked") public T getOption(SocketOption name) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) + throw new SocketException("Socket is closed"); return getImpl().getOption(name); } diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/java/net/SocketImpl.java --- a/src/java.base/share/classes/java/net/SocketImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/java/net/SocketImpl.java Wed May 29 13:58:05 2019 +0100 @@ -29,8 +29,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.FileDescriptor; +import java.util.Objects; import java.util.Set; - import sun.net.PlatformSocketImpl; /** @@ -75,21 +75,9 @@ protected int localport; /** - * Whether this is a server or not. - */ - final boolean isServer; - - - SocketImpl(boolean isServer) { - this.isServer = isServer; - } - - /** * Initialize a new instance of this class */ - public SocketImpl() { - this.isServer = false; - } + public SocketImpl() { } /** * Creates either a stream or a datagram socket. @@ -376,79 +364,54 @@ /** * Called to set a socket option. * + * @implSpec + * The default implementation of this method first checks that the given + * socket option {code name} is not null, then throws {@code + * UnsupportedOperationException}. Subclasses should override this method + * with an appropriate implementation. + * * @param 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 void setOption(SocketOption 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 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 getOption(SocketOption name) throws IOException { - if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) { - return (T)getOption(SocketOptions.SO_KEEPALIVE); - } else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) { - return (T)getOption(SocketOptions.SO_SNDBUF); - } else if (name == StandardSocketOptions.SO_RCVBUF) { - return (T)getOption(SocketOptions.SO_RCVBUF); - } else if (name == StandardSocketOptions.SO_REUSEADDR) { - return (T)getOption(SocketOptions.SO_REUSEADDR); - } else if (name == StandardSocketOptions.SO_REUSEPORT && - supportedOptions().contains(name)) { - return (T)getOption(SocketOptions.SO_REUSEPORT); - } else if (name == StandardSocketOptions.SO_LINGER && !isServer) { - return (T)getOption(SocketOptions.SO_LINGER); - } else if (name == StandardSocketOptions.IP_TOS) { - return (T)getOption(SocketOptions.IP_TOS); - } else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) { - return (T)getOption(SocketOptions.TCP_NODELAY); - } else { - throw new UnsupportedOperationException("unsupported option"); - } + Objects.requireNonNull(name); + throw new UnsupportedOperationException("'" + name + "' not supported"); } /** @@ -464,37 +427,19 @@ } catch (IOException ignore) { } } - private static final Set> socketOptions; - - private static final Set> 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> supportedOptions() { - if (!isServer) { - return socketOptions; - } else { - return serverSocketOptions; - } + return Set.of(); } } diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed May 29 13:58:05 2019 +0100 @@ -222,6 +222,8 @@ Objects.requireNonNull(name); if (!supportedOptions().contains(name)) throw new UnsupportedOperationException("'" + name + "' not supported"); + if (!name.type().isInstance(value)) + throw new IllegalArgumentException("Invalid value '" + value + "'"); synchronized (stateLock) { ensureOpen(); @@ -236,8 +238,6 @@ } if (name == StandardSocketOptions.IP_MULTICAST_IF) { - if (value == null) - throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); NetworkInterface interf = (NetworkInterface)value; if (family == StandardProtocolFamily.INET6) { int index = interf.getIndex(); diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Wed May 29 13:58:05 2019 +0100 @@ -147,6 +147,9 @@ Objects.requireNonNull(name); if (!supportedOptions().contains(name)) throw new UnsupportedOperationException("'" + name + "' not supported"); + if (!name.type().isInstance(value)) + throw new IllegalArgumentException("Invalid value '" + value + "'"); + synchronized (stateLock) { ensureOpen(); diff -r ef713640430e -r dd321e3596c0 src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Wed May 29 13:58:05 2019 +0100 @@ -218,6 +218,8 @@ Objects.requireNonNull(name); if (!supportedOptions().contains(name)) throw new UnsupportedOperationException("'" + name + "' not supported"); + if (!name.type().isInstance(value)) + throw new IllegalArgumentException("Invalid value '" + value + "'"); synchronized (stateLock) { ensureOpen(); diff -r ef713640430e -r dd321e3596c0 src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java --- a/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Wed May 29 13:58:05 2019 +0100 @@ -41,46 +41,6 @@ init(); } - static final ExtendedSocketOptions extendedOptions = - ExtendedSocketOptions.getInstance(); - - protected void setOption(SocketOption 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 getOption(SocketOption 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> supportedOptions() { - HashSet> 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)) { diff -r ef713640430e -r dd321e3596c0 src/java.base/unix/classes/java/net/PlainSocketImpl.java --- a/src/java.base/unix/classes/java/net/PlainSocketImpl.java Wed May 29 08:21:33 2019 -0400 +++ b/src/java.base/unix/classes/java/net/PlainSocketImpl.java Wed May 29 13:58:05 2019 +0100 @@ -25,7 +25,6 @@ package java.net; import java.io.IOException; -import java.io.FileDescriptor; import java.util.Set; import java.util.HashSet; import sun.net.ext.ExtendedSocketOptions; @@ -49,50 +48,6 @@ super(isServer); } - static final ExtendedSocketOptions extendedOptions = - ExtendedSocketOptions.getInstance(); - - protected void setOption(SocketOption 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 getOption(SocketOption 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> supportedOptions() { - HashSet> 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)) { diff -r ef713640430e -r dd321e3596c0 test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java Wed May 29 13:58:05 2019 +0100 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8224477 + * @summary Basic test for java.net.DatagramSocketImpl default behavior + * @run testng TestDefaultBehavior + */ + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.util.Set; +import org.testng.annotations.Test; +import static java.lang.Boolean.*; +import static java.net.StandardSocketOptions.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.expectThrows; + +public class TestDefaultBehavior { + + static final Class NPE = NullPointerException.class; + static final Class 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 FAKE_SOCK_OPT = new SocketOption<>() { + @Override public String name() { return "FAKE_SOCK_OPT"; } + @Override public Class 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 void setOption(SocketOption name, T value) throws IOException { + super.setOption(name, value); + } + + @Override + public Set> supportedOptions() { + return super.supportedOptions(); + } + + @Override + public T getOption(SocketOption 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; } + } +} diff -r ef713640430e -r dd321e3596c0 test/jdk/java/net/SocketImpl/TestDefaultBehavior.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java Wed May 29 13:58:05 2019 +0100 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8224477 + * @summary Basic test for java.net.SocketImpl default behavior + * @run testng TestDefaultBehavior + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.util.Set; +import org.testng.annotations.Test; +import static java.lang.Boolean.*; +import static java.net.StandardSocketOptions.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.expectThrows; + +public class TestDefaultBehavior { + + static final Class NPE = NullPointerException.class; + static final Class 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 FAKE_SOCK_OPT = new SocketOption<>() { + @Override public String name() { return "FAKE_SOCK_OPT"; } + @Override public Class 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 void setOption(SocketOption name, T value) throws IOException { + super.setOption(name, value); + } + + @Override + public Set> supportedOptions() { + return super.supportedOptions(); + } + + @Override + public T getOption(SocketOption 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; } + } +} diff -r ef713640430e -r dd321e3596c0 test/jdk/java/net/SocketOption/AfterClose.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/SocketOption/AfterClose.java Wed May 29 13:58:05 2019 +0100 @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8224477 + * @summary Ensures that IOException is thrown after the socket is closed + * @run testng AfterClose + */ + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.DatagramSocket; +import java.net.MulticastSocket; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketOption; +import java.nio.channels.DatagramChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.Boolean.*; +import static java.net.StandardSocketOptions.*; +import static org.testng.Assert.expectThrows; + +public class AfterClose { + + static final Class IOE = IOException.class; + + static Map,List> OPTION_VALUES_MAP = optionValueMap(); + + static Map,List> optionValueMap() { + Map,List> 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 void closedSocketImplUncreated(SocketOption option, List 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 void closedSocketImplCreated(SocketOption option, List 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 void closedSocketAdapter(SocketOption option, List 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 void closedServerSocketImplUncreated(SocketOption option, List 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 void closedServerSocketImplCreated(SocketOption option, List 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 void closedServerSocketAdapter(SocketOption option, List 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 void closedUnboundDatagramSocket(SocketOption option, List 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 void closedBoundDatagramSocket(SocketOption option, List 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 void closedDatagramAdapter(SocketOption option, List 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 void closedUnboundMulticastSocket(SocketOption option, List 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 void closedBoundMulticastSocket(SocketOption option, List 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 listOf(Object... objs) { + List 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); + } + } +} diff -r ef713640430e -r dd321e3596c0 test/jdk/java/net/SocketOption/NullsAndBadValues.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/SocketOption/NullsAndBadValues.java Wed May 29 13:58:05 2019 +0100 @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8224477 + * @summary Basic test for NPE, UOE, and IAE for get/setOption + * @run testng NullsAndBadValues + * @run testng/othervm -Dsun.net.useExclusiveBind=false NullsAndBadValues + */ + +import java.net.DatagramSocket; +import java.net.MulticastSocket; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketOption; +import java.nio.channels.DatagramChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.Boolean.*; +import static java.net.StandardSocketOptions.*; +import static org.testng.Assert.expectThrows; + +public class NullsAndBadValues { + + static final Class NPE = NullPointerException.class; + static final Class IAE = IllegalArgumentException.class; + static final Class 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 FAKE_SOCK_OPT = new SocketOption<>() { + @Override public String name() { return "FAKE_SOCK_OPT"; } + @Override public Class 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,List> BAD_OPTION_VALUES = badOptionValues(); + + static Map,List> badOptionValues() { + Map,List> 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 void socket(SocketOption option, T value) + throws Exception + { + try (Socket s = new Socket()) { + expectThrows(IAE, () -> s.setOption(option, value)); + } + } + + @Test(dataProvider = "socketBadOptionValues") + public void socketAdapter(SocketOption 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 void serverSocket(SocketOption option, T value) + throws Exception + { + try (ServerSocket ss = new ServerSocket()) { + expectThrows(IAE, () -> ss.setOption(option, value)); + } + } + + @Test(dataProvider = "serverSocketBadOptionValues") + public void serverSocketAdapter(SocketOption 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 void datagramSocket(SocketOption option, T value) + throws Exception + { + try (DatagramSocket ds = new DatagramSocket()) { + expectThrows(IAE, () -> ds.setOption(option, value)); + } + } + + @Test(dataProvider = "datagramSocketBadOptionValues") + public void datagramSocketAdapter(SocketOption 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 void multicastSocket(SocketOption option, T value) + throws Exception + { + try (MulticastSocket ms = new MulticastSocket()) { + expectThrows(IAE, () -> ms.setOption(option, value)); + } + } + + // -- + + static List listOf(Object... objs) { + List l = new ArrayList<>(); + if (objs == null) + l.add(null); + else + Arrays.stream(objs).forEachOrdered(l::add); + return l; + } + + static Stream socketOptionToBadValues(SocketOption socketOption) { + List 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 } }) ); + } +} diff -r ef713640430e -r dd321e3596c0 test/jdk/java/net/SocketOption/OptionsTest.java --- a/test/jdk/java/net/SocketOption/OptionsTest.java Wed May 29 08:21:33 2019 -0400 +++ b/test/jdk/java/net/SocketOption/OptionsTest.java Wed May 29 13:58:05 2019 +0100 @@ -61,14 +61,18 @@ Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)), - Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)) + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound }; static Test[] serverSocketTests = new Test[] { Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), - Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)) + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound }; static Test[] dgSocketTests = new Test[] { @@ -76,12 +80,16 @@ Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), - Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)) + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), + Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound }; static Test[] mcSocketTests = new Test[] { Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()), + Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)), // lower-bound Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)), + Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE) }; diff -r ef713640430e -r dd321e3596c0 test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java --- a/test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java Wed May 29 08:21:33 2019 -0400 +++ b/test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java Wed May 29 13:58:05 2019 +0100 @@ -64,6 +64,13 @@ socketOptions.add((SocketOption)field.get(null)); field = c.getField("TCP_QUICKACK"); socketOptions.add((SocketOption)field.get(null)); + field = c.getField("TCP_KEEPIDLE"); + socketOptions.add((SocketOption)field.get(null)); + field = c.getField("TCP_KEEPINTERVAL"); + socketOptions.add((SocketOption)field.get(null)); + field = c.getField("TCP_KEEPCOUNT"); + socketOptions.add((SocketOption)field.get(null)); + } catch (ClassNotFoundException e) { // ignore, jdk.net module not present } catch (ReflectiveOperationException e) {