예제 #1
0
    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)
예제 #2
0
 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
예제 #3
0
    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)
예제 #4
0
    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()
예제 #5
0
    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)
예제 #6
0
    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()
예제 #7
0
    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)
예제 #8
0
    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


      """
예제 #9
0
 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
예제 #10
0
    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()
예제 #11
0
    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()
예제 #12
0
  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)
예제 #13
0
    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
예제 #14
0
 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)
예제 #15
0
    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()
예제 #17
0
 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
예제 #18
0
    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()
예제 #19
0
    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()