Source:
public void asyncSettingsAck() throws InterruptedException { final CountDownLatch serverAckLatch = new CountDownLatch(2); final CountDownLatch clientSettingsLatch = new CountDownLatch(2); final CountDownLatch serverConnectedChannelLatch = new CountDownLatch(1); final AtomicReference<Channel> serverConnectedChannelRef = new AtomicReference<Channel>(); ServerBootstrap sb = new ServerBootstrap(); sb.group(eventLoopGroup); sb.channel(NioServerSocketChannel.class); sb.childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) { ch.pipeline().addLast(Http2MultiplexCodecBuilder.forServer(new HttpInboundHandler()).build()); ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) { serverConnectedChannelRef.set(ctx.channel()); serverConnectedChannelLatch.countDown(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof Http2SettingsAckFrame) { serverAckLatch.countDown(); } } }); } }); serverChannel = sb.bind(new InetSocketAddress(NetUtil.LOCALHOST, 0)).awaitUninterruptibly().channel(); Bootstrap bs = new Bootstrap(); bs.group(eventLoopGroup); bs.channel(NioSocketChannel.class); bs.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) { ch.pipeline().addLast(Http2MultiplexCodecBuilder .forClient(new HttpInboundHandler()).autoAckSettings(false).build()); ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof Http2SettingsFrame) { clientSettingsLatch.countDown(); } } }); } }); clientChannel = bs.connect(serverChannel.localAddress()).awaitUninterruptibly().channel(); serverConnectedChannelLatch.await(); serverConnectedChannel = serverConnectedChannelRef.get(); serverConnectedChannel.writeAndFlush(new DefaultHttp2SettingsFrame(new Http2Settings() .maxConcurrentStreams(10))); clientSettingsLatch.await(); assertFalse(serverAckLatch.await(300, MILLISECONDS)); clientChannel.write(DefaultHttp2SettingsAckFrame.INSTANCE); clientChannel.writeAndFlush(DefaultHttp2SettingsAckFrame.INSTANCE); serverAckLatch.await(); }

Target:
public void asyncSettingsAck() throws InterruptedException { final CountDownLatch serverAckOneLatch = new CountDownLatch(1); final CountDownLatch serverAckAllLatch = new CountDownLatch(2); final CountDownLatch clientSettingsLatch = new CountDownLatch(2); final CountDownLatch serverConnectedChannelLatch = new CountDownLatch(1); final AtomicReference<Channel> serverConnectedChannelRef = new AtomicReference<Channel>(); ServerBootstrap sb = new ServerBootstrap(); sb.group(eventLoopGroup); sb.channel(NioServerSocketChannel.class); sb.childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) { ch.pipeline().addLast(Http2MultiplexCodecBuilder.forServer(new HttpInboundHandler()).build()); ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) { serverConnectedChannelRef.set(ctx.channel()); serverConnectedChannelLatch.countDown(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof Http2SettingsAckFrame) { serverAckOneLatch.countDown(); serverAckAllLatch.countDown(); } ReferenceCountUtil.release(msg); } }); } }); serverChannel = sb.bind(new InetSocketAddress(NetUtil.LOCALHOST, 0)).awaitUninterruptibly().channel(); Bootstrap bs = new Bootstrap(); bs.group(eventLoopGroup); bs.channel(NioSocketChannel.class); bs.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) { ch.pipeline().addLast(Http2MultiplexCodecBuilder .forClient(new HttpInboundHandler()).autoAckSettingsFrame(false).build()); ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof Http2SettingsFrame) { clientSettingsLatch.countDown(); } ReferenceCountUtil.release(msg); } }); } }); clientChannel = bs.connect(serverChannel.localAddress()).awaitUninterruptibly().channel(); serverConnectedChannelLatch.await(); serverConnectedChannel = serverConnectedChannelRef.get(); serverConnectedChannel.writeAndFlush(new DefaultHttp2SettingsFrame(new Http2Settings() .maxConcurrentStreams(10))).sync(); clientSettingsLatch.await(); assertFalse(serverAckOneLatch.await(300, MILLISECONDS)); clientChannel.writeAndFlush(Http2SettingsAckFrame.INSTANCE).sync(); clientChannel.writeAndFlush(Http2SettingsAckFrame.INSTANCE).sync(); serverAckAllLatch.await(); }