private Session createNewSession(MQTTConnection mqttConnection, MqttConnectMessage msg, String clientId) { final boolean clean = msg.variableHeader().isCleanSession(); final Queue<SessionRegistry.EnqueuedMessage> sessionQueue = queues.computeIfAbsent(clientId, (String cli) -> queueRepository.createQueue(cli, clean)); final Session newSession; if (msg.variableHeader().isWillFlag()) { final Session.Will will = createWill(msg); newSession = new Session(clientId, clean, will, sessionQueue); } else { newSession = new Session(clean, clientId, sessionQueue); } newSession.markConnected(); newSession.bind(mqttConnection); return newSession; }
public void disconnect() { final boolean res = assignState(SessionStatus.CONNECTED, SessionStatus.DISCONNECTING); if (!res) { // someone already moved away from CONNECTED // TODO what to do? return; } mqttConnection = null; will = null; assignState(SessionStatus.DISCONNECTING, SessionStatus.DISCONNECTED); }
public void sendPublishOnSessionAtQos(Topic topic, MqttQoS qos, ByteBuf payload) { switch (qos) { case AT_MOST_ONCE: if (connected()) { mqttConnection.sendPublishNotRetainedQos0(topic, qos, payload); } break; case AT_LEAST_ONCE: sendPublishQos1(topic, qos, payload); break; case EXACTLY_ONCE: sendPublishQos2(topic, qos, payload); break; case FAILURE: LOG.error("Not admissible"); } }
public void resendInflightNotAcked() { Collection<InFlightPacket> expired = new ArrayList<>(INFLIGHT_WINDOW_SIZE); inflightTimeouts.drainTo(expired); debugLogPacketIds(expired); for (InFlightPacket notAckPacketId : expired) { if (inflightWindow.containsKey(notAckPacketId.packetId)) { final SessionRegistry.PublishedMessage msg = (SessionRegistry.PublishedMessage) inflightWindow.get(notAckPacketId.packetId); final Topic topic = msg.topic; final MqttQoS qos = msg.publishingQos; final ByteBuf payload = msg.payload; final ByteBuf copiedPayload = payload.retainedDuplicate(); MqttPublishMessage publishMsg = publishNotRetainedDuplicated(notAckPacketId, topic, qos, copiedPayload); mqttConnection.sendPublish(publishMsg); } } }
final boolean newIsClean = msg.variableHeader().isCleanSession(); final Session oldSession = pool.get(clientId); if (newIsClean && oldSession.disconnected()) { boolean result = oldSession.assignState(SessionStatus.DISCONNECTED, SessionStatus.CONNECTING); if (!result) { throw new SessionCorruptedException("old session was already changed state"); oldSession.bind(mqttConnection); result = oldSession.assignState(SessionStatus.CONNECTING, SessionStatus.CONNECTED); if (!result) { throw new SessionCorruptedException("old session moved in connected state by other thread"); } else if (!newIsClean && oldSession.disconnected()) { final boolean connecting = oldSession.assignState(SessionStatus.DISCONNECTED, SessionStatus.CONNECTING); if (!connecting) { throw new SessionCorruptedException("old session moved in connected state by other thread"); oldSession.bind(mqttConnection); final boolean connected = oldSession.assignState(SessionStatus.CONNECTING, SessionStatus.CONNECTED); if (!connected) { throw new SessionCorruptedException("old session moved in other state state by other thread"); } else if (oldSession.connected()) { oldSession.closeImmediately();
void handleConnectionLost() { String clientID = NettyUtils.clientID(channel); if (clientID == null || clientID.isEmpty()) { return; } LOG.info("Notifying connection lost event. CId: {}, channel: {}", clientID, channel); Session session = sessionRegistry.retrieve(clientID); if (session.hasWill()) { postOffice.fireWill(session.getWill()); } if (session.isClean()) { sessionRegistry.remove(clientID); } else { sessionRegistry.disconnect(clientID); } connected = false; //dispatch connection lost to intercept. String userName = NettyUtils.userName(channel); postOffice.dispatchConnectionLost(clientID,userName); LOG.trace("dispatch disconnection: clientId={}, userName={}", clientID, userName); }
private void sendPublishQos1(Topic topic, MqttQoS qos, ByteBuf payload) { if (!connected() && isClean()) { //pushing messages to disconnected not clean session return; } if (canSkipQueue()) { inflightSlots.decrementAndGet(); int packetId = mqttConnection.nextPacketId(); inflightWindow.put(packetId, new SessionRegistry.PublishedMessage(topic, qos, payload)); inflightTimeouts.add(new InFlightPacket(packetId, FLIGHT_BEFORE_RESEND_MS)); MqttPublishMessage publishMsg = MQTTConnection.notRetainedPublishWithMessageId(topic.toString(), qos, payload, packetId); mqttConnection.sendPublish(publishMsg); // TODO drainQueueToConnection();? } else { final SessionRegistry.PublishedMessage msg = new SessionRegistry.PublishedMessage(topic, qos, payload); sessionQueue.add(msg); } }
public void processPubRec(int packetId) { inflightWindow.remove(packetId); inflightSlots.incrementAndGet(); if (canSkipQueue()) { inflightSlots.decrementAndGet(); int pubRelPacketId = packetId/*mqttConnection.nextPacketId()*/; inflightWindow.put(pubRelPacketId, new SessionRegistry.PubRelMarker()); inflightTimeouts.add(new InFlightPacket(pubRelPacketId, FLIGHT_BEFORE_RESEND_MS)); MqttMessage pubRel = MQTTConnection.pubrel(pubRelPacketId); mqttConnection.sendIfWritableElseDrop(pubRel); drainQueueToConnection(); } else { sessionQueue.add(new SessionRegistry.PubRelMarker()); } }
@Test public void testZeroByteClientIdWithCleanSession() { // Connect message with clean session set to true and client id is null. MqttConnectMessage msg = MqttMessageBuilders.connect() .protocolVersion(MqttVersion.MQTT_3_1_1) .clientId(null) .cleanSession(true) .build(); sut.processConnect(msg); assertEqualsConnAck("Connection must be accepted", CONNECTION_ACCEPTED, channel.readOutbound()); assertNotNull("unique clientid must be generated", sut.getClientId()); assertTrue("clean session flag must be true", sessionRegistry.retrieve(sut.getClientId()).isClean()); assertTrue("Connection must be open", channel.isOpen()); }
public void disconnect(String clientID) { final Session session = retrieve(clientID); if (session == null) { LOG.debug("Some other thread already removed the session CId={}", clientID); return; } session.disconnect(); }
private boolean canSkipQueue() { return sessionQueue.isEmpty() && inflightSlots.get() > 0 && connected() && mqttConnection.channel.isWritable(); }
private void drainQueueToConnection() { // consume the queue while (!canSkipQueue()) { final SessionRegistry.EnqueuedMessage msg = sessionQueue.remove(); inflightSlots.decrementAndGet(); int sendPacketId = mqttConnection.nextPacketId(); inflightWindow.put(sendPacketId, msg); if (msg instanceof SessionRegistry.PubRelMarker) { MqttMessage pubRel = MQTTConnection.pubrel(sendPacketId); mqttConnection.sendIfWritableElseDrop(pubRel); } else { final SessionRegistry.PublishedMessage msgPub = (SessionRegistry.PublishedMessage) msg; MqttPublishMessage publishMsg = MQTTConnection.notRetainedPublishWithMessageId(msgPub.topic.toString(), msgPub.publishingQos, msgPub.payload, sendPacketId); mqttConnection.sendPublish(publishMsg); } } }
public void subscribeClientToTopics(MqttSubscribeMessage msg, String clientID, String username, MQTTConnection mqttConnection) { // verify which topics of the subscribe ongoing has read access permission int messageID = messageId(msg); List<MqttTopicSubscription> ackTopics = authorizator.verifyTopicsReadAccess(clientID, username, msg); MqttSubAckMessage ackMessage = doAckMessageFromValidateFilters(ackTopics, messageID); // store topics subscriptions in session List<Subscription> newSubscriptions = ackTopics.stream() .filter(req -> req.qualityOfService() != FAILURE) .map(req -> { final Topic topic = new Topic(req.topicName()); return new Subscription(clientID, topic, req.qualityOfService()); }).collect(Collectors.toList()); for (Subscription subscription : newSubscriptions) { subscriptions.add(subscription); } // add the subscriptions to Session Session session = sessionRegistry.retrieve(clientID); session.addSubscriptions(newSubscriptions); // send ack message mqttConnection.sendSubAckMessage(messageID, ackMessage); publishRetainedMessagesForSubscriptions(clientID, newSubscriptions); for (Subscription subscription : newSubscriptions) { interceptor.notifyTopicSubscribed(subscription, username); } }
private void sendPublishQos2(Topic topic, MqttQoS qos, ByteBuf payload) { if (canSkipQueue()) { inflightSlots.decrementAndGet(); int packetId = mqttConnection.nextPacketId(); inflightWindow.put(packetId, new SessionRegistry.PublishedMessage(topic, qos, payload)); inflightTimeouts.add(new InFlightPacket(packetId, FLIGHT_BEFORE_RESEND_MS)); MqttPublishMessage publishMsg = MQTTConnection.notRetainedPublishWithMessageId(topic.toString(), qos, payload, packetId); mqttConnection.sendPublish(publishMsg); drainQueueToConnection(); } else { final SessionRegistry.PublishedMessage msg = new SessionRegistry.PublishedMessage(topic, qos, payload); sessionQueue.add(msg); } }
@Test public void connectWithCleanSessionUpdateClientSession() { // first connect with clean session true MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).cleanSession(true).build(); connection.processConnect(msg); assertEqualsConnAck(CONNECTION_ACCEPTED, channel.readOutbound()); connection.processDisconnect(null); assertFalse(channel.isOpen()); // second connect with clean session false EmbeddedChannel anotherChannel = new EmbeddedChannel(); MQTTConnection anotherConnection = createMQTTConnection(ALLOW_ANONYMOUS_AND_ZEROBYTE_CLIENT_ID, anotherChannel); MqttConnectMessage secondConnMsg = MqttMessageBuilders.connect() .clientId(FAKE_CLIENT_ID) .protocolVersion(MqttVersion.MQTT_3_1) .build(); anotherConnection.processConnect(secondConnMsg); assertEqualsConnAck(CONNECTION_ACCEPTED, anotherChannel.readOutbound()); // Verify client session is clean false Session session = sut.retrieve(FAKE_CLIENT_ID); assertFalse(session.isClean()); } }
void markConnected() { assignState(SessionStatus.DISCONNECTED, SessionStatus.CONNECTED); }