Moved project package from org.eclipse.moquette to io.moquette
This commit is contained in:
@@ -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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
+140
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user