Moved project package from org.eclipse.moquette to io.moquette

This commit is contained in:
andsel
2015-11-20 21:30:59 +01:00
parent f438ef7f49
commit 371c740321
175 changed files with 629 additions and 669 deletions
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.ConnAckMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class ConnAckDecoderTest {
ByteBuf m_buff;
ConnAckDecoder m_msgdec;
@Before
public void setUp() {
m_msgdec = new ConnAckDecoder();
}
@Test
public void testHeader() throws Exception {
m_buff = Unpooled.buffer(14);
initHeader(m_buff);
List<Object> results = new ArrayList<Object >();
//Excercise
m_msgdec.decode(null, m_buff, results);
//Verify
assertFalse(results.isEmpty());
ConnAckMessage message = (ConnAckMessage)results.get(0);
assertNotNull(message);
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, message.getReturnCode());
assertEquals(AbstractMessage.CONNACK, message.getMessageType());
}
private void initHeader(ByteBuf buff) {
buff.clear().writeByte(AbstractMessage.CONNACK << 4).writeByte(2);
//reserved
buff.writeByte((byte)0);
//return code
buff.writeByte(ConnAckMessage.CONNECTION_ACCEPTED);
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.ConnAckMessage;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
*
* @author andrea
*/
public class ConnAckEncoderTest {
ConnAckEncoder m_encoder = new ConnAckEncoder();
ChannelHandlerContext m_mockedContext;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = mock(ChannelHandlerContext.class);
ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
when(m_mockedContext.alloc()).thenReturn(allocator);
}
@Test
public void testHeaderEncode() throws Exception {
ConnAckMessage msg = new ConnAckMessage();
//Exercise
ByteBuf out = Unpooled.buffer();
//Exercise
m_encoder.encode(m_mockedContext, msg, out);
//Verify
assertEquals(0x20, out.readByte()); //1 byte
assertEquals(0x02, out.readByte()); //2 byte, length
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, out.skipBytes(1).readByte());
}
}
@@ -0,0 +1,264 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.moquette.proto.messages.ConnectMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.Attribute;
import io.netty.util.AttributeMap;
import io.netty.util.DefaultAttributeMap;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import static io.moquette.parser.netty.Utils.VERSION_3_1;
import static io.moquette.parser.netty.Utils.VERSION_3_1_1;
import io.moquette.proto.messages.AbstractMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class ConnectDecoderTest {
ByteBuf m_buff;
ConnectDecoder m_msgdec;
AttributeMap attrMap;
@Before
public void setUp() {
m_msgdec = new ConnectDecoder();
attrMap = new DefaultAttributeMap();
}
@Test
public void testBaseHeader() throws Exception {
m_buff = Unpooled.buffer(14);
initBaseHeader(m_buff);
List<Object> results = new ArrayList<Object >();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
verifyBaseHeader((ConnectMessage)results.get(0));
}
@Test
public void testBaseHeader_311() throws UnsupportedEncodingException {
m_buff = Unpooled.buffer(12);
initBaseHeader311(m_buff);
List<Object> results = new ArrayList<>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
verifyBaseHeader311((ConnectMessage)results.get(0));
Attribute<Integer> attr = this.attrMap.attr(MQTTDecoder.PROTOCOL_VERSION);
assertEquals(VERSION_3_1_1, attr.get().intValue());
}
@Test(expected = CorruptedFrameException.class)
public void testBaseHeader_311_withFlagsTouched() throws UnsupportedEncodingException {
m_buff = Unpooled.buffer(12);
initBaseHeader311_withFixedFlags(m_buff, (byte) 0x01);
List<Object> results = new ArrayList<Object>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
verifyBaseHeader311((ConnectMessage)results.get(0));
Attribute<Integer> attr = this.attrMap.attr(MQTTDecoder.PROTOCOL_VERSION);
assertEquals(VERSION_3_1_1, attr.get().intValue());
}
@Test(expected = CorruptedFrameException.class)
public void testDoubleConnectInTheSameSession() throws Exception {
//setup the connection session as already connected
Attribute<Boolean> connectAttr = this.attrMap.attr(ConnectDecoder.CONNECT_STATUS);
connectAttr.set(true);
m_buff = Unpooled.buffer(12);
initBaseHeader311(m_buff);
List<Object> results = new ArrayList<Object>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
}
@Test(expected = CorruptedFrameException.class)
public void testConnectFlags_311_withNot0Reserved() throws UnsupportedEncodingException {
m_buff = Unpooled.buffer(12);
initBaseHeader311_withFixedFlags(m_buff, (byte) 0, (byte) 0xCF); // sets the bit(0) = 1
List<Object> results = new ArrayList<Object>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
verifyBaseHeader311((ConnectMessage)results.get(0));
Attribute<Integer> attr = this.attrMap.attr(MQTTDecoder.PROTOCOL_VERSION);
assertEquals(VERSION_3_1_1, attr.get().intValue());
}
@Test
public void testBaseHeader_ClientID() throws UnsupportedEncodingException, Exception {
m_buff = Unpooled.buffer(40);
initHeader(m_buff, (byte) 38);
encodeString(m_buff, "ABCDEFGH");
encodeString(m_buff, "Topic");
encodeString(m_buff, "Message");
List<Object> results = new ArrayList<Object >();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
ConnectMessage message = (ConnectMessage)results.get(0);
verifyBaseHeader(message);
assertEquals("ABCDEFGH", message.getClientID());
assertEquals("Topic", message.getWillTopic());
assertEquals("Message", new String(message.getWillMessage()));
}
@Test
public void testBaseHeader_extra_with_user_pwd() throws UnsupportedEncodingException, Exception {
m_buff = Unpooled.buffer(55);
initHeader(m_buff, (byte) 53);
encodeString(m_buff, "ABCDEFGH");
encodeString(m_buff, "Topic");
encodeString(m_buff, "Message");
encodeString(m_buff, "Fakeuser");
encodeString(m_buff, "pwd");
List<Object> results = new ArrayList<Object >();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
ConnectMessage message = (ConnectMessage)results.get(0);
verifyBaseHeader(message);
assertEquals(AbstractMessage.CONNECT, message.getMessageType());
assertEquals("ABCDEFGH", message.getClientID());
assertEquals("Topic", message.getWillTopic());
assertEquals("Message", new String(message.getWillMessage()));
assertEquals("Fakeuser", message.getUsername());
assertEquals("pwd", new String(message.getPassword()));
}
@Test(expected = CorruptedFrameException.class)
public void testBadFlagUserPwd() throws Exception {
m_buff = Unpooled.buffer(14);
m_buff.writeByte((AbstractMessage.CONNECT << 4)).writeByte(12);
//Proto name
encodeString(m_buff, "MQIsdp");
//version
m_buff.writeByte(VERSION_3_1);
//conn flags
m_buff.writeByte(0x4E); //sets user to false and password to true
//keepAlive
m_buff.writeByte(0).writeByte(0x0A);
List<Object> results = new ArrayList<Object>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
}
private void initBaseHeader(ByteBuf buff) throws UnsupportedEncodingException {
initHeader(buff, (byte)0x0C);
}
private void initHeader(ByteBuf buff, byte remaingLength) throws UnsupportedEncodingException {
buff.clear().writeByte((byte)(AbstractMessage.CONNECT << 4)).writeByte(remaingLength);
//Proto name
encodeString(buff, "MQIsdp");
//version
buff.writeByte(3);
//conn flags
buff.writeByte(0xCE);
//keepAlive
buff.writeBytes(new byte[]{(byte)0, (byte) 0x0A});
}
private void initBaseHeader311(ByteBuf buff) throws UnsupportedEncodingException {
initBaseHeader311_withFixedFlags(buff, (byte) 0);
}
private void initBaseHeader311_withFixedFlags(ByteBuf buff, byte fixedFlags) throws UnsupportedEncodingException {
initBaseHeader311_withFixedFlags(buff, fixedFlags, (byte) 0xCE);
}
private void initBaseHeader311_withFixedFlags(ByteBuf buff, byte fixedFlags, byte connectFlags) throws UnsupportedEncodingException {
buff.clear().writeByte((byte)(AbstractMessage.CONNECT << 4) | fixedFlags).writeByte((byte)0x0A);
//Proto name
encodeString(buff, "MQTT");
//version
buff.writeByte(VERSION_3_1_1);
//conn flags
buff.writeByte(connectFlags);
//keepAlive
buff.writeBytes(new byte[]{(byte)0, (byte) 0x0A});
}
/**
* Encode and insert the given string into the given buff
*/
private ByteBuf encodeString(ByteBuf buff, String str) throws UnsupportedEncodingException {
buff.writeBytes(Utils.encodeString(str));
return buff;
}
private void verifyBaseHeader(ConnectMessage connMessage) {
assertNotNull(connMessage);
assertEquals("MQIsdp", connMessage.getProtocolName());
assertEquals(VERSION_3_1, connMessage.getProtocolVersion());
assertTrue(connMessage.isUserFlag());
assertTrue(connMessage.isPasswordFlag());
assertTrue(connMessage.isCleanSession());
assertEquals(10, connMessage.getKeepAlive());
assertTrue(connMessage.isWillFlag());
assertFalse(connMessage.isWillRetain());
assertEquals(1, connMessage.getWillQos());
}
private void verifyBaseHeader311(ConnectMessage connMessage) {
assertNotNull(connMessage);
assertEquals("MQTT", connMessage.getProtocolName());
assertEquals(VERSION_3_1_1, connMessage.getProtocolVersion());
assertTrue(connMessage.isUserFlag());
assertTrue(connMessage.isPasswordFlag());
assertTrue(connMessage.isCleanSession());
assertEquals(10, connMessage.getKeepAlive());
assertTrue(connMessage.isWillFlag());
assertFalse(connMessage.isWillRetain());
assertEquals(1, connMessage.getWillQos());
}
}
@@ -0,0 +1,138 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.moquette.proto.messages.ConnectMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.*;
/**
*
* @author andrea
*/
public class ConnectEncoderTest {
ConnectEncoder m_encoder = new ConnectEncoder();
ChannelHandlerContext m_mockedContext;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
}
@Test
public void testBaseHeader() throws Exception {
ConnectMessage msg = new ConnectMessage();
msg.setWillRetain(true);
msg.setWillQos((byte)2);
msg.setWillFlag(false);
msg.setCleanSession(true);
msg.setKeepAlive(512);
ByteBuf out = Unpooled.buffer();
//Exercise
m_encoder.encode(m_mockedContext, msg, out);
//Verify
assertEquals(0x10, out.readByte()); //1 byte
assertEquals(12, out.readByte()); //remaining length
TestUtils.verifyString("MQIsdp", out);
assertEquals(0x03, out.readByte()); //protocol version
assertEquals(0x32, out.readByte()); //flags
assertEquals(2, out.readByte()); //keepAliveTimer msb
assertEquals(0, out.readByte()); //keepAliveTimer lsb
}
@Test
public void testCompleteHeader() throws Exception {
ConnectMessage msg = new ConnectMessage();
msg.setWillRetain(true);
msg.setWillQos((byte)2);
msg.setWillFlag(true);
msg.setCleanSession(true);
msg.setKeepAlive(512);
//variable part
msg.setClientID("ABCDEF");
msg.setWillTopic("Topic");
msg.setWillMessage("Message".getBytes());
ByteBuf out = Unpooled.buffer();
//Exercise
m_encoder.encode(m_mockedContext, msg, out);
//Verify
assertEquals(0x10, out.readByte()); //1 byte
assertEquals(36, out.readByte()); //remaining length
TestUtils.verifyString("MQIsdp", out);
assertEquals(0x03, out.readByte()); //protocol version
assertEquals(0x36, out.readByte()); //flags
assertEquals(2, out.readByte()); //keepAliveTimer msb
assertEquals(0, out.readByte()); //keepAliveTimer lsb
//Variable part
TestUtils.verifyString("ABCDEF", out);
TestUtils.verifyString("Topic", out);
TestUtils.verifyString("Message", out);
}
@Test
public void testCompleteHeaderWIthUser_password() throws Exception {
ConnectMessage msg = new ConnectMessage();
msg.setUserFlag(true);
msg.setPasswordFlag(true);
msg.setWillRetain(true);
msg.setWillQos((byte)2);
msg.setWillFlag(true);
msg.setCleanSession(true);
msg.setKeepAlive(512);
//variable part
msg.setClientID("ABCDEF");
msg.setWillTopic("Topic");
msg.setWillMessage("Message".getBytes());
msg.setUsername("Pablo");
msg.setPassword("PBL".getBytes());
ByteBuf out = Unpooled.buffer();
//Exercise
m_encoder.encode(m_mockedContext, msg, out);
//Verify
assertEquals(0x10, out.readByte()); //1 byte
assertEquals(48, out.readByte()); //remaining length
TestUtils.verifyString("MQIsdp", out);
assertEquals(0x03, out.readByte()); //protocol version
assertEquals((byte)0xF6, (byte)out.readByte()); //flags
assertEquals(2, out.readByte()); //keepAliveTimer msb
assertEquals(0, out.readByte()); //keepAliveTimer lsb
//Variable part
TestUtils.verifyString("ABCDEF", out);
TestUtils.verifyString("Topic", out);
TestUtils.verifyString("Message", out);
TestUtils.verifyString("Pablo", out);//username
TestUtils.verifyString("PBL", out);//password
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.moquette.proto.messages.PubAckMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class PubAckDecoderTest {
ByteBuf m_buff;
PubAckDecoder m_msgdec;
@Before
public void setUp() {
m_msgdec = new PubAckDecoder();
}
@Test
public void testHeader() throws Exception {
m_buff = Unpooled.buffer(14);
int messageId = 0xAABB;
initHeader(m_buff, messageId);
List<Object> results = new ArrayList<Object >();
//Excercise
m_msgdec.decode(null, m_buff, results);
assertFalse(results.isEmpty());
PubAckMessage message = (PubAckMessage)results.get(0);
assertNotNull(message);
assertEquals(messageId, message.getMessageID().intValue());
assertEquals(AbstractMessage.PUBACK, message.getMessageType());
}
private void initHeader(ByteBuf buff, int messageID) {
buff.clear().writeByte(AbstractMessage.PUBACK << 4).writeByte(2);
//return code
buff.writeShort(messageID);
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.moquette.proto.messages.PubAckMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.*;
/**
*
* @author andrea
*/
public class PubAckEncoderTest {
ChannelHandlerContext m_mockedContext;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
}
@Test
public void testHeaderEncode() throws Exception {
int messageID = 0xAABB;
PubAckEncoder encoder = new PubAckEncoder();
PubAckMessage msg = new PubAckMessage();
msg.setMessageID(messageID);
ByteBuf out = Unpooled.buffer();
//Exercise
encoder.encode(m_mockedContext, msg, out);
//Verify
assertEquals(0x40, out.readByte()); //1 byte
assertEquals(0x02, out.readByte()); //2 byte, length
assertEquals((byte)0xAA, out.readByte());
assertEquals((byte)0xBB, out.readByte());
}
}
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import io.netty.util.DefaultAttributeMap;
import java.util.ArrayList;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Before;
/**
*
* @author andrea
*/
public class PubRelDecoderTest {
private static final int MESSAGE_ID = 123;
ByteBuf m_buff;
PubRelDecoder m_msgdec;
AttributeMap attrMap;
@Before
public void setUp() {
m_msgdec = new PubRelDecoder();
attrMap = new DefaultAttributeMap();
}
@Test
public void testValidPurRel() throws Exception {
m_buff = Unpooled.buffer(4);
m_buff.clear().writeByte(AbstractMessage.PUBREL << 4 | 0x02).writeByte(4);
m_buff.writeShort(MESSAGE_ID); //fake message_id
List<Object> results = new ArrayList<Object>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
//Verify
assertFalse(results.isEmpty());
}
@Test(expected = CorruptedFrameException.class)
public void testInvalidPurRel_badReservedFlags() throws Exception {
m_buff = Unpooled.buffer(4);
m_buff.clear().writeByte(AbstractMessage.PUBREL << 4 | 0x03).writeByte(4);
m_buff.writeShort(MESSAGE_ID); //fake message_id
List<Object> results = new ArrayList<Object>();
//Excercise
m_msgdec.decode(this.attrMap, m_buff, results);
}
}
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.moquette.proto.messages.PubRelMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
*
* @author kazvictor
*/
public class PubRelEncoderTest {
ChannelHandlerContext m_mockedContext;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
}
@Test
public void testHeaderEncode() throws Exception {
int messageID = 0xAABB;
PubRelEncoder encoder = new PubRelEncoder();
PubRelMessage msg = new PubRelMessage();
msg.setMessageID(messageID);
ByteBuf out = Unpooled.buffer();
//Exercise
encoder.encode(m_mockedContext, msg, out);
//Verify
assertEquals(0x62, out.readByte()); //1 byte. See MQTT-3.6.1-1
assertEquals(0x02, out.readByte()); //2 byte, length
assertEquals((byte)0xAA, out.readByte());
assertEquals((byte)0xBB, out.readByte());
}
}
@@ -0,0 +1,287 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import io.netty.util.DefaultAttributeMap;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PublishMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class PublishDecoderTest {
ByteBuf m_buff;
PublishDecoder m_msgdec;
List<Object> m_results;
AttributeMap m_attrMap;
private static final int MESSAGE_ID = 123;
@Before
public void setUp() {
m_msgdec = new PublishDecoder();
m_results = new ArrayList<>();
m_attrMap = new DefaultAttributeMap();
}
@Test
public void testHeader() throws Exception {
m_buff = Unpooled.buffer(14);
initHeader(m_buff);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
assertNotNull(message);
assertEquals("Fake Topic", message.getTopicName());
assertNull(message.getMessageID());
assertEquals(AbstractMessage.PUBLISH, message.getMessageType());
}
@Test
public void testHeaderWithMessageID() throws Exception {
m_buff = Unpooled.buffer(14);
initHeaderWithMessageID(m_buff, MESSAGE_ID);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
assertNotNull(message);
assertEquals("Fake Topic", message.getTopicName());
assertEquals(MESSAGE_ID, (int) message.getMessageID());
}
@Test
public void testHeaderWithMessageID_Payload() throws Exception {
m_buff = Unpooled.buffer(14);
// byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
ByteBuffer payload = ByteBuffer.allocate(3).put(new byte[]{0x0A, 0x0B, 0x0C});
initHeaderWithMessageID_Payload(m_buff, MESSAGE_ID, payload);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
assertNotNull(message);
assertEquals("Fake Topic", message.getTopicName());
assertEquals(MESSAGE_ID, (int) message.getMessageID());
// TestUtils.verifyEquals(payload, message.getPayload());
assertEquals(payload, message.getPayload());
}
@Test(expected = CorruptedFrameException.class)
public void testDup0WithQoS0_3_1_1() throws Exception {
m_buff = preparePubclishWithQosFlags((byte) 0x08);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
}
@Test(expected = CorruptedFrameException.class)
public void testReservedQoS3_3_1_1() throws Exception {
m_buff = preparePubclishWithQosFlags((byte) 0x0E);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
}
@Test(expected = CorruptedFrameException.class)
public void testTopicWithWildCards() throws Exception {
byte[] overallMessage = new byte[]{0x30, 0x17, //fixed header, 25 byte length
0x00, 0x06, 0x2f, 0x74, 0x6f, 0x70, 0x2B /*+*/, 0x23 /*#*/, //[/top+#] string 2 len + 6 content
0x54, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x79, // [Test my payload] encoding
0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64};
m_buff = Unpooled.buffer(overallMessage.length);
m_buff.writeBytes(overallMessage);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
}
@Test
public void testBugOnRealCase() throws Exception {
byte[] overallMessage = new byte[]{0x30, 0x17, //fixed header, 25 byte length
0x00, 0x06, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, //[/topic] string 2 len + 6 content
0x54, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x79, // [Test my payload] encoding
0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64};
m_buff = Unpooled.buffer(overallMessage.length);
m_buff.writeBytes(overallMessage);
//Exercise
m_msgdec.decode(m_attrMap, m_buff, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
assertNotNull(message);
}
@Test
public void testDecodeBigContent() throws Exception {
int size = 129;
ByteBuf payload = TestUtils.generateRandomPayload(size);
ByteBuf firstPublish = generatePublishQoS0(payload);
ByteBuf secondPublish = generatePublishQoS0(TestUtils.generateRandomPayload(size));
ByteBuf doubleMessageBuf = Unpooled.buffer(size * 2);
doubleMessageBuf.writeBytes(firstPublish).writeBytes(secondPublish);
//Exercise
m_msgdec.decode(m_attrMap, doubleMessageBuf, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
assertNotNull(message);
m_results.clear();
m_msgdec.decode(m_attrMap, doubleMessageBuf, m_results);
assertFalse(m_results.isEmpty());
message = (PublishMessage)m_results.get(0);
assertNotNull(message);
}
@Test
public void testBadClaimMoreData() throws Exception {
byte[] rawMessage = new byte[] {
0x30, 0x17, 0x00, 0x06, 0x2f, (byte)0x74, 0x6f, (byte)0x70, 0x69, 0x63, 0x54, 0x65, (byte)0x73, 0x74, 0x20, 0x6d,
(byte)0x79, 0x20, (byte)0x70, 0x61, (byte)0x79, 0x6c, 0x6f, 0x61, 0x64
};
ByteBuf msgBuf = Unpooled.buffer(25);
msgBuf.writeBytes(rawMessage);
msgBuf.readByte(); //to simulate the reading of messageType done by MQTTDecoder dispatcher
//Exercise
m_msgdec.decode(m_attrMap, msgBuf, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
assertNotNull(message);
assertEquals("/topic", message.getTopicName());
// assertEquals("Test my payload", new String(message.getPayload()));
Buffer expectedPayload = ByteBuffer.allocate(15).put("Test my payload".getBytes()).flip();
assertEquals(expectedPayload, message.getPayload());
}
/*
* Check topic is at least one char [MQTT-4.7.3-1]
* */
@Test(expected = CorruptedFrameException.class)
public void testMinimumTopicLength() throws Exception {
ByteBuf packet = packetWithTopic("");
//Exercise
m_msgdec.decode(m_attrMap, packet, m_results);
}
private void initHeader(ByteBuf buff) throws IllegalAccessException {
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
buff.clear().writeByte(AbstractMessage.PUBLISH << 4).writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
//topic name
buff.writeBytes(tmp);
}
private ByteBuf packetWithTopic(String topicName) throws IllegalAccessException {
ByteBuf buff = Unpooled.buffer(14);
ByteBuf topicBuff = Unpooled.buffer(4).writeBytes(Utils.encodeString(topicName));
buff.clear().writeByte(AbstractMessage.PUBLISH << 4).writeBytes(Utils.encodeRemainingLength(topicBuff.readableBytes()));
//topic name
buff.writeBytes(topicBuff);
return buff;
}
private void initHeaderWithMessageID(ByteBuf buff, int messageID) throws IllegalAccessException {
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
tmp.writeShort(messageID);
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
//topic name
buff.writeBytes(tmp);
}
private void initHeaderWithMessageID_Payload(ByteBuf buff, int messageID, byte[] payload) throws IllegalAccessException {
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
tmp.writeShort(messageID);
tmp.writeBytes(payload);
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
//topic name
buff.writeBytes(tmp);
}
private void initHeaderWithMessageID_Payload(ByteBuf buff, int messageID, ByteBuffer payload) throws IllegalAccessException {
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
tmp.writeShort(messageID);
tmp.writeBytes(payload);
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
//topic name
buff.writeBytes(tmp);
}
private ByteBuf generatePublishQoS0(ByteBuf payload) throws IllegalAccessException {
int size = payload.capacity();
ByteBuf messageBody = Unpooled.buffer(size);
messageBody.writeBytes(Utils.encodeString("/topic"));
//ONLY for QoS > 1 Utils.writeWord(messageBody, messageID);
messageBody.writeBytes(payload);
ByteBuf completeMsg = Unpooled.buffer(size);
completeMsg.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x00) //set Qos to 0
.writeBytes(Utils.encodeRemainingLength(messageBody.readableBytes()));
completeMsg.writeBytes(messageBody);
return completeMsg;
}
private ByteBuf preparePubclishWithQosFlags(byte flags) {
ByteBuf buff = Unpooled.buffer(14);
ByteBuffer payload = ByteBuffer.allocate(3).put(new byte[]{0x0A, 0x0B, 0x0C});
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
tmp.writeShort(MESSAGE_ID);
tmp.writeBytes(payload);
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | flags) //set DUP=1 Qos to 11 => b1110
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
//topic name
buff.writeBytes(tmp);
return buff;
}
}
@@ -0,0 +1,198 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import java.nio.ByteBuffer;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import io.moquette.proto.messages.AbstractMessage.QOSType;
import io.moquette.proto.messages.PublishMessage;
/**
*
* @author andrea
*/
public class PublishEncoderTest {
PublishEncoder m_encoder = new PublishEncoder();
ChannelHandlerContext m_mockedContext;
ByteBuf m_out;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
m_out = Unpooled.buffer();
}
@Test
public void testEncodeWithQos_0_noMessageID() throws Exception {
String topic = "/photos";
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.MOST_ONE);
msg.setTopicName(topic);
//variable part
byte[] bpayload = new byte[]{0x0A, 0x0B, 0x0C};
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(3).put(bpayload).flip();
msg.setPayload(payload);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
//Verify
assertEquals(0x30, m_out.readByte()); //1 byte
//(2+7) topic + 3 payload
assertEquals(12, m_out.readByte()); //remaining length
//Variable part
TestUtils.verifyString(topic, m_out);
payload.rewind();
TestUtils.verifyBuff(bpayload.length, payload, m_out);
}
@Test
public void testEncodeWithQos_1_MessageID() throws Exception {
String topic = "/photos";
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.LEAST_ONE);
msg.setMessageID(1);
msg.setTopicName(topic);
//variable part
byte[] bpayload = new byte[]{0x0A, 0x0B, 0x0C};
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(3).put(bpayload).flip();
msg.setPayload(payload);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
//Verify
assertEquals(0x32, m_out.readByte()); //1 byte
//(2+7) topic + 2 messageID + 3 payload
assertEquals(14, m_out.readByte()); //remaining length
//Variable part
TestUtils.verifyString(topic, m_out);
assertEquals(0, m_out.readByte()); //MessageID MSB
assertEquals(1, m_out.readByte()); //MessageID LSB
payload.rewind();
TestUtils.verifyBuff(bpayload.length, payload, m_out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncodeWithQos_1_noMessageID_fail() throws Exception {
String topic = "pictures/photos";
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.LEAST_ONE);
msg.setTopicName(topic);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_empty_topic() throws Exception {
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.LEAST_ONE);
msg.setMessageID(1);
ByteBuf out = Unpooled.buffer();
//Exercise
m_encoder.encode(m_mockedContext, msg, out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_null_topic() throws Exception {
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.LEAST_ONE);
msg.setMessageID(1);
msg.setTopicName(null);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_qos_reserved() throws Exception {
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.RESERVED);
msg.setMessageID(1);
msg.setTopicName(null);
//variable part
byte[] bpayload = new byte[]{0x0A, 0x0B, 0x0C};
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(3).put(bpayload).flip();
msg.setPayload(payload);
ByteBuf out = Unpooled.buffer();
//Exercise
m_encoder.encode(m_mockedContext, msg, out);
}
@Test
public void testBugMissedMessageIDEncoding() throws Exception{
String topic = "/topic";
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.MOST_ONE);
msg.setTopicName(topic);
//variable part
byte[] bpayload = "Test my payload".getBytes();
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(bpayload.length).put(bpayload).flip();
msg.setPayload(payload);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
//Verify
//2 byte header + (2+6) topic name + [2 message ID if QoQ <> 0]+ payload
int size = m_out.readableBytes();
assertEquals(10 + bpayload.length, size);
assertEquals(0x30, m_out.readByte()); //1 byte
assertEquals(23, m_out.readByte()); //1 byte the length
}
@Test
public void testEncodeBigMessage() throws Exception {
byte[] bpayload = new byte[256];
TestUtils.generateRandomPayload(256).readBytes(bpayload);
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(bpayload.length).put(bpayload).flip();
String topic = "/topic";
PublishMessage msg = new PublishMessage();
msg.setQos(QOSType.MOST_ONE);
msg.setTopicName(topic);
//variable part
msg.setPayload(payload);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
int size = m_out.readableBytes();
assertEquals(11 + bpayload.length, size);
assertEquals(0x30, m_out.readByte()); //1 byte
}
}
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.SubAckMessage;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class SubAckDecoderTest {
ByteBuf m_buff;
SubAckDecoder m_msgdec;
List<Object> m_results;
@Before
public void setUp() {
m_msgdec = new SubAckDecoder();
m_results = new ArrayList<Object>();
m_buff = Unpooled.buffer(7);
}
@Test
public void testBadQos() throws Exception {
initHeaderQos(m_buff, 0xAABB, AbstractMessage.QOSType.LEAST_ONE, AbstractMessage.QOSType.MOST_ONE, AbstractMessage.QOSType.MOST_ONE);
//Excercise
m_msgdec.decode(null, m_buff, m_results);
//Verify
assertFalse(m_results.isEmpty());
SubAckMessage message = (SubAckMessage)m_results.get(0);
assertNotNull(message);
assertEquals(0xAABB, message.getMessageID().intValue());
List<AbstractMessage.QOSType> qoses = message.types();
assertEquals(3, qoses.size());
assertEquals(AbstractMessage.QOSType.LEAST_ONE, qoses.get(0));
assertEquals(AbstractMessage.QOSType.MOST_ONE, qoses.get(1));
assertEquals(AbstractMessage.QOSType.MOST_ONE, qoses.get(2));
assertEquals(AbstractMessage.SUBACK, message.getMessageType());
}
@Test
public void testBugBadRemainingCalculation() throws Exception {
byte[] overallMessage = new byte[] {(byte)0x90, 0x03, //fixed header
0x00, 0x0A, //MSG ID
0x01}; //QoS array
m_buff = Unpooled.buffer(overallMessage.length);
m_buff.writeBytes(overallMessage);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
assertFalse(m_results.isEmpty());
SubAckMessage message = (SubAckMessage)m_results.get(0);
assertNotNull(message);
assertEquals(0x0A, message.getMessageID().intValue());
assertEquals(1, message.types().size());
assertEquals(AbstractMessage.QOSType.LEAST_ONE, message.types().get(0));
}
@Test
public void testEncodeWithFailureQOS() throws Exception {
initHeaderQos(m_buff, 0xAABB, AbstractMessage.QOSType.FAILURE);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
//Verify
assertFalse(m_results.isEmpty());
SubAckMessage message = (SubAckMessage)m_results.get(0);
assertNotNull(message);
assertEquals(0xAABB, message.getMessageID().intValue());
List<AbstractMessage.QOSType> qoses = message.types();
assertEquals(1, qoses.size());
assertEquals(AbstractMessage.QOSType.FAILURE, qoses.get(0));
assertEquals(AbstractMessage.SUBACK, message.getMessageType());
}
private void initHeaderQos(ByteBuf buff, int messageID, AbstractMessage.QOSType... qoss) throws IllegalAccessException {
buff.clear().writeByte(AbstractMessage.SUBACK << 4).
writeBytes(Utils.encodeRemainingLength(2 + qoss.length));
buff.writeShort(messageID);
for (AbstractMessage.QOSType qos : qoss) {
buff.writeByte(qos.byteValue());
}
}
}
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.SubAckMessage;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class SubAckEncoderTest {
SubAckEncoder m_encoder = new SubAckEncoder();
ChannelHandlerContext m_mockedContext;
ByteBuf m_out;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
m_out = Unpooled.buffer();
}
@Test(expected = IllegalArgumentException.class)
public void testEncodeWithNoQoss() throws Exception {
SubAckMessage msg = new SubAckMessage();
msg.setMessageID(123);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
}
@Test
public void testEncodeWithMultipleQos() throws Exception {
SubAckMessage msg = new SubAckMessage();
int messageID = 0xAABB;
msg.setMessageID(messageID);
msg.addType(AbstractMessage.QOSType.MOST_ONE);
msg.addType(AbstractMessage.QOSType.LEAST_ONE);
msg.addType(AbstractMessage.QOSType.EXACTLY_ONCE);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
//Verify
assertEquals((byte) (AbstractMessage.SUBACK << 4 ), m_out.readByte()); //1 byte
assertEquals(5, m_out.readByte()); //remaining length
//Variable part
assertEquals((byte)0xAA, m_out.readByte()); //MessageID MSB
assertEquals((byte)0xBB, m_out.readByte()); //MessageID LSB
assertEquals(AbstractMessage.QOSType.MOST_ONE.byteValue(), m_out.readByte());
assertEquals(AbstractMessage.QOSType.LEAST_ONE.byteValue(), m_out.readByte());
assertEquals(AbstractMessage.QOSType.EXACTLY_ONCE.byteValue(), m_out.readByte());
}
@Test
public void testEncodeWithFailureQOS() throws Exception {
SubAckMessage msg = new SubAckMessage();
int messageID = 0xAABB;
msg.setMessageID(messageID);
msg.addType(AbstractMessage.QOSType.FAILURE);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
//Verify
assertEquals((byte) (AbstractMessage.SUBACK << 4 ), m_out.readByte()); //1 byte
assertEquals(3, m_out.readByte()); //remaining length
//Variable part
assertEquals((byte)0xAA, m_out.readByte()); //MessageID MSB
assertEquals((byte)0xBB, m_out.readByte()); //MessageID LSB
assertEquals((byte)0x80, m_out.readByte()); //Failure return code
}
}
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import java.util.ArrayList;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.SubscribeMessage;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class SubscribeDecoderTest {
ByteBuf m_buff;
SubscribeDecoder m_msgdec;
List<Object> m_results;
@Before
public void setUp() {
m_msgdec = new SubscribeDecoder();
m_results = new ArrayList<>();
}
@Test(expected = CorruptedFrameException.class)
public void testBadQos() throws Exception {
m_buff = Unpooled.buffer(2);
initHeaderBadQos(m_buff);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
}
@Test
public void testMultiTopic() throws Exception {
m_buff = Unpooled.buffer(4);
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "a/b");
SubscribeMessage.Couple c2 = new SubscribeMessage.Couple((byte)1, "c/d/e");
initMultiTopic(m_buff, 123, c1, c2);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
//Verify
assertFalse(m_results.isEmpty());
SubscribeMessage message = (SubscribeMessage)m_results.get(0);
assertEquals(2, message.subscriptions().size());
assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType());
}
@Test(expected = CorruptedFrameException.class)
public void testFailOnEmptyTopic() throws Exception {
m_buff = Unpooled.buffer(4);
initMultiTopic(m_buff, 123);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
//Verify
assertFalse(m_results.isEmpty());
SubscribeMessage message = (SubscribeMessage)m_results.get(0);
assertEquals(2, message.subscriptions().size());
assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType());
}
@Test(expected = CorruptedFrameException.class)
public void testFailOnSubscriptionWithReservedQosPolluted() throws Exception {
m_buff = Unpooled.buffer(4);
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "a/b");
initPollutedTopic(m_buff, 123, c1);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
//Verify
assertFalse(m_results.isEmpty());
SubscribeMessage message = (SubscribeMessage)m_results.get(0);
assertEquals(2, message.subscriptions().size());
assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType());
}
/*
* Check topic is at least one char [MQTT-4.7.3-1]
* */
@Test(expected = CorruptedFrameException.class)
public void testMinimumTopicLength() throws Exception {
m_buff = Unpooled.buffer(4);
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "");
initMultiTopic(m_buff, 123, c1);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
}
private void initHeaderBadQos(ByteBuf buff) {
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4).writeByte(0);
}
private void initMultiTopic(ByteBuf buff, int messageID, SubscribeMessage.Couple... topics) throws IllegalAccessException {
ByteBuf topicBuffer = Unpooled.buffer(4);
topicBuffer.writeShort(messageID);
for (SubscribeMessage.Couple couple : topics) {
topicBuffer.writeBytes(Utils.encodeString(couple.getTopicFilter()));
topicBuffer.writeByte(couple.getQos());
}
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02)
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
buff.writeBytes(topicBuffer);
}
private void initPollutedTopic(ByteBuf buff, int messageID, SubscribeMessage.Couple topic) throws IllegalAccessException {
ByteBuf topicBuffer = Unpooled.buffer(4);
topicBuffer.writeShort(messageID);
topicBuffer.writeBytes(Utils.encodeString(topic.getTopicFilter()));
topicBuffer.writeByte(0xF0 | topic.getQos());
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02)
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
buff.writeBytes(topicBuffer);
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.SubscribeMessage;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class SubscribeEncoderTest {
SubscribeEncoder m_encoder = new SubscribeEncoder();
ChannelHandlerContext m_mockedContext;
ByteBuf m_out;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
m_out = Unpooled.buffer();
}
@Test
public void testEncodeWithMultiTopic() throws Exception {
SubscribeMessage msg = new SubscribeMessage();
msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
msg.setMessageID(0xAABB);
//variable part
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)1, "a/b");
SubscribeMessage.Couple c2 = new SubscribeMessage.Couple((byte)0, "a/b/c");
msg.addSubscription(c1);
msg.addSubscription(c2);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
//Verify
assertEquals((byte)0x82, (byte)m_out.readByte()); //1 byte
assertEquals(16, m_out.readByte()); //remaining length
//verify MessageID
assertEquals((byte)0xAA, m_out.readByte());
assertEquals((byte)0xBB, m_out.readByte());
//Variable part
TestUtils.verifyString(c1.getTopicFilter(), m_out);
assertEquals(c1.getQos(), m_out.readByte());
TestUtils.verifyString(c2.getTopicFilter(), m_out);
assertEquals(c2.getQos(), m_out.readByte());
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_empty_subscription() throws Exception {
SubscribeMessage msg = new SubscribeMessage();
msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
msg.setMessageID(0xAABB);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_badQos() throws Exception {
SubscribeMessage msg = new SubscribeMessage();
msg.setQos(AbstractMessage.QOSType.EXACTLY_ONCE);
msg.setMessageID(0xAABB);
//Exercise
m_encoder.encode(m_mockedContext, msg, m_out);
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* Common test methods
*
* @author andrea
*/
public class TestUtils {
/**
* Verify that the given bytes buffer of the given numBytes length is present
* in the buff starting from the current position.
*/
static void verifyBuff(int numBytes, byte[] bytes, ByteBuf buff) {
assertTrue(numBytes <= buff.readableBytes());
byte[] toCheck = new byte[numBytes];
buff.readBytes(toCheck);
for (int i = 0; i < numBytes; i++) {
assertEquals(bytes[i], toCheck[i]);
}
}
static void verifyBuff(int numBytes, ByteBuffer bytes, ByteBuf buff) {
assertTrue(numBytes <= buff.readableBytes());
assertEquals(bytes, buff.nioBuffer());
}
/**
* Verify the presence of the given string starting from the current position
* inside the buffer.
*/
static void verifyString(String str, ByteBuf buff) throws UnsupportedEncodingException {
ByteBuf tmpBuff = Unpooled.buffer(2);
byte[] raw = str.getBytes("UTF-8");
tmpBuff.writeShort(raw.length);
tmpBuff.writeBytes(raw);
int buffLen = raw.length + 2;
verifyByteBuf(tmpBuff, buff.slice(buff.readerIndex(), buffLen));
buff.skipBytes(buffLen);
}
static void verifyByteBuf(ByteBuf expected, ByteBuf found) {
assertEquals(expected, found);
}
static ChannelHandlerContext mockChannelHandler() {
ChannelHandlerContext m_mockedContext = mock(ChannelHandlerContext.class);
ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
when(m_mockedContext.alloc()).thenReturn(allocator);
return m_mockedContext;
}
static void verifyEquals(byte[] expected, byte[] found) {
assertEquals(expected.length, found.length);
for (int i = 0; i < expected.length; i++) {
assertEquals(expected[i], found[i]);
}
}
public static ByteBuf generateRandomPayload(int size) {
ByteBuf payloadBuffer = Unpooled.buffer(size);
for (int i = 0; i < size; i++) {
payloadBuffer.writeByte((byte) (Math.random() * 255));
}
return payloadBuffer;
}
}
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.UnsubscribeMessage;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class UnsubscribeDecoderTest {
ByteBuf m_buff;
UnsubscribeDecoder m_msgdec;
List<Object> m_results;
@Before
public void setUp() {
m_msgdec = new UnsubscribeDecoder();
m_results = new ArrayList<>();
}
@Test(expected = CorruptedFrameException.class)
public void testBadQos() throws Exception {
m_buff = Unpooled.buffer(2);
initHeaderBadQos(m_buff);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
}
@Test
public void testMultiTopic() throws Exception {
m_buff = Unpooled.buffer(4);
String topic1 = "a/b";
String topic2 = "c/d/e";
initMultiTopic(m_buff, 123, topic1, topic2);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
//Verify
assertFalse(m_results.isEmpty());
UnsubscribeMessage message = (UnsubscribeMessage)m_results.get(0);
assertNotNull(message);
assertEquals(Arrays.asList(topic1, topic2), message.topicFilters());
assertEquals(AbstractMessage.UNSUBSCRIBE, message.getMessageType());
}
@Test(expected = CorruptedFrameException.class)
public void testFailOnEmptyTopic() throws Exception {
m_buff = Unpooled.buffer(4);
initMultiTopic(m_buff, 123, "");
//Exercise
m_msgdec.decode(null, m_buff, m_results);
}
/*
* Check topic is at least one char [MQTT-4.7.3-1]
* */
@Test(expected = CorruptedFrameException.class)
public void testMinimumTopicLength() throws Exception {
m_buff = Unpooled.buffer(4);
initMultiTopic(m_buff, 123);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
}
@Test
public void testDecodeSingleTopic_bug() throws Exception {
//A2 0C 00 01 00 06 2F 74 6F 70 69 63 //12 byte
byte[] overallMessage = new byte[] {(byte)0xA2, 0x0A, //fixed header
0x00, 0x01, //MSG ID
0x00, 0x06, 0x2F, 0x74, 0x6F, 0x70, 0x69, 0x63}; //"/topic" string
m_buff = Unpooled.buffer(overallMessage.length);
m_buff.writeBytes(overallMessage);
m_msgdec.decode(null, m_buff, m_results);
assertFalse(m_results.isEmpty());
UnsubscribeMessage message = (UnsubscribeMessage)m_results.get(0);
assertNotNull(message);
assertEquals(0x01, (int) message.getMessageID());
}
private void initHeaderBadQos(ByteBuf buff) {
buff.clear().writeByte(AbstractMessage.UNSUBSCRIBE << 4).writeByte(0);
}
private void initMultiTopic(ByteBuf buff, int messageID, String... topics) throws IllegalAccessException {
ByteBuf topicBuffer = Unpooled.buffer(4);
topicBuffer.writeShort(messageID);
for (String topic : topics) {
topicBuffer.writeBytes(Utils.encodeString(topic));
}
buff.clear().writeByte(AbstractMessage.UNSUBSCRIBE << 4 | (byte)0x02)
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
buff.writeBytes(topicBuffer);
}
}
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.UnsubscribeMessage;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author andrea
*/
public class UnsubscribeEncoderTest {
UnsubscribeEncoder m_encoder = new UnsubscribeEncoder();
ChannelHandlerContext m_mockedContext;
ByteBuf m_out;
UnsubscribeMessage m_msg;
@Before
public void setUp() {
//mock the ChannelHandlerContext to return an UnpooledAllocator
m_mockedContext = TestUtils.mockChannelHandler();
m_out = Unpooled.buffer();
m_msg = new UnsubscribeMessage();
m_msg.setMessageID(0xAABB);
}
@Test
public void testEncodeWithSingleTopic() throws Exception {
m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
//variable part
String topic1 = "/topic";
m_msg.addTopicFilter(topic1);
//Exercise
m_encoder.encode(m_mockedContext, m_msg, m_out);
//Verify
assertEquals((byte)0xA2, (byte)m_out.readByte()); //1 byte
//2 messageID + 2 length + 6 chars = 10
assertEquals(10, m_out.readByte()); //remaining length
//verify M1ssageID
assertEquals((byte)0xAA, m_out.readByte());
assertEquals((byte)0xBB, m_out.readByte());
//Variable part
TestUtils.verifyString(topic1, m_out);
}
@Test
public void testEncodeWithMultiTopic() throws Exception {
m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
//variable part
String topic1 = "a/b";
String topic2 = "a/b/c";
m_msg.addTopicFilter(topic1);
m_msg.addTopicFilter(topic2);
//Exercise
m_encoder.encode(m_mockedContext, m_msg, m_out);
//Verify
assertEquals((byte)0xA2, (byte)m_out.readByte()); //1 byte
assertEquals(14, m_out.readByte()); //remaining length
//verify M1ssageID
assertEquals((byte)0xAA, m_out.readByte());
assertEquals((byte)0xBB, m_out.readByte());
//Variable part
TestUtils.verifyString(topic1, m_out);
TestUtils.verifyString(topic2, m_out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_empty_topics() throws Exception {
m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
//Exercise
m_encoder.encode(m_mockedContext, m_msg, m_out);
}
@Test(expected = IllegalArgumentException.class)
public void testEncode_badQos() throws Exception {
m_msg.setQos(AbstractMessage.QOSType.EXACTLY_ONCE);
//Exercise
m_encoder.encode(m_mockedContext, m_msg, m_out);
}
}
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import org.junit.Before;
import org.junit.Test;
import static io.moquette.parser.netty.TestUtils.verifyBuff;
import static org.junit.Assert.*;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.UnsubscribeMessage;
/**
*
* @author andrea
*/
/*@RunWith(Suite.class)
@Suite.SuiteClasses({})*/
public class UtilsTest {
ByteBuf m_buff;
@Before
public void setUp() {
m_buff = Unpooled.buffer(4);
}
@Test
public void testDecodeRemainingLength() {
//1 byte length
m_buff.writeByte(0x0);
assertEquals(0, Utils.decodeRemainingLenght(m_buff));
m_buff.clear().writeByte(0x007F);
assertEquals(127, Utils.decodeRemainingLenght(m_buff));
//2 byte length
m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x01});
assertEquals(128, Utils.decodeRemainingLenght(m_buff));
m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0x7F});
assertEquals(16383, Utils.decodeRemainingLenght(m_buff));
//3 byte length
m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x80, (byte)0x01});
assertEquals(16384, Utils.decodeRemainingLenght(m_buff));
m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0x7F});
assertEquals(2097151, Utils.decodeRemainingLenght(m_buff));
//4 byte length
m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x80, (byte)0x80, (byte)0x01});
assertEquals(2097152, Utils.decodeRemainingLenght(m_buff));
m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x7F});
assertEquals(268435455, Utils.decodeRemainingLenght(m_buff));
}
@Test(expected=CorruptedFrameException.class)
public void testEncodeRemainingLength_invalid_upper() {
Utils.encodeRemainingLength(Utils.MAX_LENGTH_LIMIT + 1);
}
@Test(expected=CorruptedFrameException.class)
public void testEncodeRemainingLength_invalid_lower() {
Utils.encodeRemainingLength(-1);
}
@Test
public void testEncodeRemainingLenght() {
//1 byte length
verifyBuff(1, new byte[]{0}, Utils.encodeRemainingLength(0));
verifyBuff(1, new byte[]{0x7F}, Utils.encodeRemainingLength(127));
//2 byte length
verifyBuff(2, new byte[]{(byte)0x80, 0x01}, Utils.encodeRemainingLength(128));
verifyBuff(2, new byte[]{(byte)0xFF, 0x7F}, Utils.encodeRemainingLength(16383));
//3 byte length
verifyBuff(3, new byte[]{(byte)0x80, (byte)0x80, 0x01}, Utils.encodeRemainingLength(16384));
verifyBuff(3, new byte[]{(byte)0xFF, (byte)0xFF, 0x7F}, Utils.encodeRemainingLength(2097151));
//4 byte length
verifyBuff(4, new byte[]{(byte)0x80, (byte)0x80, (byte)0x80, 0x01}, Utils.encodeRemainingLength(2097152));
verifyBuff(4, new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, 0x7F}, Utils.encodeRemainingLength(268435455));
}
@Test
public void testEncodeFlags() {
UnsubscribeMessage msg = new UnsubscribeMessage();
msg.setRetainFlag(true);
msg.setQos(AbstractMessage.QOSType.MOST_ONE);
//Exercise
assertEquals(1, Utils.encodeFlags(msg));
}
}
@@ -0,0 +1,131 @@
package io.moquette.parser.netty.performance;
import io.moquette.proto.messages.*;
import static io.moquette.proto.messages.AbstractMessage.*;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.moquette.proto.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
/**
* Created by andrea on 6/2/15.
*/
@ChannelHandler.Sharable
class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(LoopMQTTHandler.class);
private ProtocolDecodingServer.SharedState m_state;
public LoopMQTTHandler(ProtocolDecodingServer.SharedState state) {
this.m_state = state;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object message) {
AbstractMessage msg = (AbstractMessage) message;
LOG.info("Received a message of type {}", Utils.msgType2String(msg.getMessageType()));
try {
switch (msg.getMessageType()) {
case CONNECT:
handleConnect(ctx, (ConnectMessage) message);
return;
case SUBSCRIBE:
handleSubscribe(ctx, (SubscribeMessage) msg);
return;
case PUBLISH:
handlePublish(ctx, (PublishMessage) msg);
return;
case DISCONNECT:
ctx.close();
// case PUBACK:
// NettyChannel channel;
// synchronized (m_channelMapper) {
// if (!m_channelMapper.containsKey(ctx)) {
// m_channelMapper.put(ctx, new NettyChannel(ctx));
// }
// channel = m_channelMapper.get(ctx);
// }
//
// m_messaging.handleProtocolMessage(channel, msg);
break;
case PINGREQ:
PingRespMessage pingResp = new PingRespMessage();
ctx.writeAndFlush(pingResp);
break;
}
} catch (Exception ex) {
LOG.error("Bad error in processing the message", ex);
}
}
private void handlePublish(ChannelHandlerContext ctx, PublishMessage msg) {
if (!m_state.isForwardable()) {
LOG.info("Subscriber not yet connected, LoopHandler instance is {}", this);
return;
}
LOG.debug("push forward message the topic {}", msg.getTopicName());
if (LOG.isDebugEnabled()) {
LOG.debug("content <{}>", payload2Str(msg.getPayload()));
}
//publish always at Qos0, to don't handle PUBACK or the complete Qos2 workflow
PublishMessage pubMessage = new PublishMessage();
pubMessage.setRetainFlag(false);
pubMessage.setTopicName(msg.getTopicName());
pubMessage.setQos(QOSType.MOST_ONE);
pubMessage.setPayload(msg.getPayload());
m_state.getSubscriberCh().writeAndFlush(pubMessage);
}
private void handleSubscribe(ChannelHandlerContext ctx, SubscribeMessage msg) {
m_state.setForwardable(true);
LOG.debug(" new value of flag {}, LoopHandler instance is {}", m_state.isForwardable(), this);
SubAckMessage ackMessage = new SubAckMessage();
ackMessage.setMessageID(msg.getMessageID());
ctx.writeAndFlush(ackMessage);
LOG.debug("subscribed client to {}", msg.subscriptions());
}
static String payload2Str(ByteBuffer content) {
byte[] b = new byte[content.remaining()];
content.mark();
content.get(b);
content.reset();
return new String(b);
}
private void handleConnect(ChannelHandlerContext ctx, ConnectMessage msg) {
String clientID = msg.getClientID();
if (clientID.toLowerCase().startsWith("sub")) {
m_state.setSubscriberCh(ctx);
} else if (clientID.toLowerCase().startsWith("pub")) {
m_state.setPublisherCh(ctx);
} else {
//we don't admit other names
ConnAckMessage koResp = new ConnAckMessage();
koResp.setReturnCode(ConnAckMessage.IDENTIFIER_REJECTED);
ctx.writeAndFlush(koResp);
ctx.close();
}
ConnAckMessage okResp = new ConnAckMessage();
okResp.setReturnCode(ConnAckMessage.CONNECTION_ACCEPTED);
ctx.writeAndFlush(okResp);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// NettyChannel channel = m_channelMapper.get(ctx);
// String clientID = (String) channel.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
// m_messaging.lostConnection(channel, clientID);
// ctx.close(/*false*/);
// synchronized (m_channelMapper) {
// m_channelMapper.remove(ctx);
// }
}
}
@@ -0,0 +1,140 @@
package io.moquette.parser.netty.performance;
import io.moquette.commons.Constants;
import io.moquette.parser.netty.MQTTDecoder;
import io.moquette.parser.netty.MQTTEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Emulates a broker, but doesn't apply any protocol logic, just forward the qos0 publishes
* to the lone subscriber, it's used just to measure the protocol decoding/encoding overhead.
*
*/
public class ProtocolDecodingServer {
class MoquetteIdleTimoutHandler extends ChannelDuplexHandler {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState e = ((IdleStateEvent) evt).state();
if (e == IdleState.ALL_IDLE) {
//fire a channelInactive to trigger publish of Will
ctx.fireChannelInactive();
ctx.close();
} /*else if (e.getState() == IdleState.WRITER_IDLE) {
ctx.writeAndFlush(new PingMessage());
}*/
}
}
}
private static final Logger LOG = LoggerFactory.getLogger(ProtocolDecodingServer.class);
EventLoopGroup m_bossGroup;
EventLoopGroup m_workerGroup;
static final class SharedState {
private volatile ChannelHandlerContext subscriberCh;
private volatile ChannelHandlerContext publisherCh;
private volatile boolean forwardPublish = false;
public boolean isForwardable() {
return forwardPublish;
}
void setForwardable(boolean forwardPublish) {
this.forwardPublish = forwardPublish;
}
public void setSubscriberCh(ChannelHandlerContext subscriberCh) {
this.subscriberCh = subscriberCh;
}
public ChannelHandlerContext getSubscriberCh() {
return subscriberCh;
}
public void setPublisherCh(ChannelHandlerContext publisherCh) {
this.publisherCh = publisherCh;
}
}
final SharedState state = new SharedState();
void init() {
String host = "0.0.0.0";
int port = 1883;
m_bossGroup = new NioEventLoopGroup();
m_workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(m_bossGroup, m_workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
try {
pipeline.addFirst("idleStateHandler", new IdleStateHandler(0, 0, Constants.DEFAULT_CONNECT_TIMEOUT));
pipeline.addAfter("idleStateHandler", "idleEventHandler", new MoquetteIdleTimoutHandler());
pipeline.addLast("decoder", new MQTTDecoder());
pipeline.addLast("encoder", new MQTTEncoder());
pipeline.addLast("handler", new LoopMQTTHandler(state));
} catch (Throwable th) {
LOG.error("Severe error during pipeline creation", th);
throw th;
}
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(host, port);
LOG.info("Server binded host: {}, port: {}", host, port);
f.sync();
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
}
public void stop() {
if (m_workerGroup == null) {
throw new IllegalStateException("Invoked close on an Acceptor that wasn't initialized");
}
if (m_bossGroup == null) {
throw new IllegalStateException("Invoked close on an Acceptor that wasn't initialized");
}
m_workerGroup.shutdownGracefully();
m_bossGroup.shutdownGracefully();
LOG.info("Closed boss and worker Event loops");
System.out.println("Server stopped");
}
public static final void main(String[] args) {
final ProtocolDecodingServer server = new ProtocolDecodingServer();
server.init();
System.out.println("Loop server started");
//Bind a shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
server.stop();
}
});
}
}