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

This commit is contained in:
andsel
2015-11-20 21:30:59 +01:00
parent f438ef7f49
commit 371c740321
175 changed files with 629 additions and 669 deletions
@@ -0,0 +1,31 @@
package io.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.util.AttributeMap;
import java.util.List;
import io.moquette.proto.messages.ConnAckMessage;
/**
*
* @author andrea
*/
class ConnAckDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
in.resetReaderIndex();
//Common decoding part
ConnAckMessage message = new ConnAckMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
//skip reserved byte
in.skipBytes(1);
//read return code
message.setReturnCode(in.readByte());
out.add(message);
}
}
@@ -0,0 +1,37 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.ConnAckMessage;
/**
*
* @author andrea
*/
class ConnAckEncoder extends DemuxEncoder<ConnAckMessage>{
@Override
protected void encode(ChannelHandlerContext chc, ConnAckMessage message, ByteBuf out) {
out.writeByte(AbstractMessage.CONNACK << 4);
out.writeBytes(Utils.encodeRemainingLength(2));
out.writeByte(message.isSessionPresent() ? 0x01 : 0x00);
out.writeByte(message.getReturnCode());
}
}
@@ -0,0 +1,220 @@
/*
* 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.handler.codec.CorruptedFrameException;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.AttributeMap;
import java.io.UnsupportedEncodingException;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
public class ConnectDecoder extends DemuxDecoder {
static final AttributeKey<Boolean> CONNECT_STATUS = AttributeKey.valueOf("connected");
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws UnsupportedEncodingException {
in.resetReaderIndex();
//Common decoding part
ConnectMessage message = new ConnectMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
int remainingLength = message.getRemainingLength();
int start = in.readerIndex();
int protocolNameLen = in.readUnsignedShort();
byte[] encProtoName;
String protoName;
Attribute<Integer> versionAttr = ctx.attr(MQTTDecoder.PROTOCOL_VERSION);
switch (protocolNameLen) {
case 6:
//MQTT version 3.1 "MQIsdp"
//ProtocolName 8 bytes or 6 bytes
if (in.readableBytes() < 10) {
in.resetReaderIndex();
return;
}
encProtoName = new byte[6];
in.readBytes(encProtoName);
protoName = new String(encProtoName, "UTF-8");
if (!"MQIsdp".equals(protoName)) {
in.resetReaderIndex();
throw new CorruptedFrameException("Invalid protoName: " + protoName);
}
message.setProtocolName(protoName);
versionAttr.set((int) Utils.VERSION_3_1);
break;
case 4:
//MQTT version 3.1.1 "MQTT"
//ProtocolName 6 bytes
if (in.readableBytes() < 8) {
in.resetReaderIndex();
return;
}
encProtoName = new byte[4];
in.readBytes(encProtoName);
protoName = new String(encProtoName, "UTF-8");
if (!"MQTT".equals(protoName)) {
in.resetReaderIndex();
throw new CorruptedFrameException("Invalid protoName: " + protoName);
}
message.setProtocolName(protoName);
versionAttr.set((int) Utils.VERSION_3_1_1);
break;
default:
//protocol broken
throw new CorruptedFrameException("Invalid protoName size: " + protocolNameLen);
}
//ProtocolVersion 1 byte (value 0x03 for 3.1, 0x04 for 3.1.1)
message.setProtocolVersion(in.readByte());
if (message.getProtocolVersion() == Utils.VERSION_3_1_1) {
//if 3.1.1, check the flags (dup, retain and qos == 0)
if (message.isDupFlag() || message.isRetainFlag() || message.getQos() != AbstractMessage.QOSType.MOST_ONE) {
throw new CorruptedFrameException("Received a CONNECT with fixed header flags != 0");
}
//check if this is another connect from the same client on the same session
Attribute<Boolean> connectAttr = ctx.attr(ConnectDecoder.CONNECT_STATUS);
Boolean alreadyConnected = connectAttr.get();
if (alreadyConnected == null) {
//never set
connectAttr.set(true);
} else if (alreadyConnected) {
throw new CorruptedFrameException("Received a second CONNECT on the same network connection");
}
}
//Connection flag
byte connFlags = in.readByte();
if (message.getProtocolVersion() == Utils.VERSION_3_1_1) {
if ((connFlags & 0x01) != 0) { //bit(0) of connection flags is != 0
throw new CorruptedFrameException("Received a CONNECT with connectionFlags[0(bit)] != 0");
}
}
boolean cleanSession = ((connFlags & 0x02) >> 1) == 1;
boolean willFlag = ((connFlags & 0x04) >> 2) == 1;
byte willQos = (byte) ((connFlags & 0x18) >> 3);
if (willQos > 2) {
in.resetReaderIndex();
throw new CorruptedFrameException("Expected will QoS in range 0..2 but found: " + willQos);
}
boolean willRetain = ((connFlags & 0x20) >> 5) == 1;
boolean passwordFlag = ((connFlags & 0x40) >> 6) == 1;
boolean userFlag = ((connFlags & 0x80) >> 7) == 1;
//a password is true iff user is true.
if (!userFlag && passwordFlag) {
in.resetReaderIndex();
throw new CorruptedFrameException("Expected password flag to true if the user flag is true but was: " + passwordFlag);
}
message.setCleanSession(cleanSession);
message.setWillFlag(willFlag);
message.setWillQos(willQos);
message.setWillRetain(willRetain);
message.setPasswordFlag(passwordFlag);
message.setUserFlag(userFlag);
//Keep Alive timer 2 bytes
//int keepAlive = Utils.readWord(in);
int keepAlive = in.readUnsignedShort();
message.setKeepAlive(keepAlive);
if ((remainingLength == 12 && message.getProtocolVersion() == Utils.VERSION_3_1) ||
(remainingLength == 10 && message.getProtocolVersion() == Utils.VERSION_3_1_1)) {
out.add(message);
return;
}
//Decode the ClientID
String clientID = Utils.decodeString(in);
if (clientID == null) {
in.resetReaderIndex();
return;
}
message.setClientID(clientID);
//Decode willTopic
if (willFlag) {
String willTopic = Utils.decodeString(in);
if (willTopic == null) {
in.resetReaderIndex();
return;
}
message.setWillTopic(willTopic);
}
//Decode willMessage
if (willFlag) {
byte[] willMessage = Utils.readFixedLengthContent(in);
if (willMessage == null) {
in.resetReaderIndex();
return;
}
message.setWillMessage(willMessage);
}
//Compatibility check with v3.0, remaining length has precedence over
//the user and password flags
int readed = in.readerIndex() - start;
if (readed == remainingLength) {
out.add(message);
return;
}
//Decode username
if (userFlag) {
String userName = Utils.decodeString(in);
if (userName == null) {
in.resetReaderIndex();
return;
}
message.setUsername(userName);
}
readed = in.readerIndex() - start;
if (readed == remainingLength) {
out.add(message);
return;
}
//Decode password
if (passwordFlag) {
byte[] password = Utils.readFixedLengthContent(in);
if (password == null) {
in.resetReaderIndex();
return;
}
message.setPassword(password);
}
out.add(message);
}
}
@@ -0,0 +1,91 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
public class ConnectEncoder extends DemuxEncoder<ConnectMessage> {
@Override
protected void encode(ChannelHandlerContext chc, ConnectMessage message, ByteBuf out) {
ByteBuf staticHeaderBuff = chc.alloc().buffer(12);
ByteBuf buff = chc.alloc().buffer();
ByteBuf variableHeaderBuff = chc.alloc().buffer(12);
try {
staticHeaderBuff.writeBytes(Utils.encodeString("MQIsdp"));
//version
staticHeaderBuff.writeByte(0x03);
//connection flags and Strings
byte connectionFlags = 0;
if (message.isCleanSession()) {
connectionFlags |= 0x02;
}
if (message.isWillFlag()) {
connectionFlags |= 0x04;
}
connectionFlags |= ((message.getWillQos() & 0x03) << 3);
if (message.isWillRetain()) {
connectionFlags |= 0x020;
}
if (message.isPasswordFlag()) {
connectionFlags |= 0x040;
}
if (message.isUserFlag()) {
connectionFlags |= 0x080;
}
staticHeaderBuff.writeByte(connectionFlags);
//Keep alive timer
staticHeaderBuff.writeShort(message.getKeepAlive());
//Variable part
if (message.getClientID() != null) {
variableHeaderBuff.writeBytes(Utils.encodeString(message.getClientID()));
if (message.isWillFlag()) {
variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillTopic()));
variableHeaderBuff.writeBytes(Utils.encodeFixedLengthContent(message.getWillMessage()));
}
if (message.isUserFlag() && message.getUsername() != null) {
variableHeaderBuff.writeBytes(Utils.encodeString(message.getUsername()));
if (message.isPasswordFlag() && message.getPassword() != null) {
variableHeaderBuff.writeBytes(Utils.encodeFixedLengthContent(message.getPassword()));
}
}
}
int variableHeaderSize = variableHeaderBuff.readableBytes();
buff.writeByte(AbstractMessage.CONNECT << 4);
buff.writeBytes(Utils.encodeRemainingLength(12 + variableHeaderSize));
buff.writeBytes(staticHeaderBuff).writeBytes(variableHeaderBuff);
out.writeBytes(buff);
} finally {
staticHeaderBuff.release();
buff.release();
variableHeaderBuff.release();
}
}
}
@@ -0,0 +1,85 @@
/*
* 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.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
abstract class DemuxDecoder {
abstract void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception;
/**
* Decodes the first 2 bytes of the MQTT packet.
* The first byte contain the packet operation code and the flags,
* the second byte and more contains the overall packet length.
*/
protected boolean decodeCommonHeader(AbstractMessage message, ByteBuf in) {
return genericDecodeCommonHeader(message, null, in);
}
/**
* Do the same as the @see#decodeCommonHeader but having a strong validation on the flags values
*/
protected boolean decodeCommonHeader(AbstractMessage message, int expectedFlags, ByteBuf in) {
return genericDecodeCommonHeader(message, expectedFlags, in);
}
private boolean genericDecodeCommonHeader(AbstractMessage message, Integer expectedFlagsOpt, ByteBuf in) {
//Common decoding part
if (in.readableBytes() < 2) {
return false;
}
byte h1 = in.readByte();
byte messageType = (byte) ((h1 & 0x00F0) >> 4);
byte flags = (byte) (h1 & 0x0F);
if (expectedFlagsOpt != null) {
int expectedFlags = expectedFlagsOpt;
if ((byte) expectedFlags != flags) {
String hexExpected = Integer.toHexString(expectedFlags);
String hexReceived = Integer.toHexString(flags);
throw new CorruptedFrameException(String.format("Received a message with fixed header flags (%s) != expected (%s)", hexReceived, hexExpected));
}
}
boolean dupFlag = ((byte) ((h1 & 0x0008) >> 3) == 1);
byte qosLevel = (byte) ((h1 & 0x0006) >> 1);
boolean retainFlag = ((byte) (h1 & 0x0001) == 1);
int remainingLength = Utils.decodeRemainingLenght(in);
if (remainingLength == -1) {
return false;
}
message.setMessageType(messageType);
message.setDupFlag(dupFlag);
try {
message.setQos(AbstractMessage.QOSType.valueOf(qosLevel));
} catch(IllegalArgumentException e) {
throw new CorruptedFrameException(String.format("Received an invalid QOS: %s", e.getMessage()), e);
}
message.setRetainFlag(retainFlag);
message.setRemainingLength(remainingLength);
return true;
}
}
@@ -0,0 +1,28 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
abstract class DemuxEncoder<T extends AbstractMessage> {
abstract protected void encode(ChannelHandlerContext chc, T msg, ByteBuf bb);
}
@@ -0,0 +1,41 @@
/*
* 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.util.AttributeMap;
import java.util.List;
import io.moquette.proto.messages.DisconnectMessage;
/**
*
* @author andrea
*/
class DisconnectDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
//Common decoding part
in.resetReaderIndex();
DisconnectMessage message = new DisconnectMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
out.add(message);
}
}
@@ -0,0 +1,34 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.DisconnectMessage;
/**
*
* @author andrea
*/
public class DisconnectEncoder extends DemuxEncoder<DisconnectMessage> {
@Override
protected void encode(ChannelHandlerContext chc, DisconnectMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.DISCONNECT << 4).writeByte(0);
}
}
@@ -0,0 +1,73 @@
/*
* 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.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
public class MQTTDecoder extends ByteToMessageDecoder {
//3 = 3.1, 4 = 3.1.1
static final AttributeKey<Integer> PROTOCOL_VERSION = AttributeKey.valueOf("version");
private final Map<Byte, DemuxDecoder> m_decoderMap = new HashMap<>();
public MQTTDecoder() {
m_decoderMap.put(AbstractMessage.CONNECT, new ConnectDecoder());
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());
m_decoderMap.put(AbstractMessage.SUBACK, new SubAckDecoder());
m_decoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeDecoder());
m_decoderMap.put(AbstractMessage.DISCONNECT, new DisconnectDecoder());
m_decoderMap.put(AbstractMessage.PINGREQ, new PingReqDecoder());
m_decoderMap.put(AbstractMessage.PINGRESP, new PingRespDecoder());
m_decoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckDecoder());
m_decoderMap.put(AbstractMessage.PUBCOMP, new PubCompDecoder());
m_decoderMap.put(AbstractMessage.PUBREC, new PubRecDecoder());
m_decoderMap.put(AbstractMessage.PUBREL, new PubRelDecoder());
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
in.markReaderIndex();
if (!Utils.checkHeaderAvailability(in)) {
in.resetReaderIndex();
return;
}
in.resetReaderIndex();
byte messageType = Utils.readMessageType(in);
DemuxDecoder decoder = m_decoderMap.get(messageType);
if (decoder == null) {
throw new CorruptedFrameException("Can't find any suitable decoder for message type: " + messageType);
}
decoder.decode(ctx, in, out);
}
}
@@ -0,0 +1,59 @@
/*
* 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.channel.ChannelHandlerContext;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.MessageToByteEncoder;
import java.util.HashMap;
import java.util.Map;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
public class MQTTEncoder extends MessageToByteEncoder<AbstractMessage> {
private Map<Byte, DemuxEncoder> m_encoderMap = new HashMap<Byte, DemuxEncoder>();
public MQTTEncoder() {
m_encoderMap.put(AbstractMessage.CONNECT, new ConnectEncoder());
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());
m_encoderMap.put(AbstractMessage.SUBACK, new SubAckEncoder());
m_encoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeEncoder());
m_encoderMap.put(AbstractMessage.DISCONNECT, new DisconnectEncoder());
m_encoderMap.put(AbstractMessage.PINGREQ, new PingReqEncoder());
m_encoderMap.put(AbstractMessage.PINGRESP, new PingRespEncoder());
m_encoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckEncoder());
m_encoderMap.put(AbstractMessage.PUBCOMP, new PubCompEncoder());
m_encoderMap.put(AbstractMessage.PUBREC, new PubRecEncoder());
m_encoderMap.put(AbstractMessage.PUBREL, new PubRelEncoder());
}
@Override
protected void encode(ChannelHandlerContext chc, AbstractMessage msg, ByteBuf bb) throws Exception {
DemuxEncoder encoder = m_encoderMap.get(msg.getMessageType());
if (encoder == null) {
throw new CorruptedFrameException("Can't find any suitable decoder for message type: " + msg.getMessageType());
}
encoder.encode(chc, msg, bb);
}
}
@@ -0,0 +1,46 @@
/*
* 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.MessageIDMessage;
import io.netty.buffer.ByteBuf;
import io.netty.util.AttributeMap;
import java.util.List;
/**
*
* @author andrea
*/
abstract class MessageIDDecoder extends DemuxDecoder {
protected abstract MessageIDMessage createMessage();
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
in.resetReaderIndex();
//Common decoding part
MessageIDMessage message = createMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
//read messageIDs
message.setMessageID(in.readUnsignedShort());
out.add(message);
}
}
@@ -0,0 +1,40 @@
/*
* 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.PingReqMessage;
import io.netty.buffer.ByteBuf;
import io.netty.util.AttributeMap;
import java.util.List;
/**
*
* @author andrea
*/
class PingReqDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
//Common decoding part
in.resetReaderIndex();
PingReqMessage message = new PingReqMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
out.add(message);
}
}
@@ -0,0 +1,33 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PingReqMessage;
/**
*
* @author andrea
*/
class PingReqEncoder extends DemuxEncoder<PingReqMessage> {
@Override
protected void encode(ChannelHandlerContext chc, PingReqMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.PINGREQ << 4).writeByte(0);
}
}
@@ -0,0 +1,40 @@
/*
* 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.util.AttributeMap;
import java.util.List;
import io.moquette.proto.messages.PingRespMessage;
/**
*
* @author andrea
*/
class PingRespDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
//Common decoding part
in.resetReaderIndex();
PingRespMessage message = new PingRespMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
out.add(message);
}
}
@@ -0,0 +1,33 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PingRespMessage;
/**
*
* @author andrea
*/
class PingRespEncoder extends DemuxEncoder<PingRespMessage> {
@Override
protected void encode(ChannelHandlerContext chc, PingRespMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.PINGRESP << 4).writeByte(0);
}
}
@@ -0,0 +1,32 @@
/*
* 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.MessageIDMessage;
import io.moquette.proto.messages.PubAckMessage;
/**
*
* @author andrea
*/
class PubAckDecoder extends MessageIDDecoder {
@Override
protected MessageIDMessage createMessage() {
return new PubAckMessage();
}
}
@@ -0,0 +1,42 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PubAckMessage;
/**
*
* @author andrea
*/
class PubAckEncoder extends DemuxEncoder<PubAckMessage> {
@Override
protected void encode(ChannelHandlerContext chc, PubAckMessage msg, ByteBuf out) {
ByteBuf buff = chc.alloc().buffer(4);
try {
buff.writeByte(AbstractMessage.PUBACK << 4);
buff.writeBytes(Utils.encodeRemainingLength(2));
buff.writeShort(msg.getMessageID());
out.writeBytes(buff);
} finally {
buff.release();
}
}
}
@@ -0,0 +1,32 @@
/*
* 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.MessageIDMessage;
import io.moquette.proto.messages.PubCompMessage;
/**
*
* @author andrea
*/
class PubCompDecoder extends MessageIDDecoder {
@Override
protected MessageIDMessage createMessage() {
return new PubCompMessage();
}
}
@@ -0,0 +1,35 @@
/*
* 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.PubCompMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
class PubCompEncoder extends DemuxEncoder<PubCompMessage> {
@Override
protected void encode(ChannelHandlerContext chc, PubCompMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.PUBCOMP << 4);
out.writeBytes(Utils.encodeRemainingLength(2));
out.writeShort(msg.getMessageID());
}
}
@@ -0,0 +1,31 @@
/*
* 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.MessageIDMessage;
import io.moquette.proto.messages.PubRecMessage;
/**
*
* @author andrea
*/
class PubRecDecoder extends MessageIDDecoder {
@Override
protected MessageIDMessage createMessage() {
return new PubRecMessage();
}
}
@@ -0,0 +1,35 @@
/*
* 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.PubRecMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
class PubRecEncoder extends DemuxEncoder<PubRecMessage> {
@Override
protected void encode(ChannelHandlerContext chc, PubRecMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.PUBREC << 4);
out.writeBytes(Utils.encodeRemainingLength(2));
out.writeShort(msg.getMessageID());
}
}
@@ -0,0 +1,47 @@
/*
* 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.MessageIDMessage;
import io.moquette.proto.messages.PubRelMessage;
import io.netty.buffer.ByteBuf;
import io.netty.util.AttributeMap;
import java.io.UnsupportedEncodingException;
import java.util.List;
/**
*
* @author andrea
*/
class PubRelDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws UnsupportedEncodingException {
in.resetReaderIndex();
//Common decoding part
MessageIDMessage message = new PubRelMessage();
if (!decodeCommonHeader(message, 0x02, in)) {
in.resetReaderIndex();
return;
}
//read messageIDs
message.setMessageID(in.readUnsignedShort());
out.add(message);
}
}
@@ -0,0 +1,35 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
class PubRelEncoder extends DemuxEncoder<PubRelMessage> {
@Override
protected void encode(ChannelHandlerContext chc, PubRelMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.PUBREL << 4 | 0x02);
out.writeBytes(Utils.encodeRemainingLength(2));
out.writeShort(msg.getMessageID());
}
}
@@ -0,0 +1,99 @@
/*
* 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 java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PublishMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author andrea
*/
class PublishDecoder extends DemuxDecoder {
private static Logger LOG = LoggerFactory.getLogger(PublishDecoder.class);
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
LOG.debug("decode invoked with buffer {}", in);
in.resetReaderIndex();
int startPos = in.readerIndex();
//Common decoding part
PublishMessage message = new PublishMessage();
if (!decodeCommonHeader(message, in)) {
LOG.debug("decode ask for more data after {}", in);
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
String topic = Utils.decodeString(in);
if (topic == null) {
in.resetReaderIndex();
return;
}
//[MQTT-3.3.2-2] The Topic Name in the PUBLISH Packet MUST NOT contain wildcard characters.
if (topic.contains("+") || topic.contains("#")) {
throw new CorruptedFrameException("Received a PUBLISH with topic containing wild card chars, topic: " + topic);
}
//check topic is at least one char [MQTT-4.7.3-1]
if (topic.length() == 0) {
throw new CorruptedFrameException("Received a PUBLISH with topic without any character");
}
message.setTopicName(topic);
if (message.getQos() == AbstractMessage.QOSType.LEAST_ONE ||
message.getQos() == AbstractMessage.QOSType.EXACTLY_ONCE) {
message.setMessageID(in.readUnsignedShort());
}
int stopPos = in.readerIndex();
//read the payload
int payloadSize = remainingLength - (stopPos - startPos - 2) + (Utils.numBytesToEncode(remainingLength) - 1);
if (in.readableBytes() < payloadSize) {
in.resetReaderIndex();
return;
}
ByteBuf bb = Unpooled.buffer(payloadSize);
in.readBytes(bb);
message.setPayload(bb.nioBuffer());
out.add(message);
}
}
@@ -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.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PublishMessage;
/**
*
* @author andrea
*/
class PublishEncoder extends DemuxEncoder<PublishMessage> {
@Override
protected void encode(ChannelHandlerContext ctx, PublishMessage message, ByteBuf out) {
if (message.getQos() == AbstractMessage.QOSType.RESERVED) {
throw new IllegalArgumentException("Found a message with RESERVED Qos");
}
if (message.getTopicName() == null || message.getTopicName().isEmpty()) {
throw new IllegalArgumentException("Found a message with empty or null topic name");
}
ByteBuf variableHeaderBuff = ctx.alloc().buffer(2);
ByteBuf buff = null;
try {
variableHeaderBuff.writeBytes(Utils.encodeString(message.getTopicName()));
if (message.getQos() == AbstractMessage.QOSType.LEAST_ONE ||
message.getQos() == AbstractMessage.QOSType.EXACTLY_ONCE ) {
if (message.getMessageID() == null) {
throw new IllegalArgumentException("Found a message with QOS 1 or 2 and not MessageID setted");
}
variableHeaderBuff.writeShort(message.getMessageID());
}
variableHeaderBuff.writeBytes(message.getPayload());
int variableHeaderSize = variableHeaderBuff.readableBytes();
byte flags = Utils.encodeFlags(message);
buff = ctx.alloc().buffer(2 + variableHeaderSize);
buff.writeByte(AbstractMessage.PUBLISH << 4 | flags);
buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
buff.writeBytes(variableHeaderBuff);
out.writeBytes(buff);
} finally {
variableHeaderBuff.release();
if (buff != null) {
buff.release();
}
}
}
}
@@ -0,0 +1,58 @@
/*
* 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.util.AttributeMap;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.SubAckMessage;
/**
*
* @author andrea
*/
class SubAckDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
//Common decoding part
in.resetReaderIndex();
SubAckMessage message = new SubAckMessage();
if (!decodeCommonHeader(message, 0x00, in)) {
in.resetReaderIndex();
return;
}
int remainingLength = message.getRemainingLength();
//MessageID
message.setMessageID(in.readUnsignedShort());
remainingLength -= 2;
//Qos array
if (in.readableBytes() < remainingLength ) {
in.resetReaderIndex();
return;
}
for (int i = 0; i < remainingLength; i++) {
byte qos = in.readByte();
message.addType(AbstractMessage.QOSType.valueOf(qos));
}
out.add(message);
}
}
@@ -0,0 +1,52 @@
/*
* 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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.AbstractMessage.QOSType;
import io.moquette.proto.messages.SubAckMessage;
/**
*
* @author andrea
*/
class SubAckEncoder extends DemuxEncoder<SubAckMessage> {
@Override
protected void encode(ChannelHandlerContext chc, SubAckMessage message, ByteBuf out) {
if (message.types().isEmpty()) {
throw new IllegalArgumentException("Found a suback message with empty topics");
}
int variableHeaderSize = 2 + message.types().size();
ByteBuf buff = chc.alloc().buffer(6 + variableHeaderSize);
try {
buff.writeByte(AbstractMessage.SUBACK << 4 );
buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
buff.writeShort(message.getMessageID());
for (QOSType c : message.types()) {
buff.writeByte(c.byteValue());
}
out.writeBytes(buff);
} finally {
buff.release();
}
}
}
@@ -0,0 +1,81 @@
/*
* 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.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import java.io.UnsupportedEncodingException;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage.QOSType;
import io.moquette.proto.messages.SubscribeMessage;
/**
*
* @author andrea
*/
class SubscribeDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
//Common decoding part
SubscribeMessage message = new SubscribeMessage();
in.resetReaderIndex();
if (!decodeCommonHeader(message, 0x02, 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 read = in.readerIndex() - start;
while (read < message.getRemainingLength()) {
decodeSubscription(in, message);
read = in.readerIndex() - start;
}
if (message.subscriptions().isEmpty()) {
throw new CorruptedFrameException("subscribe MUST have got at least 1 couple topic/QoS");
}
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);
//check topic is at least one char [MQTT-4.7.3-1]
if (topic.length() == 0) {
throw new CorruptedFrameException("Received a SUBSCRIBE with empty topic filter");
}
byte qosByte = in.readByte();
if ((qosByte & 0xFC) > 0) { //the first 6 bits is reserved => has to be 0
throw new CorruptedFrameException("subscribe MUST have QoS byte with reserved buts to 0, found " + Integer.toHexString(qosByte));
}
byte qos = (byte)(qosByte & 0x03);
//TODO check qos id 000000xx
message.addSubscription(new SubscribeMessage.Couple(qos, topic));
}
}
@@ -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.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
import io.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);
ByteBuf buff = null;
try {
variableHeaderBuff.writeShort(message.getMessageID());
for (SubscribeMessage.Couple c : message.subscriptions()) {
variableHeaderBuff.writeBytes(Utils.encodeString(c.getTopicFilter()));
variableHeaderBuff.writeByte(c.getQos());
}
int variableHeaderSize = variableHeaderBuff.readableBytes();
byte flags = Utils.encodeFlags(message);
buff = chc.alloc().buffer(2 + variableHeaderSize);
buff.writeByte(AbstractMessage.SUBSCRIBE << 4 | flags);
buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
buff.writeBytes(variableHeaderBuff);
out.writeBytes(buff);
} finally {
variableHeaderBuff.release();
buff.release();
}
}
}
@@ -0,0 +1,32 @@
/*
* 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.MessageIDMessage;
import io.moquette.proto.messages.UnsubAckMessage;
/**
*
* @author andrea
*/
class UnsubAckDecoder extends MessageIDDecoder {
@Override
protected MessageIDMessage createMessage() {
return new UnsubAckMessage();
}
}
@@ -0,0 +1,35 @@
/*
* 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.UnsubAckMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
class UnsubAckEncoder extends DemuxEncoder<UnsubAckMessage> {
@Override
protected void encode(ChannelHandlerContext chc, UnsubAckMessage msg, ByteBuf out) {
out.writeByte(AbstractMessage.UNSUBACK << 4).
writeBytes(Utils.encodeRemainingLength(2)).
writeShort(msg.getMessageID());
}
}
@@ -0,0 +1,65 @@
/*
* 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.UnsubscribeMessage;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import java.util.List;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
class UnsubscribeDecoder extends DemuxDecoder {
@Override
void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception {
//Common decoding part
in.resetReaderIndex();
UnsubscribeMessage message = new UnsubscribeMessage();
if (!decodeCommonHeader(message, 0x02, in)) {
in.resetReaderIndex();
return;
}
//check qos level
if (message.getQos() != AbstractMessage.QOSType.LEAST_ONE) {
throw new CorruptedFrameException("Found an Unsubscribe message with qos other than LEAST_ONE, was: " + message.getQos());
}
int start = in.readerIndex();
//read messageIDs
message.setMessageID(in.readUnsignedShort());
int read = in.readerIndex() - start;
while (read < message.getRemainingLength()) {
String topicFilter = Utils.decodeString(in);
//check topic is at least one char [MQTT-4.7.3-1]
if (topicFilter.length() == 0) {
throw new CorruptedFrameException("Received an UNSUBSCRIBE with empty topic filter");
}
message.addTopicFilter(topicFilter);
read = in.readerIndex() - start;
}
if (message.topicFilters().isEmpty()) {
throw new CorruptedFrameException("unsubscribe MUST have got at least 1 topic");
}
out.add(message);
}
}
@@ -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.moquette.proto.messages.UnsubscribeMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.moquette.proto.messages.AbstractMessage;
/**
*
* @author andrea
*/
class UnsubscribeEncoder extends DemuxEncoder<UnsubscribeMessage> {
@Override
protected void encode(ChannelHandlerContext chc, UnsubscribeMessage message, ByteBuf out) {
if (message.topicFilters().isEmpty()) {
throw new IllegalArgumentException("Found an unsubscribe 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);
ByteBuf buff = null;
try {
variableHeaderBuff.writeShort(message.getMessageID());
for (String topic : message.topicFilters()) {
variableHeaderBuff.writeBytes(Utils.encodeString(topic));
}
int variableHeaderSize = variableHeaderBuff.readableBytes();
byte flags = Utils.encodeFlags(message);
buff = chc.alloc().buffer(2 + variableHeaderSize);
buff.writeByte(AbstractMessage.UNSUBSCRIBE << 4 | flags);
buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
buff.writeBytes(variableHeaderBuff);
out.writeBytes(buff);
} finally {
variableHeaderBuff.release();
buff.release();
}
}
}
@@ -0,0 +1,199 @@
/*
* 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.Attribute;
import io.netty.util.AttributeMap;
import java.io.UnsupportedEncodingException;
import io.moquette.proto.messages.AbstractMessage;
import org.slf4j.LoggerFactory;
/**
*
* @author andrea
*/
public class Utils {
public static final int MAX_LENGTH_LIMIT = 268435455;
public static final byte VERSION_3_1 = 3;
public static final byte VERSION_3_1_1 = 4;
static byte readMessageType(ByteBuf in) {
byte h1 = in.readByte();
byte messageType = (byte) ((h1 & 0x00F0) >> 4);
return messageType;
}
static boolean checkHeaderAvailability(ByteBuf in) {
if (in.readableBytes() < 1) {
return false;
}
//byte h1 = in.get();
//byte messageType = (byte) ((h1 & 0x00F0) >> 4);
in.skipBytes(1); //skip the messageType byte
int remainingLength = Utils.decodeRemainingLenght(in);
if (remainingLength == -1) {
return false;
}
//check remaining length
if (in.readableBytes() < remainingLength) {
return false;
}
//return messageType == type ? MessageDecoderResult.OK : MessageDecoderResult.NOT_OK;
return true;
}
/**
* Decode the variable remaining length as defined in MQTT v3.1 specification
* (section 2.1).
*
* @return the decoded length or -1 if needed more data to decode the length field.
*/
static int decodeRemainingLenght(ByteBuf in) {
int multiplier = 1;
int value = 0;
byte digit;
do {
if (in.readableBytes() < 1) {
return -1;
}
digit = in.readByte();
value += (digit & 0x7F) * multiplier;
multiplier *= 128;
} while ((digit & 0x80) != 0);
return value;
}
/**
* Encode the value in the format defined in specification as variable length
* array.
*
* @throws IllegalArgumentException if the value is not in the specification bounds
* [0..268435455].
*/
static ByteBuf encodeRemainingLength(int value) throws CorruptedFrameException {
if (value > MAX_LENGTH_LIMIT || value < 0) {
throw new CorruptedFrameException("Value should in range 0.." + MAX_LENGTH_LIMIT + " found " + value);
}
ByteBuf encoded = Unpooled.buffer(4);
byte digit;
do {
digit = (byte) (value % 128);
value = value / 128;
// if there are more digits to encode, set the top bit of this digit
if (value > 0) {
digit = (byte) (digit | 0x80);
}
encoded.writeByte(digit);
} while (value > 0);
return encoded;
}
/**
* Load a string from the given buffer, reading first the two bytes of len
* and then the UTF-8 bytes of the string.
*
* @return the decoded string or null if NEED_DATA
*/
static String decodeString(ByteBuf in) throws UnsupportedEncodingException {
return new String(readFixedLengthContent(in), "UTF-8");
}
/**
* Read a byte array from the buffer, use two bytes as length information followed by length bytes.
* */
static byte[] readFixedLengthContent(ByteBuf in) throws UnsupportedEncodingException {
if (in.readableBytes() < 2) {
return null;
}
int strLen = in.readUnsignedShort();
if (in.readableBytes() < strLen) {
return null;
}
byte[] strRaw = new byte[strLen];
in.readBytes(strRaw);
return strRaw;
}
/**
* Return the IoBuffer with string encoded as MSB, LSB and UTF-8 encoded
* string content.
*/
public static ByteBuf encodeString(String str) {
byte[] raw;
try {
raw = str.getBytes("UTF-8");
//NB every Java platform has got UTF-8 encoding by default, so this
//exception are never raised.
} catch (UnsupportedEncodingException ex) {
LoggerFactory.getLogger(Utils.class).error(null, ex);
return null;
}
return encodeFixedLengthContent(raw);
}
/**
* Return the IoBuffer with string encoded as MSB, LSB and bytes array content.
*/
public static ByteBuf encodeFixedLengthContent(byte[] content) {
ByteBuf out = Unpooled.buffer(2);
out.writeShort(content.length);
out.writeBytes(content);
return out;
}
/**
* Return the number of bytes to encode the given remaining length value
*/
static int numBytesToEncode(int len) {
if (0 <= len && len <= 127) return 1;
if (128 <= len && len <= 16383) return 2;
if (16384 <= len && len <= 2097151) return 3;
if (2097152 <= len && len <= 268435455) return 4;
throw new IllegalArgumentException("value shoul be in the range [0..268435455]");
}
static byte encodeFlags(AbstractMessage message) {
byte flags = 0;
if (message.isDupFlag()) {
flags |= 0x08;
}
if (message.isRetainFlag()) {
flags |= 0x01;
}
flags |= ((message.getQos().byteValue() & 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;
}
}