Removed Moquette parser module
This commit is contained in:
@@ -25,16 +25,7 @@ import java.nio.ByteBuffer;
|
|||||||
*/
|
*/
|
||||||
class DebugUtils {
|
class DebugUtils {
|
||||||
|
|
||||||
static String payload2Str(ByteBuffer content) {
|
|
||||||
byte[] b = new byte[content.remaining()];
|
|
||||||
content.mark();
|
|
||||||
content.get(b);
|
|
||||||
content.reset();
|
|
||||||
return new String(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String payload2Str(ByteBuf content) {
|
static String payload2Str(ByteBuf content) {
|
||||||
// byte[] b = new byte[content.readableBytes()];
|
return new String(content.copy ().array());
|
||||||
return new String(content.copy().array());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,6 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
|
||||||
<artifactId>moquette-netty-parser</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<relativePath>../</relativePath>
|
|
||||||
<artifactId>moquette-parent</artifactId>
|
|
||||||
<groupId>io.moquette</groupId>
|
|
||||||
<version>0.10-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>moquette-netty-parser</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
<name>Moquette - Netty Parser</name>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<bintray.package>io.moquette.moquette-netty-parser</bintray.package>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-common</artifactId>
|
|
||||||
<version>${netty.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-buffer</artifactId>
|
|
||||||
<version>${netty.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-transport</artifactId>
|
|
||||||
<version>${netty.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-handler</artifactId>
|
|
||||||
<version>${netty.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
<version>1.6.4</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-log4j12</artifactId>
|
|
||||||
<version>1.6.4</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.8.2</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- needs extra dependencies: objenesis & hamcrest -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-core</artifactId>
|
|
||||||
<version>1.9.0</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.hdrhistogram</groupId>
|
|
||||||
<artifactId>HdrHistogram</artifactId>
|
|
||||||
<version>2.1.2</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.paho</groupId>
|
|
||||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
|
||||||
<version>1.1.0</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
|
||||||
<artifactId>jetty-perf-helper</artifactId>
|
|
||||||
<version>1.0.5</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<!--<dependency>-->
|
|
||||||
<!--<groupId>org.eclipse.paho</groupId>-->
|
|
||||||
<!--<artifactId>org.eclipse.paho.client.mqttv3</artifactId>-->
|
|
||||||
<!--<version>1.0.2</version>-->
|
|
||||||
<!--<scope>test</scope>-->
|
|
||||||
<!--</dependency>-->
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<excludes>
|
|
||||||
<exclude>io/moquette/parser/netty/TestUtils.java</exclude>
|
|
||||||
<exclude>io/moquette/parser/netty/TestUtils$*</exclude>
|
|
||||||
</excludes>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- Includes to generate OSGi headers -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.felix</groupId>
|
|
||||||
<artifactId>maven-bundle-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>bundle-manifest</id>
|
|
||||||
<phase>process-classes</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>manifest</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<distributionManagement>
|
|
||||||
<repository>
|
|
||||||
<id>bintray</id>
|
|
||||||
<url>https://api.bintray.com/maven/${bintray.repo}/${bintray.package}</url>
|
|
||||||
</repository>
|
|
||||||
</distributionManagement>
|
|
||||||
</project>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.commons;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains some useful constants.
|
|
||||||
*/
|
|
||||||
public class Constants {
|
|
||||||
public static final int DEFAULT_CONNECT_TIMEOUT = 10;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.util.AttributeMap;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.ConnectMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
abstract class DemuxEncoder<T extends AbstractMessage> {
|
|
||||||
abstract protected void encode(ChannelHandlerContext chc, T msg, ByteBuf bb);
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.DisconnectMessage;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
class DisconnectDecoder extends DemuxDecoder {
|
|
||||||
|
|
||||||
private static Logger LOG = LoggerFactory.getLogger(DisconnectDecoder.class);
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
LOG.debug("Decoding disconnect");
|
|
||||||
out.add(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.MessageIDMessage;
|
|
||||||
import io.moquette.parser.proto.messages.PubAckMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
class PubAckDecoder extends MessageIDDecoder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MessageIDMessage createMessage() {
|
|
||||||
return new PubAckMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.MessageIDMessage;
|
|
||||||
import io.moquette.parser.proto.messages.PubCompMessage;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
class PubCompDecoder extends MessageIDDecoder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MessageIDMessage createMessage() {
|
|
||||||
return new PubCompMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.PubCompMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.MessageIDMessage;
|
|
||||||
import io.moquette.parser.proto.messages.PubRecMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
class PubRecDecoder extends MessageIDDecoder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MessageIDMessage createMessage() {
|
|
||||||
return new PubRecMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.PubRecMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.MessageIDMessage;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.PubRelMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage.QOSType;
|
|
||||||
import io.moquette.parser.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage.QOSType;
|
|
||||||
import io.moquette.parser.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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.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.topicFilter));
|
|
||||||
variableHeaderBuff.writeByte(c.qos);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.MessageIDMessage;
|
|
||||||
import io.moquette.parser.proto.messages.UnsubAckMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
class UnsubAckDecoder extends MessageIDDecoder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MessageIDMessage createMessage() {
|
|
||||||
return new UnsubAckMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.UnsubAckMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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.parser.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.UnsubscribeMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class MQTTException extends RuntimeException {
|
|
||||||
|
|
||||||
public MQTTException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MQTTException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MQTTException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MQTTException(String msg, Throwable cause) {
|
|
||||||
super(msg, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto;
|
|
||||||
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Common utils methodd used in codecs.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class Utils {
|
|
||||||
|
|
||||||
public static final int MAX_LENGTH_LIMIT = 268435455;
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Read 2 bytes from in buffer first MSB, and then LSB returning as int.
|
|
||||||
// */
|
|
||||||
// static int readWord(IoBuffer in) {
|
|
||||||
// int msb = in.get() & 0x00FF; //remove sign extension due to casting
|
|
||||||
// int lsb = in.get() & 0x00FF;
|
|
||||||
// msb = (msb << 8) | lsb ;
|
|
||||||
// return msb;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Writes as 2 bytes the int value into buffer first MSB, and then LSB.
|
|
||||||
// */
|
|
||||||
// static void writeWord(IoBuffer out, int value) {
|
|
||||||
// out.put((byte) ((value & 0xFF00) >> 8)); //msb
|
|
||||||
// out.put((byte) (value & 0x00FF)); //lsb
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Decode the variable remaining lenght 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(IoBuffer in) {
|
|
||||||
// int multiplier = 1;
|
|
||||||
// int value = 0;
|
|
||||||
// byte digit;
|
|
||||||
// do {
|
|
||||||
// if (in.remaining() < 1) {
|
|
||||||
// return -1;
|
|
||||||
// }
|
|
||||||
// digit = in.get();
|
|
||||||
// value += (digit & 0x7F) * multiplier;
|
|
||||||
// multiplier *= 128;
|
|
||||||
// } while ((digit & 0x80) != 0);
|
|
||||||
// return value;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of bytes to encode the gicen 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]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 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 IoBuffer encodeRemainingLength(int value) throws IllegalAccessException {
|
|
||||||
// if (value > MAX_LENGTH_LIMIT || value < 0) {
|
|
||||||
// throw new IllegalAccessException("Value should in range 0.." + MAX_LENGTH_LIMIT + " found " + value);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// IoBuffer encoded = IoBuffer.allocate(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.put(digit);
|
|
||||||
// } while (value > 0);
|
|
||||||
// encoded.flip();
|
|
||||||
// return encoded;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static MessageDecoderResult checkDecodable(byte type, IoBuffer in) {
|
|
||||||
// if (in.remaining() < 1) {
|
|
||||||
// return MessageDecoderResult.NEED_DATA;
|
|
||||||
// }
|
|
||||||
// byte h1 = in.get();
|
|
||||||
// byte messageType = (byte) ((h1 & 0x00F0) >> 4);
|
|
||||||
//
|
|
||||||
// int remainingLength = Utils.decodeRemainingLenght(in);
|
|
||||||
// if (remainingLength == -1) {
|
|
||||||
// return MessageDecoderResult.NEED_DATA;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// //check remaining length
|
|
||||||
// if (in.remaining() < remainingLength) {
|
|
||||||
// return MessageDecoderResult.NEED_DATA;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return messageType == type ? MessageDecoderResult.OK : MessageDecoderResult.NOT_OK;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Return the IoBuffer with string encoded as MSB, LSB and UTF-8 encoded
|
|
||||||
// * string content.
|
|
||||||
// */
|
|
||||||
// static IoBuffer encodeString(String str) {
|
|
||||||
// IoBuffer out = IoBuffer.allocate(2).setAutoExpand(true);
|
|
||||||
// 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(ConnectEncoder.class).error(null, ex);
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// Utils.writeWord(out, raw.length);
|
|
||||||
// out.put(raw).flip();
|
|
||||||
// return out;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 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(IoBuffer in) throws UnsupportedEncodingException {
|
|
||||||
// if (in.remaining() < 2) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// int strLen = Utils.readWord(in);
|
|
||||||
// if (in.remaining() < strLen) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// byte[] strRaw = new byte[strLen];
|
|
||||||
// in.get(strRaw);
|
|
||||||
//
|
|
||||||
// return new String(strRaw, "UTF-8");
|
|
||||||
// }
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts MQTT message type to a textual description.
|
|
||||||
* */
|
|
||||||
public static String msgType2String(int type) {
|
|
||||||
switch (type) {
|
|
||||||
case AbstractMessage.CONNECT: return "CONNECT";
|
|
||||||
case AbstractMessage.CONNACK: return "CONNACK";
|
|
||||||
case AbstractMessage.PUBLISH: return "PUBLISH";
|
|
||||||
case AbstractMessage.PUBACK: return "PUBACK";
|
|
||||||
case AbstractMessage.PUBREC: return "PUBREC";
|
|
||||||
case AbstractMessage.PUBREL: return "PUBREL";
|
|
||||||
case AbstractMessage.PUBCOMP: return "PUBCOMP";
|
|
||||||
case AbstractMessage.SUBSCRIBE: return "SUBSCRIBE";
|
|
||||||
case AbstractMessage.SUBACK: return "SUBACK";
|
|
||||||
case AbstractMessage.UNSUBSCRIBE: return "UNSUBSCRIBE";
|
|
||||||
case AbstractMessage.UNSUBACK: return "UNSUBACK";
|
|
||||||
case AbstractMessage.PINGREQ: return "PINGREQ";
|
|
||||||
case AbstractMessage.PINGRESP: return "PINGRESP";
|
|
||||||
case AbstractMessage.DISCONNECT: return "DISCONNECT";
|
|
||||||
default: throw new RuntimeException("Can't decode message type " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic abstract message for all MQTT protocol messages.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public abstract class AbstractMessage {
|
|
||||||
|
|
||||||
public static final byte CONNECT = 1; // Client request to connect to Server
|
|
||||||
public static final byte CONNACK = 2; // Connect Acknowledgment
|
|
||||||
public static final byte PUBLISH = 3; // Publish message
|
|
||||||
public static final byte PUBACK = 4; // Publish Acknowledgment
|
|
||||||
public static final byte PUBREC = 5; //Publish Received (assured delivery part 1)
|
|
||||||
public static final byte PUBREL = 6; // Publish Release (assured delivery part 2)
|
|
||||||
public static final byte PUBCOMP = 7; //Publish Complete (assured delivery part 3)
|
|
||||||
public static final byte SUBSCRIBE = 8; //Client Subscribe request
|
|
||||||
public static final byte SUBACK = 9; // Subscribe Acknowledgment
|
|
||||||
public static final byte UNSUBSCRIBE = 10; //Client Unsubscribe request
|
|
||||||
public static final byte UNSUBACK = 11; // Unsubscribe Acknowledgment
|
|
||||||
public static final byte PINGREQ = 12; //PING Request
|
|
||||||
public static final byte PINGRESP = 13; //PING Response
|
|
||||||
public static final byte DISCONNECT = 14; //Client is Disconnecting
|
|
||||||
|
|
||||||
public enum QOSType {
|
|
||||||
MOST_ONE, LEAST_ONE, EXACTLY_ONCE, RESERVED, FAILURE;
|
|
||||||
|
|
||||||
public static QOSType valueOf(byte qos){
|
|
||||||
switch(qos) {
|
|
||||||
case 0x00:
|
|
||||||
return MOST_ONE;
|
|
||||||
case 0x01:
|
|
||||||
return LEAST_ONE;
|
|
||||||
case 0x02:
|
|
||||||
return EXACTLY_ONCE;
|
|
||||||
case (byte) 0x80:
|
|
||||||
return FAILURE;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid QOS Type. Expected either 0, 1, 2, or 0x80. Given: " + qos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte byteValue() {
|
|
||||||
switch(this) {
|
|
||||||
case MOST_ONE:
|
|
||||||
return 0;
|
|
||||||
case LEAST_ONE:
|
|
||||||
return 1;
|
|
||||||
case EXACTLY_ONCE:
|
|
||||||
return 2;
|
|
||||||
case FAILURE:
|
|
||||||
return (byte) 0x80;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Cannot give byteValue of QOSType: " + this.name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String formatQoS(QOSType qos) {
|
|
||||||
return String.format("%d - %s", qos.byteValue(), qos.name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//type
|
|
||||||
protected boolean m_dupFlag;
|
|
||||||
protected QOSType m_qos;
|
|
||||||
protected boolean m_retainFlag;
|
|
||||||
protected int m_remainingLength;
|
|
||||||
protected byte m_messageType;
|
|
||||||
|
|
||||||
public byte getMessageType() {
|
|
||||||
return m_messageType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessageType(byte messageType) {
|
|
||||||
this.m_messageType = messageType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDupFlag() {
|
|
||||||
return m_dupFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDupFlag(boolean dupFlag) {
|
|
||||||
this.m_dupFlag = dupFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QOSType getQos() {
|
|
||||||
return m_qos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQos(QOSType qos) {
|
|
||||||
this.m_qos = qos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRetainFlag() {
|
|
||||||
return m_retainFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRetainFlag(boolean retainFlag) {
|
|
||||||
this.m_retainFlag = retainFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TOBE used only internally
|
|
||||||
*/
|
|
||||||
public int getRemainingLength() {
|
|
||||||
return m_remainingLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TOBE used only internally
|
|
||||||
*/
|
|
||||||
public void setRemainingLength(int remainingLength) {
|
|
||||||
this.m_remainingLength = remainingLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes Qos, Dup and Retain aren't used.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class ConnAckMessage extends AbstractMessage {
|
|
||||||
public static final byte CONNECTION_ACCEPTED = 0x00;
|
|
||||||
public static final byte UNNACEPTABLE_PROTOCOL_VERSION = 0x01;
|
|
||||||
public static final byte IDENTIFIER_REJECTED = 0x02;
|
|
||||||
public static final byte SERVER_UNAVAILABLE = 0x03;
|
|
||||||
public static final byte BAD_USERNAME_OR_PASSWORD = 0x04;
|
|
||||||
public static final byte NOT_AUTHORIZED = 0x05;
|
|
||||||
|
|
||||||
private byte m_returnCode;
|
|
||||||
private boolean sessionPresent;
|
|
||||||
|
|
||||||
public ConnAckMessage() {
|
|
||||||
m_messageType = CONNACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getReturnCode() {
|
|
||||||
return m_returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReturnCode(byte returnCode) {
|
|
||||||
this.m_returnCode = returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSessionPresent() {
|
|
||||||
return this.sessionPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSessionPresent(boolean present) {
|
|
||||||
this.sessionPresent = present;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes Qos, Dup and Retain aren't used for Connect message
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class ConnectMessage extends AbstractMessage {
|
|
||||||
protected String m_protocolName;
|
|
||||||
protected byte m_protocolVersion;
|
|
||||||
|
|
||||||
//Connection flags
|
|
||||||
protected boolean m_cleanSession;
|
|
||||||
protected boolean m_willFlag;
|
|
||||||
protected byte m_willQos;
|
|
||||||
protected boolean m_willRetain;
|
|
||||||
protected boolean m_passwordFlag;
|
|
||||||
protected boolean m_userFlag;
|
|
||||||
protected int m_keepAlive;
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
protected String m_username;
|
|
||||||
protected byte[] m_password;
|
|
||||||
protected String m_clientID;
|
|
||||||
protected String m_willtopic;
|
|
||||||
protected byte[] m_willMessage;
|
|
||||||
|
|
||||||
public ConnectMessage() {
|
|
||||||
m_messageType = CONNECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCleanSession() {
|
|
||||||
return m_cleanSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCleanSession(boolean cleanSession) {
|
|
||||||
this.m_cleanSession = cleanSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getKeepAlive() {
|
|
||||||
return m_keepAlive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeepAlive(int keepAlive) {
|
|
||||||
this.m_keepAlive = keepAlive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPasswordFlag() {
|
|
||||||
return m_passwordFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPasswordFlag(boolean passwordFlag) {
|
|
||||||
this.m_passwordFlag = passwordFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getProtocolVersion() {
|
|
||||||
return m_protocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProtocolVersion(byte protocolVersion) {
|
|
||||||
this.m_protocolVersion = protocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProtocolName() {
|
|
||||||
return m_protocolName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProtocolName(String protocolName) {
|
|
||||||
this.m_protocolName = protocolName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUserFlag() {
|
|
||||||
return m_userFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserFlag(boolean userFlag) {
|
|
||||||
this.m_userFlag = userFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWillFlag() {
|
|
||||||
return m_willFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWillFlag(boolean willFlag) {
|
|
||||||
this.m_willFlag = willFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getWillQos() {
|
|
||||||
return m_willQos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWillQos(byte willQos) {
|
|
||||||
this.m_willQos = willQos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWillRetain() {
|
|
||||||
return m_willRetain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWillRetain(boolean willRetain) {
|
|
||||||
this.m_willRetain = willRetain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getPassword() {
|
|
||||||
return m_password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(byte[] password) {
|
|
||||||
this.m_password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return m_username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.m_username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientID() {
|
|
||||||
return m_clientID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientID(String clientID) {
|
|
||||||
this.m_clientID = clientID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWillTopic() {
|
|
||||||
return m_willtopic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWillTopic(String topic) {
|
|
||||||
this.m_willtopic = topic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getWillMessage() {
|
|
||||||
return m_willMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWillMessage(byte[] willMessage) {
|
|
||||||
this.m_willMessage = willMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String base = String.format("Connect [clientID: %s, prot: %s, ver: %02X, clean: %b]", m_clientID, m_protocolName, m_protocolVersion, m_cleanSession);
|
|
||||||
if (m_willFlag) {
|
|
||||||
base += String.format(" Will [QoS: %d, retain: %b]", m_willQos, m_willRetain);
|
|
||||||
}
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doesn't care DUP, QOS and RETAIN flags.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class DisconnectMessage extends ZeroLengthMessage {
|
|
||||||
|
|
||||||
public DisconnectMessage() {
|
|
||||||
m_messageType = DISCONNECT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for alla the messages that carries only MessageID. (PUBACK, PUBREC,
|
|
||||||
* PUBREL, PUBCOMP, UNSUBACK)
|
|
||||||
*
|
|
||||||
* The flags dup, QOS and Retained doesn't take care.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public abstract class MessageIDMessage extends AbstractMessage {
|
|
||||||
protected Integer m_messageID; //could be null if Qos is == 0
|
|
||||||
|
|
||||||
public Integer getMessageID() {
|
|
||||||
return m_messageID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessageID(Integer messageID) {
|
|
||||||
this.m_messageID = messageID;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doesn't care DUP, QOS and RETAIN flags.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PingReqMessage extends ZeroLengthMessage {
|
|
||||||
|
|
||||||
public PingReqMessage() {
|
|
||||||
m_messageType = PINGREQ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doesn't care DUP, QOS and RETAIN flags.
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PingRespMessage extends ZeroLengthMessage {
|
|
||||||
|
|
||||||
public PingRespMessage() {
|
|
||||||
m_messageType = PINGRESP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Placeholder for PUBACK message.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubAckMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
public PubAckMessage() {
|
|
||||||
m_messageType = PUBACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubCompMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
public PubCompMessage() {
|
|
||||||
m_messageType = PUBCOMP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Placeholder for PUBREC message.
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubRecMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
public PubRecMessage() {
|
|
||||||
m_messageType = PUBREC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubRelMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
public PubRelMessage() {
|
|
||||||
m_messageType = PUBREL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PublishMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
private boolean local = true;
|
|
||||||
private String clientId;
|
|
||||||
|
|
||||||
protected String m_topicName;
|
|
||||||
protected ByteBuffer m_payload;
|
|
||||||
|
|
||||||
public PublishMessage() {
|
|
||||||
m_messageType = AbstractMessage.PUBLISH;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTopicName() {
|
|
||||||
return m_topicName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTopicName(String topicName) {
|
|
||||||
this.m_topicName = topicName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer getPayload() {
|
|
||||||
return m_payload.duplicate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPayload(ByteBuffer payload) {
|
|
||||||
this.m_payload = payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLocal() {
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLocal(boolean local) {
|
|
||||||
this.local = local;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientId() {
|
|
||||||
return clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientId(String clientId) {
|
|
||||||
this.clientId = clientId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class SubAckMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
List<QOSType> m_types = new ArrayList<QOSType>();
|
|
||||||
|
|
||||||
public SubAckMessage() {
|
|
||||||
m_messageType = SUBACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<QOSType> types() {
|
|
||||||
return m_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addType(QOSType type) {
|
|
||||||
m_types.add(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class SubscribeMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
public static class Couple {
|
|
||||||
|
|
||||||
public final byte qos;
|
|
||||||
public final String topicFilter;
|
|
||||||
|
|
||||||
public Couple(byte qos, String topic) {
|
|
||||||
this.qos = qos;
|
|
||||||
this.topicFilter = topic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[qos=" + qos +
|
|
||||||
", topicFilter='" + topicFilter + '\'' +
|
|
||||||
']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private List<Couple> m_subscriptions = new ArrayList<>();
|
|
||||||
|
|
||||||
public SubscribeMessage() {
|
|
||||||
//Subscribe has always QoS 1
|
|
||||||
m_messageType = AbstractMessage.SUBSCRIBE;
|
|
||||||
m_qos = AbstractMessage.QOSType.LEAST_ONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Couple> subscriptions() {
|
|
||||||
return m_subscriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSubscription(Couple subscription) {
|
|
||||||
m_subscriptions.add(subscription);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class UnsubAckMessage extends MessageIDMessage {
|
|
||||||
|
|
||||||
public UnsubAckMessage() {
|
|
||||||
m_messageType = UNSUBACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class UnsubscribeMessage extends MessageIDMessage {
|
|
||||||
List<String> m_types = new ArrayList<String>();
|
|
||||||
|
|
||||||
public UnsubscribeMessage() {
|
|
||||||
m_messageType = UNSUBSCRIBE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> topicFilters() {
|
|
||||||
return m_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTopicFilter(String type) {
|
|
||||||
m_types.add(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.proto.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public abstract class ZeroLengthMessage extends AbstractMessage {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.ConnAckMessage;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class ConnAckDecoderTest {
|
|
||||||
ByteBuf m_buff;
|
|
||||||
ConnAckDecoder m_msgdec;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new ConnAckDecoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeader() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
initHeader(m_buff);
|
|
||||||
List<Object> results = new ArrayList<Object >();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(null, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
ConnAckMessage message = (ConnAckMessage)results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, message.getReturnCode());
|
|
||||||
assertEquals(AbstractMessage.CONNACK, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeader(ByteBuf buff) {
|
|
||||||
buff.clear().writeByte(AbstractMessage.CONNACK << 4).writeByte(2);
|
|
||||||
//reserved
|
|
||||||
buff.writeByte((byte)0);
|
|
||||||
//return code
|
|
||||||
buff.writeByte(ConnAckMessage.CONNECTION_ACCEPTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.proto.messages.ConnAckMessage;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class ConnAckEncoderTest {
|
|
||||||
|
|
||||||
ConnAckEncoder m_encoder = new ConnAckEncoder();
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = mock(ChannelHandlerContext.class);
|
|
||||||
ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
|
|
||||||
when(m_mockedContext.alloc()).thenReturn(allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeaderEncode() throws Exception {
|
|
||||||
ConnAckMessage msg = new ConnAckMessage();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x20, out.readByte()); //1 byte
|
|
||||||
assertEquals(0x02, out.readByte()); //2 byte, length
|
|
||||||
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, out.skipBytes(1).readByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.ConnectMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import io.netty.util.Attribute;
|
|
||||||
import io.netty.util.AttributeMap;
|
|
||||||
import io.netty.util.DefaultAttributeMap;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import static io.moquette.parser.netty.Utils.VERSION_3_1;
|
|
||||||
import static io.moquette.parser.netty.Utils.VERSION_3_1_1;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
|
|
||||||
public class ConnectDecoderTest {
|
|
||||||
|
|
||||||
ByteBuf m_buff;
|
|
||||||
ConnectDecoder m_msgdec;
|
|
||||||
AttributeMap attrMap;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new ConnectDecoder();
|
|
||||||
attrMap = new DefaultAttributeMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBaseHeader() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
initBaseHeader(m_buff);
|
|
||||||
List<Object> results = new ArrayList<Object >();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
verifyBaseHeader((ConnectMessage)results.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBaseHeader_311() throws UnsupportedEncodingException {
|
|
||||||
m_buff = Unpooled.buffer(12);
|
|
||||||
initBaseHeader311(m_buff);
|
|
||||||
List<Object> results = new ArrayList<>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
verifyBaseHeader311((ConnectMessage)results.get(0));
|
|
||||||
Attribute<Integer> attr = this.attrMap.attr(MQTTDecoder.PROTOCOL_VERSION);
|
|
||||||
assertEquals(VERSION_3_1_1, attr.get().intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testBaseHeader_311_withFlagsTouched() throws UnsupportedEncodingException {
|
|
||||||
m_buff = Unpooled.buffer(12);
|
|
||||||
initBaseHeader311_withFixedFlags(m_buff, (byte) 0x01);
|
|
||||||
List<Object> results = new ArrayList<Object>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
verifyBaseHeader311((ConnectMessage)results.get(0));
|
|
||||||
Attribute<Integer> attr = this.attrMap.attr(MQTTDecoder.PROTOCOL_VERSION);
|
|
||||||
assertEquals(VERSION_3_1_1, attr.get().intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testDoubleConnectInTheSameSession() throws Exception {
|
|
||||||
//setup the connection session as already connected
|
|
||||||
Attribute<Boolean> connectAttr = this.attrMap.attr(ConnectDecoder.CONNECT_STATUS);
|
|
||||||
connectAttr.set(true);
|
|
||||||
|
|
||||||
m_buff = Unpooled.buffer(12);
|
|
||||||
initBaseHeader311(m_buff);
|
|
||||||
List<Object> results = new ArrayList<Object>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testConnectFlags_311_withNot0Reserved() throws UnsupportedEncodingException {
|
|
||||||
m_buff = Unpooled.buffer(12);
|
|
||||||
initBaseHeader311_withFixedFlags(m_buff, (byte) 0, (byte) 0xCF); // sets the bit(0) = 1
|
|
||||||
List<Object> results = new ArrayList<Object>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
verifyBaseHeader311((ConnectMessage)results.get(0));
|
|
||||||
Attribute<Integer> attr = this.attrMap.attr(MQTTDecoder.PROTOCOL_VERSION);
|
|
||||||
assertEquals(VERSION_3_1_1, attr.get().intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBaseHeader_ClientID() throws UnsupportedEncodingException, Exception {
|
|
||||||
m_buff = Unpooled.buffer(40);
|
|
||||||
initHeader(m_buff, (byte) 38);
|
|
||||||
encodeString(m_buff, "ABCDEFGH");
|
|
||||||
encodeString(m_buff, "Topic");
|
|
||||||
encodeString(m_buff, "Message");
|
|
||||||
List<Object> results = new ArrayList<Object >();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
ConnectMessage message = (ConnectMessage)results.get(0);
|
|
||||||
verifyBaseHeader(message);
|
|
||||||
assertEquals("ABCDEFGH", message.getClientID());
|
|
||||||
assertEquals("Topic", message.getWillTopic());
|
|
||||||
assertEquals("Message", new String(message.getWillMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBaseHeader_extra_with_user_pwd() throws UnsupportedEncodingException, Exception {
|
|
||||||
m_buff = Unpooled.buffer(55);
|
|
||||||
initHeader(m_buff, (byte) 53);
|
|
||||||
encodeString(m_buff, "ABCDEFGH");
|
|
||||||
encodeString(m_buff, "Topic");
|
|
||||||
encodeString(m_buff, "Message");
|
|
||||||
encodeString(m_buff, "Fakeuser");
|
|
||||||
encodeString(m_buff, "pwd");
|
|
||||||
List<Object> results = new ArrayList<Object >();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
ConnectMessage message = (ConnectMessage)results.get(0);
|
|
||||||
verifyBaseHeader(message);
|
|
||||||
assertEquals(AbstractMessage.CONNECT, message.getMessageType());
|
|
||||||
assertEquals("ABCDEFGH", message.getClientID());
|
|
||||||
assertEquals("Topic", message.getWillTopic());
|
|
||||||
assertEquals("Message", new String(message.getWillMessage()));
|
|
||||||
assertEquals("Fakeuser", message.getUsername());
|
|
||||||
assertEquals("pwd", new String(message.getPassword()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testBadFlagUserPwd() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
m_buff.writeByte((AbstractMessage.CONNECT << 4)).writeByte(12);
|
|
||||||
//Proto name
|
|
||||||
encodeString(m_buff, "MQIsdp");
|
|
||||||
//version
|
|
||||||
m_buff.writeByte(VERSION_3_1);
|
|
||||||
//conn flags
|
|
||||||
m_buff.writeByte(0x4E); //sets user to false and password to true
|
|
||||||
//keepAlive
|
|
||||||
m_buff.writeByte(0).writeByte(0x0A);
|
|
||||||
List<Object> results = new ArrayList<Object>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void initBaseHeader(ByteBuf buff) throws UnsupportedEncodingException {
|
|
||||||
initHeader(buff, (byte)0x0C);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeader(ByteBuf buff, byte remaingLength) throws UnsupportedEncodingException {
|
|
||||||
buff.clear().writeByte((byte)(AbstractMessage.CONNECT << 4)).writeByte(remaingLength);
|
|
||||||
//Proto name
|
|
||||||
encodeString(buff, "MQIsdp");
|
|
||||||
//version
|
|
||||||
buff.writeByte(3);
|
|
||||||
//conn flags
|
|
||||||
buff.writeByte(0xCE);
|
|
||||||
//keepAlive
|
|
||||||
buff.writeBytes(new byte[]{(byte)0, (byte) 0x0A});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initBaseHeader311(ByteBuf buff) throws UnsupportedEncodingException {
|
|
||||||
initBaseHeader311_withFixedFlags(buff, (byte) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initBaseHeader311_withFixedFlags(ByteBuf buff, byte fixedFlags) throws UnsupportedEncodingException {
|
|
||||||
initBaseHeader311_withFixedFlags(buff, fixedFlags, (byte) 0xCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initBaseHeader311_withFixedFlags(ByteBuf buff, byte fixedFlags, byte connectFlags) throws UnsupportedEncodingException {
|
|
||||||
buff.clear().writeByte((byte)(AbstractMessage.CONNECT << 4) | fixedFlags).writeByte((byte)0x0A);
|
|
||||||
//Proto name
|
|
||||||
encodeString(buff, "MQTT");
|
|
||||||
//version
|
|
||||||
buff.writeByte(VERSION_3_1_1);
|
|
||||||
//conn flags
|
|
||||||
buff.writeByte(connectFlags);
|
|
||||||
//keepAlive
|
|
||||||
buff.writeBytes(new byte[]{(byte)0, (byte) 0x0A});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode and insert the given string into the given buff
|
|
||||||
*/
|
|
||||||
private ByteBuf encodeString(ByteBuf buff, String str) throws UnsupportedEncodingException {
|
|
||||||
buff.writeBytes(Utils.encodeString(str));
|
|
||||||
return buff;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyBaseHeader(ConnectMessage connMessage) {
|
|
||||||
assertNotNull(connMessage);
|
|
||||||
assertEquals("MQIsdp", connMessage.getProtocolName());
|
|
||||||
assertEquals(VERSION_3_1, connMessage.getProtocolVersion());
|
|
||||||
assertTrue(connMessage.isUserFlag());
|
|
||||||
assertTrue(connMessage.isPasswordFlag());
|
|
||||||
assertTrue(connMessage.isCleanSession());
|
|
||||||
assertEquals(10, connMessage.getKeepAlive());
|
|
||||||
assertTrue(connMessage.isWillFlag());
|
|
||||||
assertFalse(connMessage.isWillRetain());
|
|
||||||
assertEquals(1, connMessage.getWillQos());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyBaseHeader311(ConnectMessage connMessage) {
|
|
||||||
assertNotNull(connMessage);
|
|
||||||
assertEquals("MQTT", connMessage.getProtocolName());
|
|
||||||
assertEquals(VERSION_3_1_1, connMessage.getProtocolVersion());
|
|
||||||
assertTrue(connMessage.isUserFlag());
|
|
||||||
assertTrue(connMessage.isPasswordFlag());
|
|
||||||
assertTrue(connMessage.isCleanSession());
|
|
||||||
assertEquals(10, connMessage.getKeepAlive());
|
|
||||||
assertTrue(connMessage.isWillFlag());
|
|
||||||
assertFalse(connMessage.isWillRetain());
|
|
||||||
assertEquals(1, connMessage.getWillQos());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.ConnectMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.Before;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class ConnectEncoderTest {
|
|
||||||
ConnectEncoder m_encoder = new ConnectEncoder();
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBaseHeader() throws Exception {
|
|
||||||
ConnectMessage msg = new ConnectMessage();
|
|
||||||
msg.setWillRetain(true);
|
|
||||||
msg.setWillQos((byte)2);
|
|
||||||
msg.setWillFlag(false);
|
|
||||||
msg.setCleanSession(true);
|
|
||||||
msg.setKeepAlive(512);
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x10, out.readByte()); //1 byte
|
|
||||||
assertEquals(12, out.readByte()); //remaining length
|
|
||||||
TestUtils.verifyString("MQIsdp", out);
|
|
||||||
assertEquals(0x03, out.readByte()); //protocol version
|
|
||||||
assertEquals(0x32, out.readByte()); //flags
|
|
||||||
assertEquals(2, out.readByte()); //keepAliveTimer msb
|
|
||||||
assertEquals(0, out.readByte()); //keepAliveTimer lsb
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCompleteHeader() throws Exception {
|
|
||||||
ConnectMessage msg = new ConnectMessage();
|
|
||||||
msg.setWillRetain(true);
|
|
||||||
msg.setWillQos((byte)2);
|
|
||||||
msg.setWillFlag(true);
|
|
||||||
msg.setCleanSession(true);
|
|
||||||
msg.setKeepAlive(512);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
msg.setClientID("ABCDEF");
|
|
||||||
msg.setWillTopic("Topic");
|
|
||||||
msg.setWillMessage("Message".getBytes());
|
|
||||||
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x10, out.readByte()); //1 byte
|
|
||||||
assertEquals(36, out.readByte()); //remaining length
|
|
||||||
TestUtils.verifyString("MQIsdp", out);
|
|
||||||
assertEquals(0x03, out.readByte()); //protocol version
|
|
||||||
assertEquals(0x36, out.readByte()); //flags
|
|
||||||
assertEquals(2, out.readByte()); //keepAliveTimer msb
|
|
||||||
assertEquals(0, out.readByte()); //keepAliveTimer lsb
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString("ABCDEF", out);
|
|
||||||
TestUtils.verifyString("Topic", out);
|
|
||||||
TestUtils.verifyString("Message", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCompleteHeaderWIthUser_password() throws Exception {
|
|
||||||
ConnectMessage msg = new ConnectMessage();
|
|
||||||
msg.setUserFlag(true);
|
|
||||||
msg.setPasswordFlag(true);
|
|
||||||
msg.setWillRetain(true);
|
|
||||||
msg.setWillQos((byte)2);
|
|
||||||
msg.setWillFlag(true);
|
|
||||||
msg.setCleanSession(true);
|
|
||||||
msg.setKeepAlive(512);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
msg.setClientID("ABCDEF");
|
|
||||||
msg.setWillTopic("Topic");
|
|
||||||
msg.setWillMessage("Message".getBytes());
|
|
||||||
msg.setUsername("Pablo");
|
|
||||||
msg.setPassword("PBL".getBytes());
|
|
||||||
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x10, out.readByte()); //1 byte
|
|
||||||
assertEquals(48, out.readByte()); //remaining length
|
|
||||||
TestUtils.verifyString("MQIsdp", out);
|
|
||||||
assertEquals(0x03, out.readByte()); //protocol version
|
|
||||||
assertEquals((byte)0xF6, (byte)out.readByte()); //flags
|
|
||||||
assertEquals(2, out.readByte()); //keepAliveTimer msb
|
|
||||||
assertEquals(0, out.readByte()); //keepAliveTimer lsb
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString("ABCDEF", out);
|
|
||||||
TestUtils.verifyString("Topic", out);
|
|
||||||
TestUtils.verifyString("Message", out);
|
|
||||||
TestUtils.verifyString("Pablo", out);//username
|
|
||||||
TestUtils.verifyString("PBL", out);//password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.PubAckMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubAckDecoderTest {
|
|
||||||
ByteBuf m_buff;
|
|
||||||
PubAckDecoder m_msgdec;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new PubAckDecoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeader() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
int messageId = 0xAABB;
|
|
||||||
initHeader(m_buff, messageId);
|
|
||||||
List<Object> results = new ArrayList<Object >();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(null, m_buff, results);
|
|
||||||
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
PubAckMessage message = (PubAckMessage)results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(messageId, message.getMessageID().intValue());
|
|
||||||
assertEquals(AbstractMessage.PUBACK, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeader(ByteBuf buff, int messageID) {
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBACK << 4).writeByte(2);
|
|
||||||
|
|
||||||
//return code
|
|
||||||
buff.writeShort(messageID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.PubAckMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.Before;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubAckEncoderTest {
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeaderEncode() throws Exception {
|
|
||||||
int messageID = 0xAABB;
|
|
||||||
PubAckEncoder encoder = new PubAckEncoder();
|
|
||||||
PubAckMessage msg = new PubAckMessage();
|
|
||||||
msg.setMessageID(messageID);
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
encoder.encode(m_mockedContext, msg, out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x40, out.readByte()); //1 byte
|
|
||||||
assertEquals(0x02, out.readByte()); //2 byte, length
|
|
||||||
assertEquals((byte)0xAA, out.readByte());
|
|
||||||
assertEquals((byte)0xBB, out.readByte());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import io.netty.util.AttributeMap;
|
|
||||||
import io.netty.util.DefaultAttributeMap;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import org.junit.Test;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PubRelDecoderTest {
|
|
||||||
|
|
||||||
private static final int MESSAGE_ID = 123;
|
|
||||||
|
|
||||||
ByteBuf m_buff;
|
|
||||||
PubRelDecoder m_msgdec;
|
|
||||||
AttributeMap attrMap;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new PubRelDecoder();
|
|
||||||
attrMap = new DefaultAttributeMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidPurRel() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
m_buff.clear().writeByte(AbstractMessage.PUBREL << 4 | 0x02).writeByte(4);
|
|
||||||
m_buff.writeShort(MESSAGE_ID); //fake message_id
|
|
||||||
|
|
||||||
List<Object> results = new ArrayList<Object>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(results.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testInvalidPurRel_badReservedFlags() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
m_buff.clear().writeByte(AbstractMessage.PUBREL << 4 | 0x03).writeByte(4);
|
|
||||||
m_buff.writeShort(MESSAGE_ID); //fake message_id
|
|
||||||
|
|
||||||
List<Object> results = new ArrayList<Object>();
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(this.attrMap, m_buff, results);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* 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.parser.proto.messages.PubRelMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author kazvictor
|
|
||||||
*/
|
|
||||||
public class PubRelEncoderTest {
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeaderEncode() throws Exception {
|
|
||||||
int messageID = 0xAABB;
|
|
||||||
PubRelEncoder encoder = new PubRelEncoder();
|
|
||||||
PubRelMessage msg = new PubRelMessage();
|
|
||||||
msg.setMessageID(messageID);
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
encoder.encode(m_mockedContext, msg, out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x62, out.readByte()); //1 byte. See MQTT-3.6.1-1
|
|
||||||
assertEquals(0x02, out.readByte()); //2 byte, length
|
|
||||||
assertEquals((byte)0xAA, out.readByte());
|
|
||||||
assertEquals((byte)0xBB, out.readByte());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import io.netty.util.AttributeMap;
|
|
||||||
import io.netty.util.DefaultAttributeMap;
|
|
||||||
import java.nio.Buffer;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.PublishMessage;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PublishDecoderTest {
|
|
||||||
ByteBuf m_buff;
|
|
||||||
PublishDecoder m_msgdec;
|
|
||||||
List<Object> m_results;
|
|
||||||
AttributeMap m_attrMap;
|
|
||||||
|
|
||||||
private static final int MESSAGE_ID = 123;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new PublishDecoder();
|
|
||||||
m_results = new ArrayList<>();
|
|
||||||
m_attrMap = new DefaultAttributeMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeader() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
initHeader(m_buff);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
PublishMessage message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals("Fake Topic", message.getTopicName());
|
|
||||||
assertNull(message.getMessageID());
|
|
||||||
assertEquals(AbstractMessage.PUBLISH, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeaderWithMessageID() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
initHeaderWithMessageID(m_buff, MESSAGE_ID);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
PublishMessage message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals("Fake Topic", message.getTopicName());
|
|
||||||
assertEquals(MESSAGE_ID, (int) message.getMessageID());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHeaderWithMessageID_Payload() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(14);
|
|
||||||
// byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
|
|
||||||
ByteBuffer payload = ByteBuffer.allocate(3).put(new byte[]{0x0A, 0x0B, 0x0C});
|
|
||||||
initHeaderWithMessageID_Payload(m_buff, MESSAGE_ID, payload);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
PublishMessage message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals("Fake Topic", message.getTopicName());
|
|
||||||
assertEquals(MESSAGE_ID, (int) message.getMessageID());
|
|
||||||
// TestUtils.verifyEquals(payload, message.getPayload());
|
|
||||||
assertEquals(payload, message.getPayload());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testDup0WithQoS0_3_1_1() throws Exception {
|
|
||||||
m_buff = preparePubclishWithQosFlags((byte) 0x08);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testReservedQoS3_3_1_1() throws Exception {
|
|
||||||
m_buff = preparePubclishWithQosFlags((byte) 0x0E);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testTopicWithWildCards() throws Exception {
|
|
||||||
byte[] overallMessage = new byte[]{0x30, 0x17, //fixed header, 25 byte length
|
|
||||||
0x00, 0x06, 0x2f, 0x74, 0x6f, 0x70, 0x2B /*+*/, 0x23 /*#*/, //[/top+#] string 2 len + 6 content
|
|
||||||
0x54, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x79, // [Test my payload] encoding
|
|
||||||
0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64};
|
|
||||||
m_buff = Unpooled.buffer(overallMessage.length);
|
|
||||||
m_buff.writeBytes(overallMessage);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBugOnRealCase() throws Exception {
|
|
||||||
byte[] overallMessage = new byte[]{0x30, 0x17, //fixed header, 25 byte length
|
|
||||||
0x00, 0x06, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, //[/topic] string 2 len + 6 content
|
|
||||||
0x54, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x79, // [Test my payload] encoding
|
|
||||||
0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64};
|
|
||||||
m_buff = Unpooled.buffer(overallMessage.length);
|
|
||||||
m_buff.writeBytes(overallMessage);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, m_buff, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
PublishMessage message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecodeBigContent() throws Exception {
|
|
||||||
int size = 129;
|
|
||||||
ByteBuf payload = TestUtils.generateRandomPayload(size);
|
|
||||||
|
|
||||||
ByteBuf firstPublish = generatePublishQoS0(payload);
|
|
||||||
ByteBuf secondPublish = generatePublishQoS0(TestUtils.generateRandomPayload(size));
|
|
||||||
|
|
||||||
ByteBuf doubleMessageBuf = Unpooled.buffer(size * 2);
|
|
||||||
doubleMessageBuf.writeBytes(firstPublish).writeBytes(secondPublish);
|
|
||||||
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, doubleMessageBuf, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
PublishMessage message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
m_results.clear();
|
|
||||||
|
|
||||||
m_msgdec.decode(m_attrMap, doubleMessageBuf, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBadClaimMoreData() throws Exception {
|
|
||||||
byte[] rawMessage = new byte[] {
|
|
||||||
0x30, 0x17, 0x00, 0x06, 0x2f, (byte)0x74, 0x6f, (byte)0x70, 0x69, 0x63, 0x54, 0x65, (byte)0x73, 0x74, 0x20, 0x6d,
|
|
||||||
(byte)0x79, 0x20, (byte)0x70, 0x61, (byte)0x79, 0x6c, 0x6f, 0x61, 0x64
|
|
||||||
};
|
|
||||||
|
|
||||||
ByteBuf msgBuf = Unpooled.buffer(25);
|
|
||||||
msgBuf.writeBytes(rawMessage);
|
|
||||||
msgBuf.readByte(); //to simulate the reading of messageType done by MQTTDecoder dispatcher
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, msgBuf, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
PublishMessage message = (PublishMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals("/topic", message.getTopicName());
|
|
||||||
// assertEquals("Test my payload", new String(message.getPayload()));
|
|
||||||
Buffer expectedPayload = ByteBuffer.allocate(15).put("Test my payload".getBytes()).flip();
|
|
||||||
assertEquals(expectedPayload, message.getPayload());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check topic is at least one char [MQTT-4.7.3-1]
|
|
||||||
* */
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testMinimumTopicLength() throws Exception {
|
|
||||||
ByteBuf packet = packetWithTopic("");
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(m_attrMap, packet, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeader(ByteBuf buff) throws IllegalAccessException {
|
|
||||||
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBLISH << 4).writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
|
|
||||||
//topic name
|
|
||||||
buff.writeBytes(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuf packetWithTopic(String topicName) throws IllegalAccessException {
|
|
||||||
ByteBuf buff = Unpooled.buffer(14);
|
|
||||||
ByteBuf topicBuff = Unpooled.buffer(4).writeBytes(Utils.encodeString(topicName));
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBLISH << 4).writeBytes(Utils.encodeRemainingLength(topicBuff.readableBytes()));
|
|
||||||
//topic name
|
|
||||||
buff.writeBytes(topicBuff);
|
|
||||||
return buff;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeaderWithMessageID(ByteBuf buff, int messageID) throws IllegalAccessException {
|
|
||||||
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
|
|
||||||
tmp.writeShort(messageID);
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
|
|
||||||
//topic name
|
|
||||||
buff.writeBytes(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeaderWithMessageID_Payload(ByteBuf buff, int messageID, byte[] payload) throws IllegalAccessException {
|
|
||||||
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
|
|
||||||
tmp.writeShort(messageID);
|
|
||||||
tmp.writeBytes(payload);
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
|
|
||||||
//topic name
|
|
||||||
buff.writeBytes(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeaderWithMessageID_Payload(ByteBuf buff, int messageID, ByteBuffer payload) throws IllegalAccessException {
|
|
||||||
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
|
|
||||||
tmp.writeShort(messageID);
|
|
||||||
tmp.writeBytes(payload);
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
|
|
||||||
//topic name
|
|
||||||
buff.writeBytes(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuf generatePublishQoS0(ByteBuf payload) throws IllegalAccessException {
|
|
||||||
int size = payload.capacity();
|
|
||||||
ByteBuf messageBody = Unpooled.buffer(size);
|
|
||||||
messageBody.writeBytes(Utils.encodeString("/topic"));
|
|
||||||
|
|
||||||
//ONLY for QoS > 1 Utils.writeWord(messageBody, messageID);
|
|
||||||
messageBody.writeBytes(payload);
|
|
||||||
|
|
||||||
ByteBuf completeMsg = Unpooled.buffer(size);
|
|
||||||
completeMsg.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x00) //set Qos to 0
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(messageBody.readableBytes()));
|
|
||||||
completeMsg.writeBytes(messageBody);
|
|
||||||
|
|
||||||
return completeMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuf preparePubclishWithQosFlags(byte flags) {
|
|
||||||
ByteBuf buff = Unpooled.buffer(14);
|
|
||||||
ByteBuffer payload = ByteBuffer.allocate(3).put(new byte[]{0x0A, 0x0B, 0x0C});
|
|
||||||
ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic"));
|
|
||||||
tmp.writeShort(MESSAGE_ID);
|
|
||||||
tmp.writeBytes(payload);
|
|
||||||
buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | flags) //set DUP=1 Qos to 11 => b1110
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(tmp.readableBytes()));
|
|
||||||
//topic name
|
|
||||||
buff.writeBytes(tmp);
|
|
||||||
|
|
||||||
return buff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage.QOSType;
|
|
||||||
import io.moquette.parser.proto.messages.PublishMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class PublishEncoderTest {
|
|
||||||
PublishEncoder m_encoder = new PublishEncoder();
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
ByteBuf m_out;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
m_out = Unpooled.buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithQos_0_noMessageID() throws Exception {
|
|
||||||
String topic = "/photos";
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.MOST_ONE);
|
|
||||||
msg.setTopicName(topic);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
byte[] bpayload = new byte[]{0x0A, 0x0B, 0x0C};
|
|
||||||
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(3).put(bpayload).flip();
|
|
||||||
msg.setPayload(payload);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x30, m_out.readByte()); //1 byte
|
|
||||||
//(2+7) topic + 3 payload
|
|
||||||
assertEquals(12, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString(topic, m_out);
|
|
||||||
payload.rewind();
|
|
||||||
TestUtils.verifyBuff(bpayload.length, payload, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithQos_1_MessageID() throws Exception {
|
|
||||||
String topic = "/photos";
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.LEAST_ONE);
|
|
||||||
msg.setMessageID(1);
|
|
||||||
msg.setTopicName(topic);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
byte[] bpayload = new byte[]{0x0A, 0x0B, 0x0C};
|
|
||||||
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(3).put(bpayload).flip();
|
|
||||||
msg.setPayload(payload);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals(0x32, m_out.readByte()); //1 byte
|
|
||||||
//(2+7) topic + 2 messageID + 3 payload
|
|
||||||
assertEquals(14, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString(topic, m_out);
|
|
||||||
assertEquals(0, m_out.readByte()); //MessageID MSB
|
|
||||||
assertEquals(1, m_out.readByte()); //MessageID LSB
|
|
||||||
payload.rewind();
|
|
||||||
TestUtils.verifyBuff(bpayload.length, payload, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncodeWithQos_1_noMessageID_fail() throws Exception {
|
|
||||||
String topic = "pictures/photos";
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.LEAST_ONE);
|
|
||||||
msg.setTopicName(topic);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_empty_topic() throws Exception {
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.LEAST_ONE);
|
|
||||||
msg.setMessageID(1);
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_null_topic() throws Exception {
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.LEAST_ONE);
|
|
||||||
msg.setMessageID(1);
|
|
||||||
msg.setTopicName(null);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_qos_reserved() throws Exception {
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.RESERVED);
|
|
||||||
msg.setMessageID(1);
|
|
||||||
msg.setTopicName(null);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
byte[] bpayload = new byte[]{0x0A, 0x0B, 0x0C};
|
|
||||||
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(3).put(bpayload).flip();
|
|
||||||
msg.setPayload(payload);
|
|
||||||
ByteBuf out = Unpooled.buffer();
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBugMissedMessageIDEncoding() throws Exception{
|
|
||||||
String topic = "/topic";
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.MOST_ONE);
|
|
||||||
msg.setTopicName(topic);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
byte[] bpayload = "Test my payload".getBytes();
|
|
||||||
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(bpayload.length).put(bpayload).flip();
|
|
||||||
msg.setPayload(payload);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
//2 byte header + (2+6) topic name + [2 message ID if QoQ <> 0]+ payload
|
|
||||||
int size = m_out.readableBytes();
|
|
||||||
assertEquals(10 + bpayload.length, size);
|
|
||||||
assertEquals(0x30, m_out.readByte()); //1 byte
|
|
||||||
assertEquals(23, m_out.readByte()); //1 byte the length
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeBigMessage() throws Exception {
|
|
||||||
byte[] bpayload = new byte[256];
|
|
||||||
|
|
||||||
TestUtils.generateRandomPayload(256).readBytes(bpayload);
|
|
||||||
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(bpayload.length).put(bpayload).flip();
|
|
||||||
|
|
||||||
String topic = "/topic";
|
|
||||||
PublishMessage msg = new PublishMessage();
|
|
||||||
msg.setQos(QOSType.MOST_ONE);
|
|
||||||
msg.setTopicName(topic);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
msg.setPayload(payload);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
int size = m_out.readableBytes();
|
|
||||||
assertEquals(11 + bpayload.length, size);
|
|
||||||
assertEquals(0x30, m_out.readByte()); //1 byte
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.SubAckMessage;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class SubAckDecoderTest {
|
|
||||||
ByteBuf m_buff;
|
|
||||||
SubAckDecoder m_msgdec;
|
|
||||||
List<Object> m_results;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new SubAckDecoder();
|
|
||||||
m_results = new ArrayList<Object>();
|
|
||||||
m_buff = Unpooled.buffer(7);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBadQos() throws Exception {
|
|
||||||
initHeaderQos(m_buff, 0xAABB, AbstractMessage.QOSType.LEAST_ONE, AbstractMessage.QOSType.MOST_ONE, AbstractMessage.QOSType.MOST_ONE);
|
|
||||||
|
|
||||||
//Excercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
SubAckMessage message = (SubAckMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(0xAABB, message.getMessageID().intValue());
|
|
||||||
List<AbstractMessage.QOSType> qoses = message.types();
|
|
||||||
assertEquals(3, qoses.size());
|
|
||||||
assertEquals(AbstractMessage.QOSType.LEAST_ONE, qoses.get(0));
|
|
||||||
assertEquals(AbstractMessage.QOSType.MOST_ONE, qoses.get(1));
|
|
||||||
assertEquals(AbstractMessage.QOSType.MOST_ONE, qoses.get(2));
|
|
||||||
assertEquals(AbstractMessage.SUBACK, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBugBadRemainingCalculation() throws Exception {
|
|
||||||
byte[] overallMessage = new byte[] {(byte)0x90, 0x03, //fixed header
|
|
||||||
0x00, 0x0A, //MSG ID
|
|
||||||
0x01}; //QoS array
|
|
||||||
m_buff = Unpooled.buffer(overallMessage.length);
|
|
||||||
m_buff.writeBytes(overallMessage);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
SubAckMessage message = (SubAckMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(0x0A, message.getMessageID().intValue());
|
|
||||||
assertEquals(1, message.types().size());
|
|
||||||
assertEquals(AbstractMessage.QOSType.LEAST_ONE, message.types().get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithFailureQOS() throws Exception {
|
|
||||||
initHeaderQos(m_buff, 0xAABB, AbstractMessage.QOSType.FAILURE);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
SubAckMessage message = (SubAckMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(0xAABB, message.getMessageID().intValue());
|
|
||||||
List<AbstractMessage.QOSType> qoses = message.types();
|
|
||||||
assertEquals(1, qoses.size());
|
|
||||||
assertEquals(AbstractMessage.QOSType.FAILURE, qoses.get(0));
|
|
||||||
assertEquals(AbstractMessage.SUBACK, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeaderQos(ByteBuf buff, int messageID, AbstractMessage.QOSType... qoss) throws IllegalAccessException {
|
|
||||||
buff.clear().writeByte(AbstractMessage.SUBACK << 4).
|
|
||||||
writeBytes(Utils.encodeRemainingLength(2 + qoss.length));
|
|
||||||
|
|
||||||
buff.writeShort(messageID);
|
|
||||||
for (AbstractMessage.QOSType qos : qoss) {
|
|
||||||
buff.writeByte(qos.byteValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.SubAckMessage;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class SubAckEncoderTest {
|
|
||||||
SubAckEncoder m_encoder = new SubAckEncoder();
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
ByteBuf m_out;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
m_out = Unpooled.buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncodeWithNoQoss() throws Exception {
|
|
||||||
SubAckMessage msg = new SubAckMessage();
|
|
||||||
msg.setMessageID(123);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithMultipleQos() throws Exception {
|
|
||||||
SubAckMessage msg = new SubAckMessage();
|
|
||||||
|
|
||||||
int messageID = 0xAABB;
|
|
||||||
msg.setMessageID(messageID);
|
|
||||||
msg.addType(AbstractMessage.QOSType.MOST_ONE);
|
|
||||||
msg.addType(AbstractMessage.QOSType.LEAST_ONE);
|
|
||||||
msg.addType(AbstractMessage.QOSType.EXACTLY_ONCE);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals((byte) (AbstractMessage.SUBACK << 4 ), m_out.readByte()); //1 byte
|
|
||||||
assertEquals(5, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
assertEquals((byte)0xAA, m_out.readByte()); //MessageID MSB
|
|
||||||
assertEquals((byte)0xBB, m_out.readByte()); //MessageID LSB
|
|
||||||
assertEquals(AbstractMessage.QOSType.MOST_ONE.byteValue(), m_out.readByte());
|
|
||||||
assertEquals(AbstractMessage.QOSType.LEAST_ONE.byteValue(), m_out.readByte());
|
|
||||||
assertEquals(AbstractMessage.QOSType.EXACTLY_ONCE.byteValue(), m_out.readByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithFailureQOS() throws Exception {
|
|
||||||
SubAckMessage msg = new SubAckMessage();
|
|
||||||
|
|
||||||
int messageID = 0xAABB;
|
|
||||||
msg.setMessageID(messageID);
|
|
||||||
msg.addType(AbstractMessage.QOSType.FAILURE);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals((byte) (AbstractMessage.SUBACK << 4 ), m_out.readByte()); //1 byte
|
|
||||||
assertEquals(3, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
assertEquals((byte)0xAA, m_out.readByte()); //MessageID MSB
|
|
||||||
assertEquals((byte)0xBB, m_out.readByte()); //MessageID LSB
|
|
||||||
assertEquals((byte)0x80, m_out.readByte()); //Failure return code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.SubscribeMessage;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class SubscribeDecoderTest {
|
|
||||||
ByteBuf m_buff;
|
|
||||||
SubscribeDecoder m_msgdec;
|
|
||||||
List<Object> m_results;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new SubscribeDecoder();
|
|
||||||
m_results = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testBadQos() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(2);
|
|
||||||
initHeaderBadQos(m_buff);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMultiTopic() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "a/b");
|
|
||||||
SubscribeMessage.Couple c2 = new SubscribeMessage.Couple((byte)1, "c/d/e");
|
|
||||||
initMultiTopic(m_buff, 123, c1, c2);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
SubscribeMessage message = (SubscribeMessage)m_results.get(0);
|
|
||||||
assertEquals(2, message.subscriptions().size());
|
|
||||||
assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testFailOnEmptyTopic() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
initMultiTopic(m_buff, 123);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
SubscribeMessage message = (SubscribeMessage)m_results.get(0);
|
|
||||||
assertEquals(2, message.subscriptions().size());
|
|
||||||
assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testFailOnSubscriptionWithReservedQosPolluted() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "a/b");
|
|
||||||
initPollutedTopic(m_buff, 123, c1);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
SubscribeMessage message = (SubscribeMessage)m_results.get(0);
|
|
||||||
assertEquals(2, message.subscriptions().size());
|
|
||||||
assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check topic is at least one char [MQTT-4.7.3-1]
|
|
||||||
* */
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testMinimumTopicLength() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "");
|
|
||||||
initMultiTopic(m_buff, 123, c1);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeaderBadQos(ByteBuf buff) {
|
|
||||||
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4).writeByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMultiTopic(ByteBuf buff, int messageID, SubscribeMessage.Couple... topics) throws IllegalAccessException {
|
|
||||||
ByteBuf topicBuffer = Unpooled.buffer(4);
|
|
||||||
topicBuffer.writeShort(messageID);
|
|
||||||
for (SubscribeMessage.Couple couple : topics) {
|
|
||||||
topicBuffer.writeBytes(Utils.encodeString(couple.topicFilter));
|
|
||||||
topicBuffer.writeByte(couple.qos);
|
|
||||||
}
|
|
||||||
|
|
||||||
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02)
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
|
|
||||||
buff.writeBytes(topicBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initPollutedTopic(ByteBuf buff, int messageID, SubscribeMessage.Couple topic) throws IllegalAccessException {
|
|
||||||
ByteBuf topicBuffer = Unpooled.buffer(4);
|
|
||||||
topicBuffer.writeShort(messageID);
|
|
||||||
topicBuffer.writeBytes(Utils.encodeString(topic.topicFilter));
|
|
||||||
topicBuffer.writeByte(0xF0 | topic.qos);
|
|
||||||
|
|
||||||
buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02)
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
|
|
||||||
buff.writeBytes(topicBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.SubscribeMessage;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class SubscribeEncoderTest {
|
|
||||||
SubscribeEncoder m_encoder = new SubscribeEncoder();
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
ByteBuf m_out;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
m_out = Unpooled.buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithMultiTopic() throws Exception {
|
|
||||||
SubscribeMessage msg = new SubscribeMessage();
|
|
||||||
msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
|
||||||
msg.setMessageID(0xAABB);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)1, "a/b");
|
|
||||||
SubscribeMessage.Couple c2 = new SubscribeMessage.Couple((byte)0, "a/b/c");
|
|
||||||
msg.addSubscription(c1);
|
|
||||||
msg.addSubscription(c2);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals((byte)0x82, (byte)m_out.readByte()); //1 byte
|
|
||||||
assertEquals(16, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//verify MessageID
|
|
||||||
assertEquals((byte)0xAA, m_out.readByte());
|
|
||||||
assertEquals((byte)0xBB, m_out.readByte());
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString(c1.topicFilter, m_out);
|
|
||||||
assertEquals(c1.qos, m_out.readByte());
|
|
||||||
TestUtils.verifyString(c2.topicFilter, m_out);
|
|
||||||
assertEquals(c2.qos, m_out.readByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_empty_subscription() throws Exception {
|
|
||||||
SubscribeMessage msg = new SubscribeMessage();
|
|
||||||
msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
|
||||||
msg.setMessageID(0xAABB);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_badQos() throws Exception {
|
|
||||||
SubscribeMessage msg = new SubscribeMessage();
|
|
||||||
msg.setQos(AbstractMessage.QOSType.EXACTLY_ONCE);
|
|
||||||
msg.setMessageID(0xAABB);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, msg, m_out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Common test methods
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class TestUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify that the given bytes buffer of the given numBytes length is present
|
|
||||||
* in the buff starting from the current position.
|
|
||||||
*/
|
|
||||||
static void verifyBuff(int numBytes, byte[] bytes, ByteBuf buff) {
|
|
||||||
assertTrue(numBytes <= buff.readableBytes());
|
|
||||||
byte[] toCheck = new byte[numBytes];
|
|
||||||
buff.readBytes(toCheck);
|
|
||||||
|
|
||||||
for (int i = 0; i < numBytes; i++) {
|
|
||||||
assertEquals(bytes[i], toCheck[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void verifyBuff(int numBytes, ByteBuffer bytes, ByteBuf buff) {
|
|
||||||
assertTrue(numBytes <= buff.readableBytes());
|
|
||||||
assertEquals(bytes, buff.nioBuffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify the presence of the given string starting from the current position
|
|
||||||
* inside the buffer.
|
|
||||||
*/
|
|
||||||
static void verifyString(String str, ByteBuf buff) throws UnsupportedEncodingException {
|
|
||||||
ByteBuf tmpBuff = Unpooled.buffer(2);
|
|
||||||
byte[] raw = str.getBytes("UTF-8");
|
|
||||||
tmpBuff.writeShort(raw.length);
|
|
||||||
tmpBuff.writeBytes(raw);
|
|
||||||
int buffLen = raw.length + 2;
|
|
||||||
verifyByteBuf(tmpBuff, buff.slice(buff.readerIndex(), buffLen));
|
|
||||||
buff.skipBytes(buffLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void verifyByteBuf(ByteBuf expected, ByteBuf found) {
|
|
||||||
assertEquals(expected, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ChannelHandlerContext mockChannelHandler() {
|
|
||||||
ChannelHandlerContext m_mockedContext = mock(ChannelHandlerContext.class);
|
|
||||||
ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
|
|
||||||
when(m_mockedContext.alloc()).thenReturn(allocator);
|
|
||||||
return m_mockedContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void verifyEquals(byte[] expected, byte[] found) {
|
|
||||||
assertEquals(expected.length, found.length);
|
|
||||||
for (int i = 0; i < expected.length; i++) {
|
|
||||||
assertEquals(expected[i], found[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static ByteBuf generateRandomPayload(int size) {
|
|
||||||
ByteBuf payloadBuffer = Unpooled.buffer(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
payloadBuffer.writeByte((byte) (Math.random() * 255));
|
|
||||||
}
|
|
||||||
return payloadBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.UnsubscribeMessage;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class UnsubscribeDecoderTest {
|
|
||||||
ByteBuf m_buff;
|
|
||||||
UnsubscribeDecoder m_msgdec;
|
|
||||||
List<Object> m_results;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_msgdec = new UnsubscribeDecoder();
|
|
||||||
m_results = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testBadQos() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(2);
|
|
||||||
initHeaderBadQos(m_buff);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMultiTopic() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
String topic1 = "a/b";
|
|
||||||
String topic2 = "c/d/e";
|
|
||||||
initMultiTopic(m_buff, 123, topic1, topic2);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
UnsubscribeMessage message = (UnsubscribeMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(Arrays.asList(topic1, topic2), message.topicFilters());
|
|
||||||
assertEquals(AbstractMessage.UNSUBSCRIBE, message.getMessageType());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testFailOnEmptyTopic() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
initMultiTopic(m_buff, 123, "");
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check topic is at least one char [MQTT-4.7.3-1]
|
|
||||||
* */
|
|
||||||
@Test(expected = CorruptedFrameException.class)
|
|
||||||
public void testMinimumTopicLength() throws Exception {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
initMultiTopic(m_buff, 123);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecodeSingleTopic_bug() throws Exception {
|
|
||||||
//A2 0C 00 01 00 06 2F 74 6F 70 69 63 //12 byte
|
|
||||||
byte[] overallMessage = new byte[] {(byte)0xA2, 0x0A, //fixed header
|
|
||||||
0x00, 0x01, //MSG ID
|
|
||||||
0x00, 0x06, 0x2F, 0x74, 0x6F, 0x70, 0x69, 0x63}; //"/topic" string
|
|
||||||
m_buff = Unpooled.buffer(overallMessage.length);
|
|
||||||
m_buff.writeBytes(overallMessage);
|
|
||||||
|
|
||||||
m_msgdec.decode(null, m_buff, m_results);
|
|
||||||
|
|
||||||
assertFalse(m_results.isEmpty());
|
|
||||||
UnsubscribeMessage message = (UnsubscribeMessage)m_results.get(0);
|
|
||||||
assertNotNull(message);
|
|
||||||
assertEquals(0x01, (int) message.getMessageID());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void initHeaderBadQos(ByteBuf buff) {
|
|
||||||
buff.clear().writeByte(AbstractMessage.UNSUBSCRIBE << 4).writeByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMultiTopic(ByteBuf buff, int messageID, String... topics) throws IllegalAccessException {
|
|
||||||
ByteBuf topicBuffer = Unpooled.buffer(4);
|
|
||||||
topicBuffer.writeShort(messageID);
|
|
||||||
for (String topic : topics) {
|
|
||||||
topicBuffer.writeBytes(Utils.encodeString(topic));
|
|
||||||
}
|
|
||||||
|
|
||||||
buff.clear().writeByte(AbstractMessage.UNSUBSCRIBE << 4 | (byte)0x02)
|
|
||||||
.writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes()));
|
|
||||||
buff.writeBytes(topicBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.UnsubscribeMessage;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
public class UnsubscribeEncoderTest {
|
|
||||||
UnsubscribeEncoder m_encoder = new UnsubscribeEncoder();
|
|
||||||
ChannelHandlerContext m_mockedContext;
|
|
||||||
ByteBuf m_out;
|
|
||||||
UnsubscribeMessage m_msg;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
//mock the ChannelHandlerContext to return an UnpooledAllocator
|
|
||||||
m_mockedContext = TestUtils.mockChannelHandler();
|
|
||||||
m_out = Unpooled.buffer();
|
|
||||||
m_msg = new UnsubscribeMessage();
|
|
||||||
m_msg.setMessageID(0xAABB);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithSingleTopic() throws Exception {
|
|
||||||
m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
String topic1 = "/topic";
|
|
||||||
m_msg.addTopicFilter(topic1);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, m_msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals((byte)0xA2, (byte)m_out.readByte()); //1 byte
|
|
||||||
//2 messageID + 2 length + 6 chars = 10
|
|
||||||
assertEquals(10, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//verify M1ssageID
|
|
||||||
assertEquals((byte)0xAA, m_out.readByte());
|
|
||||||
assertEquals((byte)0xBB, m_out.readByte());
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString(topic1, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeWithMultiTopic() throws Exception {
|
|
||||||
m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
|
||||||
|
|
||||||
//variable part
|
|
||||||
String topic1 = "a/b";
|
|
||||||
String topic2 = "a/b/c";
|
|
||||||
m_msg.addTopicFilter(topic1);
|
|
||||||
m_msg.addTopicFilter(topic2);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, m_msg, m_out);
|
|
||||||
|
|
||||||
//Verify
|
|
||||||
assertEquals((byte)0xA2, (byte)m_out.readByte()); //1 byte
|
|
||||||
assertEquals(14, m_out.readByte()); //remaining length
|
|
||||||
|
|
||||||
//verify M1ssageID
|
|
||||||
assertEquals((byte)0xAA, m_out.readByte());
|
|
||||||
assertEquals((byte)0xBB, m_out.readByte());
|
|
||||||
|
|
||||||
//Variable part
|
|
||||||
TestUtils.verifyString(topic1, m_out);
|
|
||||||
TestUtils.verifyString(topic2, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_empty_topics() throws Exception {
|
|
||||||
m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, m_msg, m_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testEncode_badQos() throws Exception {
|
|
||||||
m_msg.setQos(AbstractMessage.QOSType.EXACTLY_ONCE);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
m_encoder.encode(m_mockedContext, m_msg, m_out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* and Apache License v2.0 which accompanies this distribution.
|
|
||||||
*
|
|
||||||
* The Eclipse Public License is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* The Apache License v2.0 is available at
|
|
||||||
* http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
*
|
|
||||||
* You may elect to redistribute this code under either of these licenses.
|
|
||||||
*/
|
|
||||||
package io.moquette.parser.netty;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static io.moquette.parser.netty.TestUtils.verifyBuff;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.UnsubscribeMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andrea
|
|
||||||
*/
|
|
||||||
/*@RunWith(Suite.class)
|
|
||||||
@Suite.SuiteClasses({})*/
|
|
||||||
public class UtilsTest {
|
|
||||||
|
|
||||||
ByteBuf m_buff;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
m_buff = Unpooled.buffer(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecodeRemainingLength() {
|
|
||||||
//1 byte length
|
|
||||||
m_buff.writeByte(0x0);
|
|
||||||
assertEquals(0, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
m_buff.clear().writeByte(0x007F);
|
|
||||||
assertEquals(127, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
|
|
||||||
//2 byte length
|
|
||||||
m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x01});
|
|
||||||
assertEquals(128, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0x7F});
|
|
||||||
assertEquals(16383, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
|
|
||||||
//3 byte length
|
|
||||||
m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x80, (byte)0x01});
|
|
||||||
assertEquals(16384, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0x7F});
|
|
||||||
assertEquals(2097151, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
|
|
||||||
//4 byte length
|
|
||||||
m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x80, (byte)0x80, (byte)0x01});
|
|
||||||
assertEquals(2097152, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x7F});
|
|
||||||
assertEquals(268435455, Utils.decodeRemainingLenght(m_buff));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected=CorruptedFrameException.class)
|
|
||||||
public void testEncodeRemainingLength_invalid_upper() {
|
|
||||||
Utils.encodeRemainingLength(Utils.MAX_LENGTH_LIMIT + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected=CorruptedFrameException.class)
|
|
||||||
public void testEncodeRemainingLength_invalid_lower() {
|
|
||||||
Utils.encodeRemainingLength(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeRemainingLenght() {
|
|
||||||
//1 byte length
|
|
||||||
verifyBuff(1, new byte[]{0}, Utils.encodeRemainingLength(0));
|
|
||||||
verifyBuff(1, new byte[]{0x7F}, Utils.encodeRemainingLength(127));
|
|
||||||
|
|
||||||
//2 byte length
|
|
||||||
verifyBuff(2, new byte[]{(byte)0x80, 0x01}, Utils.encodeRemainingLength(128));
|
|
||||||
verifyBuff(2, new byte[]{(byte)0xFF, 0x7F}, Utils.encodeRemainingLength(16383));
|
|
||||||
|
|
||||||
//3 byte length
|
|
||||||
verifyBuff(3, new byte[]{(byte)0x80, (byte)0x80, 0x01}, Utils.encodeRemainingLength(16384));
|
|
||||||
verifyBuff(3, new byte[]{(byte)0xFF, (byte)0xFF, 0x7F}, Utils.encodeRemainingLength(2097151));
|
|
||||||
|
|
||||||
//4 byte length
|
|
||||||
verifyBuff(4, new byte[]{(byte)0x80, (byte)0x80, (byte)0x80, 0x01}, Utils.encodeRemainingLength(2097152));
|
|
||||||
verifyBuff(4, new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, 0x7F}, Utils.encodeRemainingLength(268435455));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeFlags() {
|
|
||||||
UnsubscribeMessage msg = new UnsubscribeMessage();
|
|
||||||
msg.setRetainFlag(true);
|
|
||||||
msg.setQos(AbstractMessage.QOSType.MOST_ONE);
|
|
||||||
|
|
||||||
//Exercise
|
|
||||||
assertEquals(1, Utils.encodeFlags(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Set root logger level to DEBUG and its only appender to A1.
|
|
||||||
#log4j.rootLogger=INFO, stdout, file
|
|
||||||
log4j.rootLogger=INFO, stdout
|
|
||||||
|
|
||||||
log4j.logger.io.moquette=WARN
|
|
||||||
log4j.logger.io.netty.util=WARN
|
|
||||||
|
|
||||||
# stdout appender is set to be a ConsoleAppender.
|
|
||||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
|
||||||
log4j.appender.stdout.Threshold=INFO
|
|
||||||
|
|
||||||
|
|
||||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
|
||||||
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %c{1} %M %L %x - %m%n
|
|
||||||
|
|
||||||
#file appender
|
|
||||||
#log4j.appender.file=org.apache.log4j.RollingFileAppender
|
|
||||||
#log4j.appender.file.Threshold=INFO
|
|
||||||
#log4j.appender.file.File=moquette.log
|
|
||||||
#log4j.appender.file.MaxFileSize=100MB
|
|
||||||
#log4j.appender.file.MaxBackupIndex=1
|
|
||||||
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
|
||||||
#log4j.appender.file.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %c{1} %L %x - %m%n
|
|
||||||
@@ -19,12 +19,6 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
|
||||||
<artifactId>moquette-netty-parser</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<artifactId>moquette-broker</artifactId>
|
<artifactId>moquette-broker</artifactId>
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
package io.moquette.parser.netty.performance;
|
package io.moquette.parser.netty.performance;
|
||||||
|
|
||||||
import io.moquette.parser.proto.messages.*;
|
import io.netty.buffer.ByteBuf;
|
||||||
import static io.moquette.parser.proto.messages.AbstractMessage.*;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.moquette.parser.proto.Utils;
|
import io.netty.handler.codec.mqtt.*;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import org.HdrHistogram.Histogram;
|
import org.HdrHistogram.Histogram;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import static io.moquette.parser.netty.performance.NettyPublishReceiverHandler.payload2Str;
|
||||||
|
import static io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader.from;
|
||||||
|
import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
|
||||||
|
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by andrea on 6/2/15.
|
|
||||||
*/
|
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
|
class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
@@ -32,27 +30,26 @@ class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object message) {
|
public void channelRead(ChannelHandlerContext ctx, Object message) {
|
||||||
AbstractMessage msg = (AbstractMessage) message;
|
MqttMessage msg = (MqttMessage) message;
|
||||||
|
MqttMessageType messageType = msg.fixedHeader().messageType();
|
||||||
String clientID = clientID(ctx.channel());
|
String clientID = clientID(ctx.channel());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (msg.getMessageType()) {
|
switch (messageType) {
|
||||||
case CONNECT:
|
case CONNECT:
|
||||||
ConnectMessage connect = (ConnectMessage) message;
|
MqttConnectMessage connect = (MqttConnectMessage) message;
|
||||||
clientID = connect.getClientID();
|
|
||||||
LOG.info("Received a message of type {} from <{}>", Utils.msgType2String(msg.getMessageType()), clientID);
|
|
||||||
handleConnect(ctx, connect);
|
handleConnect(ctx, connect);
|
||||||
return;
|
return;
|
||||||
case SUBSCRIBE:
|
case SUBSCRIBE:
|
||||||
LOG.info("Received a message of type {} from <{}>", Utils.msgType2String(msg.getMessageType()), clientID);
|
LOG.info("Received a message of type {} from <{}>", messageType, clientID);
|
||||||
handleSubscribe(ctx, (SubscribeMessage) msg);
|
handleSubscribe(ctx, (MqttSubscribeMessage) msg);
|
||||||
return;
|
return;
|
||||||
case PUBLISH:
|
case PUBLISH:
|
||||||
LOG.info("Received a message of type {} from <{}>", Utils.msgType2String(msg.getMessageType()), clientID);
|
LOG.info("Received a message of type {} from <{}>", messageType, clientID);
|
||||||
handlePublish(ctx, (PublishMessage) msg);
|
handlePublish(ctx, (MqttPublishMessage) msg);
|
||||||
return;
|
return;
|
||||||
case DISCONNECT:
|
case DISCONNECT:
|
||||||
LOG.info("Received a message of type {} from <{}>", Utils.msgType2String(msg.getMessageType()), clientID);
|
LOG.info("Received a message of type {} from <{}>", messageType, clientID);
|
||||||
ctx.close();
|
ctx.close();
|
||||||
// case PUBACK:
|
// case PUBACK:
|
||||||
// NettyChannel channel;
|
// NettyChannel channel;
|
||||||
@@ -66,36 +63,36 @@ class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
|
|||||||
// m_messaging.handleProtocolMessage(channel, msg);
|
// m_messaging.handleProtocolMessage(channel, msg);
|
||||||
break;
|
break;
|
||||||
case PINGREQ:
|
case PINGREQ:
|
||||||
PingRespMessage pingResp = new PingRespMessage();
|
MqttFixedHeader pingHeader = new MqttFixedHeader(MqttMessageType.PINGRESP, false, AT_MOST_ONCE, false, 0);
|
||||||
|
MqttMessage pingResp = new MqttMessage(pingHeader);
|
||||||
ctx.writeAndFlush(pingResp);
|
ctx.writeAndFlush(pingResp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG.info("Received a message of type {} from <{}>", Utils.msgType2String(msg.getMessageType()), clientID);
|
LOG.info("Received a message of type {} from <{}>", messageType, clientID);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOG.error("Bad error in processing the message", ex);
|
LOG.error("Bad error in processing the message", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePublish(ChannelHandlerContext ctx, PublishMessage msg) {
|
private void handlePublish(ChannelHandlerContext ctx, MqttPublishMessage msg) {
|
||||||
if (!m_state.isForwardable()) {
|
if (!m_state.isForwardable()) {
|
||||||
LOG.info("Subscriber not yet connected, LoopHandler instance is {}", this);
|
LOG.info("Subscriber not yet connected, LoopHandler instance is {}", this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
LOG.debug("push forward message the topic {}", msg.getTopicName());
|
LOG.debug("push forward message the topic {}", msg.variableHeader().topicName());
|
||||||
LOG.debug("content <{}>", payload2Str(msg.getPayload()));
|
LOG.debug("content <{}>", payload2Str(msg.payload()));
|
||||||
String decodedPayload = payload2Str(msg.getPayload());
|
String decodedPayload = payload2Str(msg.payload());
|
||||||
long sentTime = Long.parseLong(decodedPayload.split("-")[1]);
|
long sentTime = Long.parseLong(decodedPayload.split("-")[1]);
|
||||||
forthNetworkTime.recordValue(start - sentTime);
|
forthNetworkTime.recordValue(start - sentTime);
|
||||||
|
|
||||||
//publish always at Qos0, to don't handle PUBACK or the complete Qos2 workflow
|
//publish always at Qos0, to don't handle PUBACK or the complete Qos2 workflow
|
||||||
PublishMessage pubMessage = new PublishMessage();
|
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_MOST_ONCE,
|
||||||
pubMessage.setRetainFlag(false);
|
false, 0);
|
||||||
pubMessage.setTopicName(msg.getTopicName());
|
MqttPublishVariableHeader varHeader = new MqttPublishVariableHeader(msg.variableHeader().topicName(), 0);
|
||||||
pubMessage.setQos(QOSType.MOST_ONE);
|
MqttPublishMessage pubMessage = new MqttPublishMessage(fixedHeader, varHeader, msg.payload());
|
||||||
pubMessage.setPayload(msg.getPayload());
|
|
||||||
|
|
||||||
m_state.getSubscriberCh().writeAndFlush(pubMessage);
|
m_state.getSubscriberCh().writeAndFlush(pubMessage);
|
||||||
/*Channel subscriberCh = m_state.getSubscriberCh();
|
/*Channel subscriberCh = m_state.getSubscriberCh();
|
||||||
@@ -106,28 +103,24 @@ class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
|
|||||||
}*/
|
}*/
|
||||||
long stop = System.nanoTime();
|
long stop = System.nanoTime();
|
||||||
processingTime.recordValue(stop - start);
|
processingTime.recordValue(stop - start);
|
||||||
LOG.info("Request processed in {} ns, matching {}", stop - start, payload2Str(msg.getPayload()));
|
LOG.info("Request processed in {} ns, matching {}", stop - start, payload2Str(msg.payload()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSubscribe(ChannelHandlerContext ctx, SubscribeMessage msg) {
|
private void handleSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage msg) {
|
||||||
m_state.setForwardable(true);
|
m_state.setForwardable(true);
|
||||||
LOG.debug(" new value of flag {}, LoopHandler instance is {}", m_state.isForwardable(), this);
|
LOG.debug(" new value of flag {}, LoopHandler instance is {}", m_state.isForwardable(), this);
|
||||||
SubAckMessage ackMessage = new SubAckMessage();
|
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.SUBACK, false, AT_LEAST_ONCE, false, 0);
|
||||||
ackMessage.setMessageID(msg.getMessageID());
|
MqttSubAckPayload payload = new MqttSubAckPayload(AT_MOST_ONCE.value());
|
||||||
|
MqttSubAckMessage ackMessage = new MqttSubAckMessage(fixedHeader, from(msg.variableHeader().messageId()), payload);
|
||||||
ctx.writeAndFlush(ackMessage);
|
ctx.writeAndFlush(ackMessage);
|
||||||
LOG.debug("subscribed client to {}", msg.subscriptions());
|
LOG.debug("subscribed client to {}", msg.payload().topicSubscriptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
static String payload2Str(ByteBuffer content) {
|
private void handleConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
|
||||||
byte[] b = new byte[content.remaining()];
|
MqttConnectPayload payload = msg.payload();
|
||||||
content.mark();
|
String clientID = payload.clientIdentifier();
|
||||||
content.get(b);
|
LOG.info("Received a message of type {} from <{}>", MqttMessageType.CONNECT, clientID);
|
||||||
content.reset();
|
|
||||||
return new String(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleConnect(ChannelHandlerContext ctx, ConnectMessage msg) {
|
|
||||||
String clientID = msg.getClientID();
|
|
||||||
clientID(ctx.channel(), clientID);
|
clientID(ctx.channel(), clientID);
|
||||||
if (clientID.toLowerCase().startsWith("sub")) {
|
if (clientID.toLowerCase().startsWith("sub")) {
|
||||||
m_state.setSubscriberCh(ctx.channel());
|
m_state.setSubscriberCh(ctx.channel());
|
||||||
@@ -135,14 +128,21 @@ class LoopMQTTHandler extends ChannelInboundHandlerAdapter {
|
|||||||
m_state.setPublisherCh(ctx.channel());
|
m_state.setPublisherCh(ctx.channel());
|
||||||
} else {
|
} else {
|
||||||
//we don't admit other names
|
//we don't admit other names
|
||||||
ConnAckMessage koResp = new ConnAckMessage();
|
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE,
|
||||||
koResp.setReturnCode(ConnAckMessage.IDENTIFIER_REJECTED);
|
false, 0);
|
||||||
|
MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(
|
||||||
|
MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED, false);
|
||||||
|
MqttConnAckMessage koResp = new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
|
||||||
ctx.writeAndFlush(koResp);
|
ctx.writeAndFlush(koResp);
|
||||||
ctx.close();
|
ctx.close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnAckMessage okResp = new ConnAckMessage();
|
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE,
|
||||||
okResp.setReturnCode(ConnAckMessage.CONNECTION_ACCEPTED);
|
false, 0);
|
||||||
|
MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(
|
||||||
|
MqttConnectReturnCode.CONNECTION_ACCEPTED, false);
|
||||||
|
MqttConnAckMessage okResp = new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
|
||||||
ctx.writeAndFlush(okResp);
|
ctx.writeAndFlush(okResp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package io.moquette.parser.netty.performance;
|
package io.moquette.parser.netty.performance;
|
||||||
|
|
||||||
import io.moquette.parser.commons.Constants;
|
|
||||||
import io.moquette.parser.netty.MQTTDecoder;
|
|
||||||
import io.moquette.parser.netty.MQTTEncoder;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.*;
|
import io.netty.channel.*;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.codec.mqtt.MqttDecoder;
|
||||||
|
import io.netty.handler.codec.mqtt.MqttEncoder;
|
||||||
import io.netty.handler.timeout.IdleState;
|
import io.netty.handler.timeout.IdleState;
|
||||||
import io.netty.handler.timeout.IdleStateEvent;
|
import io.netty.handler.timeout.IdleStateEvent;
|
||||||
import io.netty.handler.timeout.IdleStateHandler;
|
import io.netty.handler.timeout.IdleStateHandler;
|
||||||
@@ -87,10 +86,10 @@ public class ProtocolDecodingServer {
|
|||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
try {
|
try {
|
||||||
pipeline.addFirst("idleStateHandler", new IdleStateHandler(0, 0, Constants.DEFAULT_CONNECT_TIMEOUT));
|
pipeline.addFirst("idleStateHandler", new IdleStateHandler(0, 0, 2));
|
||||||
pipeline.addAfter("idleStateHandler", "idleEventHandler", new MoquetteIdleTimeoutHandler());
|
pipeline.addAfter("idleStateHandler", "idleEventHandler", new MoquetteIdleTimeoutHandler());
|
||||||
pipeline.addLast("decoder", new MQTTDecoder());
|
pipeline.addLast("decoder", new MqttDecoder());
|
||||||
pipeline.addLast("encoder", new MQTTEncoder());
|
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||||
pipeline.addLast("handler", new LoopMQTTHandler(state));
|
pipeline.addLast("handler", new LoopMQTTHandler(state));
|
||||||
} catch (Throwable th) {
|
} catch (Throwable th) {
|
||||||
LOG.error("Severe error during pipeline creation", th);
|
LOG.error("Severe error during pipeline creation", th);
|
||||||
|
|||||||
+2
-4
@@ -1,7 +1,5 @@
|
|||||||
package io.moquette.parser.netty.performance;
|
package io.moquette.parser.netty.performance;
|
||||||
|
|
||||||
import io.moquette.parser.netty.MQTTDecoder;
|
|
||||||
import io.moquette.parser.netty.MQTTEncoder;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.*;
|
import io.netty.channel.*;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
@@ -39,8 +37,8 @@ public class ProtocolPublishDecodingServer {
|
|||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
try {
|
try {
|
||||||
pipeline.addLast("decoder", new MQTTDecoder());
|
pipeline.addLast("decoder", new MqttDecoder());
|
||||||
pipeline.addLast("encoder", new MQTTEncoder());
|
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||||
pipeline.addLast("handler", new PublishReceiverHandler());
|
pipeline.addLast("handler", new PublishReceiverHandler());
|
||||||
// pipeline.addLast("decoder", new MqttDecoder());
|
// pipeline.addLast("decoder", new MqttDecoder());
|
||||||
// pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
// pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
package io.moquette.parser.netty.performance;
|
package io.moquette.parser.netty.performance;
|
||||||
|
|
||||||
|
|
||||||
import io.moquette.parser.netty.MQTTDecoder;
|
|
||||||
import io.moquette.parser.netty.MQTTEncoder;
|
|
||||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
|
||||||
import io.moquette.parser.proto.messages.PublishMessage;
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.*;
|
import io.netty.channel.*;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
import io.netty.handler.codec.mqtt.*;
|
||||||
import org.eclipse.jetty.toolchain.perf.PlatformTimer;
|
import org.eclipse.jetty.toolchain.perf.PlatformTimer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
class PublishBomber {
|
class PublishBomber {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PublishBomber.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PublishBomber.class);
|
||||||
@@ -34,8 +31,8 @@ class PublishBomber {
|
|||||||
@Override
|
@Override
|
||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
pipeline.addLast("decoder", new MQTTDecoder());
|
pipeline.addLast("decoder", new MqttDecoder());
|
||||||
pipeline.addLast("encoder", new MQTTEncoder());
|
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -47,7 +44,7 @@ class PublishBomber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(AbstractMessage msg) {
|
private void sendMessage(MqttMessage msg) {
|
||||||
try {
|
try {
|
||||||
channel.writeAndFlush(msg).await();
|
channel.writeAndFlush(msg).await();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -67,13 +64,14 @@ class PublishBomber {
|
|||||||
PlatformTimer timer = PlatformTimer.detect();
|
PlatformTimer timer = PlatformTimer.detect();
|
||||||
for (int i=0; i < numToSend; i++) {
|
for (int i=0; i < numToSend; i++) {
|
||||||
long nanos = System.nanoTime();
|
long nanos = System.nanoTime();
|
||||||
|
|
||||||
PublishMessage pubMessage = new PublishMessage();
|
|
||||||
pubMessage.setQos(AbstractMessage.QOSType.MOST_ONE);
|
|
||||||
pubMessage.setTopicName("/topic");
|
|
||||||
byte[] rawContent = ("Hello world!!-" + nanos).getBytes();
|
byte[] rawContent = ("Hello world!!-" + nanos).getBytes();
|
||||||
ByteBuffer payload = (ByteBuffer) ByteBuffer.allocate(rawContent.length).put(rawContent).flip();
|
ByteBuf payload = Unpooled.copiedBuffer(rawContent);
|
||||||
pubMessage.setPayload(payload);
|
|
||||||
|
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_MOST_ONCE,
|
||||||
|
false, 0);
|
||||||
|
MqttPublishVariableHeader varHeader = new MqttPublishVariableHeader("/topic", 0);
|
||||||
|
MqttPublishMessage pubMessage = new MqttPublishMessage(fixedHeader, varHeader, payload);
|
||||||
|
|
||||||
sendMessage(pubMessage);
|
sendMessage(pubMessage);
|
||||||
timer.sleep(pauseMicroseconds);
|
timer.sleep(pauseMicroseconds);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-26
@@ -1,19 +1,16 @@
|
|||||||
package io.moquette.parser.netty.performance;
|
package io.moquette.parser.netty.performance;
|
||||||
|
|
||||||
import io.moquette.parser.proto.Utils;
|
|
||||||
import io.moquette.parser.proto.messages.*;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||||
|
import io.netty.handler.codec.mqtt.MqttMessageType;
|
||||||
|
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||||
import org.HdrHistogram.Histogram;
|
import org.HdrHistogram.Histogram;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import static io.moquette.parser.netty.performance.NettyPublishReceiverHandler.payload2Str;
|
||||||
|
|
||||||
import static io.moquette.parser.proto.messages.AbstractMessage.*;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
class PublishReceiverHandler extends ChannelInboundHandlerAdapter {
|
class PublishReceiverHandler extends ChannelInboundHandlerAdapter {
|
||||||
@@ -26,43 +23,35 @@ class PublishReceiverHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object message) {
|
public void channelRead(ChannelHandlerContext ctx, Object message) {
|
||||||
AbstractMessage msg = (AbstractMessage) message;
|
MqttMessage msg = (MqttMessage) message;
|
||||||
|
MqttMessageType messageType = msg.fixedHeader().messageType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (msg.getMessageType()) {
|
switch (messageType) {
|
||||||
case PUBLISH:
|
case PUBLISH:
|
||||||
LOG.info("Received a message of type {}", Utils.msgType2String(msg.getMessageType()));
|
LOG.info("Received a message of type {}", messageType);
|
||||||
handlePublish((PublishMessage) msg);
|
handlePublish((MqttPublishMessage) msg);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
LOG.info("Received a message of type {}", Utils.msgType2String(msg.getMessageType()));
|
LOG.info("Received a message of type {}", messageType);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOG.error("Bad error in processing the message", ex);
|
LOG.error("Bad error in processing the message", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePublish(PublishMessage msg) {
|
private void handlePublish(MqttPublishMessage msg) {
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
LOG.debug("push forward message the topic {}", msg.getTopicName());
|
LOG.debug("push forward message the topic {}", msg.variableHeader().topicName());
|
||||||
LOG.debug("content <{}>", payload2Str(msg.getPayload()));
|
LOG.debug("content <{}>", payload2Str(msg.payload()));
|
||||||
String decodedPayload = payload2Str(msg.getPayload());
|
String decodedPayload = payload2Str(msg.payload());
|
||||||
long sentTime = Long.parseLong(decodedPayload.split("-")[1]);
|
long sentTime = Long.parseLong(decodedPayload.split("-")[1]);
|
||||||
forthNetworkTime.recordValue(start - sentTime);
|
forthNetworkTime.recordValue(start - sentTime);
|
||||||
|
|
||||||
long stop = System.nanoTime();
|
long stop = System.nanoTime();
|
||||||
LOG.info("Request processed in {} ns, matching {}", stop - start, payload2Str(msg.getPayload()));
|
LOG.info("Request processed in {} ns, matching {}", stop - start, payload2Str(msg.payload()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String payload2Str(ByteBuffer content) {
|
|
||||||
byte[] b = new byte[content.remaining()];
|
|
||||||
content.mark();
|
|
||||||
content.get(b);
|
|
||||||
content.reset();
|
|
||||||
return new String(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
LOG.info("Received channel inactive");
|
LOG.info("Received channel inactive");
|
||||||
|
|||||||
Reference in New Issue
Block a user