Dropper Hazelcast because Moquette is not designed to be clusterizable

Separated the connect/disconnect logic into ConnectHandler/DisconnectHandler

Moved the sending of CONN ACK after session creation/update

Moved the will message management inside the session class
This commit is contained in:
andsel
2018-07-20 18:19:10 +02:00
parent baecbbcb31
commit ba8ad02289
24 changed files with 944 additions and 1095 deletions
@@ -1,153 +0,0 @@
/*
* Copyright (c) 2012-2018 The original author or authors
* ------------------------------------------------------
* 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;
import io.moquette.interception.HazelcastInterceptHandler;
import io.moquette.server.config.IConfig;
import io.moquette.server.config.MemoryConfig;
import org.eclipse.paho.client.mqttv3.IMqttClient;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.Properties;
import static io.moquette.BrokerConstants.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
public class ServerIntegrationHazelcastHandlerInterceptorTest {
private static final Logger LOG = LoggerFactory.getLogger(ServerIntegrationHazelcastHandlerInterceptorTest.class);
static MqttClientPersistence s_dataStore;
static MqttClientPersistence s_pubDataStore;
Server server1883;
Server server1884;
IMqttClient m_listener;
IMqttClient m_publisher;
MessageCollector m_messagesCollector;
IConfig m_config_1883;
IConfig m_config_1884;
@BeforeClass
public static void beforeTests() throws NoSuchAlgorithmException, SQLException, ClassNotFoundException {
String tmpDir = System.getProperty("java.io.tmpdir");
s_dataStore = new MqttDefaultFilePersistence(tmpDir);
s_pubDataStore = new MqttDefaultFilePersistence(tmpDir + File.separator + "publisher");
}
protected Server startServer(int port, IConfig m_config) throws IOException {
Server m_server = new Server();
m_config.setProperty(PORT_PROPERTY_NAME, Integer.toString(port));
m_server.startServer(m_config);
return m_server;
}
private void stopServer(Server m_server) {
m_server.stopServer();
}
private Properties addHazelCastConf(Properties properties, int port, String hazelcastConfigurationFile) {
properties.put(PORT_PROPERTY_NAME, port);
properties.put(INTERCEPT_HANDLER_PROPERTY_NAME, HazelcastInterceptHandler.class.getCanonicalName());
properties.put(HAZELCAST_CONFIGURATION, hazelcastConfigurationFile);
return properties;
}
@Before
public void setUp() throws Exception {
final Properties configProps = addHazelCastConf(
IntegrationUtils.prepareTestClusterProperties(1883),
1883,
"config/hazelcast.xml");
m_config_1883 = new MemoryConfig(configProps);
server1883 = startServer(1883, m_config_1883);
final Properties configProps1884 = addHazelCastConf(
IntegrationUtils.prepareTestClusterProperties(1884),
1883,
"config/hazelcast.xml");
m_config_1884 = new MemoryConfig(configProps1884);
server1884 = startServer(1884, m_config_1884);
m_publisher = new MqttClient("tcp://localhost:1883", "Publisher", s_pubDataStore);
m_publisher.connect();
m_listener = new MqttClient("tcp://localhost:1884", "Listener", s_dataStore);
m_messagesCollector = new MessageCollector();
m_listener.setCallback(m_messagesCollector);
m_listener.connect();
}
@After
public void tearDown() throws Exception {
if (m_listener != null && m_listener.isConnected()) {
m_listener.disconnect();
}
if (m_publisher != null && m_publisher.isConnected()) {
m_publisher.disconnect();
}
stopServer(server1883);
stopServer(server1884);
}
@Test
public void checkPublishPassThroughCluster_Qos0() throws Exception {
LOG.info("*** checkPublishPassThroughCluster_Qos0 ***");
m_listener.subscribe("/topic", 1);
m_publisher.publish("/topic", "Hello world MQTT QoS0".getBytes(UTF_8), 0, false);
MqttMessage messageQos0 = m_messagesCollector.waitMessage(1);
assertEquals("Hello world MQTT QoS0", messageQos0.toString());
assertEquals(0, messageQos0.getQos());
}
@Test
public void checkPublishPassThroughCluster_Qos1() throws Exception {
LOG.info("*** checkPublishPassThroughCluster_Qos1 ***");
m_listener.subscribe("/topic", 1);
m_publisher.publish("/topic", "Hello world MQTT QoS1".getBytes(UTF_8), 1, false);
MqttMessage messageQos1 = m_messagesCollector.waitMessage(1);
assertEquals("Hello world MQTT QoS1", messageQos1.toString());
assertEquals(1, messageQos1.getQos());
}
@Test
public void checkPublishPassThroughCluster_Qos2() throws Exception {
LOG.info("*** checkPublishPassThroughCluster_Qos2 ***");
m_listener.subscribe("/topic", 2);
m_publisher.publish("/topic", "Hello world MQTT QoS2".getBytes(UTF_8), 2, false);
MqttMessage messageQos2 = m_messagesCollector.waitMessage(1);
assertEquals("Hello world MQTT QoS2", messageQos2.toString());
assertEquals(2, messageQos2.getQos());
}
}
@@ -0,0 +1,315 @@
package io.moquette.spi.impl;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.server.ConnectionDescriptorStore;
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.security.PermitAllAuthorizator;
import io.moquette.spi.impl.subscriptions.CTrieSubscriptionDirectory;
import io.moquette.spi.impl.subscriptions.ISubscriptionsDirectory;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.mqtt.MqttConnectMessage;
import io.netty.handler.codec.mqtt.MqttMessageBuilders;
import io.netty.handler.codec.mqtt.MqttVersion;
import org.junit.Before;
import org.junit.Test;
import static io.moquette.spi.impl.NettyChannelAssertions.assertEqualsConnAck;
import static io.moquette.spi.impl.ProtocolProcessorTest.*;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class ConnectHandlerTest {
EmbeddedChannel session;
MqttMessageBuilders.ConnectBuilder connMsg;
IMessagesStore m_messagesStore;
ISessionsStore m_sessionStore;
ISubscriptionsDirectory subscriptions;
MockAuthenticator m_mockAuthenticator;
private ConnectHandler sut;
private DisconnectHandler disconnectHandler;
private SessionsRepository sessionsRepository;
@Before
public void setUp() {
connMsg = MqttMessageBuilders.connect().protocolVersion(MqttVersion.MQTT_3_1).cleanSession(true);
session = new EmbeddedChannel();
MemoryStorageService memStorage = new MemoryStorageService(null, null);
m_messagesStore = memStorage.messagesStore();
m_sessionStore = memStorage.sessionsStore();
m_mockAuthenticator = new MockAuthenticator(singleton(FAKE_CLIENT_ID), singletonMap(TEST_USER, TEST_PWD));
subscriptions = new CTrieSubscriptionDirectory();
sessionsRepository = new SessionsRepository(m_sessionStore, null);
subscriptions.init(sessionsRepository);
final ConnectionDescriptorStore connectionsRegistry = new ConnectionDescriptorStore();
sut = new ConnectHandler(connectionsRegistry, NO_OBSERVERS_INTERCEPTOR, sessionsRepository, m_sessionStore, m_mockAuthenticator,
new PermitAllAuthorizator(), subscriptions, true, true, false, null);
disconnectHandler = new DisconnectHandler(connectionsRegistry, sessionsRepository,
subscriptions, NO_OBSERVERS_INTERCEPTOR);
}
@Test
public void testZeroByteClientIdWithCleanSession() {
// Allow zero byte client ids
sut = new ConnectHandler(new ConnectionDescriptorStore(), NO_OBSERVERS_INTERCEPTOR, new SessionsRepository(this.m_sessionStore, null), m_sessionStore, m_mockAuthenticator,
new PermitAllAuthorizator(), subscriptions, true, true, false, null);
// Connect message with clean session set to true and client id is null.
MqttConnectMessage msg = connMsg.clientId(null)
.protocolVersion(MqttVersion.MQTT_3_1_1)
.cleanSession(true)
.build();
sut.processConnect(session, msg);
assertEqualsConnAck("Connection MUST be accepted, unique clientid MUST be generated and clean session to true",
CONNECTION_ACCEPTED, session.readOutbound());
assertTrue("Connection should be valid and open,", session.isOpen());
}
@Test
public void invalidAuthentication() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER + "_fake")
.password(TEST_PWD)
.build();
// Exercise
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, session.readOutbound());
assertFalse("Connection should be closed by the broker.", session.isOpen());
}
@Test
public void testConnect_badClientID() {
connMsg.clientId("extremely_long_clientID_greater_than_23").build();
// Exercise
sut.processConnect(session, connMsg.clientId("extremely_long_clientID_greater_than_23").build());
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
}
@Test
public void testWill() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).willFlag(true)
.willTopic("topic").willMessage("Topic message").build();
// Exercise
// m_handler.setMessaging(mockedMessaging);
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", session.isOpen());
// TODO verify the call
/*
* verify(mockedMessaging).publish(eq("topic"), eq("Topic message".getBytes()),
* any(AbstractMessage.QOSType.class), anyBoolean(), eq("123"), any(IoSession.class));
*/
}
@Test
public void connectWithSameClientIDBadCredentialsDoesntDropExistingClient() {
// Connect a client1
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER)
.password(TEST_PWD)
.build();
sut.processConnect(session, msg);
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
// create another connect same clientID but with bad credentials
MqttConnectMessage evilClientConnMsg = MqttMessageBuilders.connect()
.protocolVersion(MqttVersion.MQTT_3_1)
.clientId(FAKE_CLIENT_ID)
.username(ProtocolProcessorTest.EVIL_TEST_USER)
.password(ProtocolProcessorTest.EVIL_TEST_PWD)
.build();
EmbeddedChannel evilSession = new EmbeddedChannel();
// Exercise
sut.processConnect(evilSession, evilClientConnMsg);
// Verify
// the evil client gets a not auth notification
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, evilSession.readOutbound());
// the good client remains connected
assertTrue(session.isOpen());
assertFalse(evilSession.isOpen());
}
@Test
public void acceptAnonymousClient() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).build();
// Exercise
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", session.isOpen());
}
@Test
public void validAuthentication() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER).password(TEST_PWD).build();
// Exercise
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", session.isOpen());
}
@Test
public void noPasswdAuthentication() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER)
.build();
// Exercise
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, session.readOutbound());
assertFalse("Connection should be closed by the broker.", session.isOpen());
}
@Test
public void prohibitAnonymousClient() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).build();
reinitProcessorProhibitingAnonymousClients();
// Exercise
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, session.readOutbound());
assertFalse("Connection should be closed by the broker.", session.isOpen());
}
protected void reinitProcessorProhibitingAnonymousClients() {
sut = new ConnectHandler(new ConnectionDescriptorStore(), NO_OBSERVERS_INTERCEPTOR, new SessionsRepository(this.m_sessionStore, null), m_sessionStore, m_mockAuthenticator,
new PermitAllAuthorizator(), subscriptions, false, true, false, null);
}
@Test
public void prohibitAnonymousClient_providingUsername() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER + "_fake")
.build();
reinitProcessorProhibitingAnonymousClients();
// Exercise
sut.processConnect(session, msg);
// Verify
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, session.readOutbound());
assertFalse("Connection should be closed by the broker.", session.isOpen());
}
@Test
public void testZeroByteClientIdNotAllowed() {
sut = new ConnectHandler(new ConnectionDescriptorStore(), NO_OBSERVERS_INTERCEPTOR, new SessionsRepository(this.m_sessionStore, null), m_sessionStore, m_mockAuthenticator,
new PermitAllAuthorizator(), subscriptions, false, false, false, null);
// Connect message with clean session set to true and client id is null.
MqttConnectMessage msg = connMsg.clientId(null)
.protocolVersion(MqttVersion.MQTT_3_1_1)
.cleanSession(true)
.build();
sut.processConnect(session, msg);
assertEqualsConnAck("Zero byte client identifiers are not allowed",
CONNECTION_REFUSED_IDENTIFIER_REJECTED, session.readOutbound());
assertFalse("Connection should closed.", session.isOpen());
}
@Test
public void testZeroByteClientIdWithoutCleanSession() {
// Allow zero byte client ids
reinitProtocolProcessorWithZeroLengthClientIdAndAnonymousClients();
// Connect message without clean session set to true but client id is still null
MqttConnectMessage msg = MqttMessageBuilders.connect().clientId(null).protocolVersion(MqttVersion.MQTT_3_1_1)
.build();
sut.processConnect(session, msg);
assertEqualsConnAck(
"Identifier should be rejected due to having clean session set to false.",
CONNECTION_REFUSED_IDENTIFIER_REJECTED,
session.readOutbound());
assertFalse("Connection should be closed by the broker.", session.isOpen());
}
protected void reinitProtocolProcessorWithZeroLengthClientIdAndAnonymousClients() {
sut = new ConnectHandler(new ConnectionDescriptorStore(), NO_OBSERVERS_INTERCEPTOR, new SessionsRepository(this.m_sessionStore, null), m_sessionStore, m_mockAuthenticator,
new PermitAllAuthorizator(), subscriptions, true, true, false, null);
}
@Test
public void testConnAckContainsSessionPresentFlag() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.protocolVersion(MqttVersion.MQTT_3_1_1).build();
NettyUtils.clientID(session, FAKE_CLIENT_ID);
NettyUtils.cleanSession(session, false);
// Connect a first time
sut.processConnect(session, msg);
// disconnect
disconnectHandler.processDisconnect(session, FAKE_CLIENT_ID);
// Exercise, reconnect
EmbeddedChannel firstReceiverSession = new EmbeddedChannel();
sut.processConnect(firstReceiverSession, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, firstReceiverSession.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", firstReceiverSession.isOpen());
}
@Test
public void connectWithCleanSessionUpdateClientSession() {
// first connect with clean session true
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).cleanSession(true).build();
sut.processConnect(session, msg);
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
disconnectHandler.processDisconnect(session, FAKE_CLIENT_ID);
assertFalse(session.isOpen());
// second connect with clean session false
session = new EmbeddedChannel();
MqttConnectMessage secondConnMsg = MqttMessageBuilders.connect().clientId(FAKE_CLIENT_ID)
.protocolVersion(MqttVersion.MQTT_3_1).build();
sut.processConnect(session, secondConnMsg);
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
// Verify client session is clean false
ClientSession session = sessionsRepository.sessionForClient(FAKE_CLIENT_ID);
assertFalse(session.isCleanSession());
// Verify
// assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
}
}
@@ -18,7 +18,6 @@ package io.moquette.spi.impl;
import io.moquette.persistence.MemoryStorageService;
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.security.PermitAllAuthorizator;
@@ -34,7 +33,7 @@ import org.junit.Test;
import static io.moquette.spi.impl.NettyChannelAssertions.assertEqualsConnAck;
import static io.moquette.spi.impl.NettyChannelAssertions.assertEqualsSubAck;
import static io.moquette.spi.impl.ProtocolProcessorTest.*;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertFalse;
@@ -42,322 +41,61 @@ import static org.junit.Assert.assertTrue;
public class ProtocolProcessor_CONNECT_Test {
EmbeddedChannel m_session;
MqttMessageBuilders.ConnectBuilder connMsg;
ProtocolProcessor m_processor;
IMessagesStore m_messagesStore;
ISessionsStore m_sessionStore;
ISubscriptionsDirectory subscriptions;
MockAuthenticator m_mockAuthenticator;
private EmbeddedChannel session;
private ProtocolProcessor processor;
private ISessionsStore sessionStore;
@Before
public void setUp() throws InterruptedException {
connMsg = MqttMessageBuilders.connect().protocolVersion(MqttVersion.MQTT_3_1).cleanSession(true);
m_session = new EmbeddedChannel();
public void setUp() {
session = new EmbeddedChannel();
MemoryStorageService memStorage = new MemoryStorageService(null, null);
m_messagesStore = memStorage.messagesStore();
m_sessionStore = memStorage.sessionsStore();
// m_messagesStore.initStore();
IMessagesStore m_messagesStore = memStorage.messagesStore();
sessionStore = memStorage.sessionsStore();
m_mockAuthenticator = new MockAuthenticator(singleton(FAKE_CLIENT_ID), singletonMap(TEST_USER, TEST_PWD));
MockAuthenticator mockAuthenticator = new MockAuthenticator(singleton(FAKE_CLIENT_ID), singletonMap(TEST_USER, TEST_PWD));
subscriptions = new CTrieSubscriptionDirectory();
SessionsRepository sessionsRepository = new SessionsRepository(m_sessionStore, null);
ISubscriptionsDirectory subscriptions = new CTrieSubscriptionDirectory();
SessionsRepository sessionsRepository = new SessionsRepository(sessionStore, null);
subscriptions.init(sessionsRepository);
m_processor = new ProtocolProcessor();
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
processor = new ProtocolProcessor();
processor.init(subscriptions, m_messagesStore, sessionStore, mockAuthenticator, true,
new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR,
new SessionsRepository(this.m_sessionStore, null), false);
new SessionsRepository(this.sessionStore, null), false);
}
@Test
public void testConnect_badClientID() {
connMsg.clientId("extremely_long_clientID_greater_than_23").build();
// Exercise
m_processor.processConnect(m_session, connMsg.clientId("extremely_long_clientID_greater_than_23").build());
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
}
@Test
public void testWill() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).willFlag(true)
.willTopic("topic").willMessage("Topic message").build();
// Exercise
// m_handler.setMessaging(mockedMessaging);
m_processor.processConnect(m_session, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", m_session.isOpen());
// TODO verify the call
/*
* verify(mockedMessaging).publish(eq("topic"), eq("Topic message".getBytes()),
* any(AbstractMessage.QOSType.class), anyBoolean(), eq("123"), any(IoSession.class));
*/
}
@Test
public void validAuthentication() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER).password(TEST_PWD).build();
// Exercise
m_processor.processConnect(m_session, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", m_session.isOpen());
}
@Test
public void noPasswdAuthentication() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER)
.build();
// Exercise
m_processor.processConnect(m_session, msg);
// Verify
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() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER + "_fake")
.password(TEST_PWD)
.build();
// Exercise
m_processor.processConnect(m_session, msg);
// Verify
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() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).build();
reinitProcessorProhibitingAnonymousClients();
// Exercise
m_processor.processConnect(m_session, msg);
// Verify
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, m_session.readOutbound());
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
}
protected void reinitProcessorProhibitingAnonymousClients() {
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, false,
new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR, new SessionsRepository(this.m_sessionStore, null), false);
}
@Test
public void prohibitAnonymousClient_providingUsername() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER + "_fake")
.build();
reinitProcessorProhibitingAnonymousClients();
// Exercise
m_processor.processConnect(m_session, msg);
// Verify
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() {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).build();
// Exercise
m_processor.processConnect(m_session, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", m_session.isOpen());
}
@Test
public void connectWithCleanSessionUpdateClientSession() throws InterruptedException {
// first connect with clean session true
MqttConnectMessage msg = connMsg.clientId(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();
MqttConnectMessage secondConnMsg = MqttMessageBuilders.connect().clientId(FAKE_CLIENT_ID)
.protocolVersion(MqttVersion.MQTT_3_1).build();
m_processor.processConnect(m_session, secondConnMsg);
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
// Verify client session is clean false
ClientSession session = m_processor.sessionsRepository.sessionForClient(FAKE_CLIENT_ID);
assertFalse(session.isCleanSession());
// Verify
// assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
}
@Test
public void connectWithSameClientIDBadCredentialsDoesntDropExistingClient() {
// Connect a client1
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.username(TEST_USER)
.password(TEST_PWD)
.build();
m_processor.processConnect(m_session, msg);
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
// create another connect same clientID but with bad credentials
MqttConnectMessage evilClientConnMsg = MqttMessageBuilders.connect()
.protocolVersion(MqttVersion.MQTT_3_1)
.clientId(FAKE_CLIENT_ID)
.username(ProtocolProcessorTest.EVIL_TEST_USER)
.password(ProtocolProcessorTest.EVIL_TEST_PWD)
.build();
EmbeddedChannel evilSession = new EmbeddedChannel();
// Exercise
m_processor.processConnect(evilSession, evilClientConnMsg);
// Verify
// the evil client gets a not auth notification
assertEqualsConnAck(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD, evilSession.readOutbound());
// the good client remains connected
assertTrue(m_session.isOpen());
assertFalse(evilSession.isOpen());
}
@Test
public void testConnAckContainsSessionPresentFlag() throws InterruptedException {
MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID)
.protocolVersion(MqttVersion.MQTT_3_1_1).build();
NettyUtils.clientID(m_session, FAKE_CLIENT_ID);
NettyUtils.cleanSession(m_session, false);
// Connect a first time
m_processor.processConnect(m_session, msg);
// disconnect
m_processor.processDisconnect(m_session);
// Exercise, reconnect
EmbeddedChannel firstReceiverSession = new EmbeddedChannel();
m_processor.processConnect(firstReceiverSession, msg);
// Verify
assertEqualsConnAck(CONNECTION_ACCEPTED, firstReceiverSession.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", firstReceiverSession.isOpen());
}
@Test
public void testMultipleReconnection() throws InterruptedException {
public void testMultipleReconnection() {
// connect with clean a false and subscribe to a topic
MqttConnectMessage msg = MqttMessageBuilders.connect().clientId(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());
processor.processConnect(session, msg);
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", session.isOpen());
// subscribe
MqttSubscribeMessage subscribeMsg = MqttMessageBuilders.subscribe()
.addSubscription(MqttQoS.AT_MOST_ONCE, ProtocolProcessorTest.NEWS_TOPIC).messageId(10).build();
NettyUtils.clientID(m_session, FAKE_CLIENT_ID);
NettyUtils.cleanSession(m_session, false);
m_processor.processSubscribe(m_session, subscribeMsg);
Subscription expectedSubscription = new Subscription(
FAKE_CLIENT_ID,
new Topic(ProtocolProcessorTest.NEWS_TOPIC),
MqttQoS.AT_MOST_ONCE);
verifySubscriptionExists(m_sessionStore, expectedSubscription);
assertEqualsSubAck(m_session.readOutbound());
NettyUtils.clientID(session, FAKE_CLIENT_ID);
NettyUtils.cleanSession(session, false);
processor.processSubscribe(session, subscribeMsg);
final Topic newTopic = new Topic(ProtocolProcessorTest.NEWS_TOPIC);
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, newTopic, MqttQoS.AT_MOST_ONCE);
verifySubscriptionExists(sessionStore, expectedSubscription);
assertEqualsSubAck(session.readOutbound());
// disconnect
m_processor.processDisconnect(m_session);
assertFalse(m_session.isOpen());
processor.processDisconnect(session);
assertFalse(session.isOpen());
// reconnect clean session a false
m_session = new EmbeddedChannel();
m_processor.processConnect(m_session, msg);
assertEqualsConnAck(CONNECTION_ACCEPTED, m_session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", m_session.isOpen());
session = new EmbeddedChannel();
processor.processConnect(session, msg);
assertEqualsConnAck(CONNECTION_ACCEPTED, session.readOutbound());
assertTrue("Connection is accepted and therefore should remain open.", session.isOpen());
// verify that the first subscription is still preserved
verifySubscriptionExists(m_sessionStore, expectedSubscription);
}
@Test
public void testZeroByteClientIdWithoutCleanSession() {
// Allow zero byte client ids
m_processor = new ProtocolProcessor();
reinitProtocolProcessorWithZeroLengthClientIdAndAnonymousClients();
// Connect message without clean session set to true but client id is still null
MqttConnectMessage msg = MqttMessageBuilders.connect().clientId(null).protocolVersion(MqttVersion.MQTT_3_1_1)
.build();
m_processor.processConnect(m_session, msg);
assertEqualsConnAck(
"Identifier should be rejected due to having clean session set to false.",
CONNECTION_REFUSED_IDENTIFIER_REJECTED,
m_session.readOutbound());
assertFalse("Connection should be closed by the broker.", m_session.isOpen());
}
protected void reinitProtocolProcessorWithZeroLengthClientIdAndAnonymousClients() {
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true, true,
new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR, new SessionsRepository(this.m_sessionStore, null), false);
}
@Test
public void testZeroByteClientIdWithCleanSession() {
// Allow zero byte client ids
m_processor = new ProtocolProcessor();
reinitProtocolProcessorWithZeroLengthClientIdAndAnonymousClients();
// Connect message with clean session set to true and client id is null.
MqttConnectMessage msg = connMsg.clientId(null)
.protocolVersion(MqttVersion.MQTT_3_1_1)
.cleanSession(true)
.build();
m_processor.processConnect(m_session, msg);
assertEqualsConnAck("Connection MUST be accepted, unique clientid MUST be generated and clean session to true",
CONNECTION_ACCEPTED, m_session.readOutbound());
assertTrue("Connection should be valid and open,", m_session.isOpen());
}
@Test
public void testZeroByteClientIdNotAllowed() {
// Connect message with clean session set to true and client id is null.
MqttConnectMessage msg = connMsg.clientId(null)
.protocolVersion(MqttVersion.MQTT_3_1_1)
.cleanSession(true)
.build();
m_processor.processConnect(m_session, msg);
assertEqualsConnAck("Zero byte client identifiers are not allowed",
CONNECTION_REFUSED_IDENTIFIER_REJECTED, m_session.readOutbound());
assertFalse("Connection should closed.", m_session.isOpen());
verifySubscriptionExists(sessionStore, expectedSubscription);
}
}
@@ -32,7 +32,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void testCleanSession_maintainClientSubscriptions() throws InterruptedException {
public void testCleanSession_maintainClientSubscriptions() {
connectAsClient(this.m_channel, "TestClient");
subscribe("/topic", AT_MOST_ONCE);
disconnect();
@@ -48,7 +48,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
@Ignore("At the end it's equal to testCleanSession_maintainClientSubscriptions")
@Test
public void testCleanSession_maintainClientSubscriptions_againstClientDestruction() throws InterruptedException {
public void testCleanSession_maintainClientSubscriptions_againstClientDestruction() {
connectAsClient("TestClient");
subscribe("/topic", AT_MOST_ONCE);
disconnect();
@@ -68,7 +68,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
* previous subscription
*/
@Test
public void testCleanSession_correctlyClientSubscriptions() throws InterruptedException {
public void testCleanSession_correctlyClientSubscriptions() {
connectAsClient("TestClient");
subscribe("/topic", AT_MOST_ONCE);
disconnect();
@@ -109,7 +109,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void testUnsubscribe_do_not_notify_anymore_new_session() throws InterruptedException {
public void testUnsubscribe_do_not_notify_anymore_new_session() {
connect();
subscribe("/topic", AT_MOST_ONCE);
publishToAs(FAKE_CLIENT_ID, "/topic", AT_MOST_ONCE, false);
@@ -125,7 +125,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void testPublishWithQoS1() throws InterruptedException {
public void testPublishWithQoS1() {
connect();
subscribe("/topic", AT_LEAST_ONCE);
publishToAs(FAKE_CLIENT_ID, "/topic", AT_LEAST_ONCE, 66, false);
@@ -148,21 +148,21 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
verifyPublishIsReceived();
}
protected void publishFromAnotherClient(String topic, MqttQoS qos) {
private void publishFromAnotherClient(String topic, MqttQoS qos) {
EmbeddedChannel anotherChannel = new EmbeddedChannel();
connectAsClient(anotherChannel, "AnotherClient");
publishToAs(anotherChannel, "AnotherClient", topic, qos, 67, false);
disconnect(anotherChannel);
}
protected void publishFromAnotherClient(String topic, String payload, MqttQoS qos) {
private void publishFromAnotherClient(String topic, String payload, MqttQoS qos) {
EmbeddedChannel anotherChannel = new EmbeddedChannel();
connectAsClient(anotherChannel, "AnotherClient");
publishToAs(anotherChannel, "AnotherClient", topic, payload, qos, 68, false);
disconnect(anotherChannel);
}
protected void publishQoS2FromAnotherClient(String topic) {
private void publishQoS2FromAnotherClient(String topic) {
EmbeddedChannel anotherChannel = new EmbeddedChannel();
connectAsClient(anotherChannel, "AnotherClient");
publishQoS2ToAs(anotherChannel, "AnotherClient", topic, 67, false);
@@ -187,7 +187,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void testPublishWithQoS2() throws InterruptedException {
public void testPublishWithQoS2() {
connect();
subscribe("/topic", EXACTLY_ONCE);
disconnect();
@@ -201,8 +201,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void avoidMultipleNotificationsAfterMultipleReconnection_cleanSessionFalseQoS1()
throws InterruptedException {
public void avoidMultipleNotificationsAfterMultipleReconnection_cleanSessionFalseQoS1() {
connect();
subscribe("/topic", AT_LEAST_ONCE);
disconnect();
@@ -223,7 +222,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void testConnectSubPub_cycle_getTimeout_on_second_disconnect_issue142() throws InterruptedException {
public void testConnectSubPub_cycle_getTimeout_on_second_disconnect_issue142() {
connect();
subscribe("/topic", AT_MOST_ONCE);
publishToAs(FAKE_CLIENT_ID, "/topic", AT_MOST_ONCE, true);
@@ -240,7 +239,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void checkReplayofStoredPublishResumeAfter_a_disconnect_cleanSessionFalseQoS1() throws InterruptedException {
public void checkReplayofStoredPublishResumeAfter_a_disconnect_cleanSessionFalseQoS1() {
EmbeddedChannel publisher = new EmbeddedChannel();
connectAsClient(publisher, "Publisher");
@@ -261,7 +260,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
}
@Test
public void checkReplayStoredPublish_forNoCleanSession_qos1() throws InterruptedException {
public void checkReplayStoredPublish_forNoCleanSession_qos1() {
EmbeddedChannel publisher = new EmbeddedChannel();
connectAsClient(publisher, "Publisher");
@@ -283,7 +282,7 @@ public class ProtocolProcessor_VariousUseCases_Test extends AbstractProtocolProc
* "hello2"
*/
@Test
public void checkQoS2SubscriberDisconnectReceivePersistedPublishes() throws InterruptedException {
public void checkQoS2SubscriberDisconnectReceivePersistedPublishes() {
connect();
subscribe("topic", EXACTLY_ONCE);
disconnect();