205 lines
6.5 KiB
Java
205 lines
6.5 KiB
Java
/*
|
|||
|
|
* 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;
|
||
|
|
|
||
|
|
import io.netty.buffer.ByteBuf;
|
||
|
|
import io.netty.buffer.Unpooled;
|
||
|
|
import io.netty.handler.codec.CorruptedFrameException;
|
||
|
|
import io.netty.handler.codec.mqtt.MqttMessage;
|
||
|
|
import io.netty.util.Attribute;
|
||
|
|
import io.netty.util.AttributeKey;
|
||
|
|
import io.netty.util.AttributeMap;
|
||
|
|
import org.slf4j.LoggerFactory;
|
||
|
|
|
||
|
|
import java.io.UnsupportedEncodingException;
|
||
|
|
|
||
|
|
/**
|
||
|
|
*
|
||
|
|
* @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(MqttMessage message) {
|
||
|
|
byte flags = 0;
|
||
|
|
if (message.fixedHeader().isDup()) {
|
||
|
|
flags |= 0x08;
|
||
|
|
}
|
||
|
|
if (message.fixedHeader().isRetain()) {
|
||
|
|
flags |= 0x01;
|
||
|
|
}
|
||
|
|
|
||
|
|
flags |= ((message.fixedHeader().qosLevel().value() & 0x03) << 1);
|
||
|
|
return flags;
|
||
|
|
}
|
||
|
|
|
||
|
|
//3 = 3.1, 4 = 3.1.1
|
||
|
|
// static final AttributeKey<Integer> PROTOCOL_VERSION = AttributeKey.valueOf("version");
|
||
|
|
//
|
||
|
|
// static boolean isMQTT3_1_1(AttributeMap attrsMap) {
|
||
|
|
// Attribute<Integer> versionAttr = attrsMap.attr(PROTOCOL_VERSION);
|
||
|
|
// Integer protocolVersion = versionAttr.get();
|
||
|
|
// if (protocolVersion == null) {
|
||
|
|
// return true;
|
||
|
|
// }
|
||
|
|
// return protocolVersion == VERSION_3_1_1;
|
||
|
|
// }
|
||
|
|
}
|