def test_client_topic_alias(self): callback.clear() # no server side topic aliases allowed connack = aclient.connect(host=host, port=port, cleanstart=True) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.TopicAlias = 0 # topic alias 0 not allowed aclient.publish(topics[0], "topic alias 0", 1, properties=publish_properties) # should get back a disconnect with Topic alias invalid self.waitfor(callback.disconnects, 1, 2) self.assertEqual(len(callback.disconnects), 1, callback.disconnects) #print("disconnect", str(callback.disconnects[0]["reasonCode"])) #self.assertEqual(callback.disconnects, 1, callback.disconnects) connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.TopicAliasMaximum = 0 # server topic aliases not allowed connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) clientTopicAliasMaximum = 0 if hasattr(connack.properties, "TopicAliasMaximum"): clientTopicAliasMaximum = connack.properties.TopicAliasMaximum if clientTopicAliasMaximum == 0: aclient.disconnect() return aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) self.waitfor(callback.subscribeds, 1, 3) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.TopicAlias = 1 aclient.publish(topics[0], b"topic alias 1", 1, properties=publish_properties) self.waitfor(callback.messages, 1, 3) self.assertEqual(len(callback.messages), 1, callback.messages) aclient.publish("", b"topic alias 2", 1, properties=publish_properties) self.waitfor(callback.messages, 2, 3) self.assertEqual(len(callback.messages), 2, callback.messages) aclient.disconnect() # should get rid of the topic aliases but not subscriptions # check aliases have been deleted callback.clear() aclient.connect(host=host, port=port, cleanstart=False) aclient.publish(topics[0], b"topic alias 3", 1) self.waitfor(callback.messages, 1, 3) self.assertEqual(len(callback.messages), 1, callback.messages) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.TopicAlias = 1 aclient.publish("", b"topic alias 4", 1, properties=publish_properties) # should get back a disconnect with Topic alias invalid self.waitfor(callback.disconnects, 1, 2) self.assertEqual(len(callback.disconnects), 1, callback.disconnects)
def handleRequest(self, sock): "this is going to be called from multiple threads, so synchronize" self.lock.acquire() sendWillMessage = False try: try: raw_packet = MQTTV5.getPacket(sock) except: raise MQTTV5.MQTTException( "[MQTT-4.8.0-1] 'transient error' reading packet, closing connection" ) if raw_packet == None: # will message if sock in self.clients.keys(): self.disconnect(sock, None, sendWillMessage=True) terminate = True else: try: packet = MQTTV5.unpackPacket(raw_packet, self.maximumPacketSize) if packet: terminate = self.handlePacket(packet, sock) else: self.disconnect(sock, reasonCode="Malformed packet", sendWillMessage=True) terminate = True except MQTTV5.MalformedPacket as error: traceback.print_exc() disconnect_properties = MQTTV5.Properties( MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.ReasonString = error.args[0] self.disconnect(sock, reasonCode="Malformed packet", sendWillMessage=True) terminate = True except MQTTV5.ProtocolError as error: disconnect_properties = MQTTV5.Properties( MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.ReasonString = error.args[0] self.disconnect(sock, reasonCode=error.args[0], properties=disconnect_properties, sendWillMessage=True) terminate = True finally: self.lock.release() return terminate
def test_server_topic_alias(self): callback.clear() serverTopicAliasMaximum = 1 # server topic alias allowed connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.TopicAliasMaximum = serverTopicAliasMaximum connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) clientTopicAliasMaximum = 0 if hasattr(connack.properties, "TopicAliasMaximum"): clientTopicAliasMaximum = connack.properties.TopicAliasMaximum aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) self.waitfor(callback.subscribeds, 1, 3) for qos in range(3): aclient.publish(topics[0], b"topic alias 1", qos) self.waitfor(callback.messages, 3, 3) self.assertEqual(len(callback.messages), 3, callback.messages) aclient.disconnect() # first message should set the topic alias self.assertTrue(hasattr(callback.messagedicts[0]["properties"], "TopicAlias"), callback.messagedicts[0]["properties"]) topicalias = callback.messagedicts[0]["properties"].TopicAlias self.assertEqual(callback.messagedicts[1]["properties"].TopicAlias, topicalias, topicalias) self.assertEqual(callback.messagedicts[2]["properties"].TopicAlias, topicalias, topicalias)
def test_retained_message(self): qos0topic="fromb/qos 0" qos1topic="fromb/qos 1" qos2topic="fromb/qos2" wildcardtopic="fromb/+" publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.UserPropertyList = [("a", "2"), ("c", "3")] # retained messages callback.clear() aclient.connect(host=host, port=port, cleanstart=True) aclient.publish(topics[1], b"qos 0", 0, retained=True, properties=publish_properties) aclient.publish(topics[2], b"qos 1", 1, retained=True, properties=publish_properties) aclient.publish(topics[3], b"qos 2", 2, retained=True, properties=publish_properties) time.sleep(1) aclient.subscribe([wildtopics[5]], [MQTTV5.SubscribeOptions(2)]) time.sleep(1) aclient.disconnect() self.assertEqual(len(callback.messages), 3) userprops = callback.messages[0][5].UserPropertyList self.assertTrue(userprops in [[("a", "2"), ("c", "3")],[("c", "3"), ("a", "2")]], userprops) userprops = callback.messages[1][5].UserPropertyList self.assertTrue(userprops in [[("a", "2"), ("c", "3")],[("c", "3"), ("a", "2")]], userprops) userprops = callback.messages[2][5].UserPropertyList self.assertTrue(userprops in [[("a", "2"), ("c", "3")],[("c", "3"), ("a", "2")]], userprops) qoss = [callback.messages[i][2] for i in range(3)] self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss) cleanRetained()
def test_flow_control2(self): testcallback = Callbacks() # no callback means no background thread, to control receiving testclient = mqtt_client.Client("myclientid".encode("utf-8")) # get receive maximum - the number of concurrent QoS 1 and 2 messages connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.SessionExpiryInterval = 0 connack = testclient.connect(host=host, port=port, cleanstart=True) serverReceiveMaximum = 2**16-1 # the default if hasattr(connack.properties, "ReceiveMaximum"): serverReceiveMaximum = connack.properties.ReceiveMaximum receiver = testclient.getReceiver() # send number of messages to exceed receive maximum qos = 2 pubs = 0 for i in range(1, serverReceiveMaximum + 2): testclient.publish(topics[0], "message %d" % i, qos) pubs += 1 # should get disconnected... while (testcallback.disconnects == []): receiver.receive(testcallback) self.waitfor(testcallback.disconnects, 1, 1) self.assertEqual(len(testcallback.disconnects), 1, len(testcallback.disconnects)) self.assertEqual(testcallback.disconnects[0]["reasonCode"].value, 147, testcallback.disconnects[0]["reasonCode"].value)
def test_publication_expiry(self): callback.clear() callback2.clear() bclient.connect(host=host, port=port, cleanstart=True) bclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) bclient.disconnect() aclient.connect(host=host, port=port, cleanstart=True) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.MessageExpiryInterval = 1 aclient.publish(topics[0], b"qos 1 - expire", 1, retained=False, properties=publish_properties) aclient.publish(topics[0], b"qos 2 - expire", 2, retained=False, properties=publish_properties) publish_properties.MessageExpiryInterval = 6 aclient.publish(topics[0], b"qos 1 - don't expire", 1, retained=False, properties=publish_properties) aclient.publish(topics[0], b"qos 2 - don't expire", 2, retained=False, properties=publish_properties) time.sleep(3) bclient.connect(host=host, port=port, cleanstart=False) self.waitfor(callback2.messages, 1, 3) time.sleep(1) self.assertEqual(len(callback2.messages), 2, callback2.messages) self.assertTrue(callback2.messages[0][5].MessageExpiryInterval < 6, callback2.messages[0][5].MessageExpiryInterval) self.assertTrue(callback2.messages[1][5].MessageExpiryInterval < 6, callback2.messages[1][5].MessageExpiryInterval) aclient.disconnect()
def test_payload_format(self): callback.clear() aclient.connect(host=host, port=port, cleanstart=True) aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.PayloadFormatIndicator = 1 publish_properties.ContentType = "My name" aclient.publish(topics[0], b"", 0, retained=False, properties=publish_properties) aclient.publish(topics[0], b"", 1, retained=False, properties=publish_properties) aclient.publish(topics[0], b"", 2, retained=False, properties=publish_properties) while len(callback.messages) < 3: time.sleep(.1) aclient.disconnect() self.assertEqual(len(callback.messages), 3, callback.messages) props = callback.messages[0][5] self.assertEqual(props.ContentType, "My name", props.ContentType) self.assertEqual(props.PayloadFormatIndicator, 1, props.PayloadFormatIndicator) props = callback.messages[1][5] self.assertEqual(props.ContentType, "My name", props.ContentType) self.assertEqual(props.PayloadFormatIndicator, 1, props.PayloadFormatIndicator) props = callback.messages[2][5] self.assertEqual(props.ContentType, "My name", props.ContentType) self.assertEqual(props.PayloadFormatIndicator, 1, props.PayloadFormatIndicator) qoss = [callback.messages[i][2] for i in range(3)] self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
def test_login_sub_unsub(self): callback2.clear() connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.SessionExpiryInterval = 1000 connack = bclient.connect(host=host,port=port,cleanstart=True) bclient.disconnect() time.sleep(1) connack = aclient.connect(host=host,port=port,cleanstart=True,properties=connect_properties) connack = bclient.connect(host=host,port=port,cleanstart=False,properties=connect_properties) succeeded = True time.sleep(10) # print("Basic test starting") # succeeded = True # number = len(topics) # try: # for i in range(number): # print("sub is %d"%i) # aclient.subscribe([topics[i]], [MQTTV5.SubscribeOptions(2)]) # bclient.subscribe([topics[i]], [MQTTV5.SubscribeOptions(2)]) # time.sleep(.1) # time.sleep(10) # for i in range(number): # print("unsub is %s"%i) # aclient.unsubscribe([topics[i]]) # bclient.unsubscribe([topics[i]]) # time.sleep(.1) # time.sleep(10) # except: # succeeded = False # print("Basic test", "succeeded" if succeeded else "failed") # self.assertEqual(succeeded, True) # return succeeded """
def optionsOf(self, clientid, topic): # if there are overlapping subscriptions, choose maximum QoS chosen = None for sub in self.getSubscriptions(topic, clientid): if chosen == None: if hasattr(sub, "getOptions"): chosen = sub.getOptions() else: # MQTT V3 case chosen = (MQTTV5.SubscribeOptions(QoS=sub.getQoS()), MQTTV5.Properties(MQTTV5.PacketTypes.SUBSCRIBE)) else: logger.info("[MQTT-3.3.5-1] Overlapping subscriptions max QoS") if sub.getQoS() > chosen[0].QoS: if hasattr(sub, "getOptions"): chosen = sub.getOptions() else: # MQTT V3 case chosen = (MQTTV5.SubscribeOptions(QoS=sub.getQoS()), MQTTV5.Properties(MQTTV5.PacketTypes.SUBSCRIBE)) # Omit the following optimization because we want to check for condition [MQTT-3.3.5-1] #if chosen == 2: # break return chosen
def test_subscribe_identifiers(self): callback.clear() aclient.connect(host=host, port=port, cleanstart=True) sub_properties = MQTTV5.Properties(MQTTV5.PacketTypes.SUBSCRIBE) sub_properties.SubscriptionIdentifier = 456789 aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)], properties=sub_properties) self.waitfor(callback.subscribeds, 1, 3) aclient.publish(topics[0], b"sub identifier test", 1, retained=False) self.waitfor(callback.messages, 1, 3) self.assertEqual(len(callback.messages), 1, callback.messages) self.assertEqual(callback.messages[0][5].SubscriptionIdentifier, 456789, callback.messages[0][5].SubscriptionIdentifier) aclient.disconnect() callback.clear()
def test_maximum_packet_size(self): callback.clear() # 1. server max packet size connack = aclient.connect(host=host, port=port, cleanstart=True) serverMaximumPacketSize = 2**28-1 if hasattr(connack.properties, "MaximumPacketSize"): serverMaximumPacketSize = connack.properties.MaximumPacketSize if serverMaximumPacketSize < 65535: # publish bigger packet than server can accept payload = b"."*serverMaximumPacketSize aclient.publish(topics[0], payload, 0) # should get back a disconnect with packet size too big self.waitfor(callback.disconnects, 1, 2) self.assertEqual(len(callback.disconnects), 1, callback.disconnects) self.assertEqual(str(callback.disconnects[0]["reasonCode"]), "Packet too large", str(callback.disconnects[0]["reasonCode"])) else: aclient.disconnect() # 1. client max packet size maximumPacketSize = 64 # max packet size we want to receive connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.MaximumPacketSize = maximumPacketSize connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) serverMaximumPacketSize = 2**28-1 if hasattr(connack.properties, "MaximumPacketSize"): serverMaximumPacketSize = connack.properties.MaximumPacketSize aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) self.waitfor(callback.subscribeds, 1, 3) # send a small enough packet, should get this one back payload = b"."*(int(maximumPacketSize/2)) aclient.publish(topics[0], payload, 0) self.waitfor(callback.messages, 1, 3) self.assertEqual(len(callback.messages), 1, callback.messages) # send a packet too big to receive payload = b"."*maximumPacketSize aclient.publish(topics[0], payload, 1) self.waitfor(callback.messages, 2, 3) self.assertEqual(len(callback.messages), 1, callback.messages) aclient.disconnect()
def handleBehaviourPublish(self,sock, topic, data): """Handle behaviour packet. Options: Topic: 'cmd/disconnectWithRC', Payload: A Disconnect Return code - Disconnects with the specified return code and sample properties. """ logger.info("Command Mode: Topic: %s, Payload: %s" % (topic, int(data))) if topic == "cmd/disconnectWithRC": returnCode = int(data) props = MQTTV5.Properties(MQTTV5.PacketTypes.DISCONNECT) props.ReasonString = "This is a custom Reason String" props.ServerReference = "tcp://localhost:1883" props.UserPropertyList = [("key", "value")] self.disconnect(sock, None, sendWillMessage=False, reasonCode=returnCode, properties=props)
def test_will_message(self): # will messages callback.clear() callback2.clear() self.assertEqual(len(callback2.messages), 0, callback2.messages) will_properties = MQTTV5.Properties(MQTTV5.PacketTypes.WILLMESSAGE) will_properties.WillDelayInterval = 0 # this is the default anyway will_properties.UserPropertyList = [("a", "2"), ("c", "3")] aclient.connect(host=host, port=port, cleanstart=True, willFlag=True, willTopic=topics[2], willMessage=b"will message", keepalive=2, willProperties=will_properties) bclient.connect(host=host, port=port, cleanstart=False) bclient.subscribe([topics[2]], [MQTTV5.SubscribeOptions(2)]) self.waitfor(callback2.subscribeds, 1, 3) # keep alive timeout ought to be triggered so the will message is received self.waitfor(callback2.messages, 1, 10) bclient.disconnect() self.assertEqual(len(callback2.messages), 1, callback2.messages) # should have the will message
def test_user_properties(self): callback.clear() aclient.connect(host=host, port=port, cleanstart=True) aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.UserPropertyList = [("a", "2"), ("c", "3")] aclient.publish(topics[0], b"", 0, retained=False, properties=publish_properties) aclient.publish(topics[0], b"", 1, retained=False, properties=publish_properties) aclient.publish(topics[0], b"", 2, retained=False, properties=publish_properties) while len(callback.messages) < 3: time.sleep(.1) aclient.disconnect() self.assertEqual(len(callback.messages), 3, callback.messages) userprops = callback.messages[0][5].UserPropertyList self.assertTrue(userprops in [[("a", "2"), ("c", "3")],[("c", "3"), ("a", "2")]], userprops) userprops = callback.messages[1][5].UserPropertyList self.assertTrue(userprops in [[("a", "2"), ("c", "3")],[("c", "3"), ("a", "2")]], userprops) userprops = callback.messages[2][5].UserPropertyList self.assertTrue(userprops in [[("a", "2"), ("c", "3")],[("c", "3"), ("a", "2")]], userprops) qoss = [callback.messages[i][2] for i in range(3)] self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
def test_request_response(self): callback.clear() callback2.clear() aclient.connect(host=host, port=port, cleanstart=True) bclient.connect(host=host, port=port, cleanstart=True) aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2, noLocal=True)]) self.waitfor(callback.subscribeds, 1, 3) bclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2, noLocal=True)]) self.waitfor(callback.subscribeds, 1, 3) publish_properties = MQTTV5.Properties(MQTTV5.PacketTypes.PUBLISH) publish_properties.ResponseTopic = topics[0] publish_properties.CorrelationData = b"334" # client a is the requester aclient.publish(topics[0], b"request", 1, properties=publish_properties) # client b is the responder self.waitfor(callback2.messages, 1, 3) self.assertEqual(len(callback2.messages), 1, callback2.messages) self.assertEqual(len(callback2.messages), 1, callback2.messages) self.assertEqual(callback2.messages[0][5].ResponseTopic, topics[0], callback2.messages[0][5]) self.assertEqual(callback2.messages[0][5].CorrelationData, b"334", callback2.messages[0][5]) bclient.publish(callback2.messages[0][5].ResponseTopic, b"response", 1, properties=callback2.messages[0][5]) # client a gets the response self.waitfor(callback.messages, 1, 3) self.assertEqual(len(callback.messages), 1, callback.messages) aclient.disconnect() bclient.disconnect() callback.clear() callback2.clear()
def test_will_delay(self): """ the will message should be received earlier than the session expiry """ callback.clear() callback2.clear() will_properties = MQTTV5.Properties(MQTTV5.PacketTypes.WILLMESSAGE) connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) # set the will delay and session expiry to the same value - # then both should occur at the same time will_properties.WillDelayInterval = 3 # in seconds connect_properties.SessionExpiryInterval = 5 connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties, willProperties=will_properties, willFlag=True, willTopic=topics[0], willMessage=b"test_will_delay will message") self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) connack = bclient.connect(host=host, port=port, cleanstart=True) bclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) # subscribe to will message topic self.waitfor(callback2.subscribeds, 1, 3) # terminate client a and wait for the will message aclient.terminate() start = time.time() while callback2.messages == []: time.sleep(.1) duration = time.time() - start #print(duration) self.assertAlmostEqual(duration, 4, delta=1) self.assertEqual(callback2.messages[0][0], topics[0]) self.assertEqual(callback2.messages[0][1], b"test_will_delay will message") aclient.disconnect() bclient.disconnect() callback.clear() callback2.clear() # if session expiry is less than will delay then session expiry is used will_properties.WillDelayInterval = 5 # in seconds connect_properties.SessionExpiryInterval = 0 connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties, willProperties=will_properties, willFlag=True, willTopic=topics[0], willMessage=b"test_will_delay will message") self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) connack = bclient.connect(host=host, port=port, cleanstart=True) bclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) # subscribe to will message topic self.waitfor(callback2.subscribeds, 1, 3) # terminate client a and wait for the will message aclient.terminate() start = time.time() while callback2.messages == []: time.sleep(.1) duration = time.time() - start #print(duration) self.assertAlmostEqual(duration, 1, delta=1) self.assertEqual(callback2.messages[0][0], topics[0]) self.assertEqual(callback2.messages[0][1], b"test_will_delay will message") aclient.disconnect() bclient.disconnect() callback.clear() callback2.clear() # if session expiry is less than will delay then session expiry is used will_properties.WillDelayInterval = 5 # in seconds connect_properties.SessionExpiryInterval = 2 connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties, willProperties=will_properties, willFlag=True, willTopic=topics[0], willMessage=b"test_will_delay will message") self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) connack = bclient.connect(host=host, port=port, cleanstart=True) bclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) # subscribe to will message topic self.waitfor(callback2.subscribeds, 1, 3) # terminate client a and wait for the will message aclient.terminate() start = time.time() while callback2.messages == []: time.sleep(.1) duration = time.time() - start #print(duration) self.assertAlmostEqual(duration, 3, delta=1) self.assertEqual(callback2.messages[0][0], topics[0]) self.assertEqual(callback2.messages[0][1], b"test_will_delay will message") aclient.disconnect() bclient.disconnect() callback.clear() callback2.clear()
def handleRequest(self, sock): "this is going to be called from multiple threads, so synchronize" self.lock.acquire() raw_packet = None try: try: raw_packet = MQTTV5.getPacket(sock) except: pass # handled by raw_packet == None if raw_packet == None: logger.info( "[MQTT-4.8.0-1] 'transient error' reading packet, closing connection" ) # will message if sock in self.clients.keys(): self.disconnect(sock, None, sendWillMessage=True) terminate = True else: try: packet = MQTTV5.unpackPacket( raw_packet, self.options["maximumPacketSize"]) if self.options["visual"]: clientid = self.clients[ sock].id if sock in self.clients.keys() else "" if clientid == "" and hasattr(packet, "ClientIdentifier"): clientid = packet.ClientIdentifier try: data = { "direction": "CtoS", "socket": sock.fileno(), "clientid": clientid, "packet": packet.json() } databytes = bytes(json.dumps(data), 'utf-8') self.broker.publish('$internal', '$SYS/clients-packets', databytes, 0, 0, None, time.monotonic()) except: traceback.print_exc() if packet: terminate = self.handlePacket(packet, sock) else: self.disconnect(sock, reasonCode="Malformed packet", sendWillMessage=True) terminate = True except MQTTV5.MalformedPacket as error: traceback.print_exc() disconnect_properties = MQTTV5.Properties( MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.ReasonString = error.args[0] self.disconnect(sock, reasonCode="Malformed packet", sendWillMessage=True) terminate = True except MQTTV5.ProtocolError as error: disconnect_properties = MQTTV5.Properties( MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.ReasonString = error.args[0] self.disconnect(sock, reasonCode=error.args[0], properties=disconnect_properties, sendWillMessage=True) terminate = True finally: self.lock.release() return terminate
def test_session_expiry(self): # no session expiry property == never expire connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.SessionExpiryInterval = 0 connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) aclient.disconnect() # session should immediately expire connack = aclient.connect(host=host, port=port, cleanstart=False, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) aclient.disconnect() connect_properties.SessionExpiryInterval = 5 connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) aclient.disconnect() time.sleep(2) # session should still exist connack = aclient.connect(host=host, port=port, cleanstart=False, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, True) aclient.disconnect() time.sleep(6) # session should not exist connack = aclient.connect(host=host, port=port, cleanstart=False, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) aclient.disconnect() connect_properties.SessionExpiryInterval = 1 connack = aclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) aclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) disconnect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.SessionExpiryInterval = 5 aclient.disconnect(properties = disconnect_properties) time.sleep(3) # session should still exist connack = aclient.connect(host=host, port=port, cleanstart=False, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, True) disconnect_properties.SessionExpiryInterval = 0 aclient.disconnect(properties = disconnect_properties) # session should immediately expire connack = aclient.connect(host=host, port=port, cleanstart=False, properties=connect_properties) self.assertEqual(connack.reasonCode.getName(), "Success") self.assertEqual(connack.sessionPresent, False) aclient.disconnect()
def test_flow_control1(self): testcallback = Callbacks() # no callback means no background thread, to control receiving testclient = mqtt_client.Client("myclientid".encode("utf-8")) # set receive maximum - the number of concurrent QoS 1 and 2 messages clientReceiveMaximum = 2 # set to low number so we can test connect_properties = MQTTV5.Properties(MQTTV5.PacketTypes.CONNECT) connect_properties.ReceiveMaximum = clientReceiveMaximum connect_properties.SessionExpiryInterval = 0 connack = testclient.connect(host=host, port=port, cleanstart=True, properties=connect_properties) serverReceiveMaximum = 2**16-1 # the default if hasattr(connack.properties, "ReceiveMaximum"): serverReceiveMaximum = connack.properties.ReceiveMaximum receiver = testclient.getReceiver() testclient.subscribe([topics[0]], [MQTTV5.SubscribeOptions(2)]) receiver.receive(testcallback) self.waitfor(testcallback.subscribeds, 1, 3) pubs = 0 for i in range(1, clientReceiveMaximum + 2): testclient.publish(topics[0], "message %d" % i, 1) pubs += 1 # get two publishes acks = 0 while True: response1 = MQTTV5.unpackPacket(MQTTV5.getPacket(testclient.sock)) if response1.fh.PacketType == MQTTV5.PacketTypes.PUBLISH: break self.assertEqual(response1.fh.PacketType, MQTTV5.PacketTypes.PUBACK) acks += 1 del receiver.outMsgs[response1.packetIdentifier] self.assertEqual(response1.fh.PacketType, MQTTV5.PacketTypes.PUBLISH) self.assertEqual(response1.fh.QoS, 1, response1.fh.QoS) while True: response2 = MQTTV5.unpackPacket(MQTTV5.getPacket(testclient.sock)) if response2.fh.PacketType == MQTTV5.PacketTypes.PUBLISH: break self.assertEqual(response2.fh.PacketType, MQTTV5.PacketTypes.PUBACK) acks += 1 del receiver.outMsgs[response2.packetIdentifier] self.assertEqual(response2.fh.PacketType, MQTTV5.PacketTypes.PUBLISH) self.assertEqual(response2.fh.QoS, 1, response1.fh.QoS) while acks < pubs: ack = MQTTV5.unpackPacket(MQTTV5.getPacket(testclient.sock)) self.assertEqual(ack.fh.PacketType, MQTTV5.PacketTypes.PUBACK) acks += 1 del receiver.outMsgs[ack.packetIdentifier] with self.assertRaises(socket.timeout): # this should time out because we haven't acknowledged the first one response3 = MQTTV5.unpackPacket(MQTTV5.getPacket(testclient.sock)) # ack the first one puback = MQTTV5.Pubacks() puback.packetIdentifier = response1.packetIdentifier testclient.sock.send(puback.pack()) # now get the next packet response3 = MQTTV5.unpackPacket(MQTTV5.getPacket(testclient.sock)) self.assertEqual(response3.fh.PacketType, MQTTV5.PacketTypes.PUBLISH) self.assertEqual(response3.fh.QoS, 1, response1.fh.QoS) # ack the second one puback.packetIdentifier = response2.packetIdentifier testclient.sock.send(puback.pack()) # ack the third one puback.packetIdentifier = response3.packetIdentifier testclient.sock.send(puback.pack()) testclient.disconnect()