[#5874] [#5971] Ensure SniHandlerTest.testServerNameParsing not fails with SslProvider.JDK

Motivation:

The SniHandlerTest.testServerNameParsing did fail when SslProvider.JDK was used as it the JDK SSLEngineImpl does not send an alert.

Modifications:

Ensure tests pass with JDK and OPENSSL ssl implementations.

Result:

SniHandlerTest will run with all SslProvider and not fail when SslProvider.JDK is used.
This commit is contained in:
Norman Maurer 2016-11-16 07:59:21 +01:00
parent 2c78902ebc
commit 86bbf242b4

View File

@ -26,6 +26,8 @@ import static org.junit.Assume.assumeTrue;
import java.io.File; import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -62,7 +64,10 @@ import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.ObjectUtil;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class SniHandlerTest { public class SniHandlerTest {
private static ApplicationProtocolConfig newApnConfig() { private static ApplicationProtocolConfig newApnConfig() {
@ -75,37 +80,80 @@ public class SniHandlerTest {
"myprotocol"); "myprotocol");
} }
private static SslContext makeSslContext() throws Exception { private static void assumeApnSupported(SslProvider provider) {
return makeSslContext(null); switch (provider) {
case OPENSSL:
case OPENSSL_REFCNT:
assumeTrue(OpenSsl.isAlpnSupported());
break;
case JDK:
assumeTrue(JdkAlpnSslEngine.isAvailable());
break;
default:
throw new Error();
}
}
private static SslContext makeSslContext(SslProvider provider, boolean apn) throws Exception {
if (apn) {
assumeApnSupported(provider);
} }
private static SslContext makeSslContext(SslProvider provider) throws Exception {
File keyFile = new File(SniHandlerTest.class.getResource("test_encrypted.pem").getFile()); File keyFile = new File(SniHandlerTest.class.getResource("test_encrypted.pem").getFile());
File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile()); File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile());
return SslContextBuilder.forServer(crtFile, keyFile, "12345") SslContextBuilder sslCtxBuilder = SslContextBuilder.forServer(crtFile, keyFile, "12345")
.sslProvider(provider) .sslProvider(provider);
.applicationProtocolConfig(newApnConfig()).build(); if (apn) {
sslCtxBuilder.applicationProtocolConfig(newApnConfig());
}
return sslCtxBuilder.build();
}
private static SslContext makeSslClientContext(SslProvider provider, boolean apn) throws Exception {
if (apn) {
assumeApnSupported(provider);
} }
private static SslContext makeSslClientContext() throws Exception {
File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile()); File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile());
return SslContextBuilder.forClient().trustManager(crtFile).applicationProtocolConfig(newApnConfig()).build(); SslContextBuilder sslCtxBuilder = SslContextBuilder.forClient().trustManager(crtFile).sslProvider(provider);
if (apn) {
sslCtxBuilder.applicationProtocolConfig(newApnConfig());
}
return sslCtxBuilder.build();
}
@Parameterized.Parameters(name = "{index}: sslProvider={0}")
public static Iterable<? extends Object> data() {
List<SslProvider> params = new ArrayList<SslProvider>(3);
if (OpenSsl.isAvailable()) {
params.add(SslProvider.OPENSSL);
params.add(SslProvider.OPENSSL_REFCNT);
}
params.add(SslProvider.JDK);
return params;
}
private final SslProvider provider;
public SniHandlerTest(SslProvider provider) {
this.provider = provider;
} }
@Test @Test
public void testServerNameParsing() throws Exception { public void testServerNameParsing() throws Exception {
SslContext nettyContext = makeSslContext(); SslContext nettyContext = makeSslContext(provider, false);
SslContext leanContext = makeSslContext(); SslContext leanContext = makeSslContext(provider, false);
SslContext leanContext2 = makeSslContext(); SslContext leanContext2 = makeSslContext(provider, false);
try {
DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext) DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
.add("*.netty.io", nettyContext) .add("*.netty.io", nettyContext)
// input with custom cases // input with custom cases
.add("*.LEANCLOUD.CN", leanContext) .add("*.LEANCLOUD.CN", leanContext)
// a hostname conflict with previous one, since we are using order-sensitive config, the engine won't // a hostname conflict with previous one, since we are using order-sensitive config,
// be used with the handler. // the engine won't be used with the handler.
.add("chat4.leancloud.cn", leanContext2) .add("chat4.leancloud.cn", leanContext2)
.build(); .build();
@ -132,7 +180,11 @@ public class SniHandlerTest {
// expected // expected
} }
assertThat(ch.finish(), is(true)); // Just call finish and not assert the return value. This is because OpenSSL correct produces an alert
// while the JDK SSLEngineImpl does not atm.
// See https://github.com/netty/netty/issues/5874
ch.finish();
assertThat(handler.hostname(), is("chat4.leancloud.cn")); assertThat(handler.hostname(), is("chat4.leancloud.cn"));
assertThat(handler.sslContext(), is(leanContext)); assertThat(handler.sslContext(), is(leanContext));
@ -143,20 +195,24 @@ public class SniHandlerTest {
} }
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
} }
} finally {
releaseAll(leanContext, leanContext2, nettyContext);
}
} }
@Test @Test
public void testFallbackToDefaultContext() throws Exception { public void testFallbackToDefaultContext() throws Exception {
SslContext nettyContext = makeSslContext(); SslContext nettyContext = makeSslContext(provider, false);
SslContext leanContext = makeSslContext(); SslContext leanContext = makeSslContext(provider, false);
SslContext leanContext2 = makeSslContext(); SslContext leanContext2 = makeSslContext(provider, false);
try {
DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext) DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
.add("*.netty.io", nettyContext) .add("*.netty.io", nettyContext)
// input with custom cases // input with custom cases
.add("*.LEANCLOUD.CN", leanContext) .add("*.LEANCLOUD.CN", leanContext)
// a hostname conflict with previous one, since we are using order-sensitive config, the engine won't // a hostname conflict with previous one, since we are using order-sensitive config,
// be used with the handler. // the engine won't be used with the handler.
.add("chat4.leancloud.cn", leanContext2) .add("chat4.leancloud.cn", leanContext2)
.build(); .build();
@ -164,7 +220,7 @@ public class SniHandlerTest {
EmbeddedChannel ch = new EmbeddedChannel(handler); EmbeddedChannel ch = new EmbeddedChannel(handler);
// invalid // invalid
byte[] message = { 22, 3, 1, 0, 0 }; byte[] message = {22, 3, 1, 0, 0};
try { try {
// Push the handshake message. // Push the handshake message.
@ -176,13 +232,17 @@ public class SniHandlerTest {
assertThat(ch.finish(), is(false)); assertThat(ch.finish(), is(false));
assertThat(handler.hostname(), nullValue()); assertThat(handler.hostname(), nullValue());
assertThat(handler.sslContext(), is(nettyContext)); assertThat(handler.sslContext(), is(nettyContext));
} finally {
releaseAll(leanContext, leanContext2, nettyContext);
}
} }
@Test @Test
public void testSniWithApnHandler() throws Exception { public void testSniWithApnHandler() throws Exception {
SslContext nettyContext = makeSslContext(); SslContext nettyContext = makeSslContext(provider, true);
SslContext sniContext = makeSslContext(); SslContext sniContext = makeSslContext(provider, true);
final SslContext clientContext = makeSslClientContext(); final SslContext clientContext = makeSslClientContext(provider, true);
try {
final CountDownLatch serverApnDoneLatch = new CountDownLatch(1); final CountDownLatch serverApnDoneLatch = new CountDownLatch(1);
final CountDownLatch clientApnDoneLatch = new CountDownLatch(1); final CountDownLatch clientApnDoneLatch = new CountDownLatch(1);
@ -206,7 +266,7 @@ public class SniHandlerTest {
// Catch the notification event that APN has completed successfully. // 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) {
serverApnDoneLatch.countDown(); serverApnDoneLatch.countDown();
} }
}); });
@ -224,7 +284,7 @@ public class SniHandlerTest {
// Catch the notification event that APN has completed successfully. // Catch the notification event that APN has completed successfully.
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("foo") { ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("foo") {
@Override @Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
clientApnDoneLatch.countDown(); clientApnDoneLatch.countDown();
} }
}); });
@ -250,25 +310,29 @@ public class SniHandlerTest {
} }
group.shutdownGracefully(0, 0, TimeUnit.MICROSECONDS); group.shutdownGracefully(0, 0, TimeUnit.MICROSECONDS);
} }
} finally {
releaseAll(clientContext, nettyContext, sniContext);
}
} }
@Test(timeout = 10L * 1000L) @Test(timeout = 10L * 1000L)
public void testReplaceHandler() throws Exception { public void testReplaceHandler() throws Exception {
switch (provider) {
assumeTrue(OpenSsl.isAvailable()); case OPENSSL:
case OPENSSL_REFCNT:
final String sniHost = "sni.netty.io"; final String sniHost = "sni.netty.io";
LocalAddress address = new LocalAddress("testReplaceHandler-" + Math.random()); LocalAddress address = new LocalAddress("testReplaceHandler-" + Math.random());
EventLoopGroup group = new DefaultEventLoopGroup(1); EventLoopGroup group = new DefaultEventLoopGroup(1);
Channel sc = null; Channel sc = null;
Channel cc = null; Channel cc = null;
SslContext sslContext = null;
SelfSignedCertificate cert = new SelfSignedCertificate(); SelfSignedCertificate cert = new SelfSignedCertificate();
try { try {
final SslContext sslServerContext = SslContextBuilder final SslContext sslServerContext = SslContextBuilder
.forServer(cert.key(), cert.cert()) .forServer(cert.key(), cert.cert())
.sslProvider(SslProvider.OPENSSL) .sslProvider(provider)
.build(); .build();
final Mapping<String, SslContext> mapping = new Mapping<String, SslContext>() { final Mapping<String, SslContext> mapping = new Mapping<String, SslContext>() {
@ -322,15 +386,16 @@ public class SniHandlerTest {
}; };
ServerBootstrap sb = new ServerBootstrap(); ServerBootstrap sb = new ServerBootstrap();
sc = sb.group(group).channel(LocalServerChannel.class).childHandler(new ChannelInitializer<Channel>() { sc = sb.group(group).channel(LocalServerChannel.class)
.childHandler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addFirst(handler); ch.pipeline().addFirst(handler);
} }
}).bind(address).syncUninterruptibly().channel(); }).bind(address).syncUninterruptibly().channel();
SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) sslContext = SslContextBuilder.forClient().sslProvider(provider)
.build(); .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
Bootstrap cb = new Bootstrap(); Bootstrap cb = new Bootstrap();
cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler( cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler(
@ -358,10 +423,18 @@ public class SniHandlerTest {
if (sc != null) { if (sc != null) {
sc.close().syncUninterruptibly(); sc.close().syncUninterruptibly();
} }
if (sslContext != null) {
ReferenceCountUtil.release(sslContext);
}
group.shutdownGracefully(); group.shutdownGracefully();
cert.delete(); cert.delete();
} }
case JDK:
return;
default:
throw new Error();
}
} }
/** /**
@ -384,4 +457,10 @@ public class SniHandlerTest {
ReferenceCountUtil.release(sslContext); ReferenceCountUtil.release(sslContext);
} }
} }
private static void releaseAll(SslContext... contexts) {
for (SslContext ctx: contexts) {
ReferenceCountUtil.release(ctx);
}
}
} }