3
votes

In our application we have a client/server pair that initiate their connection with a small handshake protocol P1, after which they switch to another protocol P2.

For P1 protocol the pipeline is initialised with the following handlers:

LengthFieldBasedFrameDecoder
P1ProtocolMessageDecoder
LengthFieldPrepender
P1ProtocolMessageEncoder

After the P1 handshake protocol is successfully completed, the traffic should switch to the P2 Protocol, in which case we first clear the pipeline and then add a separate set of handlers

P2MessageDecoder
P2MessageEncoder
IdleHandler

Switching the pipeline is done when last expected message in P1 protocol is received:

// switch traffic to P2 protocol
clearPipeline();
addNewHandlers();

The problem encountered is that LengthFieldBasedFrameDecoder removal triggers an unexpected read (because the are bytes left unread in the handler's ByteBuf). But, since at that point in time, the pipeline is empty (was cleared, but new handlers not yet added), the inbound message gets dropped.

Is there any "safe" way to do the pipeline switch without triggering an unwanted read during the time the handlers are not in place yet?

Thanks

Later Edit:

I've read about replacing decoders here: (The part titled "Replacing a decoder with another decoder in a pipeline")

http://netty.io/4.0/api/io/netty/handler/codec/ReplayingDecoder.html

The workaround I've applied successfully was to:

 removeOldNonByteToMessageHandlers();
 addNewHandlers()
 removeOldByteToMessageHandlers(); 
 // when the "leftover bytes" read is triggered the new handlers are already in place

My solution seems hacky. Is there a better "netty-er" way of achieving this?

1
Is this a netty 3 question ?Norman Maurer
No, and sorry for the confusion. The question is about Netty 4. The javadoc link is from v3, it explains the behaviour nicely and is valid also for v4 - just that the componenent is named differently in v4 netty.io/4.0/api/io/netty/handler/codec/…Edi
I'm very interested in a good answer on this too as I have the exact same use case, and having that handshake at the beginning makes everything a bit hacky.Pierre

1 Answers

1
votes

I noticed that removing the ByteToMessage handlers in your order wasn't enough for me, as it would still try to use them for the leftover bytes in the current message.

In my situation I have a FixedLengthFrameDecoder, that I want to switch for a LengthFieldBasedFrameDecoder after the specified number of bytes has been read.

If my message is X bytes, with the Y first bytes being the protocole signal, or "handshake", and Z the number of leftover bytes, I want to read the first Y with the ByteToMessage handler, then the Z next bytes by the new handler.

@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
    Object handshake = super.decode(ctx, in);

    ctx.pipeline().addFirst(new LengthFieldBasedFrameDecoder(ByteOrder.BIG_ENDIAN, Integer.MAX_VALUE, 0, 4, 0, 4, true));
    if (in.isReadable()) {
        Object[] response = new Object[] { in.readBytes(in.readableBytes())};
        ctx.pipeline().remove(this);
        return response;
    } else {
        ctx.pipeline().remove(this);
        return handshake;
    }

}