Motivation:
fcbeebf6df introduced a unit test to verify ApplicationProtocolNegotiationHandler is compatible with SniHandler. However only the server attempts ALPN and verifies that it completes and the client doesn't verify the handshake is completed. This can lead to the client side SSL engine to prematurely close and throw an exception.

Modifications:
- The client should wait for the SSL handshake and ALPN to complete before the test exits.

Result:
SniHandlerTest.testSniWithApnHandler is more reliable.
This commit is contained in:
Scott Mitchell 2016-04-05 10:47:57 -07:00
parent 61ae4a3a78
commit 4506bbd27b

View File

@ -22,6 +22,7 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
@ -40,6 +41,7 @@ import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.xml.bind.DatatypeConverter; import javax.xml.bind.DatatypeConverter;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
@ -165,7 +167,8 @@ public class SniHandlerTest {
SslContext leanContext = makeSslContext(); SslContext leanContext = makeSslContext();
final SslContext clientContext = makeSslClientContext(); final SslContext clientContext = makeSslClientContext();
SslContext wrappedLeanContext = null; SslContext wrappedLeanContext = null;
final CountDownLatch apnDoneLatch = new CountDownLatch(1); final CountDownLatch serverApnDoneLatch = new CountDownLatch(1);
final CountDownLatch clientApnDoneLatch = new CountDownLatch(1);
final DomainNameMapping<SslContext> mapping = new DomainMappingBuilder(nettyContext) final DomainNameMapping<SslContext> mapping = new DomainMappingBuilder(nettyContext)
.add("*.netty.io", nettyContext) .add("*.netty.io", nettyContext)
@ -191,11 +194,13 @@ public class SniHandlerTest {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
// Server side SNI.
p.addLast(new SniHandler(mapping)); p.addLast(new SniHandler(mapping));
// Catch the notification event that APN has completed successfully.
p.addLast(new ApplicationProtocolNegotiationHandler("foo") { p.addLast(new ApplicationProtocolNegotiationHandler("foo") {
@Override @Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
apnDoneLatch.countDown(); serverApnDoneLatch.countDown();
} }
}); });
} }
@ -207,10 +212,18 @@ public class SniHandlerTest {
cb.handler(new ChannelInitializer<Channel>() { cb.handler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
ChannelPipeline p = ch.pipeline(); // Simulate client side SNI (which is currently not supported).
ch.write(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex1))); ch.write(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex1)));
ch.writeAndFlush(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex))); ch.writeAndFlush(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex)));
// Add a SslHandler so we can do the client side of the handshake.
ch.pipeline().addLast(clientContext.newHandler(ch.alloc())); ch.pipeline().addLast(clientContext.newHandler(ch.alloc()));
// Catch the notification event that APN has completed successfully.
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("foo") {
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
clientApnDoneLatch.countDown();
}
});
} }
}); });
@ -220,7 +233,8 @@ public class SniHandlerTest {
assertTrue(ccf.awaitUninterruptibly().isSuccess()); assertTrue(ccf.awaitUninterruptibly().isSuccess());
clientChannel = ccf.channel(); clientChannel = ccf.channel();
assertTrue(apnDoneLatch.await(5, TimeUnit.SECONDS)); assertTrue(serverApnDoneLatch.await(5, TimeUnit.SECONDS));
assertTrue(clientApnDoneLatch.await(5, TimeUnit.SECONDS));
} finally { } finally {
if (serverChannel != null) { if (serverChannel != null) {
serverChannel.close().sync(); serverChannel.close().sync();