Fixed issue #43, not clean handlng of bad formatted topic filters during subscrion

This commit is contained in:
andrea
2015-06-20 18:27:43 +02:00
parent c4ebd75008
commit 867d78b650
7 changed files with 82 additions and 16 deletions
+1 -1
View File
@@ -118,7 +118,7 @@
<artifactId>mapdb</artifactId>
<!--version>1.1.0-SNAPSHOT</version-->
<!--<version>2.0.0-SNAPSHOT</version>-->
<!--<version>2.0-alpha2</version>-->
<!--<version>2.0-alpha3</version>-->
<version>1.0.7</version>
</dependency>
@@ -602,38 +602,38 @@ class ProtocolProcessor implements EventHandler<ValueEvent> {
boolean cleanSession = (Boolean) session.getAttribute(NettyChannel.ATTR_KEY_CLEANSESSION);
LOG.debug("SUBSCRIBE client <{}> packetID {}", clientID, msg.getMessageID());
for (SubscribeMessage.Couple req : msg.subscriptions()) {
AbstractMessage.QOSType qos = AbstractMessage.QOSType.values()[req.getQos()];
Subscription newSubscription = new Subscription(clientID, req.getTopicFilter(), qos, cleanSession);
subscribeSingleTopic(newSubscription, req.getTopicFilter());
}
//ack the client
SubAckMessage ackMessage = new SubAckMessage();
ackMessage.setMessageID(msg.getMessageID());
//reply with requested qos
for(SubscribeMessage.Couple req : msg.subscriptions()) {
for (SubscribeMessage.Couple req : msg.subscriptions()) {
AbstractMessage.QOSType qos = AbstractMessage.QOSType.values()[req.getQos()];
ackMessage.addType(qos);
Subscription newSubscription = new Subscription(clientID, req.getTopicFilter(), qos, cleanSession);
boolean valid = subscribeSingleTopic(newSubscription, req.getTopicFilter());
ackMessage.addType(valid ? qos : AbstractMessage.QOSType.FAILURE);
}
LOG.debug("SUBACK for packetID {}", msg.getMessageID());
session.write(ackMessage);
}
private void subscribeSingleTopic(Subscription newSubscription, final String topic) {
private boolean subscribeSingleTopic(Subscription newSubscription, final String topic) {
LOG.info("<{}> subscribed to topic <{}> with QoS {}",
newSubscription.getClientId(), topic,
AbstractMessage.QOSType.formatQoS(newSubscription.getRequestedQos()));
String clientID = newSubscription.getClientId();
boolean validTopic = SubscriptionsStore.validate(newSubscription);
if (!validTopic) {
//send SUBACK with 0x80 for this topic filter
return false;
}
m_sessionsStore.addNewSubscription(newSubscription, clientID);
subscriptions.add(newSubscription);
//scans retained messages to be published to the new subscription
Collection<IMessagesStore.StoredMessage> messages = m_messagesStore.searchMatching(new IMatchingCondition() {
public boolean match(String key) {
return SubscriptionsStore.matchTopics(key, topic);
return SubscriptionsStore.matchTopics(key, topic);
}
});
@@ -645,6 +645,7 @@ class ProtocolProcessor implements EventHandler<ValueEvent> {
m_messagesStore.nextPacketID(newSubscription.getClientId());
sendPublish(newSubscription.getClientId(), storedMsg.getTopic(), storedMsg.getQos(), storedMsg.getPayload(), true, packetID);
}
return true;
}
private void disruptorPublish(OutputMessagingEvent msgEvent) {
@@ -30,6 +30,19 @@ import org.slf4j.LoggerFactory;
*/
public class SubscriptionsStore {
/**
* Check if the topic filter of the subscription is well formed
* */
public static boolean validate(Subscription newSubscription) {
try {
parseTopic(newSubscription.topicFilter);
return true;
} catch (ParseException pex) {
LOG.info("Bad matching topic filter <{}>", newSubscription.topicFilter);
return false;
}
}
public static interface IVisitor<T> {
void visit(TreeNode node);
@@ -54,6 +54,7 @@ public class ProtocolProcessorTest {
final static String FAKE_CLIENT_ID2 = "FAKE_456";
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 String TEST_PWD = "fakepwd";
@@ -456,6 +457,27 @@ public class ProtocolProcessorTest {
assertTrue(subscriptions.contains(expectedSubscription));
}
@Test
public void testSubscribeWithBadFormattedTopic() {
SubscribeMessage msg = new SubscribeMessage();
msg.addSubscription(new SubscribeMessage.Couple((byte)AbstractMessage.QOSType.MOST_ONE.ordinal(), BAD_FORMATTED_TOPIC));
m_session.setAttribute(NettyChannel.ATTR_KEY_CLIENTID, FAKE_CLIENT_ID);
m_session.setAttribute(NettyChannel.ATTR_KEY_CLEANSESSION, false);
subscriptions.clearAllSubscriptions();
assertEquals(0, subscriptions.size());
//Exercise
m_processor.processSubscribe(m_session, msg);
//Verify
assertEquals(0, subscriptions.size());
assertTrue(m_receivedMessage instanceof SubAckMessage);
List<QOSType> qosSubAcked = ((SubAckMessage) m_receivedMessage).types();
assertEquals(1, qosSubAcked.size());
assertEquals(QOSType.FAILURE, qosSubAcked.get(0));
}
@Test
public void testPublishOfRetainedMessage_afterNewSubscription() throws Exception {
@@ -18,6 +18,7 @@ package org.eclipse.moquette.parser.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.eclipse.moquette.proto.messages.AbstractMessage;
import org.eclipse.moquette.proto.messages.AbstractMessage.QOSType;
import org.eclipse.moquette.proto.messages.SubAckMessage;
/**
@@ -38,8 +39,9 @@ class SubAckEncoder extends DemuxEncoder<SubAckMessage> {
buff.writeByte(AbstractMessage.SUBACK << 4 );
buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
buff.writeShort(message.getMessageID());
for (AbstractMessage.QOSType c : message.types()) {
buff.writeByte(c.ordinal());
for (QOSType c : message.types()) {
int qosValue = (c == QOSType.FAILURE) ? qosValue = 0x80 : c.ordinal();
buff.writeByte(qosValue);
}
out.writeBytes(buff);
@@ -38,7 +38,7 @@ public abstract class AbstractMessage {
public static final byte DISCONNECT = 14; //Client is Disconnecting
public static enum QOSType {
MOST_ONE, LEAST_ONE, EXACTLY_ONCE, RESERVED;
MOST_ONE, LEAST_ONE, EXACTLY_ONCE, RESERVED, FAILURE;
public static String formatQoS(QOSType qos) {
return String.format("%d - %s", qos.ordinal(), qos.name());
@@ -0,0 +1,28 @@
@Grab(group='org.fusesource.mqtt-client', module='mqtt-client', version='1.10')
import org.fusesource.mqtt.client.BlockingConnection
import org.fusesource.mqtt.client.MQTT
import org.fusesource.mqtt.client.Message
import org.fusesource.mqtt.client.QoS
import org.fusesource.mqtt.client.Topic
String host = args.size() == 1 ? args[0] : "localhost"
MQTT mqttSub = new MQTT()
mqttSub.setHost(host, 1883)
mqttSub.setClientId("Subscriber(A)")
mqttSub.cleanSession = false
//AT_MOST_ONCE,
//AT_LEAST_ONCE,
//EXACTLY_ONCE
BlockingConnection clientSub = mqttSub.blockingConnection()
clientSub.connect()
println "Sub connected"
Topic[] topics = [new Topic("#MQTTClient", QoS.AT_MOST_ONCE)]
byte[] qoses = clientSub.subscribe(topics)
println "Sub subscribed"
clientSub.disconnect()
clientSub.kill()
println "Sub disconnected"
println "Finished!!\n\n"