Fix HTTP/2 test cleanup with LocalChannel

Motivation:
When a LocalChannel is closed it is responsible to ensure all queued objects are released. When a LocalChannel is closed it will also close its peer channel. However in HTTP/2 unit tests we may not wait until all channels have completed the shutdown process before destroying the threads and exiting the test. This may mean buffers are GCed before they are released and be reported as a leak.

Modifications:
- In HTTP/2 tests when we use LocalChannel we should wait for all channels to close before exiting the test and cleaning up the associated EventLoopGroups.

Result:
More correct usage of LocalChannel in HTTP/2 tests.
This commit is contained in:
Scott Mitchell 2016-11-16 14:03:57 -08:00
parent a043cf4a98
commit 782b7bcf4a
4 changed files with 41 additions and 6 deletions

View File

@ -23,6 +23,7 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.DefaultEventLoop; import io.netty.channel.DefaultEventLoop;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
@ -34,6 +35,9 @@ import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -47,6 +51,7 @@ public class Http2CodecTest {
private static EventLoopGroup group; private static EventLoopGroup group;
private Channel serverChannel; private Channel serverChannel;
private volatile Channel serverConnectedChannel;
private Channel clientChannel; private Channel clientChannel;
private LastInboundHandler serverLastInboundHandler; private LastInboundHandler serverLastInboundHandler;
@ -57,12 +62,20 @@ public class Http2CodecTest {
@Before @Before
public void setUp() throws InterruptedException { public void setUp() throws InterruptedException {
final CountDownLatch serverChannelLatch = new CountDownLatch(1);
LocalAddress serverAddress = new LocalAddress(getClass().getName()); LocalAddress serverAddress = new LocalAddress(getClass().getName());
serverLastInboundHandler = new SharableLastInboundHandler(); serverLastInboundHandler = new SharableLastInboundHandler();
ServerBootstrap sb = new ServerBootstrap() ServerBootstrap sb = new ServerBootstrap()
.channel(LocalServerChannel.class) .channel(LocalServerChannel.class)
.group(group) .group(group)
.childHandler(new Http2Codec(true, serverLastInboundHandler)); .childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
serverConnectedChannel = ch;
ch.pipeline().addLast(new Http2Codec(true, serverLastInboundHandler));
serverChannelLatch.countDown();
}
});
serverChannel = sb.bind(serverAddress).sync().channel(); serverChannel = sb.bind(serverAddress).sync().channel();
Bootstrap cb = new Bootstrap() Bootstrap cb = new Bootstrap()
@ -70,6 +83,7 @@ public class Http2CodecTest {
.group(group) .group(group)
.handler(new Http2Codec(false, new TestChannelInitializer())); .handler(new Http2Codec(false, new TestChannelInitializer()));
clientChannel = cb.connect(serverAddress).sync().channel(); clientChannel = cb.connect(serverAddress).sync().channel();
assertTrue(serverChannelLatch.await(2, TimeUnit.SECONDS));
} }
@AfterClass @AfterClass
@ -81,6 +95,10 @@ public class Http2CodecTest {
public void tearDown() throws Exception { public void tearDown() throws Exception {
clientChannel.close().sync(); clientChannel.close().sync();
serverChannel.close().sync(); serverChannel.close().sync();
if (serverConnectedChannel != null) {
serverConnectedChannel.close().sync();
serverConnectedChannel = null;
}
} }
@Test @Test

View File

@ -88,6 +88,7 @@ public class Http2ConnectionRoundtripTest {
private ServerBootstrap sb; private ServerBootstrap sb;
private Bootstrap cb; private Bootstrap cb;
private Channel serverChannel; private Channel serverChannel;
private volatile Channel serverConnectedChannel;
private Channel clientChannel; private Channel clientChannel;
private FrameCountDown serverFrameCountDown; private FrameCountDown serverFrameCountDown;
private CountDownLatch requestLatch; private CountDownLatch requestLatch;
@ -113,6 +114,10 @@ public class Http2ConnectionRoundtripTest {
serverChannel.close().sync(); serverChannel.close().sync();
serverChannel = null; serverChannel = null;
} }
if (serverConnectedChannel != null) {
serverConnectedChannel.close().sync();
serverConnectedChannel = null;
}
Future<?> serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS);
Future<?> serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS);
Future<?> clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS);
@ -537,6 +542,7 @@ public class Http2ConnectionRoundtripTest {
sb.childHandler(new ChannelInitializer<Channel>() { sb.childHandler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
serverConnectedChannel = ch;
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
serverFrameCountDown = serverFrameCountDown =
new FrameCountDown(serverListener, serverSettingsAckLatch, new FrameCountDown(serverListener, serverSettingsAckLatch,

View File

@ -52,6 +52,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static io.netty.handler.codec.http.HttpMethod.CONNECT; import static io.netty.handler.codec.http.HttpMethod.CONNECT;
import static io.netty.handler.codec.http.HttpMethod.GET; import static io.netty.handler.codec.http.HttpMethod.GET;
@ -89,6 +90,7 @@ public class HttpToHttp2ConnectionHandlerTest {
private ServerBootstrap sb; private ServerBootstrap sb;
private Bootstrap cb; private Bootstrap cb;
private Channel serverChannel; private Channel serverChannel;
private volatile Channel serverConnectedChannel;
private Channel clientChannel; private Channel clientChannel;
private CountDownLatch requestLatch; private CountDownLatch requestLatch;
private CountDownLatch serverSettingsAckLatch; private CountDownLatch serverSettingsAckLatch;
@ -110,6 +112,10 @@ public class HttpToHttp2ConnectionHandlerTest {
serverChannel.close().sync(); serverChannel.close().sync();
serverChannel = null; serverChannel = null;
} }
if (serverConnectedChannel != null) {
serverConnectedChannel.close().sync();
serverConnectedChannel = null;
}
Future<?> serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS);
Future<?> serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS);
Future<?> clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS);
@ -493,6 +499,7 @@ public class HttpToHttp2ConnectionHandlerTest {
} }
private void bootstrapEnv(int requestCountDown, int serverSettingsAckCount, int trailersCount) throws Exception { private void bootstrapEnv(int requestCountDown, int serverSettingsAckCount, int trailersCount) throws Exception {
final CountDownLatch serverChannelLatch = new CountDownLatch(1);
requestLatch = new CountDownLatch(requestCountDown); requestLatch = new CountDownLatch(requestCountDown);
serverSettingsAckLatch = new CountDownLatch(serverSettingsAckCount); serverSettingsAckLatch = new CountDownLatch(serverSettingsAckCount);
trailersLatch = trailersCount == 0 ? null : new CountDownLatch(trailersCount); trailersLatch = trailersCount == 0 ? null : new CountDownLatch(trailersCount);
@ -505,6 +512,7 @@ public class HttpToHttp2ConnectionHandlerTest {
sb.childHandler(new ChannelInitializer<Channel>() { sb.childHandler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
serverConnectedChannel = ch;
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
serverFrameCountDown = serverFrameCountDown =
new FrameCountDown(serverListener, serverSettingsAckLatch, requestLatch, null, trailersLatch); new FrameCountDown(serverListener, serverSettingsAckLatch, requestLatch, null, trailersLatch);
@ -512,6 +520,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.server(true) .server(true)
.frameListener(serverFrameCountDown) .frameListener(serverFrameCountDown)
.build()); .build());
serverChannelLatch.countDown();
} }
}); });
@ -535,6 +544,7 @@ public class HttpToHttp2ConnectionHandlerTest {
ChannelFuture ccf = cb.connect(serverChannel.localAddress()); ChannelFuture ccf = cb.connect(serverChannel.localAddress());
assertTrue(ccf.awaitUninterruptibly().isSuccess()); assertTrue(ccf.awaitUninterruptibly().isSuccess());
clientChannel = ccf.channel(); clientChannel = ccf.channel();
assertTrue(serverChannelLatch.await(2, TimeUnit.SECONDS));
} }
private void verifyHeadersOnly(Http2Headers expected, ChannelPromise writePromise, ChannelFuture writeFuture) private void verifyHeadersOnly(Http2Headers expected, ChannelPromise writePromise, ChannelFuture writeFuture)

