diff --git a/src/java.base/share/classes/java/net/ServerSocket.java b/src/java.base/share/classes/java/net/ServerSocket.java index b3e570c858a..bb0fae2b88d 100644 --- a/src/java.base/share/classes/java/net/ServerSocket.java +++ b/src/java.base/share/classes/java/net/ServerSocket.java @@ -328,8 +328,8 @@ public class ServerSocket implements java.io.Closeable { * an ephemeral port and a valid local address to bind the socket. * * @param endpoint The IP address and port number to bind to. - * @throws IOException if the bind operation fails, or if the socket - * is already bound. + * @throws IOException if the bind operation fails, the socket + * is already bound or the socket is closed. * @throws SecurityException if a {@code SecurityManager} is present and * its {@code checkListen} method doesn't allow the operation. * @throws IllegalArgumentException if endpoint is a @@ -357,8 +357,8 @@ public class ServerSocket implements java.io.Closeable { * @param endpoint The IP address and port number to bind to. * @param backlog requested maximum length of the queue of * incoming connections. - * @throws IOException if the bind operation fails, or if the socket - * is already bound. + * @throws IOException if the bind operation fails, the socket + * is already bound or the socket is closed. * @throws SecurityException if a {@code SecurityManager} is present and * its {@code checkListen} method doesn't allow the operation. * @throws IllegalArgumentException if endpoint is a @@ -518,7 +518,7 @@ public class ServerSocket implements java.io.Closeable { * client socket implementation factory}, if one has been set. * * @throws IOException if an I/O error occurs when waiting for a - * connection. + * connection, the socket is not bound or the socket is closed. * @throws SecurityException if a security manager exists and its * {@code checkAccept} method doesn't allow the operation. * @throws SocketTimeoutException if a timeout was previously set with setSoTimeout and @@ -736,6 +736,9 @@ public class ServerSocket implements java.io.Closeable { *

If this socket has an associated channel then the channel is closed * as well. * + *

Once closed, several of the methods defined by this class will throw + * an exception if invoked on the closed socket. + * * @throws IOException if an I/O error occurs when closing the socket. */ public void close() throws IOException { @@ -806,8 +809,8 @@ public class ServerSocket implements java.io.Closeable { * operation to have effect. * * @param timeout the specified timeout, in milliseconds - * @throws SocketException if there is an error in the underlying protocol, - * such as a TCP error + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @throws IllegalArgumentException if {@code timeout} is negative * @since 1.1 * @see #getSoTimeout() @@ -824,7 +827,7 @@ public class ServerSocket implements java.io.Closeable { * Retrieve setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}. * 0 returns implies that the option is disabled (i.e., timeout of infinity). * @return the {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} value - * @throws IOException if an I/O error occurs + * @throws IOException if an I/O error occurs or the socket is closed. * @since 1.1 * @see #setSoTimeout(int) */ @@ -887,8 +890,8 @@ public class ServerSocket implements java.io.Closeable { * * @return a {@code boolean} indicating whether or not * {@code SO_REUSEADDR} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, such as a TCP error, + * or the socket is closed. * @since 1.4 * @see #setReuseAddress(boolean) */ @@ -1001,8 +1004,8 @@ public class ServerSocket implements java.io.Closeable { * requested value but the TCP receive window in sockets accepted from * this ServerSocket will be no larger than 64K bytes. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @param size the size to which to set the receive buffer * size. This value must be greater than 0. @@ -1029,8 +1032,8 @@ public class ServerSocket implements java.io.Closeable { *

Note, the value actually set in the accepted socket is determined by * calling {@link Socket#getReceiveBufferSize()}. * @return the value of the {@code SO_RCVBUF} option for this {@code Socket}. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @see #setReceiveBufferSize(int) * @since 1.4 */ diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 99a7bb6ca52..23c225fbb2d 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -683,7 +683,8 @@ public class Socket implements java.io.Closeable { * * * @param endpoint the {@code SocketAddress} - * @throws IOException if an error occurs during the connection + * @throws IOException if an error occurs during the connection, the socket + * is already connected or the socket is closed * @throws java.nio.channels.IllegalBlockingModeException * if this socket has an associated channel, * and the channel is in non-blocking mode @@ -717,7 +718,8 @@ public class Socket implements java.io.Closeable { * * @param endpoint the {@code SocketAddress} * @param timeout the timeout value to be used in milliseconds. - * @throws IOException if an error occurs during the connection + * @throws IOException if an error occurs during the connection, the socket + * is already connected or the socket is closed * @throws SocketTimeoutException if timeout expires before connecting * @throws java.nio.channels.IllegalBlockingModeException * if this socket has an associated channel, @@ -780,8 +782,8 @@ public class Socket implements java.io.Closeable { * an ephemeral port and a valid local address to bind the socket. * * @param bindpoint the {@code SocketAddress} to bind to - * @throws IOException if the bind operation fails, or if the socket - * is already bound. + * @throws IOException if the bind operation fails, the socket + * is already bound or the socket is closed. * @throws IllegalArgumentException if bindpoint is a * SocketAddress subclass not supported by this socket * @throws SecurityException if a security manager exists and its @@ -1174,8 +1176,8 @@ public class Socket implements java.io.Closeable { * will close the associated socket. * * @return an output stream for writing bytes to this socket. - * @throws IOException if an I/O error occurs when creating the - * output stream or if the socket is not connected. + * @throws IOException if an I/O error occurs when creating the + * output stream, the socket is not connected or the socket is closed. */ public OutputStream getOutputStream() throws IOException { int s = state; @@ -1251,8 +1253,8 @@ public class Socket implements java.io.Closeable { * @param on {@code true} to enable {@code TCP_NODELAY}, * {@code false} to disable. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @since 1.1 * @@ -1269,8 +1271,8 @@ public class Socket implements java.io.Closeable { * * @return a {@code boolean} indicating whether or not * {@code TCP_NODELAY} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.1 * @see #setTcpNoDelay(boolean) */ @@ -1289,9 +1291,9 @@ public class Socket implements java.io.Closeable { * * @param on whether or not to linger on. * @param linger how long to linger for, if on is true. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. - * @throws IllegalArgumentException if the linger value is negative. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. + * @throws IllegalArgumentException if the linger value is negative. * @since 1.1 * @see #getSoLinger() */ @@ -1318,8 +1320,8 @@ public class Socket implements java.io.Closeable { * The setting only affects socket close. * * @return the setting for {@code SO_LINGER}. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.1 * @see #setSoLinger(boolean, int) */ @@ -1368,8 +1370,8 @@ public class Socket implements java.io.Closeable { * @param on {@code true} to enable {@code SO_OOBINLINE}, * {@code false} to disable. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @since 1.4 * @@ -1387,8 +1389,8 @@ public class Socket implements java.io.Closeable { * @return a {@code boolean} indicating whether or not * {@code SO_OOBINLINE} is enabled. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.4 * @see #setOOBInline(boolean) */ @@ -1409,8 +1411,8 @@ public class Socket implements java.io.Closeable { * to have effect. * * @param timeout the specified timeout, in milliseconds. - * @throws SocketException if there is an error in the underlying protocol, - * such as a TCP error + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @throws IllegalArgumentException if {@code timeout} is negative * @since 1.1 * @see #getSoTimeout() @@ -1428,8 +1430,8 @@ public class Socket implements java.io.Closeable { * 0 returns implies that the option is disabled (i.e., timeout of infinity). * * @return the setting for {@code SO_TIMEOUT} - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @since 1.1 * @see #setSoTimeout(int) @@ -1455,14 +1457,12 @@ public class Socket implements java.io.Closeable { *

Because {@code SO_SNDBUF} is a hint, applications that want to verify * what size the buffers were set to should call {@link #getSendBufferSize()}. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. - * * @param size the size to which to set the send buffer * size. This value must be greater than 0. * - * @throws IllegalArgumentException if the - * value is 0 or is negative. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. + * @throws IllegalArgumentException if the value is 0 or is negative. * * @see #getSendBufferSize() * @since 1.2 @@ -1481,8 +1481,8 @@ public class Socket implements java.io.Closeable { * for output on this {@code Socket}. * @return the value of the {@code SO_SNDBUF} option for this {@code Socket}. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @see #setSendBufferSize(int) * @since 1.2 @@ -1529,8 +1529,8 @@ public class Socket implements java.io.Closeable { * @throws IllegalArgumentException if the value is 0 or is * negative. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @see #getReceiveBufferSize() * @see ServerSocket#setReceiveBufferSize(int) @@ -1550,8 +1550,8 @@ public class Socket implements java.io.Closeable { * for input on this {@code Socket}. * * @return the value of the {@code SO_RCVBUF} option for this {@code Socket}. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @see #setReceiveBufferSize(int) * @since 1.2 */ @@ -1570,8 +1570,8 @@ public class Socket implements java.io.Closeable { * Enable/disable {@link StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE}. * * @param on whether or not to have socket keep alive turned on. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.3 * @see #getKeepAlive() */ @@ -1586,8 +1586,8 @@ public class Socket implements java.io.Closeable { * * @return a {@code boolean} indicating whether or not * {@code SO_KEEPALIVE} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.3 * @see #setKeepAlive(boolean) */ @@ -1637,8 +1637,8 @@ public class Socket implements java.io.Closeable { * would be placed into the sin6_flowinfo field of the IP header. * * @param tc an {@code int} value for the bitset. - * @throws SocketException if there is an error setting the - * traffic class or type-of-service + * @throws SocketException if there is an error setting the traffic class or type-of-service, + * or the socket is closed. * @since 1.4 * @see #getTrafficClass * @see StandardSocketOptions#IP_TOS @@ -1661,8 +1661,8 @@ public class Socket implements java.io.Closeable { * set using the {@link #setTrafficClass(int)} method on this Socket. * * @return the traffic class or type-of-service already set - * @throws SocketException if there is an error obtaining the - * traffic class or type-of-service value. + * @throws SocketException if there is an error obtaining the traffic class + * or type-of-service value, or the socket is closed. * @since 1.4 * @see #setTrafficClass(int) * @see StandardSocketOptions#IP_TOS @@ -1715,8 +1715,8 @@ public class Socket implements java.io.Closeable { * * @return a {@code boolean} indicating whether or not * {@code SO_REUSEADDR} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.4 * @see #setReuseAddress(boolean) */ @@ -1733,8 +1733,9 @@ public class Socket implements java.io.Closeable { * will throw a {@link SocketException}. *

* Once a socket has been closed, it is not available for further networking - * use (i.e. can't be reconnected or rebound). A new socket needs to be - * created. + * use (i.e. can't be reconnected or rebound) and several of the methods defined + * by this class will throw an exception if invoked on the closed socket. A new + * socket needs to be created. * *

Closing this socket will also close the socket's * {@link java.io.InputStream InputStream} and @@ -1767,8 +1768,8 @@ public class Socket implements java.io.Closeable { * socket, the stream's {@code available} method will return 0, and its * {@code read} methods will return {@code -1} (end of stream). * - * @throws IOException if an I/O error occurs when shutting down this - * socket. + * @throws IOException if an I/O error occurs when shutting down this socket, the + * socket is not connected or the socket is closed. * * @since 1.3 * @see java.net.Socket#shutdownOutput() @@ -1797,8 +1798,8 @@ public class Socket implements java.io.Closeable { * shutdownOutput() on the socket, the stream will throw * an IOException. * - * @throws IOException if an I/O error occurs when shutting down this - * socket. + * @throws IOException if an I/O error occurs when shutting down this socket, the socket + * is not connected or the socket is closed. * * @since 1.3 * @see java.net.Socket#shutdownInput() diff --git a/test/jdk/java/net/ServerSocket/ClosedServerSocketTest.java b/test/jdk/java/net/ServerSocket/ClosedServerSocketTest.java new file mode 100644 index 00000000000..81d5cc9c0d0 --- /dev/null +++ b/test/jdk/java/net/ServerSocket/ClosedServerSocketTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024, 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. + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.SocketException; +import java.net.StandardSocketOptions; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary verifies that the APIs on java.net.ServerSocket throw expected exceptions + * when invoked on a closed ServerSocket + * @run junit ClosedServerSocketTest + */ +public class ClosedServerSocketTest { + + private static final InetAddress loopback = InetAddress.getLoopbackAddress(); + private static final InetSocketAddress loopbackEphemeral = new InetSocketAddress(loopback, 0); + + /** + * Verifies that various operations that specify to throw an IOException on a + * closed ServerSocket, do indeed throw it. + */ + @Test + public void testIOExceptionThrown() throws Exception { + try (final ServerSocket ss = new ServerSocket()) { + // close and then invoke the operations on the ServerSocket + ss.close(); + assertTrue(ss.isClosed(), "ServerSocket isn't closed"); + assertThrows(IOException.class, + ss::accept, + "accept() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.bind(loopbackEphemeral), + "bind() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.bind(loopbackEphemeral, 10), + "bind(SocketAddress, int) when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.getOption(StandardSocketOptions.SO_RCVBUF), + "getOption() when already closed didn't throw IOException"); + assertThrows(IOException.class, + ss::getSoTimeout, + "getSoTimeout() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.setOption(StandardSocketOptions.SO_RCVBUF, 1024), + "setOption() when already closed didn't throw IOException"); + } + } + + /** + * Verifies that various operations that specify to throw a SocketOperation on a + * closed ServerSocket, do indeed throw it. + */ + @Test + public void testSocketExceptionThrown() throws Exception { + try (final ServerSocket ss = new ServerSocket()) { + // close and then invoke the operations on the ServerSocket + ss.close(); + assertTrue(ss.isClosed(), "ServerSocket isn't closed"); + assertThrowsExactly(SocketException.class, + ss::getReceiveBufferSize, + "getReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + ss::getReuseAddress, + "getReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> ss.setReceiveBufferSize(1024), + "setReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> ss.setReuseAddress(false), + "setReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> ss.setSoTimeout(1000), + "setSoTimeout() when already closed didn't throw SocketException"); + } + } + + /** + * Verifies that various operations that aren't expected to throw an exception on a + * closed ServerSocket, complete normally. + */ + @Test + public void testNoExceptionThrown() throws Exception { + try (final ServerSocket ss = new ServerSocket()) { + // close and then invoke the operations on the ServerSocket + ss.close(); + assertTrue(ss.isClosed(), "ServerSocket isn't closed"); + ss.getInetAddress(); + ss.getLocalPort(); + ss.getLocalSocketAddress(); + ss.isBound(); + ss.supportedOptions(); + } + } +} diff --git a/test/jdk/java/net/Socket/ClosedSocketTest.java b/test/jdk/java/net/Socket/ClosedSocketTest.java new file mode 100644 index 00000000000..794f3b9cb18 --- /dev/null +++ b/test/jdk/java/net/Socket/ClosedSocketTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024, 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. + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.StandardSocketOptions; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary verifies that the APIs on java.net.Socket throw expected exceptions + * when invoked on a closed socket + * @run junit ClosedSocketTest + */ +public class ClosedSocketTest { + + private static final InetAddress loopback = InetAddress.getLoopbackAddress(); + private static final InetSocketAddress loopbackEphemeral = new InetSocketAddress(loopback, 0); + + /** + * Verifies that various operations that specify to throw an IOException on a closed socket, + * do indeed throw it. + */ + @Test + public void testIOExceptionThrown() throws Exception { + try (final Socket s = new Socket()) { + // close and then invoke the operation on the socket + s.close(); + assertTrue(s.isClosed(), "socket isn't closed"); + assertThrows(IOException.class, () -> s.bind(loopbackEphemeral), + "bind() when already closed didn't throw IOException"); + // connect() will never get to the stage of attempting + // a connection against this port + final int dummyPort = 12345; + assertThrows(IOException.class, + () -> s.connect(new InetSocketAddress(loopback, dummyPort)), + "connect() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> s.connect(new InetSocketAddress(loopback, dummyPort), 10), + "connect(SocketAddress, int) when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> s.getOption(StandardSocketOptions.SO_RCVBUF), + "getOption() when already closed didn't throw IOException"); + assertThrows(IOException.class, + s::getOutputStream, + "getOutputStream() when already closed didn't throw IOException"); + assertThrows(IOException.class, + s::shutdownInput, + "shutdownInput() when already closed didn't throw IOException"); + assertThrows(IOException.class, + s::shutdownOutput, + "shutdownOutput() when already closed didn't throw IOException"); + } + } + + /** + * Verifies that various operations that specify to throw a SocketOperation on a closed socket, + * do indeed throw it. + */ + @Test + public void testSocketExceptionThrown() throws Exception { + try (final Socket s = new Socket()) { + // close and then invoke the operations on the socket + s.close(); + assertTrue(s.isClosed(), "socket isn't closed"); + assertThrowsExactly(SocketException.class, + s::getKeepAlive, + "getKeepAlive() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getOOBInline, + "getOOBInline() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getReceiveBufferSize, + "getReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getReuseAddress, + "getReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getSendBufferSize, + "getSendBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getSoLinger, + "getSoLinger() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getSoTimeout, + "getSoTimeout() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getTcpNoDelay, + "getTcpNoDelay() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getTrafficClass, + "getTrafficClass() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setKeepAlive(false), + "setKeepAlive() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setOOBInline(false), + "setOOBInline() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setOption(StandardSocketOptions.SO_RCVBUF, 1024), + "setOption() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setReceiveBufferSize(1024), + "setReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setReuseAddress(false), + "setReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setSendBufferSize(1024), + "setSendBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setSoLinger(false, 0), + "setSoLinger() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setSoTimeout(1000), + "setSoTimeout() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setTcpNoDelay(false), + "setTcpNoDelay() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setTrafficClass(123), + "setTrafficClass() when already closed didn't throw SocketException"); + } + } + + /** + * Verifies that various operations that aren't expected to throw an exception on a + * closed socket, complete normally. + */ + @Test + public void testNoExceptionThrown() throws Exception { + try (final Socket s = new Socket()) { + // close and then invoke various operation on the socket and don't expect an exception + s.close(); + assertTrue(s.isClosed(), "socket isn't closed"); + s.getInetAddress(); + s.getLocalAddress(); + s.getLocalPort(); + s.getLocalSocketAddress(); + s.getPort(); + s.getRemoteSocketAddress(); + s.isBound(); + s.isConnected(); + s.isInputShutdown(); + s.isOutputShutdown(); + s.supportedOptions(); + } + } +}