Updated publish message parto to conform to 3.1.1 specification

This commit is contained in:
selva.andre
2014-07-31 00:08:41 +02:00
parent 482ffe0c65
commit d4ef81a7ef
9 changed files with 160 additions and 18 deletions
@@ -18,6 +18,8 @@ package org.dna.mqtt.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import java.util.List;
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
import org.dna.mqtt.moquette.proto.messages.PublishMessage;
@@ -34,6 +36,11 @@ class PublishDecoder extends DemuxDecoder {
@Override
void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
decode((AttributeMap)ctx, in, out);
}
// @Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
LOG.info("decode invoked with buffer {}", in);
in.resetReaderIndex();
int startPos = in.readerIndex();
@@ -45,6 +52,18 @@ class PublishDecoder extends DemuxDecoder {
in.resetReaderIndex();
return;
}
if (Utils.isMQTT3_1_1(ctx)) {
if (message.getQos() == AbstractMessage.QOSType.MOST_ONE && message.isDupFlag()) {
//bad protocol, if QoS=0 => DUP = 0
throw new CorruptedFrameException("Received a PUBLISH with QoS=0 & DUP = 1, MQTT 3.1.1 violation");
}
if (message.getQos() == AbstractMessage.QOSType.RESERVED) {
throw new CorruptedFrameException("Received a PUBLISH with QoS flags setted 10 b11, MQTT 3.1.1 violation");
}
}
int remainingLength = message.getRemainingLength();
//Topic name
@@ -53,6 +72,10 @@ class PublishDecoder extends DemuxDecoder {
in.resetReaderIndex();
return;
}
if (topic.contains("+") || topic.contains("#")) {
throw new CorruptedFrameException("Received a PUBLISH with topic containting wild card chars, topic: " + topic);
}
message.setTopicName(topic);
if (message.getQos() == AbstractMessage.QOSType.LEAST_ONE ||
@@ -18,6 +18,8 @@ package org.dna.mqtt.moquette.parser.netty;
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 java.io.UnsupportedEncodingException;
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
import org.slf4j.LoggerFactory;
@@ -174,4 +176,13 @@ public class Utils {
flags |= ((message.getQos().ordinal() & 0x03) << 1);
return flags;
}
static boolean isMQTT3_1_1(AttributeMap attrsMap) {
Attribute<Integer> versionAttr = attrsMap.attr(MQTTDecoder.PROTOCOL_VERSION);
Integer protocolVersion = versionAttr.get();
if (protocolVersion == null) {
return true;
}
return protocolVersion == VERSION_3_1_1;
}
}
@@ -17,6 +17,9 @@ package org.dna.mqtt.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;
@@ -38,11 +41,15 @@ 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<Object >();
m_attrMap = new DefaultAttributeMap();
}
@Test
@@ -51,7 +58,7 @@ public class PublishDecoderTest {
initHeader(m_buff);
//Excercise
m_msgdec.decode(null, m_buff, m_results);
m_msgdec.decode(m_attrMap, m_buff, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
@@ -64,40 +71,67 @@ public class PublishDecoderTest {
@Test
public void testHeaderWithMessageID() throws Exception {
m_buff = Unpooled.buffer(14);
int messageID = 123;
initHeaderWithMessageID(m_buff, messageID);
initHeaderWithMessageID(m_buff, MESSAGE_ID);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
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(messageID, (int) message.getMessageID());
assertEquals(MESSAGE_ID, (int) message.getMessageID());
}
@Test
public void testHeaderWithMessageID_Payload() throws Exception {
m_buff = Unpooled.buffer(14);
int messageID = 123;
// byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
ByteBuffer payload = ByteBuffer.allocate(3).put(new byte[]{0x0A, 0x0B, 0x0C});
initHeaderWithMessageID_Payload(m_buff, messageID, payload);
initHeaderWithMessageID_Payload(m_buff, MESSAGE_ID, payload);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
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(messageID, (int) message.getMessageID());
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 = 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 lenght
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 lenght
@@ -108,7 +142,7 @@ public class PublishDecoderTest {
m_buff.writeBytes(overallMessage);
//Exercise
m_msgdec.decode(null, m_buff, m_results);
m_msgdec.decode(m_attrMap, m_buff, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
@@ -128,14 +162,14 @@ public class PublishDecoderTest {
//Exercise
m_msgdec.decode(null, doubleMessageBuf, m_results);
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(null, doubleMessageBuf, m_results);
m_msgdec.decode(m_attrMap, doubleMessageBuf, m_results);
assertFalse(m_results.isEmpty());
message = (PublishMessage)m_results.get(0);
@@ -154,7 +188,7 @@ public class PublishDecoderTest {
msgBuf.readByte(); //to simulate the reading of messageType done by MQTTDecoder dispatcher
//Exercise
m_msgdec.decode(null, msgBuf, m_results);
m_msgdec.decode(m_attrMap, msgBuf, m_results);
assertFalse(m_results.isEmpty());
PublishMessage message = (PublishMessage)m_results.get(0);
@@ -216,4 +250,18 @@ public class PublishDecoderTest {
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;
}
}