Added encode/decode for Subscribe message
This commit is contained in:
@@ -22,6 +22,7 @@ public class MQTTDecoder extends ByteToMessageDecoder {
|
||||
m_decoderMap.put(AbstractMessage.CONNACK, new ConnAckDecoder());
|
||||
m_decoderMap.put(AbstractMessage.PUBLISH, new PublishDecoder());
|
||||
m_decoderMap.put(AbstractMessage.PUBACK, new PubAckDecoder());
|
||||
m_decoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ public class MQTTEncoder extends MessageToByteEncoder<AbstractMessage> {
|
||||
m_encoderMap.put(AbstractMessage.CONNACK, new ConnAckEncoder());
|
||||
m_encoderMap.put(AbstractMessage.PUBLISH, new PublishEncoder());
|
||||
m_encoderMap.put(AbstractMessage.PUBACK, new PubAckEncoder());
|
||||
m_encoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.dna.mqtt.moquette.parser.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.List;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
class SubscribeDecoder extends DemuxDecoder {
|
||||
|
||||
@Override
|
||||
void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
//Common decoding part
|
||||
SubscribeMessage message = new SubscribeMessage();
|
||||
in.resetReaderIndex();
|
||||
if (!decodeCommonHeader(message, in)) {
|
||||
in.resetReaderIndex();
|
||||
return;
|
||||
}
|
||||
|
||||
//check qos level
|
||||
if (message.getQos() != QOSType.LEAST_ONE) {
|
||||
throw new CorruptedFrameException("Received Subscribe message with QoS other than LEAST_ONE, was: " + message.getQos());
|
||||
}
|
||||
|
||||
int start = in.readerIndex();
|
||||
//read messageIDs
|
||||
message.setMessageID(in.readUnsignedShort());
|
||||
int readed = in.readerIndex() - start;
|
||||
while (readed < message.getRemainingLength()) {
|
||||
decodeSubscription(in, message);
|
||||
readed = in.readerIndex()- start;
|
||||
}
|
||||
|
||||
out.add(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the message with couple of Qos, topic
|
||||
*/
|
||||
private void decodeSubscription(ByteBuf in, SubscribeMessage message) throws UnsupportedEncodingException {
|
||||
String topic = Utils.decodeString(in);
|
||||
byte qos = (byte)(in.readByte() & 0x03);
|
||||
message.addSubscription(new SubscribeMessage.Couple(qos, topic));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.dna.mqtt.moquette.parser.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
class SubscribeEncoder extends DemuxEncoder<SubscribeMessage> {
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext chc, SubscribeMessage message, ByteBuf out) {
|
||||
if (message.subscriptions().isEmpty()) {
|
||||
throw new IllegalArgumentException("Found a subscribe message with empty topics");
|
||||
}
|
||||
|
||||
if (message.getQos() != AbstractMessage.QOSType.LEAST_ONE) {
|
||||
throw new IllegalArgumentException("Expected a message with QOS 1, found " + message.getQos());
|
||||
}
|
||||
|
||||
ByteBuf variableHeaderBuff = chc.alloc().buffer(4);
|
||||
variableHeaderBuff.writeShort(message.getMessageID());
|
||||
for (SubscribeMessage.Couple c : message.subscriptions()) {
|
||||
variableHeaderBuff.writeBytes(Utils.encodeString(c.getTopic()));
|
||||
variableHeaderBuff.writeByte(c.getQos());
|
||||
}
|
||||
|
||||
int variableHeaderSize = variableHeaderBuff.readableBytes();
|
||||
byte flags = Utils.encodeFlags(message);
|
||||
ByteBuf buff = chc.alloc().buffer(2 + variableHeaderSize);
|
||||
|
||||
buff.writeByte(AbstractMessage.SUBSCRIBE << 4 | flags);
|
||||
buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
|
||||
buff.writeBytes(variableHeaderBuff);
|
||||
|
||||
out.writeBytes(buff);
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package org.dna.mqtt.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 org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.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<Object >();
|
||||
}
|
||||
|
||||
@Test(expected = CorruptedFrameException.class)
|
||||
public void testBadQos() throws Exception {
|
||||
m_buff = Unpooled.buffer(2);
|
||||
initHeaderBadQos(m_buff);
|
||||
|
||||
//Excercise
|
||||
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);
|
||||
|
||||
//Excercise
|
||||
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());
|
||||
}
|
||||
|
||||
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.getTopic()));
|
||||
topicBuffer.writeByte(couple.getQos());
|
||||
}
|
||||
|
||||
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02)
|
||||
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
|
||||
buff.writeBytes(topicBuffer);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package org.dna.mqtt.moquette.parser.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.dna.mqtt.moquette.parser.netty.TestUtils.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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 = 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
|
||||
verifyString(c1.getTopic(), m_out);
|
||||
assertEquals(c1.getQos(), m_out.readByte());
|
||||
verifyString(c2.getTopic(), 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user