added a testcase and fixed Issue-918

This commit is contained in:
Marco Käferbeck
2022-04-22 12:26:32 +02:00
parent 6f35dcb785
commit 9e8ed84a37
2 changed files with 194 additions and 186 deletions

View File

@@ -38,7 +38,7 @@ public class MqttTopicTest {
String[][] matchingTopics = new String[][] { { "sport/tennis/player1/#", "sport/tennis/player1" }, String[][] matchingTopics = new String[][] { { "sport/tennis/player1/#", "sport/tennis/player1" },
{ "sport/tennis/player1/#", "sport/tennis/player1/ranking" }, { "sport/tennis/player1/#", "sport/tennis/player1/ranking" },
{ "sport/tennis/player1/#", "sport/tennis/player1/score/wimbledon" }, { "sport/#", "sport" }, { "sport/tennis/player1/#", "sport/tennis/player1/score/wimbledon" }, { "sport/#", "sport" },
{ "#", "sport/tennis/player1" } }; { "#", "sport/tennis/player1" } , {"sport/+/player1/ranking/#","sport/tennis/player1/ranking"} };
for (String[] pair : matchingTopics) { for (String[] pair : matchingTopics) {
Assert.assertTrue(pair[0] + " should match " + pair[1], MqttTopicValidator.isMatched(pair[0], pair[1])); Assert.assertTrue(pair[0] + " should match " + pair[1], MqttTopicValidator.isMatched(pair[0], pair[1]));

View File

@@ -5,22 +5,19 @@ import java.io.UnsupportedEncodingException;
public class MqttTopicValidator { public class MqttTopicValidator {
/** /**
* The forward slash (/) is used to separate each level within a topic tree and * The forward slash (/) is used to separate each level within a topic tree and provide a hierarchical structure to
* provide a hierarchical structure to the topic space. The use of the topic * the topic space. The use of the topic level separator is significant when the two wildcard characters are
* level separator is significant when the two wildcard characters are
* encountered in topics specified by subscribers. * encountered in topics specified by subscribers.
*/ */
public static final String TOPIC_LEVEL_SEPARATOR = "/"; public static final String TOPIC_LEVEL_SEPARATOR = "/";
/** /**
* Multi-level wildcard The number sign (#) is a wildcard character that matches * Multi-level wildcard The number sign (#) is a wildcard character that matches any number of levels within a topic.
* any number of levels within a topic.
*/ */
public static final String MULTI_LEVEL_WILDCARD = "#"; public static final String MULTI_LEVEL_WILDCARD = "#";
/** /**
* Single-level wildcard The plus sign (+) is a wildcard character that matches * Single-level wildcard The plus sign (+) is a wildcard character that matches only one topic level.
* only one topic level.
*/ */
public static final String SINGLE_LEVEL_WILDCARD = "+"; public static final String SINGLE_LEVEL_WILDCARD = "+";
@@ -88,10 +85,8 @@ public class MqttTopicValidator {
// - The multi-level wildcard must be the last character used within // - The multi-level wildcard must be the last character used within
// the topic tree // the topic tree
if (Strings.countMatches(topicString, MULTI_LEVEL_WILDCARD) > 1 if (Strings.countMatches(topicString, MULTI_LEVEL_WILDCARD) > 1
|| (topicString.contains(MULTI_LEVEL_WILDCARD) || (topicString.contains(MULTI_LEVEL_WILDCARD) && !topicString.endsWith(MULTI_LEVEL_WILDCARD_PATTERN))) {
&& !topicString.endsWith(MULTI_LEVEL_WILDCARD_PATTERN))) { throw new IllegalArgumentException("Invalid usage of multi-level wildcard in topic string: " + topicString);
throw new IllegalArgumentException(
"Invalid usage of multi-level wildcard in topic string: " + topicString);
} }
// 2) Check single-level wildcard // 2) Check single-level wildcard
@@ -134,9 +129,8 @@ public class MqttTopicValidator {
if (chars[i] == singleLevelWildcardChar) { if (chars[i] == singleLevelWildcardChar) {
// prev and next can be only '/' or none // prev and next can be only '/' or none
if (prev != topicLevelSeparatorChar && prev != NUL || next != topicLevelSeparatorChar && next != NUL) { if (prev != topicLevelSeparatorChar && prev != NUL || next != topicLevelSeparatorChar && next != NUL) {
throw new IllegalArgumentException( throw new IllegalArgumentException(String
String.format("Invalid usage of single-level wildcard in topic string '%s'!", .format("Invalid usage of single-level wildcard in topic string '%s'!", new Object[] { topicString }));
new Object[] { topicString }));
} }
} }
@@ -170,8 +164,7 @@ public class MqttTopicValidator {
while (filterPos < filterLen && topicPos < topicLen) { while (filterPos < filterLen && topicPos < topicLen) {
if (topicFilter.charAt(filterPos) == '#') { if (topicFilter.charAt(filterPos) == '#') {
/* /*
* next 'if' will break when topicFilter = topic/# and topicName topic/A/, * next 'if' will break when topicFilter = topic/# and topicName topic/A/, but they are matched
* but they are matched
*/ */
topicPos = topicLen; topicPos = topicLen;
filterPos = filterLen; filterPos = filterLen;
@@ -196,20 +189,35 @@ public class MqttTopicValidator {
return true; return true;
} else { } else {
/* /*
* https://github.com/eclipse/paho.mqtt.java/issues/418 Covers edge case to * https://github.com/eclipse/paho.mqtt.java/issues/418 Covers edge case to match sport/# to sport
* match sport/# to sport
*/ */
if ((topicFilter.length() - filterPos > 0) && (topicPos == topicLen)) { if ((topicFilter.length() - filterPos > 0) && (topicPos == topicLen)) {
if (topicName.charAt(topicPos - 1) == '/' && topicFilter.charAt(filterPos) == '#') if (topicName.charAt(topicPos - 1) == '/' && topicFilter.charAt(filterPos) == '#')
return true; return true;
if (topicFilter.length() - filterPos > 1 if (topicFilter.length() - filterPos > 1 && topicFilter.substring(filterPos, filterPos + 2).equals("/#")) {
&& topicFilter.substring(filterPos, filterPos + 2).equals("/#")) {
if ((topicFilter.length() - topicName.length()) == 2 if ((topicFilter.length() - topicName.length()) == 2
&& topicFilter.substring(topicFilter.length() - 2, topicFilter.length()).equals("/#")) { && topicFilter.substring(topicFilter.length() - 2, topicFilter.length()).equals("/#")) {
return true; return true;
} }
} }
} }
/*
* https://github.com/eclipse/paho.mqtt.java/issues/918
* covers cases that include more then one wildcard
* sport/+/tennis/#
*/
String[] topicFilterParts = topicFilter.split(TOPIC_LEVEL_SEPARATOR);
String[] topicParts = topicName.split(TOPIC_LEVEL_SEPARATOR);
if(topicFilterParts.length -1 == topicParts.length &&
topicFilterParts[topicFilterParts.length-1].equals( MULTI_LEVEL_WILDCARD)) {
for (int i = 0; i<topicParts.length;i++) {
if(!topicParts[i].equals(topicFilterParts[i]) && !topicFilterParts[i].equals(SINGLE_LEVEL_WILDCARD)) {
return false;
}
}
return true;
}
} }
return false; return false;
} }