View File

@ -88,7 +88,7 @@ public class InboundHttp2ToHttpAdapterTest {
private ServerBootstrap sb; private ServerBootstrap sb;
private Bootstrap cb; private Bootstrap cb;
private Channel serverChannel; private Channel serverChannel;
private Channel serverConnectedChannel; private volatile Channel serverConnectedChannel;
private Channel clientChannel; private Channel clientChannel;
private CountDownLatch serverLatch; private CountDownLatch serverLatch;
private CountDownLatch clientLatch; private CountDownLatch clientLatch;
@ -118,15 +118,16 @@ public class InboundHttp2ToHttpAdapterTest {
serverChannel.close().sync(); serverChannel.close().sync();
serverChannel = null; serverChannel = null;
} }
if (serverConnectedChannel != null) {
serverConnectedChannel.close().sync();
serverConnectedChannel = null;
}
Future<?> serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS);
Future<?> serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS);
Future<?> clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS);
serverGroup.sync(); serverGroup.sync();
serverChildGroup.sync(); serverChildGroup.sync();
clientGroup.sync(); clientGroup.sync();
clientDelegator = null;
serverDelegator = null;
serverConnectedChannel = null;
} }
@Test @Test
@ -763,6 +764,7 @@ public class InboundHttp2ToHttpAdapterTest {
sb.childHandler(new ChannelInitializer<Channel>() { sb.childHandler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
serverConnectedChannel = ch;
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
Http2Connection connection = new DefaultHttp2Connection(true); Http2Connection connection = new DefaultHttp2Connection(true);
@ -779,7 +781,6 @@ public class InboundHttp2ToHttpAdapterTest {
serverDelegator = new HttpResponseDelegator(serverListener, serverLatch, serverLatch2); serverDelegator = new HttpResponseDelegator(serverListener, serverLatch, serverLatch2);
p.addLast(serverDelegator); p.addLast(serverDelegator);
serverConnectedChannel = ch;
settingsDelegator = new HttpSettingsDelegator(settingsListener, settingsLatch); settingsDelegator = new HttpSettingsDelegator(settingsListener, settingsLatch);
p.addLast(settingsDelegator); p.addLast(settingsDelegator);
serverChannelLatch.countDown(); serverChannelLatch.countDown();