Improve client SNI in testSniWithApnHandler

Motivations
The test SniHandlerTest#testSniWithApnHandler() does not actually
involve SNI: given the client setup, the ClientHello in the form of hex
strings is not actually written to the wire, so the server never receives that.
We may need to write in somewhere else (e.g., channelActive()) instead of in
initChannel() in order for the hex strings to reach the server. So here
what's actually going on is an ordinary TLS C/S communication without SNI.

Modifications
The client part is modified to enable SNI by using an SslHandler with an
SSLEngine created by io.netty.handler.ssl.SslContext#newEngine(), where
the server hostname is specified. Also, more clauses are added to verify that
the SNI is indeed successful.

Results
Now the test verifies that both SNI and APN actually happen and succeed.
This commit is contained in:
Renjie Sun 2016-04-26 10:22:44 +08:00 committed by Norman Maurer
parent 467e5a1019
commit 6b427aaee5

View File

@ -18,11 +18,11 @@ package io.netty.handler.ssl;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
@ -41,7 +41,6 @@ import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.xml.bind.DatatypeConverter;
import static org.hamcrest.CoreMatchers.is;
@ -164,25 +163,15 @@ public class SniHandlerTest {
@Test
public void testSniWithApnHandler() throws Exception {
SslContext nettyContext = makeSslContext();
SslContext leanContext = makeSslContext();
SslContext sniContext = makeSslContext();
final SslContext clientContext = makeSslClientContext();
SslContext wrappedLeanContext = null;
final CountDownLatch serverApnDoneLatch = new CountDownLatch(1);
final CountDownLatch clientApnDoneLatch = new CountDownLatch(1);
final DomainNameMapping<SslContext> mapping = new DomainMappingBuilder(nettyContext)
.add("*.netty.io", nettyContext)
.add("*.LEANCLOUD.CN", leanContext).build();
// hex dump of a client hello packet, which contains hostname "CHAT4。LEANCLOUD。CN"
final String tlsHandshakeMessageHex1 = "16030100";
// part 2
final String tlsHandshakeMessageHex = "bd010000b90303a74225676d1814ba57faff3b366" +
"3656ed05ee9dbb2a4dbb1bb1c32d2ea5fc39e0000000100008c0000001700150000164348" +
"415434E380824C45414E434C4F5544E38082434E000b000403000102000a00340032000e0" +
"00d0019000b000c00180009000a0016001700080006000700140015000400050012001300" +
"0100020003000f0010001100230000000d0020001e0601060206030501050205030401040" +
"20403030103020303020102020203000f00010133740000";
.add("sni.fake.site", sniContext).build();
final SniHandler handler = new SniHandler(mapping);
EventLoopGroup group = new NioEventLoopGroup(2);
Channel serverChannel = null;
Channel clientChannel = null;
@ -195,7 +184,7 @@ public class SniHandlerTest {
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
// Server side SNI.
p.addLast(new SniHandler(mapping));
p.addLast(handler);
// Catch the notification event that APN has completed successfully.
p.addLast(new ApplicationProtocolNegotiationHandler("foo") {
@Override
@ -212,11 +201,8 @@ public class SniHandlerTest {
cb.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
// Simulate client side SNI (which is currently not supported).
ch.write(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex1)));
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(new SslHandler(clientContext.newEngine(
ch.alloc(), "sni.fake.site", -1)));
// Catch the notification event that APN has completed successfully.
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("foo") {
@Override
@ -235,6 +221,8 @@ public class SniHandlerTest {
assertTrue(serverApnDoneLatch.await(5, TimeUnit.SECONDS));
assertTrue(clientApnDoneLatch.await(5, TimeUnit.SECONDS));
assertThat(handler.hostname(), is("sni.fake.site"));
assertThat(handler.sslContext(), is(sniContext));
} finally {
if (serverChannel != null) {
serverChannel.close().sync();