Switched from Moquette's MQTT codec to Netty's MQTT codec ones
This commit is contained in:
+6
-13
@@ -1,4 +1,3 @@
|
||||
|
||||
<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>
|
||||
@@ -19,18 +18,6 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>moquette-netty-parser</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>com.lmax</groupId>-->
|
||||
<!--<artifactId>disruptor</artifactId>-->
|
||||
<!--<version>3.3.2</version>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
@@ -73,6 +60,12 @@
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-mqtt</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
|
||||
@@ -4,9 +4,12 @@ import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.core.ITopic;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.server.Server;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.moquette.spi.impl.Utils.readBytesAndRewind;
|
||||
|
||||
/**
|
||||
* Created by mackristof on 28/05/2016.
|
||||
*/
|
||||
@@ -26,7 +29,11 @@ public class HazelcastInterceptHandler extends AbstractInterceptHandler {
|
||||
|
||||
@Override
|
||||
public void onPublish(InterceptPublishMessage msg) {
|
||||
LOG.info("{} publish on {} message: {}", msg.getClientID(), msg.getTopicName(), new String(msg.getPayload().array()));
|
||||
//TODO ugly, too much array copy
|
||||
ByteBuf payload = msg.getPayload();
|
||||
byte[] payloadContent = readBytesAndRewind(payload);
|
||||
|
||||
LOG.info("{} publish on {} message: {}", msg.getClientID(), msg.getTopicName(), new String(payloadContent));
|
||||
ITopic<HazelcastMsg> topic = hz.getTopic("moquette");
|
||||
HazelcastMsg hazelcastMsg = new HazelcastMsg(msg);
|
||||
topic.publish(hazelcastMsg);
|
||||
|
||||
@@ -4,6 +4,8 @@ import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static io.moquette.spi.impl.Utils.readBytesAndRewind;
|
||||
|
||||
/**
|
||||
* Created by mackristof on 28/05/2016.
|
||||
*/
|
||||
@@ -11,22 +13,22 @@ public class HazelcastMsg implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1431584750134928273L;
|
||||
private final String clientId;
|
||||
private final byte qos;
|
||||
private final int qos;
|
||||
private final byte[] payload;
|
||||
private final String topic;
|
||||
|
||||
public HazelcastMsg(InterceptPublishMessage msg) {
|
||||
this.clientId = msg.getClientID();
|
||||
this.topic = msg.getTopicName();
|
||||
this.qos = msg.getQos().byteValue();
|
||||
this.payload = msg.getPayload().array();
|
||||
this.qos = msg.getQos().value();
|
||||
this.payload = readBytesAndRewind(msg.getPayload());
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public byte getQos() {
|
||||
public int getQos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,22 +15,16 @@
|
||||
*/
|
||||
package io.moquette.interception;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectionLostMessage;
|
||||
import io.moquette.interception.messages.InterceptDisconnectMessage;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.interception.messages.InterceptSubscribeMessage;
|
||||
import io.moquette.interception.messages.InterceptUnsubscribeMessage;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
|
||||
/**
|
||||
* This interface is used to inject code for intercepting broker events.
|
||||
* <p>
|
||||
* The events can act only as observers.
|
||||
* <p>
|
||||
* Almost every method receives a subclass of {@link AbstractMessage}, except
|
||||
* Almost every method receives a subclass of {@link MqttMessage}, except
|
||||
* <code>onDisconnect</code> that receives the client id string and
|
||||
* <code>onSubscribe</code> and <code>onUnsubscribe</code> that receive a
|
||||
* {@link Subscription} object.
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
package io.moquette.interception;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
|
||||
/**
|
||||
* This interface is to be used internally by the broker components.
|
||||
@@ -35,13 +35,13 @@ import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
*/
|
||||
public interface Interceptor {
|
||||
|
||||
void notifyClientConnected(ConnectMessage msg);
|
||||
void notifyClientConnected(MqttConnectMessage msg);
|
||||
|
||||
void notifyClientDisconnected(String clientID, String username);
|
||||
|
||||
void notifyClientConnectionLost(String clientID, String username);
|
||||
|
||||
void notifyTopicPublished(PublishMessage msg, String clientID, final String username);
|
||||
void notifyTopicPublished(MqttPublishMessage msg, String clientID, final String username);
|
||||
|
||||
void notifyTopicSubscribed(Subscription sub, final String username);
|
||||
|
||||
|
||||
+8
-7
@@ -15,27 +15,28 @@
|
||||
*/
|
||||
package io.moquette.interception.messages;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public abstract class InterceptAbstractMessage implements InterceptMessage {
|
||||
private final AbstractMessage msg;
|
||||
private final MqttMessage msg;
|
||||
|
||||
InterceptAbstractMessage(AbstractMessage msg) {
|
||||
InterceptAbstractMessage(MqttMessage msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public boolean isRetainFlag() {
|
||||
return msg.isRetainFlag();
|
||||
return msg.fixedHeader().isRetain();
|
||||
}
|
||||
|
||||
public boolean isDupFlag() {
|
||||
return msg.isDupFlag();
|
||||
return msg.fixedHeader().isDup();
|
||||
}
|
||||
|
||||
public AbstractMessage.QOSType getQos() {
|
||||
return msg.getQos();
|
||||
public MqttQoS getQos() {
|
||||
return msg.fixedHeader().qosLevel();
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -1,71 +1,71 @@
|
||||
package io.moquette.interception.messages;
|
||||
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public class InterceptConnectMessage extends InterceptAbstractMessage {
|
||||
private final ConnectMessage msg;
|
||||
private final MqttConnectMessage msg;
|
||||
|
||||
public InterceptConnectMessage(ConnectMessage msg) {
|
||||
public InterceptConnectMessage(MqttConnectMessage msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String getClientID() {
|
||||
return msg.getClientID();
|
||||
return msg.payload().clientIdentifier();
|
||||
}
|
||||
|
||||
public boolean isCleanSession() {
|
||||
return msg.isCleanSession();
|
||||
return msg.variableHeader().isCleanSession();
|
||||
}
|
||||
|
||||
public int getKeepAlive() {
|
||||
return msg.getKeepAlive();
|
||||
return msg.variableHeader().keepAliveTimeSeconds();
|
||||
}
|
||||
|
||||
public boolean isPasswordFlag() {
|
||||
return msg.isPasswordFlag();
|
||||
return msg.variableHeader().hasPassword();
|
||||
}
|
||||
|
||||
public byte getProtocolVersion() {
|
||||
return msg.getProtocolVersion();
|
||||
return (byte) msg.variableHeader().version();
|
||||
}
|
||||
|
||||
public String getProtocolName() {
|
||||
return msg.getProtocolName();
|
||||
return msg.variableHeader().name();
|
||||
}
|
||||
|
||||
public boolean isUserFlag() {
|
||||
return msg.isUserFlag();
|
||||
return msg.variableHeader().hasUserName();
|
||||
}
|
||||
|
||||
public boolean isWillFlag() {
|
||||
return msg.isWillFlag();
|
||||
return msg.variableHeader().isWillFlag();
|
||||
}
|
||||
|
||||
public byte getWillQos() {
|
||||
return msg.getWillQos();
|
||||
return (byte) msg.variableHeader().willQos();
|
||||
}
|
||||
|
||||
public boolean isWillRetain() {
|
||||
return msg.isWillRetain();
|
||||
return msg.variableHeader().isWillRetain();
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return msg.getUsername();
|
||||
return msg.payload().userName();
|
||||
}
|
||||
|
||||
public byte[] getPassword() {
|
||||
return msg.getPassword();
|
||||
return msg.payload().password().getBytes();
|
||||
}
|
||||
|
||||
public String getWillTopic() {
|
||||
return msg.getWillTopic();
|
||||
return msg.payload().willTopic();
|
||||
}
|
||||
|
||||
public byte[] getWillMessage() {
|
||||
return msg.getWillMessage();
|
||||
return msg.payload().willMessage().getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package io.moquette.interception.messages;
|
||||
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public class InterceptPublishMessage extends InterceptAbstractMessage {
|
||||
private final PublishMessage msg;
|
||||
private final MqttPublishMessage msg;
|
||||
private final String clientID;
|
||||
private final String username;
|
||||
|
||||
public InterceptPublishMessage(PublishMessage msg, String clientID, String username) {
|
||||
public InterceptPublishMessage(MqttPublishMessage msg, String clientID, String username) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
this.clientID = clientID;
|
||||
@@ -20,11 +19,11 @@ public class InterceptPublishMessage extends InterceptAbstractMessage {
|
||||
}
|
||||
|
||||
public String getTopicName() {
|
||||
return msg.getTopicName();
|
||||
return msg.variableHeader().topicName();
|
||||
}
|
||||
|
||||
public ByteBuffer getPayload() {
|
||||
return msg.getPayload();
|
||||
public ByteBuf getPayload() {
|
||||
return msg.payload();
|
||||
}
|
||||
|
||||
public String getClientID() {
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package io.moquette.interception.messages;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
@@ -19,7 +19,7 @@ public class InterceptSubscribeMessage implements InterceptMessage {
|
||||
return subscription.getClientId();
|
||||
}
|
||||
|
||||
public AbstractMessage.QOSType getRequestedQos() {
|
||||
public MqttQoS getRequestedQos() {
|
||||
return subscription.getRequestedQos();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
package io.moquette.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.moquette.connections.IConnectionsManager;
|
||||
import io.moquette.connections.MqttConnectionMetrics;
|
||||
import io.moquette.connections.MqttSession;
|
||||
import io.moquette.connections.MqttSubscription;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.server.netty.metrics.BytesMetrics;
|
||||
import io.moquette.server.netty.metrics.MessageMetrics;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttMessageType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class ConnectionDescriptorStore implements IConnectionsManager {
|
||||
|
||||
@@ -27,38 +27,29 @@ public class ConnectionDescriptorStore implements IConnectionsManager {
|
||||
private final ISessionsStore sessionsStore;
|
||||
|
||||
public ConnectionDescriptorStore(ISessionsStore sessionsStore) {
|
||||
this.connectionDescriptors = new ConcurrentHashMap<String, ConnectionDescriptor>();
|
||||
this.connectionDescriptors = new ConcurrentHashMap<>();
|
||||
this.sessionsStore = sessionsStore;
|
||||
}
|
||||
|
||||
public boolean sendMessage(AbstractMessage message, String clientId) {
|
||||
return sendMessage(message, null, clientId);
|
||||
}
|
||||
|
||||
public boolean sendMessage(AbstractMessage message, Integer messageID, String clientID) {
|
||||
public boolean sendMessage(MqttMessage message, Integer messageID, String clientID) {
|
||||
final MqttMessageType messageType = message.fixedHeader().messageType();
|
||||
try {
|
||||
// The connection descriptors map will never be null: it's a final
|
||||
// attribute.
|
||||
|
||||
if (messageID != null) {
|
||||
LOG.info("Sending {} message. MqttClientId = {}, messageId = {}.", message.getMessageType(), clientID,
|
||||
messageID);
|
||||
LOG.info("Sending {} message CId=<{}>, messageId={}", messageType, clientID, messageID);
|
||||
} else {
|
||||
LOG.debug("Sending {} message. MqttClientId = {}.", message.getMessageType(), clientID);
|
||||
LOG.debug("Sending {} message CId=<{}>", messageType, clientID);
|
||||
}
|
||||
|
||||
ConnectionDescriptor descriptor = connectionDescriptors.get(clientID);
|
||||
if (descriptor == null) {
|
||||
if (messageID != null) {
|
||||
LOG.error(
|
||||
"The client has just disconnected. The {} message could not be sent. MqttClientId = {}, messageId = {}.",
|
||||
message.getMessageType(), clientID, messageID);
|
||||
LOG.error("Client has just disconnected. The {} message could not be sent. CId=<{}>, messageId={}",
|
||||
messageType, clientID, messageID);
|
||||
} else {
|
||||
LOG.error("The client has just disconnected. The {} could not be sent. MqttClientId = {}.",
|
||||
message.getMessageType(), clientID);
|
||||
LOG.error("Client has just disconnected. The {} could not be sent. CId=<{}>", messageType, clientID);
|
||||
}
|
||||
/*
|
||||
* If the client hast just disconnected, its connection
|
||||
* If the client has just disconnected, its connection
|
||||
* descriptor will be null. We don't have to make the broker
|
||||
* crash: we'll just discard the PUBACK message.
|
||||
*/
|
||||
@@ -67,14 +58,11 @@ public class ConnectionDescriptorStore implements IConnectionsManager {
|
||||
descriptor.writeAndFlush(message);
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
String errorMsg = "Unable to send " + messageType + " message. CId=<" + clientID + ">";
|
||||
if (messageID != null) {
|
||||
LOG.error(
|
||||
"Unable to send {} message. MqttClientId = {}, messageId = {}, cause = {}, errorMessage = {}.",
|
||||
message.getMessageType(), clientID, messageID, e.getCause(), e.getMessage());
|
||||
} else {
|
||||
LOG.error("Unable to send {} message. MqttClientId = {}, cause = {}, errorMessage = {}.",
|
||||
message.getMessageType(), clientID, e.getCause(), e.getMessage());
|
||||
errorMsg += ", messageId=" + messageID;
|
||||
}
|
||||
LOG.error(errorMsg, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -111,7 +99,7 @@ public class ConnectionDescriptorStore implements IConnectionsManager {
|
||||
ConnectionDescriptor descriptor = connectionDescriptors.get(clientID);
|
||||
if (descriptor == null) {
|
||||
LOG.error(
|
||||
"The connection descriptor doesn't exist. The MQTT connection cannot be closed. MqttClientId = {}, closeImmediately = {}.",
|
||||
"The connection descriptor doesn't exist. The MQTT connection cannot be closed. CId=<{}>, closeImmediately = {}.",
|
||||
clientID, closeImmediately);
|
||||
return false;
|
||||
}
|
||||
@@ -125,10 +113,10 @@ public class ConnectionDescriptorStore implements IConnectionsManager {
|
||||
|
||||
@Override
|
||||
public MqttSession getSessionStatus(String clientID) {
|
||||
LOG.info("Retrieving status of session. MqttClientId = {}.", clientID);
|
||||
LOG.info("Retrieving status of session. CId=<{}>", clientID);
|
||||
ClientSession session = sessionsStore.sessionForClient(clientID);
|
||||
if (session == null) {
|
||||
LOG.error("The given MQTT client ID doesn't have an associated session. MqttClientId = {}.", clientID);
|
||||
LOG.error("The given MQTT client ID doesn't have an associated session. CId=<{}>", clientID);
|
||||
return null;
|
||||
}
|
||||
return buildMqttSession(session);
|
||||
@@ -166,7 +154,7 @@ public class ConnectionDescriptorStore implements IConnectionsManager {
|
||||
result.setPendingPublishMessagesNo(session.getPendingPublishMessagesNo());
|
||||
result.setSecondPhaseAckPendingMessages(session.getSecondPhaseAckPendingMessages());
|
||||
result.setInflightMessages(session.getInflightMessagesNo());
|
||||
LOG.info("The status of the session has been retrieved successfully. MqttClientId = {}.", session.clientID);
|
||||
LOG.info("The status of the session has been retrieved successfully. CId=<{}>", session.clientID);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@ package io.moquette.server;
|
||||
import com.hazelcast.core.Message;
|
||||
import com.hazelcast.core.MessageListener;
|
||||
import io.moquette.interception.HazelcastMsg;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Created by mackristof on 28/05/2016.
|
||||
*/
|
||||
@@ -29,13 +28,14 @@ public class HazelcastListener implements MessageListener<HazelcastMsg> {
|
||||
HazelcastMsg hzMsg = msg.getMessageObject();
|
||||
LOG.info("{} received from hazelcast for topic {} message: {}", hzMsg.getClientId(),
|
||||
hzMsg.getTopic(), hzMsg.getPayload());
|
||||
PublishMessage publishMessage = new PublishMessage();
|
||||
publishMessage.setTopicName(hzMsg.getTopic());
|
||||
publishMessage.setQos(AbstractMessage.QOSType.valueOf(hzMsg.getQos()));
|
||||
publishMessage.setPayload(ByteBuffer.wrap(hzMsg.getPayload()));
|
||||
publishMessage.setLocal(false);
|
||||
publishMessage.setClientId(hzMsg.getClientId());
|
||||
server.internalPublish(publishMessage);
|
||||
//TODO pass forward this information in somehow publishMessage.setLocal(false);
|
||||
|
||||
MqttQoS qos = MqttQoS.valueOf(hzMsg.getQos());
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, false, 0);
|
||||
MqttPublishVariableHeader varHeader = new MqttPublishVariableHeader(hzMsg.getTopic(), 0);
|
||||
ByteBuf payload = Unpooled.wrappedBuffer(hzMsg.getPayload());
|
||||
MqttPublishMessage publishMessage = new MqttPublishMessage(fixedHeader, varHeader, payload);
|
||||
server.internalPublish(publishMessage, hzMsg.getClientId());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("error polling hazelcast msg queue", ex);
|
||||
|
||||
@@ -27,15 +27,15 @@ import io.moquette.connections.IConnectionsManager;
|
||||
import io.moquette.interception.HazelcastInterceptHandler;
|
||||
import io.moquette.interception.HazelcastMsg;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.config.*;
|
||||
import io.moquette.spi.impl.ProtocolProcessorBootstrapper;
|
||||
import io.moquette.server.netty.NettyAcceptor;
|
||||
import io.moquette.spi.impl.ProtocolProcessor;
|
||||
import io.moquette.spi.impl.ProtocolProcessorBootstrapper;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.security.IAuthenticator;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import io.moquette.spi.security.ISslContextCreator;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -221,16 +221,18 @@ public class Server {
|
||||
* It can be used only after the server is correctly started with startServer.
|
||||
*
|
||||
* @param msg the message to forward.
|
||||
* @param clientId the id of the sending server.
|
||||
* @throws IllegalStateException if the server is not yet started
|
||||
* */
|
||||
public void internalPublish(PublishMessage msg) {
|
||||
public void internalPublish(MqttPublishMessage msg, final String clientId) {
|
||||
final int messageID = msg.variableHeader().messageId();
|
||||
if (!m_initialized) {
|
||||
LOG.error("The server is not started. The message cannot be published. MqttClientId = {}, messageId = {}.",
|
||||
msg.getClientId(), msg.getMessageID());
|
||||
LOG.error("The server is not started. The message cannot be published. MqttClientId = {}, messageId = {}",
|
||||
clientId, messageID);
|
||||
throw new IllegalStateException("Can't publish on a server is not yet started");
|
||||
}
|
||||
LOG.debug("Publishing message. MqttClientId = {}, messageId = {}.", msg.getClientId(), msg.getMessageID());
|
||||
m_processor.internalPublish(msg);
|
||||
LOG.debug("Publishing message. MqttClientId = {}, messageId = {}", clientId, messageID);
|
||||
m_processor.internalPublish(msg, clientId);
|
||||
}
|
||||
|
||||
public void stopServer() {
|
||||
@@ -266,7 +268,6 @@ public class Server {
|
||||
/**
|
||||
* SPI method used by Broker embedded applications to add intercept handlers.
|
||||
* @param interceptHandler the handler to add.
|
||||
* @return true id operation was successful.
|
||||
* */
|
||||
public void addInterceptHandler(InterceptHandler interceptHandler) {
|
||||
if (!m_initialized) {
|
||||
@@ -282,7 +283,6 @@ public class Server {
|
||||
/**
|
||||
* SPI method used by Broker embedded applications to remove intercept handlers.
|
||||
* @param interceptHandler the handler to remove.
|
||||
* @return true id operation was successful.
|
||||
* */
|
||||
public void removeInterceptHandler(InterceptHandler interceptHandler) {
|
||||
if (!m_initialized) {
|
||||
@@ -297,7 +297,7 @@ public class Server {
|
||||
/**
|
||||
* Returns the connections manager of this broker.
|
||||
*
|
||||
* @return
|
||||
* @return IConnectionsManager the instance used bt the broker.
|
||||
*/
|
||||
public IConnectionsManager getConnectionsManager() {
|
||||
return m_processorBootstrapper.getConnectionDescriptors();
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.server.netty;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MessageBuilder {
|
||||
|
||||
public static class PublishBuilder {
|
||||
private String topic = null;
|
||||
private boolean retained = false;
|
||||
private MqttQoS qos;
|
||||
private byte[] payload;
|
||||
private int messageId = 0;
|
||||
|
||||
public PublishBuilder topicName(String topic) {
|
||||
this.topic = topic;
|
||||
return PublishBuilder.this;
|
||||
}
|
||||
|
||||
public PublishBuilder retained(boolean retained) {
|
||||
this.retained = retained;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublishBuilder qos(MqttQoS qos) {
|
||||
this.qos = qos;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublishBuilder payload(byte[] payload) {
|
||||
this.payload = payload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublishBuilder messageId(int messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttPublishMessage build() {
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0);
|
||||
MqttPublishVariableHeader mqttVariableHeader = new MqttPublishVariableHeader(topic, messageId);
|
||||
return new MqttPublishMessage(mqttFixedHeader, mqttVariableHeader, Unpooled.buffer().writeBytes(payload));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConnectBuilder {
|
||||
|
||||
private MqttVersion version = MqttVersion.MQTT_3_1_1;
|
||||
private String clientId;
|
||||
private boolean cleanSession = false;
|
||||
private boolean hasUser = false;
|
||||
private boolean hasPassword = false;
|
||||
private int keepAliveSecs;
|
||||
private boolean willFlag = false;
|
||||
private MqttQoS willQos = MqttQoS.AT_MOST_ONCE;
|
||||
private String willTopic = null;
|
||||
private String willMessage = null;
|
||||
private String username = null;
|
||||
private String password = null;
|
||||
|
||||
public ConnectBuilder protocolVersion(MqttVersion version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder clientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder cleanSession(boolean cleanSession) {
|
||||
this.cleanSession = cleanSession;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder keepAlive(int keepAliveSecs) {
|
||||
this.keepAliveSecs = keepAliveSecs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder willFlag(boolean willFlag) {
|
||||
this.willFlag = willFlag;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder willQoS(MqttQoS willQos) {
|
||||
this.willQos = willQos;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder willTopic(String willTopic) {
|
||||
this.willTopic = willTopic;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder willMessage(String willMessage) {
|
||||
this.willMessage = willMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder hasUser() {
|
||||
this.hasUser = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder hasPassword() {
|
||||
this.hasPassword = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectBuilder password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttConnectMessage build() {
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||
MqttConnectVariableHeader mqttConnectVariableHeader =
|
||||
new MqttConnectVariableHeader(
|
||||
version.protocolName(),
|
||||
version.protocolLevel(),
|
||||
hasUser,
|
||||
hasPassword,
|
||||
false,
|
||||
willQos.value(),
|
||||
willFlag,
|
||||
cleanSession,
|
||||
keepAliveSecs);
|
||||
MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(clientId, willTopic, willMessage, username, password);
|
||||
return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SubscribeBuilder {
|
||||
|
||||
private List<MqttTopicSubscription> subscriptions = new ArrayList<>();
|
||||
private int messageId;
|
||||
|
||||
public SubscribeBuilder addSubscription(MqttQoS qos, String topic) {
|
||||
subscriptions.add(new MqttTopicSubscription(topic, qos));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubscribeBuilder messageId(int messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttSubscribeMessage build() {
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
|
||||
MqttMessageIdVariableHeader mqttVariableHeader = MqttMessageIdVariableHeader.from(messageId);
|
||||
MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(subscriptions);
|
||||
return new MqttSubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnsubscribeBuilder {
|
||||
|
||||
private List<String> topicFilters = new ArrayList<>();
|
||||
private int messageId = 0;
|
||||
|
||||
public UnsubscribeBuilder addTopicFilter(String topic) {
|
||||
topicFilters.add(topic);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UnsubscribeBuilder messageId(int messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttUnsubscribeMessage build() {
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false,
|
||||
MqttQoS.AT_LEAST_ONCE, false, 0);
|
||||
MqttMessageIdVariableHeader mqttVariableHeader = MqttMessageIdVariableHeader.from(messageId);
|
||||
MqttUnsubscribePayload mqttSubscribePayload = new MqttUnsubscribePayload(topicFilters);
|
||||
return new MqttUnsubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
|
||||
}
|
||||
}
|
||||
|
||||
public static ConnectBuilder connect() {
|
||||
return new ConnectBuilder();
|
||||
}
|
||||
|
||||
public static PublishBuilder publish() {
|
||||
return new PublishBuilder();
|
||||
}
|
||||
|
||||
public static SubscribeBuilder subscribe() {
|
||||
return new SubscribeBuilder();
|
||||
}
|
||||
|
||||
public static UnsubscribeBuilder unsubscribe() {
|
||||
return new UnsubscribeBuilder();
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,11 @@
|
||||
package io.moquette.server.netty;
|
||||
|
||||
import io.moquette.BrokerConstants;
|
||||
import static io.moquette.BrokerConstants.*;
|
||||
import io.moquette.parser.netty.MQTTDecoder;
|
||||
import io.moquette.parser.netty.MQTTEncoder;
|
||||
import io.moquette.spi.security.ISslContextCreator;
|
||||
import io.moquette.server.ServerAcceptor;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.server.netty.metrics.*;
|
||||
import io.moquette.spi.impl.ProtocolProcessor;
|
||||
import io.moquette.spi.security.ISslContextCreator;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
@@ -38,6 +35,8 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import io.netty.handler.codec.mqtt.MqttDecoder;
|
||||
import io.netty.handler.codec.mqtt.MqttEncoder;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import io.netty.util.concurrent.Future;
|
||||
@@ -50,6 +49,8 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.moquette.BrokerConstants.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
@@ -179,8 +180,8 @@ public class NettyAcceptor implements ServerAcceptor {
|
||||
pipeline.addAfter("idleStateHandler", "idleEventHandler", timeoutHandler);
|
||||
// pipeline.addLast("logger", new LoggingHandler("Netty", LogLevel.ERROR));
|
||||
pipeline.addFirst("bytemetrics", new BytesMetricsHandler(m_bytesMetricsCollector));
|
||||
pipeline.addLast("decoder", new MQTTDecoder());
|
||||
pipeline.addLast("encoder", new MQTTEncoder());
|
||||
pipeline.addLast("decoder", new MqttDecoder());
|
||||
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||
pipeline.addLast("metrics", new MessageMetricsHandler(m_metricsCollector));
|
||||
pipeline.addLast("messageLogger", new MQTTMessageLogger());
|
||||
pipeline.addLast("handler", handler);
|
||||
@@ -213,8 +214,8 @@ public class NettyAcceptor implements ServerAcceptor {
|
||||
pipeline.addFirst("idleStateHandler", new IdleStateHandler(0, 0, nettyChannelTimeoutSeconds));
|
||||
pipeline.addAfter("idleStateHandler", "idleEventHandler", timeoutHandler);
|
||||
pipeline.addFirst("bytemetrics", new BytesMetricsHandler(m_bytesMetricsCollector));
|
||||
pipeline.addLast("decoder", new MQTTDecoder());
|
||||
pipeline.addLast("encoder", new MQTTEncoder());
|
||||
pipeline.addLast("decoder", new MqttDecoder());
|
||||
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||
pipeline.addLast("metrics", new MessageMetricsHandler(m_metricsCollector));
|
||||
pipeline.addLast("messageLogger", new MQTTMessageLogger());
|
||||
pipeline.addLast("handler", handler);
|
||||
@@ -247,8 +248,8 @@ public class NettyAcceptor implements ServerAcceptor {
|
||||
pipeline.addAfter("idleStateHandler", "idleEventHandler", timeoutHandler);
|
||||
//pipeline.addLast("logger", new LoggingHandler("Netty", LogLevel.ERROR));
|
||||
pipeline.addFirst("bytemetrics", new BytesMetricsHandler(m_bytesMetricsCollector));
|
||||
pipeline.addLast("decoder", new MQTTDecoder());
|
||||
pipeline.addLast("encoder", new MQTTEncoder());
|
||||
pipeline.addLast("decoder", new MqttDecoder());
|
||||
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||
pipeline.addLast("metrics", new MessageMetricsHandler(m_metricsCollector));
|
||||
pipeline.addLast("messageLogger", new MQTTMessageLogger());
|
||||
pipeline.addLast("handler", handler);
|
||||
@@ -283,8 +284,8 @@ public class NettyAcceptor implements ServerAcceptor {
|
||||
pipeline.addFirst("idleStateHandler", new IdleStateHandler(0, 0, nettyChannelTimeoutSeconds));
|
||||
pipeline.addAfter("idleStateHandler", "idleEventHandler", timeoutHandler);
|
||||
pipeline.addFirst("bytemetrics", new BytesMetricsHandler(m_bytesMetricsCollector));
|
||||
pipeline.addLast("decoder", new MQTTDecoder());
|
||||
pipeline.addLast("encoder", new MQTTEncoder());
|
||||
pipeline.addLast("decoder", new MqttDecoder());
|
||||
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||
pipeline.addLast("metrics", new MessageMetricsHandler(m_metricsCollector));
|
||||
pipeline.addLast("messageLogger", new MQTTMessageLogger());
|
||||
pipeline.addLast("handler", handler);
|
||||
|
||||
@@ -15,18 +15,16 @@
|
||||
*/
|
||||
package io.moquette.server.netty;
|
||||
|
||||
import io.moquette.parser.proto.Utils;
|
||||
import io.moquette.parser.proto.messages.*;
|
||||
import io.moquette.spi.impl.ProtocolProcessor;
|
||||
import static io.moquette.parser.proto.messages.AbstractMessage.*;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -44,47 +42,46 @@ public class NettyMQTTHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object message) {
|
||||
AbstractMessage msg = (AbstractMessage) message;
|
||||
String messageType = Utils.msgType2String(msg.getMessageType());
|
||||
LOG.debug("Processing MQTT message. MessageType = {}.", messageType);
|
||||
MqttMessage msg = (MqttMessage) message;
|
||||
MqttMessageType messageType = msg.fixedHeader().messageType();
|
||||
LOG.debug("Processing MQTT message, type={}", messageType);
|
||||
try {
|
||||
switch (msg.getMessageType()) {
|
||||
switch (messageType) {
|
||||
case CONNECT:
|
||||
m_processor.processConnect(ctx.channel(), (ConnectMessage) msg);
|
||||
m_processor.processConnect(ctx.channel(), (MqttConnectMessage) msg);
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
m_processor.processSubscribe(ctx.channel(), (SubscribeMessage) msg);
|
||||
m_processor.processSubscribe(ctx.channel(), (MqttSubscribeMessage) msg);
|
||||
break;
|
||||
case UNSUBSCRIBE:
|
||||
m_processor.processUnsubscribe(ctx.channel(), (UnsubscribeMessage) msg);
|
||||
m_processor.processUnsubscribe(ctx.channel(), (MqttUnsubscribeMessage) msg);
|
||||
break;
|
||||
case PUBLISH:
|
||||
m_processor.processPublish(ctx.channel(), (PublishMessage) msg);
|
||||
m_processor.processPublish(ctx.channel(), (MqttPublishMessage) msg);
|
||||
break;
|
||||
case PUBREC:
|
||||
m_processor.processPubRec(ctx.channel(), (PubRecMessage) msg);
|
||||
m_processor.processPubRec(ctx.channel(), msg);
|
||||
break;
|
||||
case PUBCOMP:
|
||||
m_processor.processPubComp(ctx.channel(), (PubCompMessage) msg);
|
||||
m_processor.processPubComp(ctx.channel(), msg);
|
||||
break;
|
||||
case PUBREL:
|
||||
m_processor.processPubRel(ctx.channel(), (PubRelMessage) msg);
|
||||
m_processor.processPubRel(ctx.channel(), msg);
|
||||
break;
|
||||
case DISCONNECT:
|
||||
m_processor.processDisconnect(ctx.channel());
|
||||
break;
|
||||
case PUBACK:
|
||||
m_processor.processPubAck(ctx.channel(), (PubAckMessage) msg);
|
||||
m_processor.processPubAck(ctx.channel(), (MqttPubAckMessage) msg);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
LOG.error(
|
||||
"An unexpected exception was caught while processing MQTT message. MessageType = {}, cause = {}, errorMessage = {}.",
|
||||
messageType, ex.getCause(), ex.getMessage());
|
||||
LOG.error("Exception was caught while processing MQTT message, " + ex.getCause(), ex);
|
||||
ctx.fireExceptionCaught(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package io.moquette.server.netty.metrics;
|
||||
|
||||
import io.moquette.parser.proto.messages.*;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.moquette.parser.proto.messages.AbstractMessage.*;
|
||||
import static io.moquette.spi.impl.Utils.messageId;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -42,46 +42,42 @@ public class MQTTMessageLogger extends ChannelDuplexHandler {
|
||||
}
|
||||
|
||||
private void logMQTTMessage(ChannelHandlerContext ctx, Object message, String direction) {
|
||||
if (!(message instanceof AbstractMessage)) {
|
||||
if (!(message instanceof MqttMessage)) {
|
||||
return;
|
||||
}
|
||||
AbstractMessage msg = (AbstractMessage) message;
|
||||
MqttMessage msg = (MqttMessage) message;
|
||||
String clientID = NettyUtils.clientID(ctx.channel());
|
||||
switch (msg.getMessageType()) {
|
||||
MqttMessageType messageType = msg.fixedHeader().messageType();
|
||||
switch (messageType) {
|
||||
case CONNECT:
|
||||
ConnectMessage connect = (ConnectMessage) msg;
|
||||
LOG.info("{} CONNECT client <{}>", direction, connect.getClientID());
|
||||
LOG.info("{} CONNECT client <{}>", direction, clientID);
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
SubscribeMessage subscribe = (SubscribeMessage) msg;
|
||||
LOG.info("{} SUBSCRIBE <{}> to topics {}", direction, clientID, subscribe.subscriptions());
|
||||
MqttSubscribeMessage subscribe = (MqttSubscribeMessage) msg;
|
||||
LOG.info("{} SUBSCRIBE <{}> to topics {}", direction, clientID, subscribe.payload().topicSubscriptions());
|
||||
break;
|
||||
case UNSUBSCRIBE:
|
||||
UnsubscribeMessage unsubscribe = (UnsubscribeMessage) msg;
|
||||
LOG.info("{} UNSUBSCRIBE <{}> to topics <{}>", direction, clientID, unsubscribe.topicFilters());
|
||||
MqttUnsubscribeMessage unsubscribe = (MqttUnsubscribeMessage) msg;
|
||||
LOG.info("{} UNSUBSCRIBE <{}> to topics <{}>", direction, clientID, unsubscribe.payload().topics());
|
||||
break;
|
||||
case PUBLISH:
|
||||
PublishMessage publish = (PublishMessage) msg;
|
||||
LOG.info("{} PUBLISH <{}> to topics <{}>", direction, clientID, publish.getTopicName());
|
||||
MqttPublishMessage publish = (MqttPublishMessage) msg;
|
||||
LOG.info("{} PUBLISH <{}> to topics <{}>", direction, clientID, publish.variableHeader().topicName());
|
||||
break;
|
||||
case PUBREC:
|
||||
PubRecMessage pubrec = (PubRecMessage) msg;
|
||||
LOG.info("{} PUBREC <{}> packetID <{}>", direction, clientID, pubrec.getMessageID());
|
||||
LOG.info("{} PUBREC <{}> packetID <{}>", direction, clientID, messageId(msg));
|
||||
break;
|
||||
case PUBCOMP:
|
||||
PubCompMessage pubCompleted = (PubCompMessage) msg;
|
||||
LOG.info("{} PUBCOMP <{}> packetID <{}>", direction, clientID, pubCompleted.getMessageID());
|
||||
LOG.info("{} PUBCOMP <{}> packetID <{}>", direction, clientID, messageId(msg));
|
||||
break;
|
||||
case PUBREL:
|
||||
PubRelMessage pubRelease = (PubRelMessage) msg;
|
||||
LOG.info("{} PUBREL <{}> packetID <{}>", direction, clientID, pubRelease.getMessageID());
|
||||
LOG.info("{} PUBREL <{}> packetID <{}>", direction, clientID, messageId(msg));
|
||||
break;
|
||||
case DISCONNECT:
|
||||
LOG.info("{} DISCONNECT <{}>", direction, clientID);
|
||||
break;
|
||||
case PUBACK:
|
||||
PubAckMessage pubAck = (PubAckMessage) msg;
|
||||
LOG.info("{} PUBACK <{}> packetID <{}>", direction, clientID, pubAck.getMessageID());
|
||||
LOG.info("{} PUBACK <{}> packetID <{}>", direction, clientID, messageId(msg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -100,24 +96,4 @@ public class MQTTMessageLogger extends ChannelDuplexHandler {
|
||||
logMQTTMessage(ctx, msg, "C<-B");
|
||||
ctx.write(msg, promise);
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
// if (cause instanceof CorruptedFrameException) {
|
||||
// //something goes bad with decoding
|
||||
// LOG.warn("Error decoding a packet, probably a bad formatted packet, message: " + cause.getMessage());
|
||||
// } else {
|
||||
// LOG.error("Ugly error on networking", cause);
|
||||
// }
|
||||
// ctx.close();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
// if (ctx.channel().isWritable()) {
|
||||
// m_processor.notifyChannelWritable(ctx.channel());
|
||||
// }
|
||||
// ctx.fireChannelWritabilityChanged();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.moquette.spi;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
@@ -85,7 +84,7 @@ public class ClientSession {
|
||||
|
||||
public boolean subscribe(Subscription newSubscription) {
|
||||
LOG.info("Adding new subscription. MqttClientId = {}, topics = {}, qos = {}.", newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter(), AbstractMessage.QOSType.formatQoS(newSubscription.getRequestedQos()));
|
||||
newSubscription.getTopicFilter(), newSubscription.getRequestedQos());
|
||||
boolean validTopic = SubscriptionsStore.validate(newSubscription.getTopicFilter());
|
||||
if (!validTopic) {
|
||||
LOG.warn("The topic filter is not valid. MqttClientId = {}, topics = {}.", newSubscription.getClientId(),
|
||||
@@ -96,7 +95,7 @@ public class ClientSession {
|
||||
ClientTopicCouple matchingCouple = new ClientTopicCouple(this.clientID, newSubscription.getTopicFilter());
|
||||
Subscription existingSub = m_sessionsStore.getSubscription(matchingCouple);
|
||||
//update the selected subscriptions if not present or if has a greater qos
|
||||
if (existingSub == null || existingSub.getRequestedQos().byteValue() < newSubscription.getRequestedQos().byteValue()) {
|
||||
if (existingSub == null || existingSub.getRequestedQos().value() < newSubscription.getRequestedQos().value()) {
|
||||
if (existingSub != null) {
|
||||
LOG.info("The subscription already existed with a lower QoS value. It will be updated. MqttClientId = {}, topics = {}, existingQos = {}, newQos = {}.",
|
||||
newSubscription.getClientId(), newSubscription.getTopicFilter(), existingSub.getRequestedQos(),
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package io.moquette.spi;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.ByteBuffer;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -28,7 +30,7 @@ public interface IMessagesStore {
|
||||
|
||||
class StoredMessage implements Serializable {
|
||||
private static final long serialVersionUID = 1755296138639817304L;
|
||||
final AbstractMessage.QOSType m_qos;
|
||||
final MqttQoS m_qos;
|
||||
final byte[] m_payload;
|
||||
final String m_topic;
|
||||
private boolean m_retained;
|
||||
@@ -37,13 +39,13 @@ public interface IMessagesStore {
|
||||
private Integer m_msgID;
|
||||
private MessageGUID m_guid;
|
||||
|
||||
public StoredMessage(byte[] message, AbstractMessage.QOSType qos, String topic) {
|
||||
public StoredMessage(byte[] message, MqttQoS qos, String topic) {
|
||||
m_qos = qos;
|
||||
m_payload = message;
|
||||
m_topic = topic;
|
||||
}
|
||||
|
||||
public AbstractMessage.QOSType getQos() {
|
||||
public MqttQoS getQos() {
|
||||
return m_qos;
|
||||
}
|
||||
|
||||
@@ -79,8 +81,8 @@ public interface IMessagesStore {
|
||||
return m_msgID;
|
||||
}
|
||||
|
||||
public ByteBuffer getMessage() {
|
||||
return ByteBuffer.wrap(m_payload);
|
||||
public ByteBuf getMessage() {
|
||||
return Unpooled.copiedBuffer(m_payload);
|
||||
}
|
||||
|
||||
public void setRetained(boolean retained) {
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.Interceptor;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -23,23 +34,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.Interceptor;
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectionLostMessage;
|
||||
import io.moquette.interception.messages.InterceptDisconnectMessage;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.interception.messages.InterceptSubscribeMessage;
|
||||
import io.moquette.interception.messages.InterceptUnsubscribeMessage;
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static io.moquette.logging.LoggingUtils.getInterceptorIds;
|
||||
|
||||
/**
|
||||
@@ -98,10 +92,10 @@ final class BrokerInterceptor implements Interceptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientConnected(final ConnectMessage msg) {
|
||||
public void notifyClientConnected(final MqttConnectMessage msg) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptConnectMessage.class)) {
|
||||
LOG.debug("Sending MQTT CONNECT message to interceptor. MqttClientId = {}, interceptorId = {}.",
|
||||
msg.getClientID(), handler.getID());
|
||||
msg.payload().clientIdentifier(), handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -140,10 +134,12 @@ final class BrokerInterceptor implements Interceptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicPublished(final PublishMessage msg, final String clientID, final String username) {
|
||||
public void notifyTopicPublished(final MqttPublishMessage msg, final String clientID, final String username) {
|
||||
int messageId = msg.variableHeader().messageId();
|
||||
String topic = msg.variableHeader().topicName();
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptPublishMessage.class)) {
|
||||
LOG.debug("Notifying MQTT PUBLISH message to interceptor. MqttClientId = {}, messageId = {}, topic = {}, " +
|
||||
"interceptorId = {}.", clientID, msg.getMessageID(), msg.getTopicName(), handler.getID());
|
||||
"interceptorId = {}.", clientID, messageId, topic, handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
@@ -22,11 +24,16 @@ import java.nio.ByteBuffer;
|
||||
* @author andrea
|
||||
*/
|
||||
class DebugUtils {
|
||||
static String payload2Str(ByteBuffer content) {
|
||||
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) {
|
||||
//byte[] b = new byte[content.readableBytes()];
|
||||
return new String(content.copy().array());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -27,21 +26,23 @@ class InternalRepublisher {
|
||||
void publishRetained(ClientSession targetSession, Collection<IMessagesStore.StoredMessage> messages) {
|
||||
for (IMessagesStore.StoredMessage storedMsg : messages) {
|
||||
//fire as retained the message
|
||||
Integer packetID = storedMsg.getQos() == AbstractMessage.QOSType.MOST_ONE ? null : targetSession.nextPacketId();
|
||||
Integer packetID = storedMsg.getQos() == MqttQoS.AT_MOST_ONCE ? null : targetSession.nextPacketId();
|
||||
if (packetID != null) {
|
||||
LOG.debug("Adding message to inflight zone. MqttClientId = {}, packetId = {}, messageId = {}, topic = {}.",
|
||||
targetSession.clientID, packetID, storedMsg.getMessageID(), storedMsg.getTopic());
|
||||
targetSession.inFlightAckWaiting(storedMsg.getGuid(), packetID);
|
||||
}
|
||||
PublishMessage publishMsg = retainedPublish(storedMsg);
|
||||
MqttPublishMessage publishMsg = retainedPublish(storedMsg);
|
||||
//set the PacketIdentifier only for QoS > 0
|
||||
if (publishMsg.getQos() != AbstractMessage.QOSType.MOST_ONE) {
|
||||
publishMsg.setMessageID(packetID);
|
||||
if (publishMsg.fixedHeader().qosLevel() != MqttQoS.AT_MOST_ONCE) {
|
||||
publishMsg = retainedPublish(storedMsg, packetID);
|
||||
}
|
||||
this.messageSender.sendPublish(targetSession, publishMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void publishStored(ClientSession clientSession, BlockingQueue<IMessagesStore.StoredMessage> publishedEvents) {
|
||||
List<IMessagesStore.StoredMessage> storedPublishes = new ArrayList<>();
|
||||
publishedEvents.drainTo(storedPublishes);
|
||||
@@ -51,29 +52,34 @@ class InternalRepublisher {
|
||||
LOG.debug("Adding message ot inflight zone. MqttClientId = {}, guid = {}, messageId = {}, topic = {}.",
|
||||
clientSession.clientID, pubEvt.getGuid(), pubEvt.getMessageID(), pubEvt.getTopic());
|
||||
clientSession.inFlightAckWaiting(pubEvt.getGuid(), pubEvt.getMessageID());
|
||||
PublishMessage publishMsg = notRetainedPublish(pubEvt);
|
||||
MqttPublishMessage publishMsg = notRetainedPublish(pubEvt);
|
||||
//set the PacketIdentifier only for QoS > 0
|
||||
if (publishMsg.getQos() != AbstractMessage.QOSType.MOST_ONE) {
|
||||
publishMsg.setMessageID(pubEvt.getMessageID());
|
||||
if (publishMsg.fixedHeader().qosLevel() != MqttQoS.AT_MOST_ONCE) {
|
||||
publishMsg = notRetainedPublish(pubEvt, pubEvt.getMessageID());
|
||||
}
|
||||
this.messageSender.sendPublish(clientSession, publishMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private PublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage) {
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false);
|
||||
private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage, Integer messageID) {
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false, messageID);
|
||||
}
|
||||
|
||||
private PublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage) {
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true);
|
||||
private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage) {
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false, 0);
|
||||
}
|
||||
|
||||
public static PublishMessage createPublishForQos(String topic, AbstractMessage.QOSType qos, ByteBuffer message, boolean retained) {
|
||||
PublishMessage pubMessage = new PublishMessage();
|
||||
pubMessage.setRetainFlag(retained);
|
||||
pubMessage.setTopicName(topic);
|
||||
pubMessage.setQos(qos);
|
||||
pubMessage.setPayload(message);
|
||||
return pubMessage;
|
||||
private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage) {
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true, 0);
|
||||
}
|
||||
|
||||
private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage, Integer packetID) {
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true, packetID);
|
||||
}
|
||||
|
||||
public static MqttPublishMessage createPublishForQos(String topic, MqttQoS qos, ByteBuf message, boolean retained, int messageId) {
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0);
|
||||
MqttPublishVariableHeader varHeader = new MqttPublishVariableHeader(topic, messageId);
|
||||
return new MqttPublishMessage(fixedHeader, varHeader, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.ConnectionDescriptorStore;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import static io.moquette.spi.impl.ProtocolProcessor.lowerQosToTheSubscriptionDesired;
|
||||
@@ -32,50 +31,51 @@ class MessagesPublisher {
|
||||
this.messageSender = messageSender;
|
||||
}
|
||||
|
||||
static PublishMessage notRetainedPublish(String topic, AbstractMessage.QOSType qos, ByteBuffer message) {
|
||||
PublishMessage pubMessage = new PublishMessage();
|
||||
pubMessage.setRetainFlag(false);
|
||||
pubMessage.setTopicName(topic);
|
||||
pubMessage.setQos(qos);
|
||||
pubMessage.setPayload(message);
|
||||
return pubMessage;
|
||||
static MqttPublishMessage notRetainedPublish(String topic, MqttQoS qos, ByteBuf message) {
|
||||
return notRetainedPublishWithMessageId(topic, qos, message, 0);
|
||||
}
|
||||
|
||||
private static MqttPublishMessage notRetainedPublishWithMessageId(String topic, MqttQoS qos, ByteBuf message, int messageId) {
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, false, 0);
|
||||
MqttPublishVariableHeader varHeader = new MqttPublishVariableHeader(topic, messageId);
|
||||
return new MqttPublishMessage(fixedHeader, varHeader, message);
|
||||
}
|
||||
|
||||
void publish2Subscribers(IMessagesStore.StoredMessage pubMsg, List<Subscription> topicMatchingSubscriptions) {
|
||||
final String topic = pubMsg.getTopic();
|
||||
final AbstractMessage.QOSType publishingQos = pubMsg.getQos();
|
||||
final ByteBuffer origMessage = pubMsg.getMessage();
|
||||
final MqttQoS publishingQos = pubMsg.getQos();
|
||||
final ByteBuf origMessage = pubMsg.getMessage();
|
||||
|
||||
//if QoS 1 or 2 store the message
|
||||
//TODO perhaps this block is not needed.
|
||||
MessageGUID guid = null;
|
||||
if (publishingQos != AbstractMessage.QOSType.MOST_ONE) {
|
||||
if (publishingQos != MqttQoS.AT_MOST_ONCE) {
|
||||
guid = m_messagesStore.storePublishForFuture(pubMsg);
|
||||
}
|
||||
|
||||
for (final Subscription sub : topicMatchingSubscriptions) {
|
||||
AbstractMessage.QOSType qos = lowerQosToTheSubscriptionDesired(sub, publishingQos);
|
||||
MqttQoS qos = lowerQosToTheSubscriptionDesired(sub, publishingQos);
|
||||
ClientSession targetSession = m_sessionsStore.sessionForClient(sub.getClientId());
|
||||
|
||||
boolean targetIsActive = this.connectionDescriptors.isConnected(sub.getClientId());
|
||||
|
||||
ByteBuffer message = origMessage.duplicate();
|
||||
//we need to retain because duplicate only copy r/w indexes and don't retain() causing refCnt = 0
|
||||
ByteBuf message = origMessage.retainedDuplicate();
|
||||
if (targetIsActive) {
|
||||
LOG.debug("Sending PUBLISH message to active subscriber. MessageId = {}, mqttClientId = {}, topicFilter = {}, " +
|
||||
"qos = {}.", pubMsg.getMessageID(), sub.getClientId(), sub.getTopicFilter(), qos);
|
||||
PublishMessage publishMsg = notRetainedPublish(topic, qos, message);
|
||||
if (qos != AbstractMessage.QOSType.MOST_ONE) {
|
||||
LOG.debug("Sending PUBLISH message to active subscriber. MessageId={}, CId={}, topicFilter={}, qos={}",
|
||||
pubMsg.getMessageID(), sub.getClientId(), sub.getTopicFilter(), qos);
|
||||
MqttPublishMessage publishMsg = notRetainedPublish(topic, qos, message);
|
||||
if (qos != MqttQoS.AT_MOST_ONCE) {
|
||||
//QoS 1 or 2
|
||||
int messageId = targetSession.nextPacketId();
|
||||
targetSession.inFlightAckWaiting(guid, messageId);
|
||||
//set the PacketIdentifier only for QoS > 0
|
||||
publishMsg.setMessageID(messageId);
|
||||
publishMsg = notRetainedPublishWithMessageId(topic, qos, message, messageId);
|
||||
}
|
||||
this.messageSender.sendPublish(targetSession, publishMsg);
|
||||
} else {
|
||||
if (!targetSession.isCleanSession()) {
|
||||
LOG.debug("Storing pending PUBLISH inactive message. MessageId = {}, mqttClientId = {}, " +
|
||||
"topicFilter = {}, qos = {}.",
|
||||
LOG.debug("Storing pending PUBLISH inactive message. MessageId={}, CId={}, topicFilter={}, qos={}",
|
||||
pubMsg.getMessageID(), sub.getClientId(), sub.getTopicFilter(), qos);
|
||||
//store the message in targetSession queue to deliver
|
||||
targetSession.enqueue(pubMsg);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.ConnectionDescriptorStore;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
import static io.moquette.parser.proto.messages.AbstractMessage.QOSType.MOST_ONE;
|
||||
import static io.moquette.spi.impl.ProtocolProcessor.asStoredMessage;
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
|
||||
|
||||
class PersistentQueueMessageSender {
|
||||
|
||||
@@ -19,28 +19,29 @@ class PersistentQueueMessageSender {
|
||||
this.connectionDescriptorStore = connectionDescriptorStore;
|
||||
}
|
||||
|
||||
void sendPublish(ClientSession clientsession, PublishMessage pubMessage) {
|
||||
void sendPublish(ClientSession clientsession, MqttPublishMessage pubMessage) {
|
||||
String clientId = clientsession.clientID;
|
||||
final int messageId = pubMessage.variableHeader().messageId();
|
||||
final String topicName = pubMessage.variableHeader().topicName();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Sending PUBLISH message. MessageId = {}, mqttClientId = {}, topic = {}, qos = {}, payload = {}.",
|
||||
pubMessage.getMessageID(),
|
||||
clientId, pubMessage.getTopicName(), DebugUtils.payload2Str(pubMessage.getPayload()));
|
||||
LOG.debug("Sending PUBLISH message. MessageId={}, CId={}, topic={}, qos={}, payload={}", messageId,
|
||||
clientId, topicName, DebugUtils.payload2Str(pubMessage.payload()));
|
||||
} else {
|
||||
LOG.info("Sending PUBLISH message. MessageId = {}, mqttClientId = {}, topic = {}.", pubMessage.getMessageID(),
|
||||
clientId, pubMessage.getTopicName());
|
||||
LOG.info("Sending PUBLISH message. MessageId={}, CId={}, topic={}", messageId, clientId, topicName);
|
||||
}
|
||||
|
||||
boolean messageDelivered = connectionDescriptorStore.sendMessage(pubMessage, pubMessage.getMessageID(), clientId);
|
||||
|
||||
if (!messageDelivered && pubMessage.getQos() != MOST_ONE && !clientsession.isCleanSession()) {
|
||||
boolean messageDelivered = connectionDescriptorStore.sendMessage(pubMessage, messageId, clientId);
|
||||
|
||||
MqttQoS qos = pubMessage.fixedHeader().qosLevel();
|
||||
if (!messageDelivered && qos != AT_MOST_ONCE && !clientsession.isCleanSession()) {
|
||||
LOG.warn(
|
||||
"The PUBLISH message could not be delivered. It will be stored. MessageId = {}, mqttClientId = {}, topic = {}, qos = {}, cleanSession = {}.",
|
||||
pubMessage.getMessageID(), clientId, pubMessage.getTopicName(), pubMessage.getQos(), false);
|
||||
"The PUBLISH message could not be delivered. It will be stored. MessageId = {}, CId={}, topic = {}, qos = {}, cleanSession = {}.",
|
||||
messageId, clientId, topicName, qos, false);
|
||||
clientsession.enqueue(asStoredMessage(pubMessage));
|
||||
} else {
|
||||
LOG.warn(
|
||||
"The PUBLISH message could not be delivered. It will be discarded. MessageId = {}, mqttClientId = {}, topic = {}, qos = {}, cleanSession = {}.",
|
||||
pubMessage.getMessageID(), clientId, pubMessage.getTopicName(), pubMessage.getQos(), true);
|
||||
"The PUBLISH message could not be delivered. It will be discarded. MessageId = {}, CId={}, topic = {}, qos = {}, cleanSession = {}.",
|
||||
messageId, clientId, topicName, qos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,35 +15,42 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.parser.proto.messages.*;
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.server.ConnectionDescriptor;
|
||||
import io.moquette.server.ConnectionDescriptor.ConnectionState;
|
||||
import io.moquette.server.ConnectionDescriptorStore;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.moquette.spi.*;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.security.IAuthenticator;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
|
||||
import static io.moquette.parser.netty.Utils.VERSION_3_1;
|
||||
import static io.moquette.parser.netty.Utils.VERSION_3_1_1;
|
||||
import static io.moquette.spi.impl.InternalRepublisher.createPublishForQos;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage.QOSType;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static io.moquette.spi.impl.InternalRepublisher.createPublishForQos;
|
||||
import static io.moquette.spi.impl.Utils.messageId;
|
||||
import static io.moquette.spi.impl.Utils.readBytesAndRewind;
|
||||
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
|
||||
import static io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader.from;
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.*;
|
||||
|
||||
/**
|
||||
* Class responsible to handle the logic of MQTT protocol it's the director of
|
||||
* the protocol execution.
|
||||
@@ -58,9 +65,9 @@ public class ProtocolProcessor {
|
||||
private final String topic;
|
||||
private final ByteBuffer payload;
|
||||
private final boolean retained;
|
||||
private final QOSType qos;
|
||||
private final MqttQoS qos;
|
||||
|
||||
public WillMessage(String topic, ByteBuffer payload, boolean retained, QOSType qos) {
|
||||
public WillMessage(String topic, ByteBuffer payload, boolean retained, MqttQoS qos) {
|
||||
this.topic = topic;
|
||||
this.payload = payload;
|
||||
this.retained = retained;
|
||||
@@ -79,7 +86,7 @@ public class ProtocolProcessor {
|
||||
return retained;
|
||||
}
|
||||
|
||||
public QOSType getQos() {
|
||||
public MqttQoS getQos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
@@ -105,8 +112,7 @@ public class ProtocolProcessor {
|
||||
|
||||
RunningSubscription that = (RunningSubscription) o;
|
||||
|
||||
if (packetId != that.packetId) return false;
|
||||
return clientID != null ? clientID.equals(that.clientID) : that.clientID == null;
|
||||
return packetId == that.packetId && (clientID != null ? clientID.equals(that.clientID) : that.clientID == null);
|
||||
|
||||
}
|
||||
|
||||
@@ -217,65 +223,63 @@ public class ProtocolProcessor {
|
||||
this.internalRepublisher = new InternalRepublisher(messageSender);
|
||||
}
|
||||
|
||||
public void processConnect(Channel channel, ConnectMessage msg) {
|
||||
LOG.info("Processing CONNECT message. MqttClientId = {}, username = {}, password = {}.", msg.getClientID(),
|
||||
msg.getUsername(), msg.getPassword());
|
||||
public void processConnect(Channel channel, MqttConnectMessage msg) {
|
||||
MqttConnectPayload payload = msg.payload();
|
||||
String clientId = payload.clientIdentifier();
|
||||
LOG.info("Processing CONNECT message. CId = {}, username = {}", clientId, payload.userName());
|
||||
|
||||
if (msg.getProtocolVersion() != VERSION_3_1 && msg.getProtocolVersion() != VERSION_3_1_1) {
|
||||
ConnAckMessage badProto = new ConnAckMessage();
|
||||
badProto.setReturnCode(ConnAckMessage.UNNACEPTABLE_PROTOCOL_VERSION);
|
||||
LOG.error("The MQTT protocol version is not valid. MqttClientId = {}, protocolVersion = {}.",
|
||||
msg.getProtocolVersion());
|
||||
if (msg.variableHeader().version() != MqttVersion.MQTT_3_1.protocolLevel() &&
|
||||
msg.variableHeader().version() != MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
||||
MqttConnAckMessage badProto = connAck(CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION);
|
||||
|
||||
LOG.error("The MQTT protocol version is not valid. CId = {}", clientId);
|
||||
channel.writeAndFlush(badProto);
|
||||
channel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.getClientID() == null || msg.getClientID().length() == 0) {
|
||||
if(!msg.isCleanSession() || !this.allowZeroByteClientId) {
|
||||
ConnAckMessage okResp = new ConnAckMessage();
|
||||
okResp.setReturnCode(ConnAckMessage.IDENTIFIER_REJECTED);
|
||||
channel.writeAndFlush(okResp);
|
||||
if (clientId == null || clientId.length() == 0) {
|
||||
if (!msg.variableHeader().isCleanSession() || !this.allowZeroByteClientId) {
|
||||
MqttConnAckMessage badId = connAck(CONNECTION_REFUSED_IDENTIFIER_REJECTED);
|
||||
|
||||
channel.writeAndFlush(badId);
|
||||
channel.close();
|
||||
LOG.error("The MQTT client ID cannot be empty., Username = {}, password = {}.", msg.getUsername(),
|
||||
msg.getPassword());
|
||||
LOG.error("The MQTT client ID cannot be empty. Username = {}", payload.userName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Generating client id.
|
||||
String randomIdentifier = UUID.randomUUID().toString().replace("-", "");
|
||||
msg.setClientID(randomIdentifier);
|
||||
LOG.info("The client has connected with a server generated identifier. MqttClientId = {}, username = {}, password = {}.",
|
||||
randomIdentifier, msg.getUsername(), msg.getPassword());
|
||||
clientId = UUID.randomUUID().toString().replace("-", "");
|
||||
LOG.info("The client has connected with a server generated identifier. CId = {}, username = {}",
|
||||
clientId, payload.userName());
|
||||
}
|
||||
|
||||
if (!login(channel, msg)) {
|
||||
if (!login(channel, msg, clientId)) {
|
||||
channel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
final String clientID = msg.getClientID();
|
||||
ConnectionDescriptor descriptor = new ConnectionDescriptor(clientID, channel, msg.isCleanSession());
|
||||
ConnectionDescriptor descriptor = new ConnectionDescriptor(clientId, channel, msg.variableHeader().isCleanSession());
|
||||
ConnectionDescriptor existing = this.connectionDescriptors.addConnection(descriptor);
|
||||
if (existing != null) {
|
||||
LOG.info("The client ID is being used in an existing connection. It will be closed. MqttClientId = {}.",
|
||||
msg.getClientID());
|
||||
LOG.info("The client ID is being used in an existing connection. It will be closed. CId = {}",
|
||||
clientId);
|
||||
existing.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
initializeKeepAliveTimeout(channel, msg);
|
||||
initializeKeepAliveTimeout(channel, msg, clientId);
|
||||
|
||||
storeWillMessage(msg);
|
||||
storeWillMessage(msg, clientId);
|
||||
|
||||
if (!sendAck(descriptor, msg)) {
|
||||
if (!sendAck(descriptor, msg, clientId)) {
|
||||
channel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
m_interceptor.notifyClientConnected(msg);
|
||||
|
||||
final ClientSession clientSession = createOrLoadClientSession(descriptor, msg);
|
||||
final ClientSession clientSession = createOrLoadClientSession(descriptor, msg, clientId);
|
||||
if (clientSession == null) {
|
||||
channel.close();
|
||||
return;
|
||||
@@ -290,117 +294,133 @@ public class ProtocolProcessor {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
LOG.info("The CONNECT message has been processed. MqttClientId = {}, username = {}, password = {}.",
|
||||
msg.getClientID(), msg.getUsername(), msg.getPassword());
|
||||
LOG.info("The CONNECT message has been processed. CId = {}, username = {}.", clientId, payload.userName());
|
||||
}
|
||||
|
||||
private boolean login(Channel channel, ConnectMessage msg) {
|
||||
private MqttConnAckMessage connAck(MqttConnectReturnCode returnCode) {
|
||||
return connAck(returnCode, false);
|
||||
}
|
||||
|
||||
private MqttConnAckMessage connAckWithSessionPresent(MqttConnectReturnCode returnCode) {
|
||||
return connAck(returnCode, true);
|
||||
}
|
||||
|
||||
private MqttConnAckMessage connAck(MqttConnectReturnCode returnCode, boolean sessionPresent) {
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||
MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(returnCode, sessionPresent);
|
||||
return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
|
||||
}
|
||||
|
||||
private boolean login(Channel channel, MqttConnectMessage msg, final String clientId) {
|
||||
//handle user authentication
|
||||
if (msg.isUserFlag()) {
|
||||
if (msg.variableHeader().hasUserName()) {
|
||||
byte[] pwd = null;
|
||||
if (msg.isPasswordFlag()) {
|
||||
pwd = msg.getPassword();
|
||||
if (msg.variableHeader().hasPassword()) {
|
||||
pwd = msg.payload().password().getBytes();
|
||||
} else if (!this.allowAnonymous) {
|
||||
LOG.error("The client didn't supply any password and MQTT anonymous mode is disabled. MqttClientId = {}.",
|
||||
msg.getClientID());
|
||||
LOG.error("The client didn't supply any password and MQTT anonymous mode is disabled. CId = {}.",
|
||||
clientId);
|
||||
failedCredentials(channel);
|
||||
return false;
|
||||
}
|
||||
if (!m_authenticator.checkValid(msg.getClientID(), msg.getUsername(), pwd)) {
|
||||
LOG.error("The authenticator has rejected the MQTT credentials. MqttClientId = {}, username = {}, password = {}.",
|
||||
msg.getClientID(), msg.getUsername(), pwd);
|
||||
if (!m_authenticator.checkValid(clientId, msg.payload().userName(), pwd)) {
|
||||
LOG.error("The authenticator has rejected the MQTT credentials. CId = {}, username = {}, password = {}.",
|
||||
clientId, msg.payload().userName(), pwd);
|
||||
failedCredentials(channel);
|
||||
return false;
|
||||
}
|
||||
NettyUtils.userName(channel, msg.getUsername());
|
||||
NettyUtils.userName(channel, msg.payload().userName());
|
||||
} else if (!this.allowAnonymous) {
|
||||
LOG.error("The client didn't supply any credentials and MQTT anonymous mode is disabled. MqttClientId = {}.",
|
||||
msg.getClientID());
|
||||
LOG.error("The client didn't supply any credentials and MQTT anonymous mode is disabled. CId = {}.",
|
||||
clientId);
|
||||
failedCredentials(channel);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean sendAck(ConnectionDescriptor descriptor, ConnectMessage msg) {
|
||||
LOG.info("Sending connect ACK. MqttClientId = {}.", msg.getClientID());
|
||||
private boolean sendAck(ConnectionDescriptor descriptor, MqttConnectMessage msg, final String clientId) {
|
||||
LOG.info("Sending connect ACK. CId = {}.", clientId);
|
||||
final boolean success = descriptor.assignState(ConnectionState.DISCONNECTED, ConnectionState.SENDACK);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
ConnAckMessage okResp = new ConnAckMessage();
|
||||
okResp.setReturnCode(ConnAckMessage.CONNECTION_ACCEPTED);
|
||||
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(msg.getClientID());
|
||||
MqttConnAckMessage okResp;
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(clientId);
|
||||
boolean isSessionAlreadyStored = clientSession != null;
|
||||
if (!msg.isCleanSession() && isSessionAlreadyStored) {
|
||||
okResp.setSessionPresent(true);
|
||||
if (!msg.variableHeader().isCleanSession() && isSessionAlreadyStored) {
|
||||
okResp = connAckWithSessionPresent(CONNECTION_ACCEPTED);
|
||||
} else {
|
||||
okResp = connAck(CONNECTION_ACCEPTED);
|
||||
}
|
||||
|
||||
if (isSessionAlreadyStored) {
|
||||
LOG.info("Cleaning session. MqttClientId = {}.", msg.getClientID());
|
||||
clientSession.cleanSession(msg.isCleanSession());
|
||||
LOG.info("Cleaning session. CId = {}.", clientId);
|
||||
clientSession.cleanSession(msg.variableHeader().isCleanSession());
|
||||
}
|
||||
descriptor.writeAndFlush(okResp);
|
||||
LOG.info("The connect ACK has been sent. MqttClientId = {}.", msg.getClientID());
|
||||
LOG.info("The connect ACK has been sent. CId = {}.", clientId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initializeKeepAliveTimeout(Channel channel, ConnectMessage msg) {
|
||||
int keepAlive = msg.getKeepAlive();
|
||||
LOG.info("Configuring connection. MqttClientId = {}.", msg.getClientID());
|
||||
private void initializeKeepAliveTimeout(Channel channel, MqttConnectMessage msg, final String clientId) {
|
||||
int keepAlive = msg.variableHeader().keepAliveTimeSeconds();
|
||||
LOG.info("Configuring connection. CId = {}.", clientId);
|
||||
NettyUtils.keepAlive(channel, keepAlive);
|
||||
//session.attr(NettyUtils.ATTR_KEY_CLEANSESSION).set(msg.isCleanSession());
|
||||
NettyUtils.cleanSession(channel, msg.isCleanSession());
|
||||
//session.attr(NettyUtils.ATTR_KEY_CLEANSESSION).set(msg.variableHeader().isCleanSession());
|
||||
NettyUtils.cleanSession(channel, msg.variableHeader().isCleanSession());
|
||||
//used to track the client in the subscription and publishing phases.
|
||||
//session.attr(NettyUtils.ATTR_KEY_CLIENTID).set(msg.getClientID());
|
||||
NettyUtils.clientID(channel, msg.getClientID());
|
||||
NettyUtils.clientID(channel, clientId);
|
||||
int idleTime = Math.round(keepAlive * 1.5f);
|
||||
setIdleTime(channel.pipeline(), idleTime);
|
||||
|
||||
LOG.info("The connection has been configured. MqttClientId = {}, keepAlive = {}, cleanSession = {}, idleTime = {}.",
|
||||
msg.getClientID(), keepAlive, msg.isCleanSession(), idleTime);
|
||||
LOG.info("The connection has been configured. CId = {}, keepAlive = {}, cleanSession = {}, idleTime = {}.",
|
||||
clientId, keepAlive, msg.variableHeader().isCleanSession(), idleTime);
|
||||
}
|
||||
|
||||
private void storeWillMessage(ConnectMessage msg) {
|
||||
private void storeWillMessage(MqttConnectMessage msg, final String clientId) {
|
||||
//Handle will flag
|
||||
if (msg.isWillFlag()) {
|
||||
AbstractMessage.QOSType willQos = AbstractMessage.QOSType.valueOf(msg.getWillQos());
|
||||
LOG.info("Configuring MQTT last will and testament. MqttClientId = {}, willTopic = {}, willQos = {}, willRetain = {}.",
|
||||
msg.getClientID(), willQos, msg.getWillTopic(), msg.isWillRetain());
|
||||
byte[] willPayload = msg.getWillMessage();
|
||||
if (msg.variableHeader().isWillFlag()) {
|
||||
MqttQoS willQos = MqttQoS.valueOf(msg.variableHeader().willQos());
|
||||
LOG.info("Configuring MQTT last will and testament. CId = {}, willQos = {}, willTopic = {}, willRetain = {}.",
|
||||
clientId, willQos, msg.payload().willTopic(), msg.variableHeader().isWillRetain());
|
||||
byte[] willPayload = msg.payload().willMessage().getBytes();
|
||||
ByteBuffer bb = (ByteBuffer) ByteBuffer.allocate(willPayload.length).put(willPayload).flip();
|
||||
//save the will testament in the clientID store
|
||||
WillMessage will = new WillMessage(msg.getWillTopic(), bb, msg.isWillRetain(),willQos);
|
||||
m_willStore.put(msg.getClientID(), will);
|
||||
LOG.info("MQTT last will and testament has been configured. MqttClientId = {}.", msg.getClientID());
|
||||
WillMessage will = new WillMessage(msg.payload().willTopic(), bb, msg.variableHeader().isWillRetain(), willQos);
|
||||
m_willStore.put(clientId, will);
|
||||
LOG.info("MQTT last will and testament has been configured. CId = {}.", clientId);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientSession createOrLoadClientSession(ConnectionDescriptor descriptor, ConnectMessage msg) {
|
||||
private ClientSession createOrLoadClientSession(ConnectionDescriptor descriptor, MqttConnectMessage msg,
|
||||
String clientId) {
|
||||
final boolean success = descriptor.assignState(ConnectionState.SENDACK, ConnectionState.SESSION_CREATED);
|
||||
if (!success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(msg.getClientID());
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(clientId);
|
||||
boolean isSessionAlreadyStored = clientSession != null;
|
||||
if (!isSessionAlreadyStored) {
|
||||
clientSession = m_sessionsStore.createNewSession(msg.getClientID(), msg.isCleanSession());
|
||||
clientSession = m_sessionsStore.createNewSession(clientId, msg.variableHeader().isCleanSession());
|
||||
}
|
||||
if (msg.isCleanSession()) {
|
||||
LOG.info("Cleaning session. MqttClientId = {}.", msg.getClientID());
|
||||
if (msg.variableHeader().isCleanSession()) {
|
||||
LOG.info("Cleaning session. CId = {}.", clientId);
|
||||
clientSession.cleanSession();
|
||||
}
|
||||
return clientSession;
|
||||
}
|
||||
|
||||
private boolean republish(ConnectionDescriptor descriptor, ConnectMessage msg, ClientSession clientSession) {
|
||||
private boolean republish(ConnectionDescriptor descriptor, MqttConnectMessage msg, ClientSession clientSession) {
|
||||
final boolean success = descriptor.assignState(ConnectionState.SESSION_CREATED, ConnectionState.MESSAGES_REPUBLISHED);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!msg.isCleanSession()) {
|
||||
if (!msg.variableHeader().isCleanSession()) {
|
||||
//force the republish of stored QoS1 and QoS2
|
||||
republishStoredInSession(clientSession);
|
||||
}
|
||||
@@ -410,9 +430,7 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
private void failedCredentials(Channel session) {
|
||||
ConnAckMessage okResp = new ConnAckMessage();
|
||||
okResp.setReturnCode(ConnAckMessage.BAD_USERNAME_OR_PASSWORD);
|
||||
session.writeAndFlush(okResp);
|
||||
session.writeAndFlush(connAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD));
|
||||
LOG.info("Client {} failed to connect with bad username or password.", session);
|
||||
}
|
||||
|
||||
@@ -427,7 +445,7 @@ public class ProtocolProcessor {
|
||||
* Republish QoS1 and QoS2 messages stored into the session for the clientID.
|
||||
* */
|
||||
private void republishStoredInSession(ClientSession clientSession) {
|
||||
LOG.info("Republishing stored publish events. MqttClientId = {}.", clientSession.clientID);
|
||||
LOG.info("Republishing stored publish events. CId = {}.", clientSession.clientID);
|
||||
BlockingQueue<StoredMessage> publishedEvents = clientSession.queue();
|
||||
if (publishedEvents.isEmpty()) {
|
||||
LOG.info("There are no stored publish events. ClientId = {}.", clientSession.clientID);
|
||||
@@ -437,9 +455,9 @@ public class ProtocolProcessor {
|
||||
this.internalRepublisher.publishStored(clientSession, publishedEvents);
|
||||
}
|
||||
|
||||
public void processPubAck(Channel channel, PubAckMessage msg) {
|
||||
public void processPubAck(Channel channel, MqttPubAckMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
int messageID = msg.getMessageID();
|
||||
int messageID = msg.variableHeader().messageId();
|
||||
String username = NettyUtils.userName(channel);
|
||||
LOG.trace("retrieving inflight for messageID <{}>", messageID);
|
||||
|
||||
@@ -448,18 +466,18 @@ public class ProtocolProcessor {
|
||||
targetSession.inFlightAcknowledged(messageID);
|
||||
|
||||
String topic = inflightMsg.getTopic();
|
||||
|
||||
// MessageGUID guid = inflightMsg.getGuid();
|
||||
//Remove the message from message store
|
||||
// m_messagesStore.decUsageCounter(guid);
|
||||
|
||||
m_interceptor.notifyMessageAcknowledged(new InterceptAcknowledgedMessage(inflightMsg, topic, username));
|
||||
}
|
||||
|
||||
public static IMessagesStore.StoredMessage asStoredMessage(PublishMessage msg) {
|
||||
IMessagesStore.StoredMessage stored = new IMessagesStore.StoredMessage(msg.getPayload().array(), msg.getQos(), msg.getTopicName());
|
||||
stored.setRetained(msg.isRetainFlag());
|
||||
stored.setMessageID(msg.getMessageID());
|
||||
public static IMessagesStore.StoredMessage asStoredMessage(MqttPublishMessage msg) {
|
||||
//TODO ugly, too much array copy
|
||||
ByteBuf payload = msg.payload();
|
||||
byte[] payloadContent = readBytesAndRewind(payload);
|
||||
|
||||
IMessagesStore.StoredMessage stored = new IMessagesStore.StoredMessage(payloadContent,
|
||||
msg.fixedHeader().qosLevel(), msg.variableHeader().topicName());
|
||||
stored.setRetained(msg.fixedHeader().isRetain());
|
||||
stored.setMessageID(msg.variableHeader().messageId());
|
||||
return stored;
|
||||
}
|
||||
|
||||
@@ -469,15 +487,16 @@ public class ProtocolProcessor {
|
||||
return pub;
|
||||
}
|
||||
|
||||
public void processPublish(Channel channel, PublishMessage msg) {
|
||||
LOG.info("Processing PUBLISH message. MqttClientId = {}, topic = {}, messageId = {}, qos = {}.",
|
||||
msg.getClientId(), msg.getTopicName(), msg.getMessageID(), msg.getQos());
|
||||
final AbstractMessage.QOSType qos = msg.getQos();
|
||||
public void processPublish(Channel channel, MqttPublishMessage msg) {
|
||||
final MqttQoS qos = msg.fixedHeader().qosLevel();
|
||||
final String clientId = NettyUtils.clientID(channel);
|
||||
LOG.info("Processing PUBLISH message. CId = {}, topic = {}, messageId = {}, qos = {}.",
|
||||
clientId, msg.variableHeader().topicName(), msg.variableHeader().messageId(), qos);
|
||||
switch (qos) {
|
||||
case MOST_ONE:
|
||||
case AT_MOST_ONCE:
|
||||
this.qos0PublishHandler.receivedPublishQos0(channel, msg);
|
||||
break;
|
||||
case LEAST_ONE:
|
||||
case AT_LEAST_ONCE:
|
||||
this.qos1PublishHandler.receivedPublishQos1(channel, msg);
|
||||
break;
|
||||
case EXACTLY_ONCE:
|
||||
@@ -497,30 +516,31 @@ public class ProtocolProcessor {
|
||||
* it's publishing.
|
||||
*
|
||||
* @param msg the message to publish.
|
||||
* @param clientId the clientID
|
||||
* */
|
||||
public void internalPublish(PublishMessage msg) {
|
||||
final AbstractMessage.QOSType qos = msg.getQos();
|
||||
final String topic = msg.getTopicName();
|
||||
public void internalPublish(MqttPublishMessage msg, final String clientId) {
|
||||
final MqttQoS qos = msg.fixedHeader().qosLevel();
|
||||
final String topic = msg.variableHeader().topicName();
|
||||
LOG.info("Sending PUBLISH message. Topic = {}, qos = {}.", topic, qos);
|
||||
|
||||
MessageGUID guid = null;
|
||||
IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
|
||||
if (msg.getClientId() == null || msg.getClientId().isEmpty()) {
|
||||
if (clientId == null || clientId.isEmpty()) {
|
||||
toStoreMsg.setClientID("BROKER_SELF");
|
||||
} else {
|
||||
toStoreMsg.setClientID(msg.getClientId());
|
||||
toStoreMsg.setClientID(clientId);
|
||||
}
|
||||
toStoreMsg.setMessageID(1);
|
||||
if (qos == AbstractMessage.QOSType.EXACTLY_ONCE) { //QoS2
|
||||
if (qos == EXACTLY_ONCE) { //QoS2
|
||||
guid = m_messagesStore.storePublishForFuture(toStoreMsg);
|
||||
}
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
this.messagesPublisher.publish2Subscribers(toStoreMsg, topicMatchingSubscriptions);
|
||||
|
||||
if (!msg.isRetainFlag()) {
|
||||
if (!msg.fixedHeader().isRetain()) {
|
||||
return;
|
||||
}
|
||||
if (qos == AbstractMessage.QOSType.MOST_ONE || !msg.getPayload().hasRemaining()) {
|
||||
if (qos == AT_MOST_ONCE || msg.payload().readableBytes() == 0) {
|
||||
//QoS == 0 && retain => clean old retained
|
||||
m_messagesStore.cleanRetained(topic);
|
||||
return;
|
||||
@@ -539,7 +559,7 @@ public class ProtocolProcessor {
|
||||
//it has just to publish the message downstream to the subscribers
|
||||
//NB it's a will publish, it needs a PacketIdentifier for this conn, default to 1
|
||||
Integer messageId = null;
|
||||
if (will.getQos() != AbstractMessage.QOSType.MOST_ONE) {
|
||||
if (will.getQos() != AT_MOST_ONCE) {
|
||||
messageId = m_sessionsStore.nextPacketID(clientID);
|
||||
}
|
||||
|
||||
@@ -549,12 +569,12 @@ public class ProtocolProcessor {
|
||||
String topic = tobeStored.getTopic();
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
|
||||
LOG.info("Publishing will message. MqttClientId = {}, messageId = {}, topic = {}.", clientID, messageId, topic);
|
||||
LOG.info("Publishing will message. CId = {}, messageId = {}, topic = {}.", clientID, messageId, topic);
|
||||
this.messagesPublisher.publish2Subscribers(tobeStored, topicMatchingSubscriptions);
|
||||
}
|
||||
|
||||
static QOSType lowerQosToTheSubscriptionDesired(Subscription sub, QOSType qos) {
|
||||
if (qos.byteValue() > sub.getRequestedQos().byteValue()) {
|
||||
static MqttQoS lowerQosToTheSubscriptionDesired(Subscription sub, MqttQoS qos) {
|
||||
if (qos.value() > sub.getRequestedQos().value()) {
|
||||
qos = sub.getRequestedQos();
|
||||
}
|
||||
return qos;
|
||||
@@ -566,29 +586,28 @@ public class ProtocolProcessor {
|
||||
* @param channel the channel of the incoming message.
|
||||
* @param msg the decoded pubrel message.
|
||||
* */
|
||||
public void processPubRel(Channel channel, PubRelMessage msg) {
|
||||
public void processPubRel(Channel channel, MqttMessage msg) {
|
||||
this.qos2PublishHandler.processPubRel(channel, msg);
|
||||
}
|
||||
|
||||
public void processPubRec(Channel channel, PubRecMessage msg) {
|
||||
public void processPubRec(Channel channel, MqttMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
|
||||
//remove from the inflight and move to the QoS2 second phase queue
|
||||
int messageID = msg.getMessageID();
|
||||
int messageID = messageId(msg);
|
||||
targetSession.moveInFlightToSecondPhaseAckWaiting(messageID);
|
||||
//once received a PUBREC reply with a PUBREL(messageID)
|
||||
LOG.debug("Processing PUBREC message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
PubRelMessage pubRelMessage = new PubRelMessage();
|
||||
pubRelMessage.setMessageID(messageID);
|
||||
pubRelMessage.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
||||
LOG.debug("Processing PUBREC message. CId = {}, messageId = {}.", clientID, messageID);
|
||||
|
||||
MqttFixedHeader pubRelHeader = new MqttFixedHeader(MqttMessageType.PUBREL, false, AT_LEAST_ONCE, false, 0);
|
||||
MqttMessage pubRelMessage = new MqttMessage(pubRelHeader, from(messageID));
|
||||
channel.writeAndFlush(pubRelMessage);
|
||||
}
|
||||
|
||||
public void processPubComp(Channel channel, PubCompMessage msg) {
|
||||
public void processPubComp(Channel channel, MqttMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
int messageID = msg.getMessageID();
|
||||
LOG.debug("Processing PUBCOMP message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
int messageID = messageId(msg);
|
||||
LOG.debug("Processing PUBCOMP message. CId = {}, messageId = {}.", clientID, messageID);
|
||||
//once received the PUBCOMP then remove the message from the temp memory
|
||||
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
|
||||
StoredMessage inflightMsg = targetSession.secondPhaseAcknowledged(messageID);
|
||||
@@ -599,7 +618,7 @@ public class ProtocolProcessor {
|
||||
|
||||
public void processDisconnect(Channel channel) throws InterruptedException {
|
||||
final String clientID = NettyUtils.clientID(channel);
|
||||
LOG.info("Processing DISCONNECT message. MqttClientId = {}.", clientID);
|
||||
LOG.info("Processing DISCONNECT message. CId = {}.", clientID);
|
||||
channel.flush();
|
||||
final ConnectionDescriptor existingDescriptor = this.connectionDescriptors.getConnection(clientID);
|
||||
if (existingDescriptor == null) {
|
||||
@@ -610,43 +629,43 @@ public class ProtocolProcessor {
|
||||
|
||||
if (existingDescriptor.doesNotUseChannel(channel)) {
|
||||
//another client saved it's descriptor, exit
|
||||
LOG.warn("Another client is using the connection descriptor. Closing connection. MqttClientId = {}.",
|
||||
LOG.warn("Another client is using the connection descriptor. Closing connection. CId = {}.",
|
||||
clientID);
|
||||
existingDescriptor.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeSubscriptions(existingDescriptor, clientID)) {
|
||||
LOG.warn("Unable to remove subscriptions. Closing connection. MqttClientId = {}.", clientID);
|
||||
LOG.warn("Unable to remove subscriptions. Closing connection. CId = {}.", clientID);
|
||||
existingDescriptor.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dropStoredMessages(existingDescriptor, clientID)) {
|
||||
LOG.warn("Unable to drop stored messages. Closing connection. MqttClientId = {}.", clientID);
|
||||
LOG.warn("Unable to drop stored messages. Closing connection. CId = {}.", clientID);
|
||||
existingDescriptor.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cleanWillMessageAndNotifyInterceptor(existingDescriptor, clientID)) {
|
||||
LOG.warn("Unable to drop will message. Closing connection. MqttClientId = {}.", clientID);
|
||||
LOG.warn("Unable to drop will message. Closing connection. CId = {}.", clientID);
|
||||
existingDescriptor.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existingDescriptor.close()) {
|
||||
LOG.info("The connection has been closed. MqttClientId = {}.", clientID);
|
||||
LOG.info("The connection has been closed. CId = {}.", clientID);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean stillPresent = this.connectionDescriptors.removeConnection(existingDescriptor);
|
||||
if (!stillPresent) {
|
||||
//another descriptor was inserted
|
||||
LOG.warn("Another descriptor has been inserted. MqttClientId = {}.", clientID);
|
||||
LOG.warn("Another descriptor has been inserted. CId = {}.", clientID);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("The DISCONNECT message has been processed. MqttClientId = {}.", clientID);
|
||||
LOG.info("The DISCONNECT message has been processed. CId = {}.", clientID);
|
||||
}
|
||||
|
||||
private boolean removeSubscriptions(ConnectionDescriptor descriptor, String clientID) {
|
||||
@@ -656,9 +675,9 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
if (descriptor.cleanSession) {
|
||||
LOG.info("Removing saved subscriptions. MqttClientId = {}.", descriptor.clientID);
|
||||
LOG.info("Removing saved subscriptions. CId = {}.", descriptor.clientID);
|
||||
m_sessionsStore.wipeSubscriptions(clientID);
|
||||
LOG.info("The saved subscriptions have been removed. MqttClientId = {}.", descriptor.clientID);
|
||||
LOG.info("The saved subscriptions have been removed. CId = {}.", descriptor.clientID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -670,9 +689,9 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
if (descriptor.cleanSession) {
|
||||
LOG.debug("Removing messages of session. MqttClientId = {}.", descriptor.clientID);
|
||||
LOG.debug("Removing messages of session. CId = {}.", descriptor.clientID);
|
||||
this.m_sessionsStore.dropQueue(clientID);
|
||||
LOG.debug("The messages of the session have been removed. MqttClientId = {}.", descriptor.clientID);
|
||||
LOG.debug("The messages of the session have been removed. CId = {}.", descriptor.clientID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -692,7 +711,7 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
public void processConnectionLost(String clientID, Channel channel) {
|
||||
LOG.info("Processing connection lost event. MqttClientId = {}.", clientID);
|
||||
LOG.info("Processing connection lost event. CId = {}.", clientID);
|
||||
ConnectionDescriptor oldConnDescr = new ConnectionDescriptor(clientID, channel, true);
|
||||
connectionDescriptors.removeConnection(oldConnDescr);
|
||||
//publish the Will message (if any) for the clientID
|
||||
@@ -712,11 +731,11 @@ public class ProtocolProcessor {
|
||||
* @param channel the channel of the incoming message.
|
||||
* @param msg the decoded unsubscribe message.
|
||||
*/
|
||||
public void processUnsubscribe(Channel channel, UnsubscribeMessage msg) {
|
||||
List<String> topics = msg.topicFilters();
|
||||
public void processUnsubscribe(Channel channel, MqttUnsubscribeMessage msg) {
|
||||
List<String> topics = msg.payload().topics();
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
|
||||
LOG.info("Processing UNSUBSCRIBE message. MqttClientId = {}, topics = {}.", clientID, topics);
|
||||
LOG.info("Processing UNSUBSCRIBE message. CId = {}, topics = {}.", clientID, topics);
|
||||
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
|
||||
for (String topic : topics) {
|
||||
@@ -724,12 +743,12 @@ public class ProtocolProcessor {
|
||||
if (!validTopic) {
|
||||
//close the connection, not valid topicFilter is a protocol violation
|
||||
channel.close();
|
||||
LOG.error("The topic filter is not valid. MqttClientId = {}, topics = {}, badTopicFilter = {}.",
|
||||
LOG.error("The topic filter is not valid. CId = {}, topics = {}, badTopicFilter = {}.",
|
||||
clientID, topics, topic);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.debug("Removing subscription. MqttClientId = {}, topic = {}.", clientID, topic);
|
||||
LOG.debug("Removing subscription. CId = {}, topic = {}.", clientID, topic);
|
||||
subscriptions.removeSubscription(topic, clientID);
|
||||
clientSession.unsubscribeFrom(topic);
|
||||
String username = NettyUtils.userName(channel);
|
||||
@@ -737,40 +756,37 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
//ack the client
|
||||
int messageID = msg.getMessageID();
|
||||
UnsubAckMessage ackMessage = new UnsubAckMessage();
|
||||
ackMessage.setMessageID(messageID);
|
||||
int messageID = msg.variableHeader().messageId();
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBACK, false, AT_LEAST_ONCE, false, 0);
|
||||
MqttUnsubAckMessage ackMessage = new MqttUnsubAckMessage(fixedHeader, from(messageID));
|
||||
|
||||
LOG.info("Sending UNSUBACK message. MqttClientId = {}, topics = {}, messageId = {}.", clientID, topics,
|
||||
messageID);
|
||||
LOG.info("Sending UNSUBACK message. CId = {}, topics = {}, messageId = {}.", clientID, topics, messageID);
|
||||
channel.writeAndFlush(ackMessage);
|
||||
}
|
||||
|
||||
public void processSubscribe(Channel channel, SubscribeMessage msg) {
|
||||
public void processSubscribe(Channel channel, MqttSubscribeMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
int messageID = msg.getMessageID();
|
||||
LOG.info("Processing SUBSCRIBE message. MqttClientId = {}, messageId = {}.", clientID, msg.getMessageID());
|
||||
int messageID = messageId(msg);
|
||||
LOG.info("Processing SUBSCRIBE message. CId = {}, messageId = {}.", clientID, messageID);
|
||||
|
||||
RunningSubscription executionKey = new RunningSubscription(clientID, messageID);
|
||||
SubscriptionState currentStatus = this.subscriptionInCourse.putIfAbsent(executionKey, SubscriptionState.VERIFIED);
|
||||
if (currentStatus != null) {
|
||||
LOG.warn("The client sent another SUBSCRIBE message while this one was being processed. MqttClientId = {}, messageId = {}.",
|
||||
clientID, msg.getMessageID());
|
||||
LOG.warn("The client sent another SUBSCRIBE message while this one was being processed. " +
|
||||
"CId = {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
String username = NettyUtils.userName(channel);
|
||||
List<SubscribeMessage.Couple> ackTopics = doVerify(clientID, username, msg);
|
||||
SubAckMessage ackMessage = doAckMessageFromValidateFilters(ackTopics);
|
||||
List<MqttTopicSubscription> ackTopics = doVerify(clientID, username, msg);
|
||||
MqttSubAckMessage ackMessage = doAckMessageFromValidateFilters(ackTopics, messageID);
|
||||
if (!this.subscriptionInCourse.replace(executionKey, SubscriptionState.VERIFIED, SubscriptionState.STORED)) {
|
||||
LOG.warn("The client sent another SUBSCRIBE message while the topic filters were being verified. MqttClientId = {}, messageId = {}.",
|
||||
clientID, msg.getMessageID());
|
||||
LOG.warn("The client sent another SUBSCRIBE message while the topic filters were being verified. " +
|
||||
"CId = {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Creating and storing subscriptions. MqttClientId = {}, messageId = {}, topics = {}.", clientID,
|
||||
msg.getMessageID(), ackTopics);
|
||||
LOG.info("Creating and storing subscriptions CId={}, messageId={}, topics={}", clientID, messageID, ackTopics);
|
||||
|
||||
ackMessage.setMessageID(messageID);
|
||||
List<Subscription> newSubscriptions = doStoreSubscription(ackTopics, clientID);
|
||||
|
||||
//save session, persist subscriptions from session
|
||||
@@ -779,7 +795,7 @@ public class ProtocolProcessor {
|
||||
subscriptions.add(subscription.asClientTopicCouple());
|
||||
}
|
||||
|
||||
LOG.info("Sending SUBACK response. MqttClientId = {}, messageId = {}.", clientID, msg.getMessageID());
|
||||
LOG.info("Sending SUBACK response CId={}, messageId={}", clientID, messageID);
|
||||
channel.writeAndFlush(ackMessage);
|
||||
|
||||
//fire the persisted messages in session
|
||||
@@ -789,22 +805,20 @@ public class ProtocolProcessor {
|
||||
|
||||
boolean success = this.subscriptionInCourse.remove(executionKey, SubscriptionState.STORED);
|
||||
if (!success) {
|
||||
LOG.warn("Unable to perform the final subscription state update. MqttClientId = {}, messageId = {}.",
|
||||
clientID, msg.getMessageID());
|
||||
LOG.warn("Unable to perform the final subscription state update CId={}, messageId={}", clientID, messageID);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Subscription> doStoreSubscription(List<SubscribeMessage.Couple> ackTopics, String clientID) {
|
||||
private List<Subscription> doStoreSubscription(List<MqttTopicSubscription> ackTopics, String clientID) {
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
|
||||
|
||||
List<Subscription> newSubscriptions = new ArrayList<>();
|
||||
for (SubscribeMessage.Couple req : ackTopics) {
|
||||
for (MqttTopicSubscription req : ackTopics) {
|
||||
//TODO this is SUPER UGLY
|
||||
if (req.qos == AbstractMessage.QOSType.FAILURE.byteValue()) {
|
||||
if (req.qualityOfService() == FAILURE) {
|
||||
continue;
|
||||
}
|
||||
AbstractMessage.QOSType qos = AbstractMessage.QOSType.valueOf(req.qos);
|
||||
Subscription newSubscription = new Subscription(clientID, req.topicFilter, qos);
|
||||
Subscription newSubscription = new Subscription(clientID, req.topicName(), req.qualityOfService());
|
||||
clientSession.subscribe(newSubscription);
|
||||
newSubscriptions.add(newSubscription);
|
||||
}
|
||||
@@ -812,32 +826,34 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of verified topics
|
||||
* @param clientID
|
||||
* @param username
|
||||
* @param clientID the clientID
|
||||
* @param username the username
|
||||
* @param msg the subscribe message to verify
|
||||
* @return the list of verified topics for the given subscribe message.
|
||||
* */
|
||||
private List<SubscribeMessage.Couple> doVerify(String clientID, String username, SubscribeMessage msg) {
|
||||
private List<MqttTopicSubscription> doVerify(String clientID, String username, MqttSubscribeMessage msg) {
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
|
||||
List<SubscribeMessage.Couple> ackTopics = new ArrayList<>();
|
||||
List<MqttTopicSubscription> ackTopics = new ArrayList<>();
|
||||
|
||||
for (SubscribeMessage.Couple req : msg.subscriptions()) {
|
||||
if (!m_authorizator.canRead(req.topicFilter, username, clientSession.clientID)) {
|
||||
final int messageId = messageId(msg);
|
||||
for (MqttTopicSubscription req : msg.payload().topicSubscriptions()) {
|
||||
if (!m_authorizator.canRead(req.topicName(), username, clientSession.clientID)) {
|
||||
//send SUBACK with 0x80, the user hasn't credentials to read the topic
|
||||
LOG.error("The client does not have read permissions on the topic. MqttClientId = {}, username = {}, messageId = {}, topic = {}.",
|
||||
clientID, username, msg.getMessageID(), req.topicFilter);
|
||||
ackTopics.add(new SubscribeMessage.Couple(AbstractMessage.QOSType.FAILURE.byteValue(), req.topicFilter));
|
||||
LOG.error("The client does not have read permissions on the topic. CId = {}, username = {}, messageId = {}, topic = {}.",
|
||||
clientID, username, messageId, req.topicName());
|
||||
ackTopics.add(new MqttTopicSubscription(req.topicName(), FAILURE));
|
||||
} else {
|
||||
AbstractMessage.QOSType qos = null;
|
||||
if (SubscriptionsStore.validate(req.topicFilter)) {
|
||||
qos = AbstractMessage.QOSType.valueOf(req.qos);
|
||||
LOG.info("The client will be subscribed to the topic. MqttClientId = {}, username = {}, messageId = {}, topic = {}.",
|
||||
clientID, username, msg.getMessageID(), req.topicFilter);
|
||||
MqttQoS qos;
|
||||
if (SubscriptionsStore.validate(req.topicName())) {
|
||||
LOG.info("The client will be subscribed to the topic. CId = {}, username = {}, messageId = {}, topic = {}.",
|
||||
clientID, username, messageId, req.topicName());
|
||||
qos = req.qualityOfService();
|
||||
} else {
|
||||
LOG.error("The topic filter is not valid. MqttClientId = {}, username = {}, messageId = {}, topic = {}.",
|
||||
clientID, username, msg.getMessageID(), req.topicFilter);
|
||||
qos = AbstractMessage.QOSType.FAILURE;
|
||||
LOG.error("The topic filter is not valid. CId = {}, username = {}, messageId = {}, topic = {}.",
|
||||
clientID, username, messageId, req.topicName());
|
||||
qos = FAILURE;
|
||||
}
|
||||
ackTopics.add(new SubscribeMessage.Couple(qos.byteValue(), req.topicFilter));
|
||||
ackTopics.add(new MqttTopicSubscription(req.topicName(), qos));
|
||||
}
|
||||
}
|
||||
return ackTopics;
|
||||
@@ -846,17 +862,19 @@ public class ProtocolProcessor {
|
||||
/**
|
||||
* Create the SUBACK response from a list of topicFilters
|
||||
* */
|
||||
private SubAckMessage doAckMessageFromValidateFilters(List<SubscribeMessage.Couple> topicFilters) {
|
||||
//ack the client
|
||||
SubAckMessage ackMessage = new SubAckMessage();
|
||||
for (SubscribeMessage.Couple req : topicFilters) {
|
||||
ackMessage.addType(AbstractMessage.QOSType.valueOf(req.qos));
|
||||
private MqttSubAckMessage doAckMessageFromValidateFilters(List<MqttTopicSubscription> topicFilters, int messageId) {
|
||||
List<Integer> grantedQoSLevels = new ArrayList<>();
|
||||
for (MqttTopicSubscription req : topicFilters) {
|
||||
grantedQoSLevels.add(req.qualityOfService().value());
|
||||
}
|
||||
return ackMessage;
|
||||
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.SUBACK, false, AT_LEAST_ONCE, false, 0);
|
||||
MqttSubAckPayload payload = new MqttSubAckPayload(grantedQoSLevels);
|
||||
return new MqttSubAckMessage(fixedHeader, from(messageId), payload);
|
||||
}
|
||||
|
||||
private void publishRetainedMessagesInSession(final Subscription newSubscription, String username) {
|
||||
LOG.info("Retrieving retained messages. MqttClientId = {}, topics = {}.", newSubscription.getClientId(),
|
||||
LOG.info("Retrieving retained messages. CId = {}, topics = {}.", newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter());
|
||||
|
||||
//scans retained messages to be published to the new subscription
|
||||
@@ -869,7 +887,7 @@ public class ProtocolProcessor {
|
||||
});
|
||||
|
||||
if (!messages.isEmpty()) {
|
||||
LOG.info("Publishing retained messages. MqttClientId = {}, topics = {}, messagesNo = {}.",
|
||||
LOG.info("Publishing retained messages. CId = {}, topics = {}, messagesNo = {}.",
|
||||
newSubscription.getClientId(), newSubscription.getTopicFilter(), messages.size());
|
||||
}
|
||||
ClientSession targetSession = m_sessionsStore.sessionForClient(newSubscription.getClientId());
|
||||
@@ -890,7 +908,7 @@ public class ProtocolProcessor {
|
||||
} else {
|
||||
//recreate a publish from stored publish in queue
|
||||
boolean retained = m_messagesStore.getMessageByGuid(msg.getGuid()) != null;
|
||||
PublishMessage pubMsg = createPublishForQos(msg.getTopic(), msg.getQos(), msg.getMessage(), retained);
|
||||
MqttPublishMessage pubMsg = createPublishForQos(msg.getTopic(), msg.getQos(), msg.getMessage(), retained, 0);
|
||||
channel.write(pubMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,9 +33,9 @@ class Qos0PublishHandler extends QosPublishHandler {
|
||||
this.publisher = messagesPublisher;
|
||||
}
|
||||
|
||||
void receivedPublishQos0(Channel channel, PublishMessage msg) {
|
||||
void receivedPublishQos0(Channel channel, MqttPublishMessage msg) {
|
||||
//verify if topic can be write
|
||||
final String topic = msg.getTopicName();
|
||||
final String topic = msg.variableHeader().topicName();
|
||||
if (checkWriteOnTopic(topic, channel)) {
|
||||
return;
|
||||
}
|
||||
@@ -45,19 +45,21 @@ class Qos0PublishHandler extends QosPublishHandler {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
toStoreMsg.setClientID(clientID);
|
||||
|
||||
final int messageID = msg.variableHeader().messageId();
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID, topic, msg.getMessageID(), DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", clientID,
|
||||
topic, msg.getMessageID());
|
||||
topic, messageID);
|
||||
}
|
||||
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
this.publisher.publish2Subscribers(toStoreMsg, topicMatchingSubscriptions);
|
||||
|
||||
if (msg.isRetainFlag()) {
|
||||
if (msg.fixedHeader().isRetain()) {
|
||||
//QoS == 0 && retain => clean old retained
|
||||
m_messagesStore.cleanRetained(topic);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.PubAckMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.ConnectionDescriptorStore;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
@@ -10,12 +8,18 @@ import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.mqtt.MqttFixedHeader;
|
||||
import io.netty.handler.codec.mqtt.MqttMessageType;
|
||||
import io.netty.handler.codec.mqtt.MqttPubAckMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.moquette.spi.impl.ProtocolProcessor.asStoredMessage;
|
||||
import static io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader.from;
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
|
||||
|
||||
class Qos1PublishHandler extends QosPublishHandler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Qos1PublishHandler.class);
|
||||
@@ -38,13 +42,15 @@ class Qos1PublishHandler extends QosPublishHandler {
|
||||
this.publisher = messagesPublisher;
|
||||
}
|
||||
|
||||
void receivedPublishQos1(Channel channel, PublishMessage msg) {
|
||||
void receivedPublishQos1(Channel channel, MqttPublishMessage msg) {
|
||||
//verify if topic can be write
|
||||
final String topic = msg.getTopicName();
|
||||
final String topic = msg.variableHeader().topicName();
|
||||
if (checkWriteOnTopic(topic, channel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int messageID = msg.variableHeader().messageId();
|
||||
|
||||
//route message to subscribers
|
||||
IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
@@ -52,24 +58,23 @@ class Qos1PublishHandler extends QosPublishHandler {
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID, topic, msg.getMessageID(), DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", clientID,
|
||||
topic, msg.getMessageID());
|
||||
topic, messageID);
|
||||
}
|
||||
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
this.publisher.publish2Subscribers(toStoreMsg, topicMatchingSubscriptions);
|
||||
|
||||
//send PUBACK
|
||||
final Integer messageID = msg.getMessageID();
|
||||
if (msg.isLocal()) {
|
||||
//TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) {
|
||||
sendPubAck(clientID, messageID);
|
||||
}
|
||||
// }
|
||||
|
||||
if (msg.isRetainFlag()) {
|
||||
if (!msg.getPayload().hasRemaining()) {
|
||||
if (msg.fixedHeader().isRetain()) {
|
||||
if (!msg.payload().isReadable()) {
|
||||
m_messagesStore.cleanRetained(topic);
|
||||
} else {
|
||||
//before wasn't stored
|
||||
@@ -84,8 +89,8 @@ class Qos1PublishHandler extends QosPublishHandler {
|
||||
|
||||
private void sendPubAck(String clientId, int messageID) {
|
||||
LOG.trace("sendPubAck invoked");
|
||||
PubAckMessage pubAckMessage = new PubAckMessage();
|
||||
pubAckMessage.setMessageID(messageID);
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK, false, AT_MOST_ONCE, false, 0);
|
||||
MqttPubAckMessage pubAckMessage = new MqttPubAckMessage(fixedHeader, from(messageID));
|
||||
|
||||
try {
|
||||
if (connectionDescriptors == null) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.*;
|
||||
import io.moquette.server.ConnectionDescriptorStore;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.moquette.spi.ClientSession;
|
||||
@@ -11,13 +10,16 @@ import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.moquette.spi.impl.ProtocolProcessor.asStoredMessage;
|
||||
|
||||
import java.util.List;
|
||||
import static io.moquette.spi.impl.Utils.messageId;
|
||||
import static io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader.from;
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
|
||||
|
||||
class Qos2PublishHandler extends QosPublishHandler {
|
||||
|
||||
@@ -43,37 +45,38 @@ class Qos2PublishHandler extends QosPublishHandler {
|
||||
this.publisher = messagesPublisher;
|
||||
}
|
||||
|
||||
void receivedPublishQos2(Channel channel, PublishMessage msg) {
|
||||
final String topic = msg.getTopicName();
|
||||
void receivedPublishQos2(Channel channel, MqttPublishMessage msg) {
|
||||
final String topic = msg.variableHeader().topicName();
|
||||
//check if the topic can be wrote
|
||||
if (checkWriteOnTopic(topic, channel)) {
|
||||
return;
|
||||
}
|
||||
final Integer messageID = msg.getMessageID();
|
||||
final int messageID = msg.variableHeader().messageId();
|
||||
|
||||
IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
toStoreMsg.setClientID(clientID);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID, topic, msg.getMessageID(), DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
LOG.trace("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, " +
|
||||
"payload = {}, subscriptionTree = {}.",
|
||||
clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", clientID,
|
||||
topic, msg.getMessageID());
|
||||
LOG.info("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.",
|
||||
clientID, topic, messageID);
|
||||
}
|
||||
|
||||
//QoS2
|
||||
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
|
||||
if (msg.isLocal()) {
|
||||
//TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) {
|
||||
sendPubRec(clientID, messageID);
|
||||
}
|
||||
//}
|
||||
//Next the client will send us a pub rel
|
||||
//NB publish to subscribers for QoS 2 happen upon PUBREL from publisher
|
||||
|
||||
if (msg.isRetainFlag()) {
|
||||
if (!msg.getPayload().hasRemaining()) {
|
||||
if (msg.fixedHeader().isRetain()) {
|
||||
if (msg.payload().readableBytes() == 0) {
|
||||
m_messagesStore.cleanRetained(topic);
|
||||
} else {
|
||||
m_messagesStore.storeRetained(topic, guid);
|
||||
@@ -87,9 +90,9 @@ class Qos2PublishHandler extends QosPublishHandler {
|
||||
* Second phase of a publish QoS2 protocol, sent by publisher to the broker. Search the stored message and publish
|
||||
* to all interested subscribers.
|
||||
* */
|
||||
void processPubRel(Channel channel, PubRelMessage msg) {
|
||||
void processPubRel(Channel channel, MqttMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
int messageID = msg.getMessageID();
|
||||
int messageID = messageId(msg);
|
||||
LOG.info("Processing PUBREL message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
|
||||
IMessagesStore.StoredMessage evt = targetSession.storedMessage(messageID);
|
||||
@@ -98,16 +101,16 @@ class Qos2PublishHandler extends QosPublishHandler {
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID, topic, msg.getMessageID(), DebugUtils.payload2Str(evt.getMessage()),
|
||||
clientID, topic, messageID, DebugUtils.payload2Str(evt.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info("Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", clientID,
|
||||
topic, msg.getMessageID());
|
||||
topic, messageID);
|
||||
}
|
||||
this.publisher.publish2Subscribers(evt, topicMatchingSubscriptions);
|
||||
|
||||
if (evt.isRetained()) {
|
||||
if (!evt.getMessage().hasRemaining()) {
|
||||
if (evt.getMessage().readableBytes() == 0) {
|
||||
m_messagesStore.cleanRetained(topic);
|
||||
} else {
|
||||
m_messagesStore.storeRetained(topic, evt.getGuid());
|
||||
@@ -120,15 +123,15 @@ class Qos2PublishHandler extends QosPublishHandler {
|
||||
|
||||
private void sendPubRec(String clientID, int messageID) {
|
||||
LOG.debug("Sending PUBREC message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
PubRecMessage pubRecMessage = new PubRecMessage();
|
||||
pubRecMessage.setMessageID(messageID);
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC, false, AT_MOST_ONCE, false, 0);
|
||||
MqttMessage pubRecMessage = new MqttMessage(fixedHeader, from(messageID));
|
||||
connectionDescriptors.sendMessage(pubRecMessage,messageID, clientID);
|
||||
}
|
||||
|
||||
private void sendPubComp(String clientID, int messageID) {
|
||||
LOG.debug("Sending PUBCOMP message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
PubCompMessage pubCompMessage = new PubCompMessage();
|
||||
pubCompMessage.setMessageID(messageID);
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, AT_MOST_ONCE, false, 0);
|
||||
MqttMessage pubCompMessage = new MqttMessage(fixedHeader, from(messageID));
|
||||
connectionDescriptors.sendMessage(pubCompMessage, messageID, clientID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utility static methods, like Map get with default value, or elvis operator.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
public static <T, K> T defaultGet(Map<K, T> map, K key, T defaultValue) {
|
||||
T value = map.get(key);
|
||||
if (value != null) {
|
||||
@@ -13,4 +18,16 @@ public class Utils {
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static int messageId(MqttMessage msg) {
|
||||
return ((MqttMessageIdVariableHeader) msg.variableHeader()).messageId();
|
||||
}
|
||||
|
||||
public static byte[] readBytesAndRewind(ByteBuf payload) {
|
||||
byte[] payloadContent = new byte[payload.readableBytes()];
|
||||
int mark = payload.readerIndex();
|
||||
payload.readBytes(payloadContent);
|
||||
payload.readerIndex(mark);
|
||||
return payloadContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package io.moquette.spi.impl.subscriptions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage.QOSType;
|
||||
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Maintain the information about which Topic a certain ClientID is subscribed
|
||||
@@ -29,12 +30,12 @@ import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
public final class Subscription implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3383457629635732794L;
|
||||
final QOSType requestedQos; //max QoS acceptable
|
||||
final MqttQoS requestedQos; //max QoS acceptable
|
||||
final String clientId;
|
||||
final String topicFilter;
|
||||
final boolean active;
|
||||
|
||||
public Subscription(String clientId, String topicFilter, QOSType requestedQos) {
|
||||
public Subscription(String clientId, String topicFilter, MqttQoS requestedQos) {
|
||||
this.requestedQos = requestedQos;
|
||||
this.clientId = clientId;
|
||||
this.topicFilter = topicFilter;
|
||||
@@ -52,7 +53,7 @@ public final class Subscription implements Serializable {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public QOSType getRequestedQos() {
|
||||
public MqttQoS getRequestedQos() {
|
||||
return requestedQos;
|
||||
}
|
||||
|
||||
@@ -85,7 +86,8 @@ public final class Subscription implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[filter:%s, cliID: %s, qos: %s, active: %s]", this.topicFilter, this.clientId, this.requestedQos, this.active);
|
||||
return String.format("[filter:%s, cliID: %s, qos: %s, active: %s]", this.topicFilter, this.clientId,
|
||||
this.requestedQos, this.active);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package io.moquette.spi.impl.subscriptions;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Represents a tree of topics subscriptions.
|
||||
*
|
||||
@@ -235,7 +235,7 @@ public class SubscriptionsStore {
|
||||
continue;
|
||||
}
|
||||
//update the selected subscriptions if not present or if has a greater qos
|
||||
if (existingSub == null || existingSub.getRequestedQos().byteValue() < sub.getRequestedQos().byteValue()) {
|
||||
if (existingSub == null || existingSub.getRequestedQos().value() < sub.getRequestedQos().value()) {
|
||||
subsForClient.put(matchingCouple.clientID, sub);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package io.moquette.spi.persistence;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.parser.proto.MQTTException;
|
||||
import org.mapdb.DB;
|
||||
import org.mapdb.DBMaker;
|
||||
import org.slf4j.Logger;
|
||||
@@ -32,8 +31,8 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
|
||||
import static io.moquette.BrokerConstants.AUTOSAVE_INTERVAL_PROPERTY_NAME;
|
||||
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
|
||||
|
||||
/**
|
||||
* MapDB main persistence implementation
|
||||
@@ -94,7 +93,7 @@ public class MapDBPersistentStore {
|
||||
LOG.warn("Using {} MapDB store file. Path = {}.", fileNewlyCreated ? "fresh" : "existing", m_storePath);
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to open MapDB store file. Path = {}, cause = {}, errorMessage = {}.", m_storePath, ex.getCause(), ex.getMessage());
|
||||
throw new MQTTException("Can't create temp file for subscriptions storage [" + m_storePath + "]", ex);
|
||||
throw new RuntimeException("Can't create temp file for subscriptions storage [" + m_storePath + "]", ex);
|
||||
}
|
||||
m_db = DBMaker.newFileDB(tmpFile).make();
|
||||
}
|
||||
|
||||
@@ -66,13 +66,13 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public void addNewSubscription(Subscription newSubscription) {
|
||||
LOG.info("Adding new subscription. MqttClientId = {}, topics = {}.", newSubscription.getClientId(),
|
||||
LOG.info("Adding new subscription. CId= {}, topics = {}.", newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter());
|
||||
final String clientID = newSubscription.getClientId();
|
||||
m_db.getHashMap("subscriptions_" + clientID).put(newSubscription.getTopicFilter(), newSubscription);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("The subscription has been added. MqttClientId = {}, topics = {}, clientSubscriptions = {}.",
|
||||
LOG.trace("The subscription has been added. CId= {}, topics = {}, clientSubscriptions = {}.",
|
||||
newSubscription.getClientId(), newSubscription.getTopicFilter(),
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
@@ -80,23 +80,23 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public void removeSubscription(String topicFilter, String clientID) {
|
||||
LOG.info("Removing subscription. MqttClientId = {}, topics = {}.", clientID, topicFilter);
|
||||
LOG.info("Removing subscription. CId= {}, topics = {}.", clientID, topicFilter);
|
||||
if (!m_db.exists("subscriptions_" + clientID)) {
|
||||
return;
|
||||
}
|
||||
m_db.getHashMap("subscriptions_" + clientID).remove(topicFilter);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The subscription has been removed. MqttClientId = {}, topics = {}, clientSubscriptions = {}.",
|
||||
LOG.debug("The subscription has been removed. CId= {}, topics = {}, clientSubscriptions = {}.",
|
||||
clientID, topicFilter, m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeSubscriptions(String clientID) {
|
||||
LOG.info("Wiping subscriptions. MqttClientId = {}.", clientID);
|
||||
LOG.info("Wiping subscriptions. CId= {}.", clientID);
|
||||
m_db.delete("subscriptions_" + clientID);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The subscriptions have been removed. MqttClientId = {}, clientSubscriptions = {}.", clientID,
|
||||
LOG.debug("The subscriptions have been removed. CId= {}, clientSubscriptions = {}.", clientID,
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
@Override
|
||||
public Subscription getSubscription(ClientTopicCouple couple) {
|
||||
ConcurrentMap<String, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + couple.clientID);
|
||||
LOG.info("Retrieving subscriptions. MqttClientId = {}, subscriptions = {}.", couple.clientID,
|
||||
LOG.info("Retrieving subscriptions. CId= {}, subscriptions = {}.", couple.clientID,
|
||||
clientSubscriptions);
|
||||
return clientSubscriptions.get(couple.topicFilter);
|
||||
}
|
||||
@@ -146,20 +146,20 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
public ClientSession createNewSession(String clientID, boolean cleanSession) {
|
||||
if (m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.error(
|
||||
"Unable to create a new session: the client ID is already in use. MqttClientId = {}, cleanSession = {}.",
|
||||
"Unable to create a new session: the client ID is already in use. CId= {}, cleanSession = {}.",
|
||||
clientID, cleanSession);
|
||||
throw new IllegalArgumentException("Can't create a session with the ID of an already existing" + clientID);
|
||||
}
|
||||
LOG.info("Creating new session. MqttClientId = {}, cleanSession = {}.", clientID, cleanSession);
|
||||
LOG.info("Creating new session. CId= {}, cleanSession = {}.", clientID, cleanSession);
|
||||
m_persistentSessions.putIfAbsent(clientID, new PersistentSession(cleanSession));
|
||||
return new ClientSession(clientID, m_messagesStore, this, cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession sessionForClient(String clientID) {
|
||||
LOG.info("Retrieving session. MqttClientId = {}.", clientID);
|
||||
LOG.info("Retrieving session. CId= {}.", clientID);
|
||||
if (!m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.warn("The session does not exist. MqttClientId = {}.", clientID);
|
||||
LOG.warn("The session does not exist. CId= {}.", clientID);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public void updateCleanStatus(String clientID, boolean cleanSession) {
|
||||
LOG.info("Updating cleanSession flag. MqttClientId = {}, cleanSession = {}.", clientID, cleanSession);
|
||||
LOG.info("Updating cleanSession flag. CId= {}, cleanSession = {}.", clientID, cleanSession);
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
@Override
|
||||
public int nextPacketID(String clientID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Generating next packet ID. MqttClientId = {}.", clientID);
|
||||
LOG.debug("Generating next packet ID. CId= {}.", clientID);
|
||||
}
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient == null) {
|
||||
@@ -204,17 +204,17 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
int nextPacketId = (maxId + 1) % 0xFFFF;
|
||||
inFlightForClient.add(nextPacketId);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The next packet ID has been generated. MqttClientId = {}, result = {}.", clientID, nextPacketId);
|
||||
LOG.debug("The next packet ID has been generated. CId= {}, result = {}.", clientID, nextPacketId);
|
||||
}
|
||||
return nextPacketId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlightAck(String clientID, int messageID) {
|
||||
LOG.debug("Acknowledging inflight message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
LOG.debug("Acknowledging inflight message. CId= {}, messageId = {}.", clientID, messageID);
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.warn("Unable to retrieve inflight message record. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
m.remove(messageID);
|
||||
@@ -228,7 +228,7 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public void inFlight(String clientID, int messageID, MessageGUID guid) {
|
||||
LOG.debug("Storing inflight message. MqttClientId = {}, messageId = {}, guid = {}.", clientID, messageID, guid);
|
||||
LOG.debug("Storing inflight message. CId= {}, messageId = {}, guid = {}.", clientID, messageID, guid);
|
||||
ConcurrentMap<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new ConcurrentHashMap<>();
|
||||
@@ -240,26 +240,24 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public BlockingQueue<StoredMessage> queue(String clientID) {
|
||||
LOG.info("Queuing pending message. MqttClientId = {}, guid = {}.", clientID);
|
||||
LOG.info("Queuing pending message. CId= {}, guid = {}.", clientID);
|
||||
return this.m_db.getQueue(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropQueue(String clientID) {
|
||||
LOG.info("Removing pending messages. MqttClientId = {}.", clientID);
|
||||
LOG.info("Removing pending messages. CId= {}.", clientID);
|
||||
this.m_db.delete(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Moving inflight message to second phase ack state. MqttClientId = {}, messageID = {}.", clientID,
|
||||
messageID);
|
||||
LOG.debug("Moving inflight message to 2nd phase ack state. CId= {}, messageID = {}.", clientID, messageID);
|
||||
}
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.warn("Unable to retrieve inflight message record. MqttClientId = {}, messageId = {}.", clientID,
|
||||
messageID);
|
||||
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
MessageGUID guid = m.remove(messageID);
|
||||
@@ -278,7 +276,7 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
@Override
|
||||
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Processing second phase ACK. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
LOG.debug("Processing second phase ACK CId={}, messageId={}", clientID, messageID);
|
||||
}
|
||||
Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
MessageGUID guid = messageIDs.remove(messageID);
|
||||
@@ -288,11 +286,10 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public MessageGUID mapToGuid(String clientID, int messageID) {
|
||||
LOG.debug("Mapping message ID to GUID. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
LOG.debug("Mapping message ID to GUID CId={}, messageId={}", clientID, messageID);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(messageId2GuidsMapName(clientID));
|
||||
MessageGUID result = messageIdToGuid.get(messageID);
|
||||
LOG.debug("The message ID has been mapped to a GUID. MqttClientId = {}, messageId = {}, guid = {}.",
|
||||
clientID, messageID, result);
|
||||
LOG.debug("Message ID has been mapped to a GUID CId={}, messageId={}, guid={}", clientID, messageID, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -302,15 +299,15 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
@Override
|
||||
public StoredMessage getInflightMessage(String clientID, int messageID) {
|
||||
LOG.info("Retrieving inflight message. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
LOG.info("Retrieving inflight message CId={}, messageId={}", clientID, messageID);
|
||||
Map<Integer, MessageGUID> clientEntries = m_inflightStore.get(clientID);
|
||||
if (clientEntries == null) {
|
||||
LOG.warn("The client has no inflight messages. MqttClientId = {}, messageId = {}.", clientID, messageID);
|
||||
LOG.warn("The client has no inflight messages CId={}, messageId={}", clientID, messageID);
|
||||
return null;
|
||||
}
|
||||
MessageGUID guid = clientEntries.get(messageID);
|
||||
if (guid == null) {
|
||||
LOG.warn("The message ID does not have an associated GUID. MqttClientId = {}, messageId = {}.", clientID,
|
||||
LOG.warn("The message ID does not have an associated GUID. CId= {}, messageId = {}.", clientID,
|
||||
messageID);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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;
|
||||
// }
|
||||
}
|
||||
@@ -39,14 +39,21 @@ public class MessageCollector implements MqttCallback {
|
||||
private BlockingQueue<ReceivedMessage> m_messages = new LinkedBlockingQueue<>();
|
||||
private boolean m_connectionLost = false;
|
||||
|
||||
public MqttMessage getMessage(boolean checkElapsed) {
|
||||
if (!checkElapsed && m_messages.isEmpty()) {
|
||||
/**
|
||||
* Return the message from the queue if not empty, else return null with wait period.
|
||||
* */
|
||||
public MqttMessage getMessageImmediate() {
|
||||
if (m_messages.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return m_messages.take().message;
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
return getMessage(1);
|
||||
}
|
||||
|
||||
public MqttMessage getMessage(int delay) {
|
||||
public MqttMessage waitMessage(int delay) {
|
||||
try {
|
||||
ReceivedMessage msg = m_messages.poll(delay, TimeUnit.SECONDS);
|
||||
if (msg == null) {
|
||||
@@ -91,7 +98,7 @@ public class MessageCollector implements MqttCallback {
|
||||
@Override
|
||||
public void deliveryComplete(IMqttDeliveryToken token) {
|
||||
// try {
|
||||
// m_messages.offer(new ReceivedMessage(token.getMessage(), token.getTopics()[0]));
|
||||
// m_messages.offer(new ReceivedMessage(token.waitMessage(), token.getTopics()[0]));
|
||||
// } catch (MqttException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package io.moquette.server;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage.QOSType;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.server.config.MemoryConfig;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
|
||||
import org.junit.After;
|
||||
@@ -30,7 +31,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -88,13 +88,14 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
m_subscriber.subscribe(topic, qos);
|
||||
}
|
||||
|
||||
private void internalPublishToWithQosAndRetained(String topic, QOSType qos, boolean retained) {
|
||||
PublishMessage message = new PublishMessage();
|
||||
message.setTopicName(topic);
|
||||
message.setRetainFlag(retained);
|
||||
message.setQos(qos);
|
||||
message.setPayload(ByteBuffer.wrap("Hello world MQTT!!".getBytes()));
|
||||
m_server.internalPublish(message);
|
||||
private void internalPublishToWithQosAndRetained(String topic, MqttQoS qos, boolean retained) {
|
||||
MqttPublishMessage message = MessageBuilder.publish()
|
||||
.topicName(topic)
|
||||
.retained(retained)
|
||||
.qos(qos)
|
||||
.payload("Hello world MQTT!!".getBytes())
|
||||
.build();
|
||||
m_server.internalPublish(message, "INTRPUBL");
|
||||
}
|
||||
|
||||
private void connectNoCleanSession() throws Exception {
|
||||
@@ -109,13 +110,13 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
}
|
||||
|
||||
private void verifyNoMessageIsReceived() throws Exception {
|
||||
MqttMessage msg = m_callback.getMessage(false);
|
||||
MqttMessage msg = m_callback.getMessageImmediate();
|
||||
assertNull(msg);
|
||||
}
|
||||
|
||||
private void verifyMessageIsReceivedSuccessfully() throws Exception {
|
||||
//check in 2 seconds
|
||||
MqttMessage msg = m_callback.getMessage(2);
|
||||
MqttMessage msg = m_callback.waitMessage(2);
|
||||
assertNotNull(msg);
|
||||
assertEquals("Hello world MQTT!!", new String(msg.getPayload()));
|
||||
}
|
||||
@@ -125,7 +126,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
LOG.info("*** testClientSubscribeAfterNotRetainedQoS0IsSent ***");
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.MOST_ONE, false);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, false);
|
||||
subscribeToWithQos("/topic", 0);
|
||||
|
||||
//Verify
|
||||
@@ -139,7 +140,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("/topic", 0);
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.MOST_ONE, false);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, false);
|
||||
|
||||
//Verify
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
@@ -152,7 +153,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("/topic", 0);
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.MOST_ONE, true);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, true);
|
||||
|
||||
//Verify
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
@@ -163,7 +164,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
LOG.info("*** testClientSubscribeAfterRetainedQoS0IsSent ***");
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.MOST_ONE, true);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, true);
|
||||
//LOG.info("** post internalPublish **");
|
||||
subscribeToWithQos("/topic", 0);
|
||||
|
||||
@@ -178,7 +179,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("/topic", 1);
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.LEAST_ONE, false);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, false);
|
||||
|
||||
//Verify
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
@@ -189,7 +190,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
LOG.info("*** testClientSubscribeAfterNotRetainedQoS1IsSent ***");
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.LEAST_ONE, false);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, false);
|
||||
subscribeToWithQos("/topic", 1);
|
||||
|
||||
//Verify
|
||||
@@ -203,7 +204,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("/topic", 1);
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.LEAST_ONE, true);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, true);
|
||||
|
||||
//Verify
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
@@ -214,8 +215,8 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
LOG.info("*** testClientSubscribeAfterRetainedQoS0IsSent ***");
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.LEAST_ONE, true);
|
||||
subscribeToWithQos("/topic", 1);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, true);
|
||||
subscribeToWithQos("/topic", MqttQoS.AT_LEAST_ONCE.value());
|
||||
LOG.info("** After subscribe **");
|
||||
|
||||
//Verify
|
||||
@@ -230,7 +231,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("/topic", 2);
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.EXACTLY_ONCE, false);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, false);
|
||||
|
||||
//Verify
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
@@ -241,7 +242,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
LOG.info("*** testClientSubscribeAfterNotRetainedQoS2IsSent ***");
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.EXACTLY_ONCE, false);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, false);
|
||||
subscribeToWithQos("/topic", 2);
|
||||
|
||||
//Verify
|
||||
@@ -255,7 +256,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("/topic", 2);
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.EXACTLY_ONCE, true);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, true);
|
||||
|
||||
//Verify
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
@@ -266,7 +267,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
LOG.info("*** testClientSubscribeAfterRetainedQoS2IsSent ***");
|
||||
|
||||
//Exercise
|
||||
internalPublishToWithQosAndRetained("/topic", QOSType.EXACTLY_ONCE, true);
|
||||
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, true);
|
||||
subscribeToWithQos("/topic", 2);
|
||||
|
||||
//Verify
|
||||
@@ -280,11 +281,11 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
subscribeToWithQos("foo", 0);
|
||||
m_subscriber.disconnect();
|
||||
|
||||
internalPublishToWithQosAndRetained("foo", QOSType.MOST_ONE, false);
|
||||
internalPublishToWithQosAndRetained("foo", MqttQoS.AT_MOST_ONCE, false);
|
||||
|
||||
//verifyMessageIsReceivedSuccessfully();
|
||||
//m_subscriber.receive(2, TimeUnit.MILLISECONDS);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertTrue(message == null);
|
||||
}
|
||||
|
||||
@@ -296,7 +297,7 @@ public class ServerIntegrationEmbeddedPublishTest {
|
||||
assertTrue(m_server.getSubscriptions().size() == 1);
|
||||
connectNoCleanSession();
|
||||
assertTrue(m_server.getSubscriptions().size() == 1);
|
||||
internalPublishToWithQosAndRetained("foo", QOSType.MOST_ONE, false);
|
||||
internalPublishToWithQosAndRetained("foo", MqttQoS.AT_MOST_ONCE, false);
|
||||
verifyMessageIsReceivedSuccessfully();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -105,21 +105,21 @@ public class ServerIntegrationHazelcastHandlerInterceptorTest{
|
||||
m_listener.subscribe("/topic", 1);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS0".getBytes(), 0, false);
|
||||
MqttMessage messageQos0 = m_messagesCollector.getMessage(true);
|
||||
MqttMessage messageQos0 = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS0", messageQos0.toString());
|
||||
assertEquals(0, messageQos0.getQos());
|
||||
|
||||
m_listener.subscribe("/topic", 1);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS1".getBytes(), 1, false);
|
||||
MqttMessage messageQos1 = m_messagesCollector.getMessage(true);
|
||||
MqttMessage messageQos1 = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS1", messageQos1.toString());
|
||||
assertEquals(1, messageQos1.getQos());
|
||||
|
||||
m_listener.subscribe("/topic", 2);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(), 2, false);
|
||||
MqttMessage messageQos2 = m_messagesCollector.getMessage(true);
|
||||
MqttMessage messageQos2 = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS2", messageQos2.toString());
|
||||
assertEquals(2, messageQos2.getQos());
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ import io.moquette.server.config.IConfig;
|
||||
import io.moquette.server.config.MemoryConfig;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -176,7 +173,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_client.connect(options);
|
||||
m_client.publish("/topic", "Test my payload".getBytes(), 0, false);
|
||||
//Ugly, we need to get self notified after the publish
|
||||
assertEquals("Test my payload", new String(subListener.getMessage(1).getPayload()));
|
||||
assertEquals("Test my payload", new String(subListener.waitMessage(1).getPayload()));
|
||||
|
||||
//close
|
||||
m_client.disconnect();
|
||||
@@ -231,7 +228,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_messagesCollector.reinit();
|
||||
m_client.publish("/topic", "Test my payload".getBytes(), 0, false);
|
||||
|
||||
assertNull(m_messagesCollector.getMessage(false));
|
||||
assertNull(m_messagesCollector.getMessageImmediate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -252,7 +249,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_client.connect(options);
|
||||
m_client.publish("/topic", "Test my payload".getBytes(), 0, false);
|
||||
|
||||
assertNull(m_messagesCollector.getMessage(false));
|
||||
assertNull(m_messagesCollector.getMessageImmediate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,7 +261,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_client.disconnect();
|
||||
|
||||
//reconnect and publish
|
||||
MqttMessage message = m_messagesCollector.getMessage(true);
|
||||
MqttMessage message = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello MQTT", message.toString());
|
||||
assertEquals(1, message.getQos());
|
||||
}
|
||||
@@ -283,7 +280,7 @@ public class ServerIntegrationPahoTest {
|
||||
|
||||
m_client.connect(options);
|
||||
|
||||
assertEquals("Hello MQTT", m_messagesCollector.getMessage(true).toString());
|
||||
assertEquals("Hello MQTT", m_messagesCollector.waitMessage(1).toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -303,7 +300,7 @@ public class ServerIntegrationPahoTest {
|
||||
publishFromAnotherClient("/topic", "Hello MQTT".getBytes(), 1);
|
||||
|
||||
//Verify that after a reconnection the client receive the message
|
||||
MqttMessage message = m_messagesCollector.getMessage(true);
|
||||
MqttMessage message = m_messagesCollector.waitMessage(1);
|
||||
assertNotNull(message);
|
||||
assertEquals("Hello MQTT", message.toString());
|
||||
}
|
||||
@@ -329,7 +326,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_messagesCollector.reinit();
|
||||
m_client.connect(options);
|
||||
|
||||
MqttMessage message = m_messagesCollector.getMessage(true);
|
||||
MqttMessage message = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello MQTT", message.toString());
|
||||
assertEquals(2, message.getQos());
|
||||
}
|
||||
@@ -349,7 +346,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_client.connect(options);
|
||||
|
||||
assertNotNull(m_messagesCollector);
|
||||
MqttMessage message = m_messagesCollector.getMessage(true);
|
||||
MqttMessage message = m_messagesCollector.waitMessage(1);
|
||||
assertNotNull(message);
|
||||
assertEquals("Hello MQTT", message.toString());
|
||||
}
|
||||
@@ -368,7 +365,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_client.connect(options);
|
||||
|
||||
assertNotNull(m_messagesCollector);
|
||||
MqttMessage message = m_messagesCollector.getMessage(true);
|
||||
MqttMessage message = m_messagesCollector.waitMessage(1);
|
||||
assertNotNull(message);
|
||||
assertEquals("Hello MQTT 1", message.toString());
|
||||
m_client.disconnect();
|
||||
@@ -380,7 +377,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_messagesCollector.reinit();
|
||||
m_client.connect(options);
|
||||
assertNotNull(m_messagesCollector);
|
||||
message = m_messagesCollector.getMessage(true);
|
||||
message = m_messagesCollector.waitMessage(1);
|
||||
assertNotNull(message);
|
||||
assertEquals("Hello MQTT 2", message.toString());
|
||||
}
|
||||
@@ -417,12 +414,12 @@ public class ServerIntegrationPahoTest {
|
||||
m_client.connect();
|
||||
m_client.publish("a/b", "Hello world MQTT!!".getBytes(), 2, false);
|
||||
|
||||
MqttMessage messageOnA = cbSubscriberA.getMessage(true);
|
||||
MqttMessage messageOnA = cbSubscriberA.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!", new String(messageOnA.getPayload()));
|
||||
assertEquals(1, messageOnA.getQos());
|
||||
subscriberA.disconnect();
|
||||
|
||||
MqttMessage messageOnB = cbSubscriberB.getMessage(true);
|
||||
MqttMessage messageOnB = cbSubscriberB.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!", new String(messageOnB.getPayload()));
|
||||
assertEquals(2, messageOnB.getQos());
|
||||
subscriberB.disconnect();
|
||||
@@ -466,7 +463,7 @@ public class ServerIntegrationPahoTest {
|
||||
assertFalse(clientForSubscribe1.isConnected());
|
||||
assertTrue(clientForSubscribe2.isConnected());
|
||||
LOG.info("Waiting to receive 1 sec from " + clientForSubscribe2.getClientId());
|
||||
MqttMessage messageOnB = cbSubscriber2.getMessage(true);
|
||||
MqttMessage messageOnB = cbSubscriber2.waitMessage(1);
|
||||
assertEquals("Hello", new String(messageOnB.getPayload()));
|
||||
}
|
||||
|
||||
@@ -495,7 +492,7 @@ public class ServerIntegrationPahoTest {
|
||||
|
||||
//Verify that the second subscriber client get notified and not the first.
|
||||
assertTrue(cbSubscriber1.connectionLost());
|
||||
assertEquals("Hello 2", new String(cbSubscriber2.getMessage(true).getPayload()));
|
||||
assertEquals("Hello 2", new String(cbSubscriber2.waitMessage(1).getPayload()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -533,7 +530,7 @@ public class ServerIntegrationPahoTest {
|
||||
m_publisher.publish("/topic", "Hello world MQTT!!-1".getBytes(), 1, false);
|
||||
|
||||
//read the first message and drop the connection
|
||||
MqttMessage msg = m_messagesCollector.getMessage(true);
|
||||
MqttMessage msg = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!-1", new String(msg.getPayload()));
|
||||
m_client.disconnect();
|
||||
|
||||
@@ -542,10 +539,10 @@ public class ServerIntegrationPahoTest {
|
||||
|
||||
//reconnect and expect to receive the hello 2 message
|
||||
m_client.connect(options);
|
||||
msg = m_messagesCollector.getMessage(true);
|
||||
msg = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!-2", new String(msg.getPayload()));
|
||||
|
||||
msg = m_messagesCollector.getMessage(true);
|
||||
msg = m_messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!-3", new String(msg.getPayload()));
|
||||
m_client.disconnect();
|
||||
}
|
||||
@@ -569,7 +566,7 @@ public class ServerIntegrationPahoTest {
|
||||
//reconnect and expect to receive the hello 2 message
|
||||
m_client.connect(options);
|
||||
m_client.subscribe("/topic", 1);
|
||||
MqttMessage msg = messagesCollector.getMessage(true);
|
||||
MqttMessage msg = messagesCollector.waitMessage(1);
|
||||
assertNotNull(msg);
|
||||
assertEquals("Hello world MQTT!!-1", new String(msg.getPayload()));
|
||||
}
|
||||
@@ -599,10 +596,10 @@ public class ServerIntegrationPahoTest {
|
||||
//subscriber reconnects
|
||||
m_client.connect(options);
|
||||
//subscriber should receive the 2 messages missed
|
||||
MqttMessage msg = messagesCollector.getMessage(1);
|
||||
MqttMessage msg = messagesCollector.waitMessage(1);
|
||||
assertNotNull(msg);
|
||||
assertEquals("Hello1", new String(msg.getPayload()));
|
||||
msg = messagesCollector.getMessage(1);
|
||||
msg = messagesCollector.waitMessage(1);
|
||||
assertNotNull(msg);
|
||||
assertEquals("Hello2", new String(msg.getPayload()));
|
||||
}
|
||||
@@ -629,11 +626,11 @@ public class ServerIntegrationPahoTest {
|
||||
m_publisher.publish("a/b", "Hello world MQTT!!".getBytes(), 1, false);
|
||||
|
||||
//reconnect and expect to receive the hello 2 message
|
||||
MqttMessage msg = messagesCollector.getMessage(1);
|
||||
MqttMessage msg = messagesCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!", new String(msg.getPayload()));
|
||||
|
||||
//try to listen a second publish
|
||||
msg = messagesCollector.getMessage(1);
|
||||
msg = messagesCollector.waitMessage(1);
|
||||
assertNull(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 0);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS0".getBytes(), 0, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS0", message.toString());
|
||||
assertEquals(0, message.getQos());
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 0);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS1".getBytes(), 1, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS1", message.toString());
|
||||
assertEquals(0, message.getQos());
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 0);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(), 2, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS2", message.toString());
|
||||
assertEquals(0, message.getQos());
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 1);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS0".getBytes(), 0, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS0", message.toString());
|
||||
assertEquals(0, message.getQos());
|
||||
}
|
||||
@@ -144,7 +144,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 1);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS1".getBytes(), 1, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS1", message.toString());
|
||||
assertEquals(1, message.getQos());
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 1);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(), 2, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS2", message.toString());
|
||||
assertEquals(1, message.getQos());
|
||||
}
|
||||
@@ -166,7 +166,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 2);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(), 0, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS2", message.toString());
|
||||
assertEquals(0, message.getQos());
|
||||
}
|
||||
@@ -177,7 +177,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 2);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(), 1, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS2", message.toString());
|
||||
assertEquals(1, message.getQos());
|
||||
}
|
||||
@@ -188,7 +188,7 @@ public class ServerIntegrationQoSValidationTest {
|
||||
m_subscriber.subscribe("/topic", 2);
|
||||
|
||||
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(), 2, false);
|
||||
MqttMessage message = m_callback.getMessage(true);
|
||||
MqttMessage message = m_callback.waitMessage(1);
|
||||
assertEquals("Hello world MQTT QoS2", message.toString());
|
||||
assertEquals(2, message.getQos());
|
||||
}
|
||||
|
||||
+32
-13
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
package io.moquette.server;
|
||||
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.ConnAckMessage;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.server.config.MemoryConfig;
|
||||
import io.moquette.testclient.Client;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -30,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -45,7 +44,7 @@ public class ServerLowlevelMessagesIntegrationTests {
|
||||
IMqttClient m_willSubscriber;
|
||||
MessageCollector m_messageCollector;
|
||||
IConfig m_config;
|
||||
AbstractMessage receivedMsg;
|
||||
MqttMessage receivedMsg;
|
||||
|
||||
protected void startServer() throws IOException {
|
||||
m_server = new Server();
|
||||
@@ -72,14 +71,17 @@ public class ServerLowlevelMessagesIntegrationTests {
|
||||
LOG.debug("After asked server to stop");
|
||||
IntegrationUtils.cleanPersistenceFile(m_config);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void elapseKeepAliveTime() throws InterruptedException {
|
||||
int keepAlive = 2; //secs
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
|
||||
MqttConnectMessage connectMessage = createConnectMessage("FAKECLNT", keepAlive);
|
||||
|
||||
/*ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setProtocolVersion((byte) 3);
|
||||
connectMessage.setClientID("FAKECLNT");
|
||||
connectMessage.setKeepAlive(keepAlive);
|
||||
connectMessage.setKeepAlive(keepAlive);*/
|
||||
m_client.sendMessage(connectMessage);
|
||||
|
||||
//wait 2 times the keepAlive
|
||||
@@ -87,6 +89,23 @@ public class ServerLowlevelMessagesIntegrationTests {
|
||||
|
||||
assertTrue(m_client.isConnectionLost());
|
||||
}
|
||||
|
||||
private static MqttConnectMessage createConnectMessage(String clientID, int keepAlive) {
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||
MqttConnectVariableHeader mqttConnectVariableHeader =
|
||||
new MqttConnectVariableHeader(
|
||||
MqttVersion.MQTT_3_1.protocolName(),
|
||||
MqttVersion.MQTT_3_1.protocolLevel(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
keepAlive);
|
||||
MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(clientID, null, null, null, null);
|
||||
return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWillMessageIsWiredOnClientKeepAliveExpiry() throws Exception {
|
||||
@@ -102,7 +121,7 @@ public class ServerLowlevelMessagesIntegrationTests {
|
||||
|
||||
//but after the 2 KEEP ALIVE timeout expires it gets fired,
|
||||
//NB it's 1,5 * KEEP_ALIVE so 3 secs and some millis to propagate the message
|
||||
MqttMessage msg = m_messageCollector.getMessage(5);
|
||||
org.eclipse.paho.client.mqttv3.MqttMessage msg = m_messageCollector.waitMessage(5);
|
||||
assertNotNull("the will message should be fired after keep alive!", msg);
|
||||
//the will message hasn't to be received before the elapsing of Keep Alive timeout
|
||||
assertTrue(System.currentTimeMillis() - connectTime > 3000);
|
||||
@@ -118,9 +137,9 @@ public class ServerLowlevelMessagesIntegrationTests {
|
||||
|
||||
this.receivedMsg = this.m_client.lastReceivedMessage();
|
||||
|
||||
assertTrue(receivedMsg instanceof ConnAckMessage);
|
||||
ConnAckMessage connAck = (ConnAckMessage) receivedMsg;
|
||||
assertEquals(ConnAckMessage.IDENTIFIER_REJECTED, connAck.getReturnCode());
|
||||
assertTrue(receivedMsg instanceof MqttConnAckMessage);
|
||||
MqttConnAckMessage connAck = (MqttConnAckMessage) receivedMsg;
|
||||
assertEquals(CONNECTION_REFUSED_IDENTIFIER_REJECTED, connAck.variableHeader().connectReturnCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,7 +155,7 @@ public class ServerLowlevelMessagesIntegrationTests {
|
||||
m_client.close();
|
||||
|
||||
//Verify will testament is published
|
||||
MqttMessage receivedTestament = m_messageCollector.getMessage(1);
|
||||
org.eclipse.paho.client.mqttv3.MqttMessage receivedTestament = m_messageCollector.waitMessage(1);
|
||||
assertEquals(willTestamentMsg, new String(receivedTestament.getPayload()));
|
||||
m_willSubscriber.disconnect();
|
||||
}
|
||||
|
||||
@@ -112,10 +112,10 @@ public class ServerRestartIntegrationTest {
|
||||
m_publisher.publish("/topic", "Hello world MQTT!!".getBytes(), 1, false);
|
||||
|
||||
//read the messages
|
||||
MqttMessage msg = m_messageCollector.getMessage(true);
|
||||
MqttMessage msg = m_messageCollector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!", new String(msg.getPayload()));
|
||||
//no more messages on the same topic will be received
|
||||
assertNull(m_messageCollector.getMessage(1));
|
||||
assertNull(m_messageCollector.waitMessage(1));
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ public class ServerRestartIntegrationTest {
|
||||
|
||||
//Expected
|
||||
//the subscriber doesn't get notified (it's fully unsubscribed)
|
||||
assertNull(m_messageCollector.getMessage(1));
|
||||
assertNull(m_messageCollector.waitMessage(1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,7 +174,7 @@ public class ServerRestartIntegrationTest {
|
||||
client.connect();
|
||||
client.subscribe(topic, 1);
|
||||
client.publish(topic, "Hello world MQTT!!".getBytes(), 0, false);
|
||||
MqttMessage msg = collector.getMessage(true);
|
||||
MqttMessage msg = collector.waitMessage(1);
|
||||
assertEquals("Hello world MQTT!!", new String(msg.getPayload()));
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.moquette.spi;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.spi.impl.MemoryStorageService;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -11,9 +11,6 @@ import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Created by nedermail on 17/07/16.
|
||||
*/
|
||||
public class ClientSessionTest {
|
||||
|
||||
ClientSession session1;
|
||||
@@ -36,12 +33,12 @@ public class ClientSessionTest {
|
||||
@Test
|
||||
public void overridingSubscriptions() {
|
||||
// Subscribe on /topic with QOSType.MOST_ONE
|
||||
Subscription oldSubscription = new Subscription(session1.clientID, "/topic", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription oldSubscription = new Subscription(session1.clientID, "/topic", AT_MOST_ONCE);
|
||||
session1.subscribe(oldSubscription);
|
||||
store.add(oldSubscription.asClientTopicCouple());
|
||||
|
||||
// Subscribe on /topic again that overrides the previous subscription.
|
||||
Subscription overrindingSubscription = new Subscription(session1.clientID, "/topic", AbstractMessage.QOSType.EXACTLY_ONCE);
|
||||
Subscription overrindingSubscription = new Subscription(session1.clientID, "/topic", EXACTLY_ONCE);
|
||||
session1.subscribe(overrindingSubscription);
|
||||
store.add(overrindingSubscription.asClientTopicCouple());
|
||||
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -28,9 +27,10 @@ import org.junit.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.refEq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
@@ -105,7 +105,7 @@ public class BrokerInterceptorTest {
|
||||
|
||||
@Test
|
||||
public void testNotifyClientConnected() throws Exception {
|
||||
interceptor.notifyClientConnected(new ConnectMessage());
|
||||
interceptor.notifyClientConnected(MessageBuilder.connect().build());
|
||||
interval();
|
||||
assertEquals(40, n.get());
|
||||
}
|
||||
@@ -119,14 +119,17 @@ public class BrokerInterceptorTest {
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicPublished() throws Exception {
|
||||
interceptor.notifyTopicPublished(new PublishMessage(), "cli1234", "cli1234");
|
||||
interceptor.notifyTopicPublished(MessageBuilder.publish()
|
||||
.qos(MqttQoS.AT_MOST_ONCE)
|
||||
.payload("Hello".getBytes())
|
||||
.build(), "cli1234", "cli1234");
|
||||
interval();
|
||||
assertEquals(60, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicSubscribed() throws Exception {
|
||||
interceptor.notifyTopicSubscribed(new Subscription("cli1", "o2", AbstractMessage.QOSType.MOST_ONE), "cli1234");
|
||||
interceptor.notifyTopicSubscribed(new Subscription("cli1", "o2", MqttQoS.AT_MOST_ONCE), "cli1234");
|
||||
interval();
|
||||
assertEquals(70, n.get());
|
||||
}
|
||||
@@ -146,7 +149,7 @@ public class BrokerInterceptorTest {
|
||||
interceptor.addInterceptHandler(interceptHandlerMock1);
|
||||
interceptor.addInterceptHandler(interceptHandlerMock2);
|
||||
|
||||
Subscription subscription = new Subscription("cli1", "o2", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription subscription = new Subscription("cli1", "o2", MqttQoS.AT_MOST_ONCE);
|
||||
interceptor.notifyTopicSubscribed(subscription, "cli1234");
|
||||
interval();
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ import io.moquette.spi.security.IAuthenticator;
|
||||
class MockAuthenticator implements IAuthenticator {
|
||||
|
||||
private Set<String> m_clientIds;
|
||||
private Map<String, byte[]> m_userPwds;
|
||||
private Map<String, String> m_userPwds;
|
||||
|
||||
MockAuthenticator(Set<String> clientIds, Map<String, byte[]> userPwds) {
|
||||
MockAuthenticator(Set<String> clientIds, Map<String, String> userPwds) {
|
||||
m_clientIds = clientIds;
|
||||
m_userPwds = userPwds;
|
||||
}
|
||||
@@ -42,7 +42,10 @@ class MockAuthenticator implements IAuthenticator {
|
||||
if (!m_userPwds.containsKey(username)) {
|
||||
return false;
|
||||
}
|
||||
return m_userPwds.get(username).equals(password);
|
||||
if (password == null) {
|
||||
return false;
|
||||
}
|
||||
return m_userPwds.get(username).equals(new String(password));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
* http://www.opensource.org/licenses/apache2.0.php
|
||||
*
|
||||
* You may elect to redistribute this code under either of these licenses.
|
||||
*/package io.moquette.spi.impl;
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -23,9 +24,8 @@ import java.util.Map;
|
||||
/**
|
||||
* This a synchronous channel that avoid output ring buffer from Processor
|
||||
*/
|
||||
class MockReceiverChannel /*implements ServerChannel*/ {
|
||||
// byte m_returnCode;
|
||||
AbstractMessage m_receivedMessage;
|
||||
class MockReceiverChannel {
|
||||
MqttMessage m_receivedMessage;
|
||||
private Map<Object, Object> m_attributes = new HashMap<>();
|
||||
|
||||
public Object getAttribute(AttributeKey<Object> key) {
|
||||
@@ -42,20 +42,13 @@ class MockReceiverChannel /*implements ServerChannel*/ {
|
||||
public void close(boolean immediately) {
|
||||
}
|
||||
|
||||
public AbstractMessage getMessage() {
|
||||
public MqttMessage getMessage() {
|
||||
return this.m_receivedMessage;
|
||||
}
|
||||
|
||||
// public byte getReturnCode() {
|
||||
// return this.m_returnCode;
|
||||
// }
|
||||
|
||||
public void write(Object value) {
|
||||
try {
|
||||
this.m_receivedMessage = (AbstractMessage) value;
|
||||
// if (this.m_receivedMessage instanceof PublishMessage) {
|
||||
// T buf = (T) this.m_receivedMessage;
|
||||
// }
|
||||
this.m_receivedMessage = (MqttMessage) value;
|
||||
} catch (Exception ex) {
|
||||
throw new AssertionError("Wrong return code");
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.ConnAckMessage;
|
||||
import io.moquette.parser.proto.messages.SubAckMessage;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.mqtt.MqttConnAckMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
|
||||
import io.netty.handler.codec.mqtt.MqttSubAckMessage;
|
||||
|
||||
import static io.moquette.parser.proto.messages.ConnAckMessage.CONNECTION_ACCEPTED;
|
||||
|
||||
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -29,18 +29,18 @@ import static org.junit.Assert.assertTrue;
|
||||
*/
|
||||
class NettyChannelAssertions {
|
||||
|
||||
static void assertEqualsConnAck(byte expectedCode, Object connAck) {
|
||||
static void assertEqualsConnAck(MqttConnectReturnCode expectedCode, Object connAck) {
|
||||
assertEqualsConnAck(null, expectedCode, connAck);
|
||||
}
|
||||
|
||||
static void assertEqualsConnAck(String msg, byte expectedCode, Object connAck) {
|
||||
assertTrue("connAck is not an instance of ConnAckMessage", connAck instanceof ConnAckMessage);
|
||||
ConnAckMessage connAckMsg = (ConnAckMessage) connAck;
|
||||
static void assertEqualsConnAck(String msg, MqttConnectReturnCode expectedCode, Object connAck) {
|
||||
assertTrue("connAck is not an instance of ConnAckMessage", connAck instanceof MqttConnAckMessage);
|
||||
MqttConnAckMessage connAckMsg = (MqttConnAckMessage) connAck;
|
||||
|
||||
if(msg == null)
|
||||
assertEquals(expectedCode, connAckMsg.getReturnCode());
|
||||
if (msg == null)
|
||||
assertEquals(expectedCode, connAckMsg.variableHeader().connectReturnCode());
|
||||
else
|
||||
assertEquals(msg, expectedCode, connAckMsg.getReturnCode());
|
||||
assertEquals(msg, expectedCode, connAckMsg.variableHeader().connectReturnCode());
|
||||
}
|
||||
|
||||
static void assertConnAckAccepted(EmbeddedChannel channel) {
|
||||
@@ -49,7 +49,7 @@ class NettyChannelAssertions {
|
||||
}
|
||||
|
||||
static void assertEqualsSubAck(/*byte expectedCode,*/ Object subAck) {
|
||||
assertTrue(subAck instanceof SubAckMessage);
|
||||
assertTrue(subAck instanceof MqttSubAckMessage);
|
||||
//SubAckMessage connAckMsg = (SubAckMessage) connAck;
|
||||
//assertEquals(expectedCode, connAckMsg.getReturnCode());
|
||||
}
|
||||
|
||||
@@ -15,28 +15,28 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.*;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage.QOSType;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.moquette.spi.*;
|
||||
import io.moquette.spi.IMatchingCondition;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.impl.security.PermitAllAuthorizator;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import static io.moquette.spi.impl.NettyChannelAssertions.assertConnAckAccepted;
|
||||
import static io.moquette.spi.impl.ProtocolProcessor.lowerQosToTheSubscriptionDesired;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -49,29 +49,35 @@ public class ProtocolProcessorTest {
|
||||
final static String FAKE_PUBLISHER_ID = "Publisher";
|
||||
final static String FAKE_TOPIC = "/news";
|
||||
final static String BAD_FORMATTED_TOPIC = "#MQTTClient";
|
||||
|
||||
|
||||
final static String TEST_USER = "fakeuser";
|
||||
final static byte[] TEST_PWD = "fakepwd".getBytes();
|
||||
final static String TEST_PWD = "fakepwd";
|
||||
final static String EVIL_TEST_USER = "eviluser";
|
||||
final static byte[] EVIL_TEST_PWD = "unsecret".getBytes();
|
||||
final static String EVIL_TEST_PWD = "unsecret";
|
||||
|
||||
final static List<InterceptHandler> EMPTY_OBSERVERS = Collections.emptyList();
|
||||
final static BrokerInterceptor NO_OBSERVERS_INTERCEPTOR = new BrokerInterceptor(EMPTY_OBSERVERS);
|
||||
|
||||
|
||||
EmbeddedChannel m_channel;
|
||||
ConnectMessage connMsg;
|
||||
MqttConnectMessage connMsg;
|
||||
ProtocolProcessor m_processor;
|
||||
|
||||
|
||||
IMessagesStore m_messagesStore;
|
||||
ISessionsStore m_sessionStore;
|
||||
SubscriptionsStore subscriptions;
|
||||
MockAuthenticator m_mockAuthenticator;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws InterruptedException {
|
||||
connMsg = new ConnectMessage();
|
||||
/*connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion((byte) 0x03);
|
||||
|
||||
connMsg = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.clientId(FAKE_CLIENT_ID)
|
||||
.cleanSession(true)
|
||||
.build();*/
|
||||
|
||||
m_channel = new EmbeddedChannel();
|
||||
NettyUtils.clientID(m_channel, FAKE_CLIENT_ID);
|
||||
NettyUtils.cleanSession(m_channel, false);
|
||||
@@ -87,7 +93,7 @@ public class ProtocolProcessorTest {
|
||||
Set<String> clientIds = new HashSet<>();
|
||||
clientIds.add(FAKE_CLIENT_ID);
|
||||
clientIds.add(FAKE_CLIENT_ID2);
|
||||
Map<String, byte[]> users = new HashMap<>();
|
||||
Map<String, String> users = new HashMap<>();
|
||||
users.put(TEST_USER, TEST_PWD);
|
||||
m_mockAuthenticator = new MockAuthenticator(clientIds, users);
|
||||
|
||||
@@ -102,7 +108,7 @@ public class ProtocolProcessorTest {
|
||||
@Test
|
||||
public void testPublishToItself() throws InterruptedException {
|
||||
final Subscription subscription = new Subscription(FAKE_CLIENT_ID,
|
||||
FAKE_TOPIC, AbstractMessage.QOSType.MOST_ONE);
|
||||
FAKE_TOPIC, MqttQoS.AT_MOST_ONCE);
|
||||
|
||||
//subscriptions.matches(topic) redefine the method to return true
|
||||
SubscriptionsStore subs = new SubscriptionsStore() {
|
||||
@@ -121,21 +127,22 @@ public class ProtocolProcessorTest {
|
||||
storageService.initStore();
|
||||
subs.init(storageService.sessionsStore());
|
||||
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setProtocolVersion((byte) 3);
|
||||
connectMessage.setClientID(FAKE_CLIENT_ID);
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
|
||||
connectMessage.setCleanSession(true);
|
||||
MqttConnectMessage connectMessage = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.clientId(FAKE_CLIENT_ID)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
m_processor.processConnect(m_channel, connectMessage);
|
||||
|
||||
|
||||
//Exercise
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5).put("Hello".getBytes());
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setTopicName(FAKE_TOPIC);
|
||||
msg.setQos(QOSType.MOST_ONE);
|
||||
msg.setPayload(buffer);
|
||||
msg.setRetainFlag(false);
|
||||
MqttPublishMessage msg = MessageBuilder.publish()
|
||||
.topicName(FAKE_TOPIC)
|
||||
.qos(MqttQoS.AT_MOST_ONCE)
|
||||
.retained(false)
|
||||
.payload("Hello".getBytes())
|
||||
.build();
|
||||
|
||||
NettyUtils.userName(m_channel, "FakeCLI");
|
||||
m_processor.processPublish(m_channel, msg);
|
||||
|
||||
@@ -147,9 +154,9 @@ public class ProtocolProcessorTest {
|
||||
@Test
|
||||
public void testPublishToMultipleSubscribers() throws InterruptedException {
|
||||
final Subscription subscription = new Subscription(FAKE_CLIENT_ID,
|
||||
FAKE_TOPIC, AbstractMessage.QOSType.MOST_ONE);
|
||||
FAKE_TOPIC, MqttQoS.AT_MOST_ONCE);
|
||||
final Subscription subscriptionClient2 = new Subscription(FAKE_CLIENT_ID2,
|
||||
FAKE_TOPIC, AbstractMessage.QOSType.MOST_ONE);
|
||||
FAKE_TOPIC, MqttQoS.AT_MOST_ONCE);
|
||||
|
||||
//subscriptions.matches(topic) redefine the method to return true
|
||||
SubscriptionsStore subs = new SubscriptionsStore() {
|
||||
@@ -170,61 +177,62 @@ public class ProtocolProcessorTest {
|
||||
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
EmbeddedChannel firstReceiverChannel = new EmbeddedChannel();
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setProtocolVersion((byte) 3);
|
||||
connectMessage.setClientID(FAKE_CLIENT_ID);
|
||||
connectMessage.setCleanSession(true);
|
||||
MqttConnectMessage connectMessage = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.clientId(FAKE_CLIENT_ID)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
m_processor.processConnect(firstReceiverChannel, connectMessage);
|
||||
assertConnAckAccepted(firstReceiverChannel);
|
||||
|
||||
//connect the second fake subscriber
|
||||
EmbeddedChannel secondReceiverChannel = new EmbeddedChannel();
|
||||
ConnectMessage connectMessage2 = new ConnectMessage();
|
||||
connectMessage2.setProtocolVersion((byte) 3);
|
||||
connectMessage2.setClientID(FAKE_CLIENT_ID2);
|
||||
connectMessage2.setCleanSession(true);
|
||||
connectMessage2.setCleanSession(true);
|
||||
MqttConnectMessage connectMessage2 = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.clientId(FAKE_CLIENT_ID2)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
m_processor.processConnect(secondReceiverChannel, connectMessage2);
|
||||
assertConnAckAccepted(secondReceiverChannel);
|
||||
|
||||
//Exercise
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5).put("Hello".getBytes());
|
||||
buffer.rewind();
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setTopicName(FAKE_TOPIC);
|
||||
msg.setQos(QOSType.MOST_ONE);
|
||||
msg.setPayload(buffer);
|
||||
msg.setRetainFlag(false);
|
||||
MqttPublishMessage msg = MessageBuilder.publish()
|
||||
.topicName(FAKE_TOPIC)
|
||||
.qos(MqttQoS.AT_MOST_ONCE)
|
||||
.retained(false)
|
||||
.payload("Hello".getBytes())
|
||||
.build();
|
||||
NettyUtils.userName(m_channel, "FakeCLI");
|
||||
m_processor.processPublish(m_channel, msg);
|
||||
|
||||
|
||||
//Verify
|
||||
firstReceiverChannel.flush();
|
||||
PublishMessage pub2FirstSubscriber = (PublishMessage) firstReceiverChannel.readOutbound();
|
||||
MqttPublishMessage pub2FirstSubscriber = firstReceiverChannel.readOutbound();
|
||||
assertNotNull(pub2FirstSubscriber);
|
||||
String firstMessageContent = DebugUtils.payload2Str(pub2FirstSubscriber.getPayload());
|
||||
String firstMessageContent = DebugUtils.payload2Str(pub2FirstSubscriber.payload());
|
||||
assertEquals("Hello", firstMessageContent);
|
||||
|
||||
secondReceiverChannel.flush();
|
||||
PublishMessage pub2SecondSubscriber = (PublishMessage) secondReceiverChannel.readOutbound();
|
||||
MqttPublishMessage pub2SecondSubscriber = secondReceiverChannel.readOutbound();
|
||||
assertNotNull(pub2SecondSubscriber);
|
||||
String secondMessageContent = DebugUtils.payload2Str(pub2SecondSubscriber.getPayload());
|
||||
String secondMessageContent = DebugUtils.payload2Str(pub2SecondSubscriber.payload());
|
||||
assertEquals("Hello", secondMessageContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribe() {
|
||||
MqttSubscribeMessage msg = MessageBuilder.subscribe()
|
||||
.addSubscription(MqttQoS.AT_MOST_ONCE, FAKE_TOPIC)
|
||||
.messageId(10)
|
||||
.build();
|
||||
//Exercise
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), FAKE_TOPIC));
|
||||
msg.setMessageID(10);
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
|
||||
m_processor.processSubscribe(m_channel, msg);
|
||||
|
||||
//Verify
|
||||
assertTrue(m_channel.readOutbound() instanceof SubAckMessage);
|
||||
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, AbstractMessage.QOSType.MOST_ONE);
|
||||
assertTrue(m_channel.readOutbound() instanceof MqttSubAckMessage);
|
||||
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, MqttQoS.AT_MOST_ONCE);
|
||||
assertTrue(subscriptions.contains(expectedSubscription));
|
||||
}
|
||||
|
||||
@@ -239,25 +247,33 @@ public class ProtocolProcessorTest {
|
||||
mockAuthorizator, NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
//Exercise
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setMessageID(10);
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), FAKE_TOPIC));
|
||||
MqttSubscribeMessage msg = MessageBuilder.subscribe()
|
||||
.addSubscription(MqttQoS.AT_MOST_ONCE, FAKE_TOPIC)
|
||||
.messageId(10)
|
||||
.build();
|
||||
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
|
||||
m_processor.processSubscribe(m_channel, msg);
|
||||
|
||||
//Verify
|
||||
Object ackMsg = m_channel.readOutbound();
|
||||
assertTrue(ackMsg instanceof SubAckMessage);
|
||||
SubAckMessage subAckMsg = (SubAckMessage) ackMsg;
|
||||
assertEquals(1, subAckMsg.types().size());
|
||||
assertTrue(subAckMsg.types().contains(AbstractMessage.QOSType.FAILURE));
|
||||
assertTrue(ackMsg instanceof MqttSubAckMessage);
|
||||
MqttSubAckMessage subAckMsg = (MqttSubAckMessage) ackMsg;
|
||||
verifyFailureQos(subAckMsg);
|
||||
}
|
||||
|
||||
|
||||
private void verifyFailureQos(MqttSubAckMessage subAckMsg) {
|
||||
List<Integer> grantedQoSes = subAckMsg.payload().grantedQoSLevels();
|
||||
assertEquals(1, grantedQoSes.size());
|
||||
assertTrue(grantedQoSes.contains(MqttQoS.FAILURE.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleSubscribe() {
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setMessageID(10);
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), FAKE_TOPIC));
|
||||
MqttSubscribeMessage msg = MessageBuilder.subscribe()
|
||||
.addSubscription(MqttQoS.AT_MOST_ONCE, FAKE_TOPIC)
|
||||
.messageId(10)
|
||||
.build();
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
|
||||
assertEquals(0, subscriptions.size());
|
||||
|
||||
@@ -268,16 +284,18 @@ public class ProtocolProcessorTest {
|
||||
|
||||
//Verify
|
||||
assertEquals(1, subscriptions.size());
|
||||
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, MqttQoS.AT_MOST_ONCE);
|
||||
assertTrue(subscriptions.contains(expectedSubscription));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSubscribeWithBadFormattedTopic() {
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setMessageID(10);
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), BAD_FORMATTED_TOPIC));
|
||||
MqttSubscribeMessage msg = MessageBuilder.subscribe()
|
||||
.addSubscription(MqttQoS.AT_MOST_ONCE, BAD_FORMATTED_TOPIC)
|
||||
.messageId(10)
|
||||
.build();
|
||||
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
|
||||
assertEquals(0, subscriptions.size());
|
||||
|
||||
@@ -287,10 +305,8 @@ public class ProtocolProcessorTest {
|
||||
//Verify
|
||||
assertEquals(0, subscriptions.size());
|
||||
Object recvSubAckMessage = m_channel.readOutbound();
|
||||
assertTrue(recvSubAckMessage instanceof SubAckMessage);
|
||||
List<QOSType> qosSubAcked = ((SubAckMessage)recvSubAckMessage).types();
|
||||
assertEquals(1, qosSubAcked.size());
|
||||
assertEquals(QOSType.FAILURE, qosSubAcked.get(0));
|
||||
assertTrue(recvSubAckMessage instanceof MqttSubAckMessage);
|
||||
verifyFailureQos((MqttSubAckMessage) recvSubAckMessage);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -298,9 +314,10 @@ public class ProtocolProcessorTest {
|
||||
* */
|
||||
@Test
|
||||
public void testUnsubscribeWithBadFormattedTopic() {
|
||||
UnsubscribeMessage msg = new UnsubscribeMessage();
|
||||
msg.setMessageID(1);
|
||||
msg.addTopicFilter(BAD_FORMATTED_TOPIC);
|
||||
MqttUnsubscribeMessage msg = MessageBuilder.unsubscribe()
|
||||
.addTopicFilter(BAD_FORMATTED_TOPIC)
|
||||
.messageId(1)
|
||||
.build();
|
||||
|
||||
//Exercise
|
||||
m_processor.processUnsubscribe(m_channel, msg);
|
||||
@@ -314,7 +331,7 @@ public class ProtocolProcessorTest {
|
||||
public void testPublishOfRetainedMessage_afterNewSubscription() throws Exception {
|
||||
//simulate a connect that register a clientID to an IoSession
|
||||
final Subscription subscription = new Subscription(FAKE_PUBLISHER_ID,
|
||||
FAKE_TOPIC, AbstractMessage.QOSType.MOST_ONE);
|
||||
FAKE_TOPIC, MqttQoS.AT_MOST_ONCE);
|
||||
|
||||
//subscriptions.matches(topic) redefine the method to return true
|
||||
SubscriptionsStore subs = new SubscriptionsStore() {
|
||||
@@ -333,34 +350,38 @@ public class ProtocolProcessorTest {
|
||||
|
||||
//simulate a connect that register a clientID to an IoSession
|
||||
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setClientID(FAKE_PUBLISHER_ID);
|
||||
connectMessage.setProtocolVersion((byte) 3);
|
||||
connectMessage.setCleanSession(true);
|
||||
MqttConnectMessage connectMessage = MessageBuilder.connect()
|
||||
.clientId(FAKE_PUBLISHER_ID)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
|
||||
m_processor.processConnect(m_channel, connectMessage);
|
||||
assertConnAckAccepted(m_channel);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5).put("Hello".getBytes());
|
||||
PublishMessage pubmsg = new PublishMessage();
|
||||
pubmsg.setTopicName(FAKE_TOPIC);
|
||||
pubmsg.setQos(QOSType.MOST_ONE);
|
||||
pubmsg.setPayload(buffer);
|
||||
pubmsg.setRetainFlag(true);
|
||||
MqttPublishMessage pubmsg = MessageBuilder.publish()
|
||||
.topicName(FAKE_TOPIC)
|
||||
.qos(MqttQoS.AT_MOST_ONCE)
|
||||
.payload("Hello".getBytes())
|
||||
.retained(true)
|
||||
.build();
|
||||
|
||||
NettyUtils.clientID(m_channel, FAKE_PUBLISHER_ID);
|
||||
m_processor.processPublish(m_channel, pubmsg);
|
||||
NettyUtils.cleanSession(m_channel, false);
|
||||
|
||||
//Exercise
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setMessageID(10);
|
||||
msg.addSubscription(new SubscribeMessage.Couple(QOSType.MOST_ONE.byteValue(), "#"));
|
||||
MqttSubscribeMessage msg = MessageBuilder.subscribe()
|
||||
.messageId(10)
|
||||
.addSubscription(MqttQoS.AT_MOST_ONCE, "#")
|
||||
.build();
|
||||
m_processor.processSubscribe(m_channel, msg);
|
||||
|
||||
|
||||
//Verify
|
||||
//wait the latch
|
||||
Object pubMessage = m_channel.readOutbound();
|
||||
assertNotNull(pubMessage);
|
||||
assertTrue(pubMessage instanceof PublishMessage);
|
||||
assertEquals(FAKE_TOPIC, ((PublishMessage) pubMessage).getTopicName());
|
||||
assertTrue(pubMessage instanceof MqttPublishMessage);
|
||||
assertEquals(FAKE_TOPIC, ((MqttPublishMessage) pubMessage).variableHeader().topicName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -369,17 +390,19 @@ public class ProtocolProcessorTest {
|
||||
List<Subscription> emptySubs = Collections.emptyList();
|
||||
when(subs.matches(anyString())).thenReturn(emptySubs);
|
||||
|
||||
StoredMessage retainedMessage = new StoredMessage("Hello".getBytes(), QOSType.EXACTLY_ONCE, "/topic");
|
||||
StoredMessage retainedMessage = new StoredMessage("Hello".getBytes(), MqttQoS.EXACTLY_ONCE, "/topic");
|
||||
retainedMessage.setRetained(true);
|
||||
retainedMessage.setMessageID(120);
|
||||
retainedMessage.setClientID(FAKE_PUBLISHER_ID);
|
||||
m_messagesStore.storePublishForFuture(retainedMessage);
|
||||
|
||||
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setClientID(FAKE_PUBLISHER_ID);
|
||||
connectMessage.setProtocolVersion((byte) 3);
|
||||
connectMessage.setCleanSession(false);
|
||||
|
||||
MqttConnectMessage connectMessage = MessageBuilder.connect()
|
||||
.clientId(FAKE_PUBLISHER_ID)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.build();
|
||||
|
||||
m_processor.processConnect(m_channel, connectMessage);
|
||||
|
||||
//Verify no messages are still stored
|
||||
@@ -393,7 +416,7 @@ public class ProtocolProcessorTest {
|
||||
m_sessionStore.createNewSession("Subscriber", false);
|
||||
|
||||
SubscriptionsStore mockedSubscriptions = mock(SubscriptionsStore.class);
|
||||
Subscription inactiveSub = new Subscription("Subscriber", "/topic", QOSType.LEAST_ONE);
|
||||
Subscription inactiveSub = new Subscription("Subscriber", "/topic", MqttQoS.AT_LEAST_ONCE);
|
||||
List<Subscription> inactiveSubscriptions = Collections.singletonList(inactiveSub);
|
||||
when(mockedSubscriptions.matches(eq("/topic"))).thenReturn(inactiveSubscriptions);
|
||||
m_processor = new ProtocolProcessor();
|
||||
@@ -401,12 +424,13 @@ public class ProtocolProcessorTest {
|
||||
NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
//Exercise
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5).put("Hello".getBytes());
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setTopicName("/topic");
|
||||
msg.setQos(QOSType.MOST_ONE);
|
||||
msg.setPayload(buffer);
|
||||
msg.setRetainFlag(true);
|
||||
MqttPublishMessage msg = MessageBuilder.publish()
|
||||
.topicName("/topic")
|
||||
.qos(MqttQoS.AT_MOST_ONCE)
|
||||
.payload("Hello".getBytes())
|
||||
.retained(true)
|
||||
.build();
|
||||
|
||||
NettyUtils.clientID(m_channel, "Publisher");
|
||||
m_processor.processPublish(m_channel, msg);
|
||||
|
||||
@@ -421,25 +445,31 @@ public class ProtocolProcessorTest {
|
||||
@Test
|
||||
public void testCleanRetainedStoreAfterAQoS0AndRetainedTrue() {
|
||||
//force a connect
|
||||
connMsg.setClientID("Publisher");
|
||||
connMsg = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.clientId("Publisher")
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
m_processor.processConnect(m_channel, connMsg);
|
||||
//prepare and existing retained store
|
||||
NettyUtils.clientID(m_channel, "Publisher");
|
||||
ByteBuffer payload = ByteBuffer.allocate(5).put("Hello".getBytes());
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setTopicName(FAKE_TOPIC);
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setPayload(payload);
|
||||
msg.setRetainFlag(true);
|
||||
msg.setMessageID(100);
|
||||
MqttPublishMessage msg = MessageBuilder.publish()
|
||||
.topicName(FAKE_TOPIC)
|
||||
.qos(MqttQoS.AT_LEAST_ONCE)
|
||||
.payload("Hello".getBytes())
|
||||
.retained(true)
|
||||
.messageId(100)
|
||||
.build();
|
||||
m_processor.processPublish(m_channel, msg);
|
||||
|
||||
//Exercise
|
||||
PublishMessage cleanPubMsg = new PublishMessage();
|
||||
cleanPubMsg.setTopicName(FAKE_TOPIC);
|
||||
cleanPubMsg.setPayload(payload);
|
||||
cleanPubMsg.setQos(QOSType.MOST_ONE);
|
||||
cleanPubMsg.setRetainFlag(true);
|
||||
MqttPublishMessage cleanPubMsg = MessageBuilder.publish()
|
||||
.topicName(FAKE_TOPIC)
|
||||
.qos(MqttQoS.AT_MOST_ONCE)
|
||||
.payload("Hello".getBytes())
|
||||
.retained(true)
|
||||
.build();
|
||||
|
||||
m_processor.processPublish(m_channel, cleanPubMsg);
|
||||
|
||||
//Verify
|
||||
@@ -453,10 +483,10 @@ public class ProtocolProcessorTest {
|
||||
|
||||
@Test
|
||||
public void testLowerTheQosToTheRequestedBySubscription() {
|
||||
Subscription subQos1 = new Subscription("Sub A", "a/b", QOSType.LEAST_ONE);
|
||||
assertEquals(QOSType.LEAST_ONE, lowerQosToTheSubscriptionDesired(subQos1, QOSType.EXACTLY_ONCE));
|
||||
Subscription subQos1 = new Subscription("Sub A", "a/b", MqttQoS.AT_LEAST_ONCE);
|
||||
assertEquals(MqttQoS.AT_LEAST_ONCE, lowerQosToTheSubscriptionDesired(subQos1, MqttQoS.EXACTLY_ONCE));
|
||||
|
||||
Subscription subQos2 = new Subscription("Sub B", "a/+", QOSType.EXACTLY_ONCE);
|
||||
assertEquals(QOSType.EXACTLY_ONCE, lowerQosToTheSubscriptionDesired(subQos2, QOSType.EXACTLY_ONCE));
|
||||
Subscription subQos2 = new Subscription("Sub B", "a/+", MqttQoS.EXACTLY_ONCE);
|
||||
assertEquals(MqttQoS.EXACTLY_ONCE, lowerQosToTheSubscriptionDesired(subQos2, MqttQoS.EXACTLY_ONCE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,19 @@
|
||||
*/
|
||||
package io.moquette.spi.impl;
|
||||
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.moquette.parser.proto.messages.SubscribeMessage;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.moquette.server.netty.NettyUtils;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.impl.security.PermitAllAuthorizator;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttVersion;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -34,10 +36,9 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.moquette.parser.netty.Utils.VERSION_3_1_1;
|
||||
import static io.moquette.parser.proto.messages.ConnAckMessage.*;
|
||||
import static io.moquette.spi.impl.NettyChannelAssertions.assertEqualsConnAck;
|
||||
import static io.moquette.spi.impl.NettyChannelAssertions.assertEqualsSubAck;
|
||||
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -48,7 +49,7 @@ import static org.junit.Assert.assertTrue;
|
||||
public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
EmbeddedChannel m_session;
|
||||
ConnectMessage connMsg;
|
||||
MessageBuilder.ConnectBuilder connMsg;
|
||||
ProtocolProcessor m_processor;
|
||||
|
||||
IMessagesStore m_messagesStore;
|
||||
@@ -58,8 +59,9 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
@Before
|
||||
public void setUp() throws InterruptedException {
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion((byte) 0x03);
|
||||
connMsg = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.cleanSession(true);
|
||||
|
||||
m_session = new EmbeddedChannel();
|
||||
|
||||
@@ -73,7 +75,7 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
Set<String> clientIds = new HashSet<>();
|
||||
clientIds.add(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
Map<String, byte[]> users = new HashMap<>();
|
||||
Map<String, String> users = new HashMap<>();
|
||||
users.put(ProtocolProcessorTest.TEST_USER, ProtocolProcessorTest.TEST_PWD);
|
||||
m_mockAuthenticator = new MockAuthenticator(clientIds, users);
|
||||
|
||||
@@ -84,24 +86,24 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleConnect_BadProtocol() {
|
||||
connMsg.setProtocolVersion((byte) 0x02);
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(UNNACEPTABLE_PROTOCOL_VERSION, m_session.readOutbound());
|
||||
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
}
|
||||
// @Test
|
||||
// public void testHandleConnect_BadProtocol() {
|
||||
// connMsg.setProtocolVersion((byte) 0x02);
|
||||
//
|
||||
// //Exercise
|
||||
// m_processor.processConnect(m_session, connMsg);
|
||||
//
|
||||
// //Verify
|
||||
// assertEqualsConnAck(UNNACEPTABLE_PROTOCOL_VERSION, m_session.readOutbound());
|
||||
// assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testConnect_badClientID() {
|
||||
connMsg.setClientID("extremely_long_clientID_greater_than_23");
|
||||
connMsg.clientId("extremely_long_clientID_greater_than_23").build();
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, connMsg.clientId("extremely_long_clientID_greater_than_23").build());
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
@@ -109,15 +111,16 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
@Test
|
||||
public void testWill() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setWillFlag(true);
|
||||
connMsg.setWillTopic("topic");
|
||||
connMsg.setWillMessage("Topic message".getBytes());
|
||||
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.willFlag(true)
|
||||
.willTopic("topic")
|
||||
.willMessage("Topic message")
|
||||
.build();
|
||||
|
||||
//Exercise
|
||||
//m_handler.setMessaging(mockedMessaging);
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
@@ -129,14 +132,16 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
@Test
|
||||
public void validAuthentication() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setUserFlag(true);
|
||||
connMsg.setPasswordFlag(true);
|
||||
connMsg.setUsername(ProtocolProcessorTest.TEST_USER);
|
||||
connMsg.setPassword(ProtocolProcessorTest.TEST_PWD);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.hasUser()
|
||||
.hasPassword()
|
||||
.username(ProtocolProcessorTest.TEST_USER)
|
||||
.password(ProtocolProcessorTest.TEST_PWD)
|
||||
.build();
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
@@ -145,73 +150,82 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
@Test
|
||||
public void noPasswdAuthentication() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setUserFlag(true);
|
||||
connMsg.setPasswordFlag(false);
|
||||
connMsg.setUsername(ProtocolProcessorTest.TEST_USER);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.hasUser()
|
||||
.username(ProtocolProcessorTest.TEST_USER)
|
||||
.build();
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(BAD_USERNAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidAuthentication() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setUserFlag(true);
|
||||
connMsg.setPasswordFlag(true);
|
||||
connMsg.setUsername(ProtocolProcessorTest.TEST_USER + "_fake");
|
||||
connMsg.setPassword(ProtocolProcessorTest.TEST_PWD);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.hasUser()
|
||||
.hasPassword()
|
||||
.username(ProtocolProcessorTest.TEST_USER + "_fake")
|
||||
.password(ProtocolProcessorTest.TEST_PWD)
|
||||
.build();
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(BAD_USERNAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prohibitAnonymousClient() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.build();
|
||||
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, false,
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(BAD_USERNAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prohibitAnonymousClient_providingUsername() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setUserFlag(true);
|
||||
connMsg.setUsername(ProtocolProcessorTest.TEST_USER + "_fake");
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.hasUser()
|
||||
.username(ProtocolProcessorTest.TEST_USER + "_fake")
|
||||
.build();
|
||||
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, false,
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(BAD_USERNAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, m_session.readOutbound());
|
||||
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptAnonymousClient() {
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.build();
|
||||
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
//Exercise
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
@@ -224,18 +238,22 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
//first connect with clean session true
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setCleanSession(true);
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
m_processor.processDisconnect(m_session);
|
||||
assertFalse(m_session.isOpen());
|
||||
|
||||
//second connect with clean session false
|
||||
m_session = new EmbeddedChannel();
|
||||
ConnectMessage secondConnMsg = new ConnectMessage();
|
||||
secondConnMsg.setProtocolVersion((byte) 0x03);
|
||||
secondConnMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
MqttConnectMessage secondConnMsg = MessageBuilder.connect()
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.build();
|
||||
|
||||
m_processor.processConnect(m_session, secondConnMsg);
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
|
||||
@@ -250,22 +268,24 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
@Test
|
||||
public void connectWithSameClientIDBadCredentialsDoesntDropExistingClient() {
|
||||
//Connect a client1
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setUserFlag(true);
|
||||
connMsg.setPasswordFlag(true);
|
||||
connMsg.setUsername(ProtocolProcessorTest.TEST_USER);
|
||||
connMsg.setPassword(ProtocolProcessorTest.TEST_PWD);
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.hasUser()
|
||||
.hasPassword()
|
||||
.username(ProtocolProcessorTest.TEST_USER)
|
||||
.password(ProtocolProcessorTest.TEST_PWD)
|
||||
.build();
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
|
||||
//create another connect same clientID but with bad credentials
|
||||
ConnectMessage evilClientConnMsg = new ConnectMessage();
|
||||
evilClientConnMsg.setProtocolVersion((byte) 0x03);
|
||||
evilClientConnMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
evilClientConnMsg.setUserFlag(true);
|
||||
evilClientConnMsg.setPasswordFlag(true);
|
||||
evilClientConnMsg.setUsername(ProtocolProcessorTest.EVIL_TEST_USER);
|
||||
evilClientConnMsg.setPassword(ProtocolProcessorTest.EVIL_TEST_PWD);
|
||||
MqttConnectMessage evilClientConnMsg = MessageBuilder.connect().
|
||||
protocolVersion(MqttVersion.MQTT_3_1)
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.hasUser().username(ProtocolProcessorTest.EVIL_TEST_USER)
|
||||
.hasPassword().password(ProtocolProcessorTest.EVIL_TEST_PWD)
|
||||
.build();
|
||||
|
||||
EmbeddedChannel evilSession = new EmbeddedChannel();
|
||||
|
||||
//Exercise
|
||||
@@ -273,7 +293,7 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
//Verify
|
||||
//the evil client gets a not auth notification
|
||||
assertEqualsConnAck(BAD_USERNAME_OR_PASSWORD, evilSession.readOutbound());
|
||||
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, evilSession.readOutbound());
|
||||
//the good client remains connected
|
||||
assertTrue(m_session.isOpen());
|
||||
assertFalse(evilSession.isOpen());
|
||||
@@ -282,21 +302,21 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
@Test
|
||||
public void testConnAckContainsSessionPresentFlag() throws InterruptedException {
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion(VERSION_3_1_1);
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setCleanSession(false);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1_1)
|
||||
.build();
|
||||
NettyUtils.clientID(m_session, ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
NettyUtils.cleanSession(m_session, false);
|
||||
|
||||
//Connect a first time
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
//disconnect
|
||||
m_processor.processDisconnect(m_session);
|
||||
|
||||
//Exercise, reconnect
|
||||
EmbeddedChannel firstReceiverSession = new EmbeddedChannel();
|
||||
m_processor.processConnect(firstReceiverSession, connMsg);
|
||||
m_processor.processConnect(firstReceiverSession, msg);
|
||||
|
||||
//Verify
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, firstReceiverSession.readOutbound());
|
||||
@@ -307,24 +327,25 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
@Test
|
||||
public void testMultipleReconnection() throws InterruptedException {
|
||||
//connect with clean a false and subscribe to a topic
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion(VERSION_3_1_1);
|
||||
connMsg.setClientID(ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
connMsg.setCleanSession(false);
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
MqttConnectMessage msg = MessageBuilder.connect()
|
||||
.clientId(ProtocolProcessorTest.FAKE_CLIENT_ID)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1_1)
|
||||
.build();
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
assertTrue("Connection is accepted and therefore should remain open.", m_session.isOpen());
|
||||
|
||||
//subscribe
|
||||
SubscribeMessage subscribeMsg = new SubscribeMessage();
|
||||
subscribeMsg.setMessageID(10);
|
||||
subscribeMsg.addSubscription(new SubscribeMessage.Couple((byte) AbstractMessage.QOSType.MOST_ONE.ordinal(),
|
||||
ProtocolProcessorTest.FAKE_TOPIC));
|
||||
MqttSubscribeMessage subscribeMsg = MessageBuilder.subscribe()
|
||||
.addSubscription(MqttQoS.AT_MOST_ONCE, ProtocolProcessorTest.FAKE_TOPIC)
|
||||
.messageId(10)
|
||||
.build();
|
||||
|
||||
NettyUtils.clientID(m_session, ProtocolProcessorTest.FAKE_CLIENT_ID);
|
||||
NettyUtils.cleanSession(m_session, false);
|
||||
m_processor.processSubscribe(m_session, subscribeMsg);
|
||||
Subscription expectedSubscription = new Subscription(ProtocolProcessorTest.FAKE_CLIENT_ID, ProtocolProcessorTest.FAKE_TOPIC,
|
||||
AbstractMessage.QOSType.MOST_ONE);
|
||||
MqttQoS.AT_MOST_ONCE);
|
||||
assertTrue(subscriptions.contains(expectedSubscription));
|
||||
assertEqualsSubAck(m_session.readOutbound());
|
||||
|
||||
@@ -334,7 +355,7 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
|
||||
//reconnect clean session a false
|
||||
m_session = new EmbeddedChannel();
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
assertTrue("Connection is accepted and therefore should remain open.", m_session.isOpen());
|
||||
|
||||
@@ -350,14 +371,14 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
// Connect message without clean session set to true but client id is still null
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion(VERSION_3_1_1);
|
||||
connMsg.setClientID(null);
|
||||
connMsg.setCleanSession(false);
|
||||
MqttConnectMessage msg = MessageBuilder.connect()
|
||||
.clientId(null)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1_1)
|
||||
.build();
|
||||
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck("Identifier should be rejected due to having clean session set to false.",
|
||||
IDENTIFIER_REJECTED, m_session.readOutbound());
|
||||
CONNECTION_REFUSED_IDENTIFIER_REJECTED, m_session.readOutbound());
|
||||
|
||||
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
|
||||
}
|
||||
@@ -370,12 +391,13 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
new PermitAllAuthorizator(), ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
|
||||
|
||||
// Connect message with clean session set to true and client id is null.
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion(VERSION_3_1_1);
|
||||
connMsg.setClientID(null);
|
||||
connMsg.setCleanSession(true);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(null)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1_1)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck("Connection should be accepted. A unique clientid should be generated" +
|
||||
" and clean session set to true.", CONNECTION_ACCEPTED, m_session.readOutbound());
|
||||
|
||||
@@ -385,14 +407,15 @@ public class ProtocolProcessor_CONNECT_Test {
|
||||
@Test
|
||||
public void testZeroByteClientIdNotAllowed() {
|
||||
// Connect message with clean session set to true and client id is null.
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProtocolVersion(VERSION_3_1_1);
|
||||
connMsg.setClientID(null);
|
||||
connMsg.setCleanSession(true);
|
||||
MqttConnectMessage msg = connMsg
|
||||
.clientId(null)
|
||||
.protocolVersion(MqttVersion.MQTT_3_1_1)
|
||||
.cleanSession(true)
|
||||
.build();
|
||||
|
||||
m_processor.processConnect(m_session, connMsg);
|
||||
m_processor.processConnect(m_session, msg);
|
||||
assertEqualsConnAck("Zero byte client identifiers are not allowed.",
|
||||
IDENTIFIER_REJECTED, m_session.readOutbound());
|
||||
CONNECTION_REFUSED_IDENTIFIER_REJECTED, m_session.readOutbound());
|
||||
|
||||
assertFalse("Connection should closed.", m_session.isOpen());
|
||||
}
|
||||
|
||||
+35
-35
@@ -15,20 +15,20 @@
|
||||
*/
|
||||
package io.moquette.spi.impl.subscriptions;
|
||||
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import io.moquette.spi.impl.MemoryStorageService;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import io.moquette.spi.impl.MemoryStorageService;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -106,12 +106,12 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchSimple() {
|
||||
Subscription slashSub = new Subscription("FAKE_CLI_ID_1", "/", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashSub = new Subscription("FAKE_CLI_ID_1", "/", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashSub);
|
||||
store.add(slashSub.asClientTopicCouple());
|
||||
assertTrue(store.matches("finance").isEmpty());
|
||||
|
||||
Subscription slashFinanceSub = new Subscription("FAKE_CLI_ID_1", "/finance", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashFinanceSub = new Subscription("FAKE_CLI_ID_1", "/finance", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashFinanceSub);
|
||||
store.add(slashFinanceSub.asClientTopicCouple());
|
||||
assertTrue(store.matches("finance").isEmpty());
|
||||
@@ -122,12 +122,12 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleMulti() {
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(anySub);
|
||||
store.add(anySub.asClientTopicCouple());
|
||||
assertTrue(store.matches("finance").contains(anySub));
|
||||
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_2", "finance/#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_2", "finance/#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(financeAnySub);
|
||||
store.add(financeAnySub.asClientTopicCouple());
|
||||
assertTrue(store.matches("finance").containsAll(Arrays.asList(financeAnySub, anySub)));
|
||||
@@ -135,8 +135,8 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchingDeepMulti_one_layer() {
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_2", "finance/#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "#", MqttQoS.AT_MOST_ONCE);
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_2", "finance/#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(anySub);
|
||||
sessionsStore.addNewSubscription(financeAnySub);
|
||||
store.add(anySub.asClientTopicCouple());
|
||||
@@ -150,7 +150,7 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchingDeepMulti_two_layer() {
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_1", "finance/stock/#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_1", "finance/stock/#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(financeAnySub);
|
||||
store.add(financeAnySub.asClientTopicCouple());
|
||||
|
||||
@@ -160,12 +160,12 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleSingle() {
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "+", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "+", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(anySub);
|
||||
store.add(anySub.asClientTopicCouple());
|
||||
assertTrue(store.matches("finance").contains(anySub));
|
||||
|
||||
Subscription financeOne = new Subscription("FAKE_CLI_ID_1", "finance/+", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription financeOne = new Subscription("FAKE_CLI_ID_1", "finance/+", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(financeOne);
|
||||
store.add(financeOne.asClientTopicCouple());
|
||||
assertTrue(store.matches("finance/stock").contains(financeOne));
|
||||
@@ -173,7 +173,7 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchManySingle() {
|
||||
Subscription manySub = new Subscription("FAKE_CLI_ID_1", "+/+", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription manySub = new Subscription("FAKE_CLI_ID_1", "+/+", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(manySub);
|
||||
store.add(manySub.asClientTopicCouple());
|
||||
|
||||
@@ -184,10 +184,10 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchSlashSingle() {
|
||||
Subscription slashPlusSub = new Subscription("FAKE_CLI_ID_1", "/+", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashPlusSub = new Subscription("FAKE_CLI_ID_1", "/+", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashPlusSub);
|
||||
store.add(slashPlusSub.asClientTopicCouple());
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "+", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "+", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(anySub);
|
||||
store.add(anySub.asClientTopicCouple());
|
||||
|
||||
@@ -200,11 +200,11 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchManyDeepSingle() {
|
||||
Subscription slashPlusSub = new Subscription("FAKE_CLI_ID_1", "/finance/+/ibm", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashPlusSub = new Subscription("FAKE_CLI_ID_1", "/finance/+/ibm", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashPlusSub);
|
||||
store.add(slashPlusSub.asClientTopicCouple());
|
||||
|
||||
Subscription slashPlusDeepSub = new Subscription("FAKE_CLI_ID_2", "/+/stock/+", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashPlusDeepSub = new Subscription("FAKE_CLI_ID_2", "/+/stock/+", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashPlusDeepSub);
|
||||
store.add(slashPlusDeepSub.asClientTopicCouple());
|
||||
|
||||
@@ -214,7 +214,7 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleMulti_allTheTree() {
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", "#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", "#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(sub);
|
||||
store.add(sub.asClientTopicCouple());
|
||||
assertFalse(store.matches("finance").isEmpty());
|
||||
@@ -224,7 +224,7 @@ public class SubscriptionsStoreTest {
|
||||
@Test
|
||||
public void testMatchSimpleMulti_zeroLevel() {
|
||||
//check MULTI in case of zero level match
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", "finance/#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", "finance/#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(sub);
|
||||
store.add(sub.asClientTopicCouple());
|
||||
assertFalse(store.matches("finance").isEmpty());
|
||||
@@ -260,7 +260,7 @@ public class SubscriptionsStoreTest {
|
||||
memStore.initStore();
|
||||
ISessionsStore aSessionsStore = memStore.sessionsStore();
|
||||
store.init(aSessionsStore);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", subscription, AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", subscription, MqttQoS.AT_MOST_ONCE);
|
||||
aSessionsStore.addNewSubscription(sub);
|
||||
store.add(sub.asClientTopicCouple());
|
||||
assertFalse(store.matches(topic).isEmpty());
|
||||
@@ -271,7 +271,7 @@ public class SubscriptionsStoreTest {
|
||||
MemoryStorageService memStore = new MemoryStorageService();
|
||||
memStore.initStore();
|
||||
store.init(memStore.sessionsStore());
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", subscription, AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", subscription, MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(sub);
|
||||
store.add(sub.asClientTopicCouple());
|
||||
assertTrue(store.matches(topic).isEmpty());
|
||||
@@ -281,7 +281,7 @@ public class SubscriptionsStoreTest {
|
||||
@Test
|
||||
public void testRemoveClientSubscriptions_existingClientID() {
|
||||
String cliendID = "FAKE_CLID_1";
|
||||
Subscription sub = new Subscription(cliendID, "finance/#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription sub = new Subscription(cliendID, "finance/#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(sub);
|
||||
store.add(sub.asClientTopicCouple());
|
||||
|
||||
@@ -295,7 +295,7 @@ public class SubscriptionsStoreTest {
|
||||
@Test
|
||||
public void testRemoveClientSubscriptions_notexistingClientID() {
|
||||
String clientID = "FAKE_CLID_1";
|
||||
Subscription s = new Subscription(clientID, "finance/#", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription s = new Subscription(clientID, "finance/#", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(s);
|
||||
store.add(s.asClientTopicCouple());
|
||||
|
||||
@@ -308,10 +308,10 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
@Test
|
||||
public void testOverlappingSubscriptions() {
|
||||
Subscription genericSub = new Subscription("FAKE_CLI_ID_1", "a/+", AbstractMessage.QOSType.EXACTLY_ONCE);
|
||||
Subscription genericSub = new Subscription("FAKE_CLI_ID_1", "a/+", MqttQoS.EXACTLY_ONCE);
|
||||
sessionsStore.addNewSubscription(genericSub);
|
||||
store.add(genericSub.asClientTopicCouple());
|
||||
Subscription specificSub = new Subscription("FAKE_CLI_ID_1", "a/b", AbstractMessage.QOSType.LEAST_ONE);
|
||||
Subscription specificSub = new Subscription("FAKE_CLI_ID_1", "a/b", MqttQoS.AT_LEAST_ONCE);
|
||||
sessionsStore.addNewSubscription(specificSub);
|
||||
store.add(specificSub.asClientTopicCouple());
|
||||
|
||||
@@ -380,12 +380,12 @@ public class SubscriptionsStoreTest {
|
||||
aStore.init(sessionsStore);
|
||||
//subscribe a not active clientID1 to /topic
|
||||
sessionsStore.createNewSession("FAKE_CLI_ID_1", true);
|
||||
Subscription slashSub = new Subscription("FAKE_CLI_ID_1", "/topic", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashSub = new Subscription("FAKE_CLI_ID_1", "/topic", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashSub);
|
||||
aStore.add(slashSub.asClientTopicCouple());
|
||||
|
||||
//subscribe an active clientID2 to /topic
|
||||
Subscription slashSub2 = new Subscription("FAKE_CLI_ID_2", "/topic", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription slashSub2 = new Subscription("FAKE_CLI_ID_2", "/topic", MqttQoS.AT_MOST_ONCE);
|
||||
sessionsStore.addNewSubscription(slashSub2);
|
||||
aStore.add(new ClientTopicCouple("FAKE_CLI_ID_2", "/topic"));
|
||||
|
||||
@@ -403,15 +403,15 @@ public class SubscriptionsStoreTest {
|
||||
@Test
|
||||
public void duplicatedSubscriptionsWithDifferentQos() {
|
||||
ClientSession session2 = sessionsStore.createNewSession("client2", true);
|
||||
Subscription client2Sub = new Subscription("client2", "client/test/b", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription client2Sub = new Subscription("client2", "client/test/b", MqttQoS.AT_MOST_ONCE);
|
||||
session2.subscribe(client2Sub);
|
||||
store.add(client2Sub.asClientTopicCouple());
|
||||
ClientSession session1 = sessionsStore.createNewSession("client1", true);
|
||||
Subscription client1SubQoS0 = new Subscription("client1", "client/test/b", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription client1SubQoS0 = new Subscription("client1", "client/test/b", MqttQoS.AT_MOST_ONCE);
|
||||
session1.subscribe(client1SubQoS0);
|
||||
store.add(client1SubQoS0.asClientTopicCouple());
|
||||
|
||||
Subscription client1SubQoS2 = new Subscription("client1", "client/test/b", AbstractMessage.QOSType.EXACTLY_ONCE);
|
||||
Subscription client1SubQoS2 = new Subscription("client1", "client/test/b", MqttQoS.EXACTLY_ONCE);
|
||||
session1.subscribe(client1SubQoS2);
|
||||
store.add(client1SubQoS2.asClientTopicCouple());
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -73,11 +73,11 @@ public class MapDBPersistentStoreTest {
|
||||
ClientSession session1 = m_sessionsStore.createNewSession("SESSION_ID_1", true);
|
||||
|
||||
// Subscribe on /topic with QOSType.MOST_ONE
|
||||
Subscription oldSubscription = new Subscription(session1.clientID, "/topic", AbstractMessage.QOSType.MOST_ONE);
|
||||
Subscription oldSubscription = new Subscription(session1.clientID, "/topic", MqttQoS.AT_MOST_ONCE);
|
||||
session1.subscribe(oldSubscription);
|
||||
|
||||
// Subscribe on /topic again that overrides the previous subscription.
|
||||
Subscription overridingSubscription = new Subscription(session1.clientID, "/topic", AbstractMessage.QOSType.EXACTLY_ONCE);
|
||||
Subscription overridingSubscription = new Subscription(session1.clientID, "/topic", MqttQoS.EXACTLY_ONCE);
|
||||
session1.subscribe(overridingSubscription);
|
||||
|
||||
//Verify
|
||||
@@ -134,7 +134,7 @@ public class MapDBPersistentStoreTest {
|
||||
public void testDropMessagesInSessionCleanAllNotRetainedStoredMessages() {
|
||||
m_sessionsStore.createNewSession("TestClient", true);
|
||||
IMessagesStore.StoredMessage publishToStore = new IMessagesStore.StoredMessage("Hello".getBytes(),
|
||||
AbstractMessage.QOSType.EXACTLY_ONCE, "/topic");
|
||||
MqttQoS.EXACTLY_ONCE, "/topic");
|
||||
publishToStore.setClientID(TEST_CLIENT);
|
||||
publishToStore.setMessageID(1);
|
||||
publishToStore.setRetained(false);
|
||||
@@ -152,7 +152,7 @@ public class MapDBPersistentStoreTest {
|
||||
public void testDropMessagesInSessionDoesntCleanAnyRetainedStoredMessages() {
|
||||
m_sessionsStore.createNewSession("TestClient", true);
|
||||
IMessagesStore.StoredMessage publishToStore = new IMessagesStore.StoredMessage("Hello".getBytes(),
|
||||
AbstractMessage.QOSType.EXACTLY_ONCE, "/topic");
|
||||
MqttQoS.EXACTLY_ONCE, "/topic");
|
||||
publishToStore.setClientID(TEST_CLIENT);
|
||||
publishToStore.setMessageID(1);
|
||||
publishToStore.setRetained(true);
|
||||
|
||||
@@ -16,28 +16,19 @@
|
||||
package io.moquette.testclient;
|
||||
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.parser.netty.MQTTDecoder;
|
||||
import io.moquette.parser.netty.MQTTEncoder;
|
||||
import io.moquette.parser.proto.messages.ConnAckMessage;
|
||||
import io.moquette.parser.proto.messages.ConnectMessage;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Class used just to send and receive MQTT messages without any protocol login
|
||||
* in action, just use the encoder/decoder part.
|
||||
@@ -47,7 +38,7 @@ import static org.junit.Assert.assertTrue;
|
||||
public class Client {
|
||||
|
||||
public interface ICallback {
|
||||
void call(AbstractMessage msg);
|
||||
void call(MqttMessage msg);
|
||||
}
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Client.class);
|
||||
@@ -57,7 +48,7 @@ public class Client {
|
||||
private boolean m_connectionLost = false;
|
||||
private ICallback callback;
|
||||
private String clientId;
|
||||
private AbstractMessage receivedMsg;
|
||||
private MqttMessage receivedMsg;
|
||||
|
||||
public Client(String host) {
|
||||
this(host, BrokerConstants.PORT);
|
||||
@@ -75,8 +66,8 @@ public class Client {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast("decoder", new MQTTDecoder());
|
||||
pipeline.addLast("encoder", new MQTTEncoder());
|
||||
pipeline.addLast("decoder", new MqttDecoder());
|
||||
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
|
||||
pipeline.addLast("handler", handler);
|
||||
}
|
||||
});
|
||||
@@ -95,34 +86,53 @@ public class Client {
|
||||
}
|
||||
|
||||
public void connect(String willTestamentTopic, String willTestamentMsg) {
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||
MqttConnectVariableHeader mqttConnectVariableHeader =
|
||||
new MqttConnectVariableHeader(
|
||||
MqttVersion.MQTT_3_1.protocolName(),
|
||||
MqttVersion.MQTT_3_1.protocolLevel(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MqttQoS.AT_MOST_ONCE.value(),
|
||||
true,
|
||||
true,
|
||||
2);
|
||||
MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(this.clientId, willTestamentTopic, willTestamentMsg,
|
||||
null, null);
|
||||
MqttConnectMessage connectMessage = new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader,
|
||||
mqttConnectPayload);
|
||||
|
||||
|
||||
/*ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setProtocolVersion((byte) 3);
|
||||
connectMessage.setClientID(this.clientId);
|
||||
connectMessage.setKeepAlive(2); //secs
|
||||
connectMessage.setWillFlag(true);
|
||||
connectMessage.setWillMessage(willTestamentMsg.getBytes());
|
||||
connectMessage.setWillTopic(willTestamentTopic);
|
||||
connectMessage.setWillQos(AbstractMessage.QOSType.MOST_ONE.byteValue());
|
||||
connectMessage.setWillQos(MqttQoS.AT_MOST_ONCE.byteValue());*/
|
||||
|
||||
doConnect(connectMessage);
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
ConnectMessage connectMessage = new ConnectMessage();
|
||||
connectMessage.setProtocolVersion((byte) 4);
|
||||
connectMessage.setClientID("");
|
||||
connectMessage.setKeepAlive(2); //secs
|
||||
connectMessage.setWillFlag(false);
|
||||
connectMessage.setWillQos(AbstractMessage.QOSType.MOST_ONE.byteValue());
|
||||
MqttConnectMessage connectMessage = MessageBuilder.connect()
|
||||
.protocolVersion(MqttVersion.MQTT_3_1_1)
|
||||
.clientId("")
|
||||
.keepAlive(2) //secs
|
||||
.willFlag(false)
|
||||
.willQoS(MqttQoS.AT_MOST_ONCE)
|
||||
.build();
|
||||
|
||||
doConnect(connectMessage);
|
||||
}
|
||||
|
||||
private void doConnect(ConnectMessage connectMessage) {
|
||||
private void doConnect(MqttConnectMessage connectMessage) {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
this.setCallback(new Client.ICallback() {
|
||||
|
||||
public void call(AbstractMessage msg) {
|
||||
public void call(MqttMessage msg) {
|
||||
receivedMsg = msg;
|
||||
latch.countDown();
|
||||
}
|
||||
@@ -135,8 +145,9 @@ public class Client {
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Cannot receive message in 200 ms", e);
|
||||
}
|
||||
if (!(this.receivedMsg instanceof ConnAckMessage)) {
|
||||
throw new RuntimeException("Expected a CONN_ACK message but received " + this.receivedMsg.getMessageType());
|
||||
if (!(this.receivedMsg instanceof MqttConnAckMessage)) {
|
||||
MqttMessageType messageType = this.receivedMsg.fixedHeader().messageType();
|
||||
throw new RuntimeException("Expected a CONN_ACK message but received " + messageType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,15 +155,15 @@ public class Client {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void sendMessage(AbstractMessage msg) {
|
||||
public void sendMessage(MqttMessage msg) {
|
||||
m_channel.writeAndFlush(msg);
|
||||
}
|
||||
|
||||
public AbstractMessage lastReceivedMessage() {
|
||||
public MqttMessage lastReceivedMessage() {
|
||||
return this.receivedMsg;
|
||||
}
|
||||
|
||||
void messageReceived(AbstractMessage msg) {
|
||||
void messageReceived(MqttMessage msg) {
|
||||
LOG.info("Received message {}", msg);
|
||||
if (this.callback != null) {
|
||||
this.callback.call(msg);
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
package io.moquette.testclient;
|
||||
|
||||
import io.moquette.parser.proto.Utils;
|
||||
import io.moquette.server.netty.NettyMQTTHandler;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.server.netty.NettyMQTTHandler;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -36,8 +35,8 @@ class ClientNettyMQTTHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object message) {
|
||||
AbstractMessage msg = (AbstractMessage) message;
|
||||
LOG.info("Received a message of type {}", Utils.msgType2String(msg.getMessageType()));
|
||||
MqttMessage msg = (MqttMessage) message;
|
||||
LOG.info("Received a message of type {}", msg.fixedHeader().messageType());
|
||||
m_client.messageReceived(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.moquette.parser.netty.Utils;
|
||||
import io.moquette.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -15,19 +15,22 @@
|
||||
*/
|
||||
package io.moquette.testembedded;
|
||||
|
||||
import io.moquette.interception.AbstractInterceptHandler;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.parser.proto.messages.AbstractMessage;
|
||||
import io.moquette.parser.proto.messages.PublishMessage;
|
||||
import io.moquette.server.Server;
|
||||
import io.moquette.server.config.*;
|
||||
import io.moquette.interception.AbstractInterceptHandler;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.server.Server;
|
||||
import io.moquette.server.config.ClasspathResourceLoader;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.server.config.IResourceLoader;
|
||||
import io.moquette.server.config.ResourceLoaderConfig;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class EmbeddedLauncher {
|
||||
static class PublisherListener extends AbstractInterceptHandler {
|
||||
@@ -64,14 +67,15 @@ import static java.util.Arrays.asList;
|
||||
|
||||
Thread.sleep(20000);
|
||||
System.out.println("Before self publish");
|
||||
PublishMessage message = new PublishMessage();
|
||||
message.setTopicName("/exit");
|
||||
message.setRetainFlag(true);
|
||||
// message.setQos(AbstractMessage.QOSType.MOST_ONE);
|
||||
// message.setQos(AbstractMessage.QOSType.LEAST_ONE);
|
||||
message.setQos(AbstractMessage.QOSType.EXACTLY_ONCE);
|
||||
message.setPayload(ByteBuffer.wrap("Hello World!!".getBytes()));
|
||||
mqttBroker.internalPublish(message);
|
||||
MqttPublishMessage message = MessageBuilder.publish()
|
||||
.topicName("/exit")
|
||||
.retained(true)
|
||||
// qos(MqttQoS.AT_MOST_ONCE);
|
||||
// qQos(MqttQoS.AT_LEAST_ONCE);
|
||||
.qos(MqttQoS.EXACTLY_ONCE)
|
||||
.payload("Hello World!!".getBytes())
|
||||
.build();
|
||||
mqttBroker.internalPublish(message, "INTRLPUB");
|
||||
System.out.println("After self publish");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user