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