diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java index 65f369f996..150ba0f09c 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java @@ -25,6 +25,8 @@ import org.junit.Test; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketException; +import java.nio.channels.ClosedChannelException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -76,6 +78,48 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest { } } + @Test(timeout = 30000) + public void testShutdownOutputAfterClosed() throws Throwable { + run(); + } + + public void testShutdownOutputAfterClosed(Bootstrap cb) throws Throwable { + TestHandler h = new TestHandler(); + ServerSocket ss = new ServerSocket(); + Socket s = null; + try { + ss.bind(addr); + SocketChannel ch = (SocketChannel) cb.handler(h).connect().sync().channel(); + assertTrue(ch.isActive()); + s = ss.accept(); + + ch.close().syncUninterruptibly(); + try { + ch.shutdownInput().syncUninterruptibly(); + fail(); + } catch (Throwable cause) { + checkThrowable(cause); + } + try { + ch.shutdownOutput().syncUninterruptibly(); + fail(); + } catch (Throwable cause) { + checkThrowable(cause); + } + } finally { + if (s != null) { + s.close(); + } + ss.close(); + } + } + + private static void checkThrowable(Throwable cause) throws Throwable { + // Depending on OIO / NIO both are ok + if (!(cause instanceof ClosedChannelException) && !(cause instanceof SocketException)) { + throw cause; + } + } private static class TestHandler extends SimpleChannelInboundHandler { volatile SocketChannel ch; final BlockingQueue queue = new LinkedBlockingQueue(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java index fe52d79eea..0da6a2de00 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java @@ -82,6 +82,9 @@ public final class Socket extends FileDescriptor { // represents the previous incarnation of the FD we need to be sure we don't inadvertently shutdown the // "new" FD without explicitly having a change. final int oldState = this.state; + if (isClosed(oldState)) { + throw new ClosedChannelException(); + } int newState = oldState; if (read && !isInputShutdown(newState)) { newState = inputShutdown(newState);