package com.spotify.netty4.handler.codec.zmtp; import org.junit.Test; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import static com.google.common.base.Strings.repeat; import static com.spotify.netty4.handler.codec.zmtp.Buffers.buf; import static com.spotify.netty4.handler.codec.zmtp.Buffers.bytes; import static com.spotify.netty4.handler.codec.zmtp.ZMTPProtocols.ZMTP10; import static com.spotify.netty4.handler.codec.zmtp.ZMTPProtocols.ZMTP20; import static com.spotify.netty4.handler.codec.zmtp.ZMTPSocketType.REQ; import static io.netty.util.CharsetUtil.UTF_8; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; /** * These tests has a full pipeline setup. */ public class PipelineTests { // FIXME (dano): Tests are hardcoded to use a message 260 bytes long private static final byte[] LONG_MSG = repeat("data", 100).substring(0, 260).getBytes(UTF_8); /** * First let's just exercise the PipelineTester a bit. */ @Test public void testPipelineTester() { final ByteBuf buf = Unpooled.copiedBuffer("Hello, world", UTF_8); final PipelineTester pipelineTester = new PipelineTester(new ChannelInboundHandlerAdapter() { @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); ctx.channel().writeAndFlush(buf); } }); assertThat(pipelineTester.readClient(), is(buf)); final ByteBuf foo = Unpooled.copiedBuffer("foo", UTF_8); pipelineTester.writeClient(foo.retain()); assertThat(foo, is(pipelineTester.readServer())); final ByteBuf bar = Unpooled.copiedBuffer("bar", UTF_8); pipelineTester.writeServer(bar.retain()); assertThat(bar, is(pipelineTester.readClient())); } @Test public void testZMTPPipeline() { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP20) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0xff, 0, 0, 0, 0, 0, 0, 0, 4, 0x7f), is(pt.readClient())); pt.writeClient(buf(0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0x7f, 1, 4, 0, 1, 0x63)); assertThat(buf(1, 3, 0, 3, 0x66, 0x6f, 0x6f), is(pt.readClient())); pt.writeClient(buf(1, 1, 0x65, 1, 0, 0, 1, 0x62)); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(3)); assertThat(buf(0x65), is(m.frame(0))); assertThat(buf(), is(m.frame(1))); assertThat(buf(0x62), is(m.frame(2))); } @Test public void testZMTPPipelineFragmented() { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP20) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0xff, 0, 0, 0, 0, 0, 0, 0, 4, 0x7f), is(pt.readClient())); pt.writeClient(buf(0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0x7f, 1, 4, 0, 1, 0x63, 1, 1, 0x65, 1)); assertThat(buf(1, 3, 0, 3, 0x66, 0x6f, 0x6f), is(pt.readClient())); pt.writeClient(buf(0, 0, 1, 0x62)); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(3)); assertThat(buf(0x65), is(m.frame(0))); assertThat(buf(), is(m.frame(1))); assertThat(buf(0x62), is(m.frame(2))); } @Test public void testZMTP1PipelineLongMessage() { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP10) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0x04, 0, 0x66, 0x6f, 0x6f), is(pt.readClient())); ByteBuf cb = Unpooled.buffer(); // handshake: length + flag + client identity octets "BAR" cb.writeBytes(buf(4, 0, 0x62, 0x61, 0x72)); // two octet envelope delimiter cb.writeBytes(bytes(0x01, 0x01)); // content frame size + flag octet cb.writeBytes(bytes(0x0ff, 0, 0, 0, 0, 0, 0, 0x01, 0x05, 0)); // payload cb.writeBytes(LONG_MSG); pt.writeClient(cb); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(2)); assertThat(buf(), is(m.frame(0))); assertThat(buf(LONG_MSG), is(m.frame(1))); } @Test public void testZMTP1PipelineFragmentedHandshake() { doTestZMTP1PipelineFragmentedHandshake(buf(4), buf(0, 0x62, 0x61, 0x72)); doTestZMTP1PipelineFragmentedHandshake(buf(4, 0), buf(0x62, 0x61, 0x72)); doTestZMTP1PipelineFragmentedHandshake(buf(4, 0, 0x62), buf(0x61, 0x72)); doTestZMTP1PipelineFragmentedHandshake(buf(4, 0, 0x62, 0x61), buf(0x72)); } private void doTestZMTP1PipelineFragmentedHandshake(ByteBuf first, ByteBuf second) { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP10) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0x04, 0, 0x66, 0x6f, 0x6f), is(pt.readClient())); // write both fragments of client handshake pt.writeClient(first); pt.writeClient(second); ByteBuf cb = Unpooled.buffer(); // two octet envelope delimiter cb.writeBytes(bytes(0x01, 0x01)); // content frame size + flag octet cb.writeBytes(bytes(0x0ff, 0, 0, 0, 0, 0, 0, 0x01, 0x05, 0)); // payload cb.writeBytes(LONG_MSG); pt.writeClient(cb); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(2)); assertThat(buf(), is(m.frame(0))); assertThat(buf(LONG_MSG), is(m.frame(1))); } @Test // tests the case when the message to be parsed is fragmented inside the long long size field public void testZMTP1PipelineLongMessageFragmentedLong() { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP10) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0x04, 0, 0x66, 0x6f, 0x6f), is(pt.readClient())); ByteBuf cb = Unpooled.buffer(); // handshake: length + flag + client identity octets "BAR" cb.writeBytes(buf(4, 0, 0x62, 0x61, 0x72)); // two octet envelope delimiter cb.writeBytes(bytes(0x01, 0x01)); // fragmented first part of frame size cb.writeBytes(bytes(0x0ff, 0, 0)); pt.writeClient(cb); cb = Unpooled.buffer(); // fragmented second part of frame size cb.writeBytes(bytes(0, 0, 0, 0, 0x01, 0x05, 0)); // payload cb.writeBytes(LONG_MSG); pt.writeClient(cb); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(2)); assertThat(buf(), is(m.frame(0))); assertThat(buf(LONG_MSG), is(m.frame(1))); } @Test // tests the case when the message to be parsed is fragmented between 0xff flag and 8 octet length public void testZMTP1PipelineLongMessageFragmentedSize() { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP10) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0x04, 0, 0x66, 0x6f, 0x6f), is(pt.readClient())); ByteBuf cb = Unpooled.buffer(); // handshake: length + flag + client identity octets "BAR" cb.writeBytes(buf(4, 0, 0x62, 0x61, 0x72)); // two octet envelope delimiter cb.writeBytes(bytes(0x01, 0x01)); // fragmented first part of frame size cb.writeBytes(bytes(0x0ff)); pt.writeClient(cb); cb = Unpooled.buffer(); // fragmented second part of frame size cb.writeBytes(bytes(0, 0, 0, 0, 0, 0, 0x01, 0x05, 0)); // payload cb.writeBytes(LONG_MSG); pt.writeClient(cb); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(2)); assertThat(buf(), is(m.frame(0))); assertThat(buf(LONG_MSG), is(m.frame(1))); } @Test // tests fragmentation in the size field of the second message public void testZMTP1PipelineMultiMessage() { final PipelineTester pt = new PipelineTester( ZMTPCodec.builder() .protocol(ZMTP10) .socketType(REQ) .localIdentity("foo") .build()); assertThat(buf(0x04, 0, 0x66, 0x6f, 0x6f), is(pt.readClient())); ByteBuf cb = Unpooled.buffer(); // handshake: length + flag + client identity octets "BAR" cb.writeBytes(buf(4, 0, 0x62, 0x61, 0x72)); // two octet envelope delimiter cb.writeBytes(bytes(0x01, 0x01)); // content frame size + flag octet cb.writeBytes(bytes(0x0ff, 0, 0, 0, 0, 0, 0, 0x01, 0x05, 0)); // payload cb.writeBytes(LONG_MSG); // second message, first fragment // fragmented first part of frame size cb.writeBytes(bytes(1, 1, 0x0ff, 0, 0)); pt.writeClient(cb); ZMTPMessage m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(2)); assertThat(buf(), is(m.frame(0))); assertThat(buf(LONG_MSG), is(m.frame(1))); // send the rest of the second message cb = Unpooled.buffer(); // fragmented second part of frame size cb.writeBytes(bytes(0, 0, 0, 0, 0x01, 0x05, 0)); // payload cb.writeBytes(LONG_MSG); pt.writeClient(cb); m = (ZMTPMessage) pt.readServer(); assertThat(m.size(), is(2)); assertThat(buf(), is(m.frame(0))); assertThat(buf(LONG_MSG), is(m.frame(1))); } }