[#5566] Ensure using a ChannelInitializer via ServerBootstrap.handler(...) produce correct ordering.

Motivation:

When a ChannelInitializer is used via ServerBootstrap.handler(...) the users handlers may be added after the internal ServerBootstrapAcceptor. This should not happen.

Modifications:

Delay the adding of the ServerBootstrapAcceptor until the initChannel(....) method returns.

Result:

Correct order of handlers in the ServerChannels ChannelPipeline.
This commit is contained in:
Norman Maurer 2016-07-22 08:21:42 +02:00
parent 445a547265
commit 4638df2062
2 changed files with 91 additions and 3 deletions

View File

@ -169,13 +169,23 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
// We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
// In this case the initChannel(...) method will only be called after this method returns. Because
// of this we need to ensure we add our handler in a delayed fashion so all the users handler are
// placed in front of the ServerBootstrapAcceptor.
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}

View File

@ -15,13 +15,21 @@
*/
package io.netty.bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel;
import org.junit.Test;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
@ -59,4 +67,74 @@ public class ServerBootstrapTest {
group.shutdownGracefully();
}
}
@Test(timeout = 3000)
public void testParentHandler() throws Exception {
testParentHandler(false);
}
@Test(timeout = 3000)
public void testParentHandlerViaChannelInitializer() throws Exception {
testParentHandler(true);
}
private static void testParentHandler(boolean channelInitializer) throws Exception {
final LocalAddress addr = new LocalAddress(UUID.randomUUID().toString());
final CountDownLatch readLatch = new CountDownLatch(1);
final CountDownLatch initLatch = new CountDownLatch(1);
final ChannelHandler handler = new ChannelInboundHandlerAdapter() {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
initLatch.countDown();
super.handlerAdded(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
readLatch.countDown();
super.channelRead(ctx, msg);
}
};
EventLoopGroup group = new DefaultEventLoopGroup(1);
Channel sch = null;
Channel cch = null;
try {
ServerBootstrap sb = new ServerBootstrap();
sb.channel(LocalServerChannel.class)
.group(group)
.childHandler(new ChannelInboundHandlerAdapter());
if (channelInitializer) {
sb.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(handler);
}
});
} else {
sb.handler(handler);
}
Bootstrap cb = new Bootstrap();
cb.group(group)
.channel(LocalChannel.class)
.handler(new ChannelInboundHandlerAdapter());
sch = sb.bind(addr).syncUninterruptibly().channel();
cch = cb.connect(addr).syncUninterruptibly().channel();
initLatch.await();
readLatch.await();
} finally {
if (sch != null) {
sch.close().syncUninterruptibly();
}
if (cch != null) {
cch.close().syncUninterruptibly();
}
group.shutdownGracefully();
}
}
}