@Override
    public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception {
        if (closeStatus == null || !ctx.channel().isActive()) {
            ctx.close(promise);
        } else {
            if (closeSent == null) {
                write(ctx, new CloseWebSocketFrame(closeStatus), ctx.newPromise());
            }
            flush(ctx);
            applyCloseSentTimeout(ctx);
            closeSent.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    ctx.close(promise);
                }
            });
        }
    }
@Test
    public void testTimeout() throws Exception {
        final AtomicReference<ChannelPromise> ref = new AtomicReference<ChannelPromise>();
        WebSocketProtocolHandler handler = new WebSocketProtocolHandler(
                false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { };
        EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() {
            @Override
            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
                ref.set(promise);
                ReferenceCountUtil.release(msg);
            }
        }, handler);

        ChannelFuture future = channel.writeAndFlush(new CloseWebSocketFrame());
        ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class);
        handler.close(ctx, ctx.newPromise());

        do {
            Thread.sleep(10);
            channel.runPendingTasks();
        } while (!future.isDone());

        assertThat(future.cause(), Matchers.instanceOf(WebSocketHandshakeException.class));
        assertFalse(ref.get().isDone());
        assertFalse(channel.finish());
    }