Exemple #1
0
    def test_unknown_connect_code_must_lose_connection(self):
        """
        A non-zero, and non-1-to-5 connect code from the handler must result in
        a lost connection, and no CONNACK.

        Compliance statements MQTT-3.2.2-4, MQTT-3.2.2-5
        """
        sessions = {}

        d = Deferred()  # noqa
        h = BasicHandler(6)
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)
        cp = MQTTClientParser()  # noqa

        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        self.assertTrue(t.disconnecting)
        self.assertEqual(t.value(), b'')
Exemple #2
0
    def test_non_allowed_qos_not_queued(self):
        """
        A non-QoS 0, 1, or 2 message will be rejected by the publish layer.
        """
        h = BasicHandler()
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        # Connect has happened
        events = cp.data_received(t.value())
        t.clear()
        self.assertFalse(t.disconnecting)
        self.assertIsInstance(events[0], ConnACK)

        # WAMP layer calls send_publish w/ invalid QoS
        with self.assertRaises(ValueError):
            p.send_publish(u"hello", 5, b'some bytes', False)

        # Nothing will be sent
        self.assertEqual(t.value(), b'')

        # Advance the clock
        r.advance(0.1)

        # Still nothing
        self.assertEqual(t.value(), b'')
Exemple #3
0
    def test_transport_paused_while_processing(self):
        """
        The transport is paused whilst the MQTT protocol is parsing/handling
        existing items.
        """
        sessions = {}

        d = Deferred()
        h = BasicHandler()
        h.process_connect = lambda x: d
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)

        t.connected = True
        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        self.assertEqual(t.producerState, 'producing')

        for x in iterbytes(data):
            p.dataReceived(x)

        self.assertEqual(t.producerState, 'paused')
        d.callback(0)
        self.assertEqual(t.producerState, 'producing')
Exemple #4
0
    def test_exception_in_connect_drops_connection(self):
        """
        Transient failures (like an exception from handler.process_connect)
        will cause the connection it happened on to be dropped.

        Compliance statement MQTT-4.8.0-2
        """
        class SubHandler(BasicHandler):
            def process_unsubscribe(self, event):
                raise Exception("boom!")

        h = SubHandler()
        r, t, p, cp = make_test_items(h)

        data = (
            Connect(client_id=u"test123",
                    flags=ConnectFlags(clean_session=True)).serialise() +
            Unsubscribe(packet_identifier=1234, topics=[u"foo"]).serialise())

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        sent_logs = logs.get_category("MQ502")
        self.assertEqual(len(sent_logs), 1)
        self.assertEqual(sent_logs[0]["log_level"], LogLevel.critical)
        self.assertEqual(sent_logs[0]["log_failure"].value.args[0], "boom!")

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertTrue(t.disconnecting)

        # We got the error, we need to flush it so it doesn't make the test
        # error
        self.flushLoggedErrors()
Exemple #5
0
    def test_non_zero_connect_code_must_have_no_present_session(self):
        """
        A non-zero connect code in a CONNACK must be paired with no session
        present.

        Compliance statement MQTT-3.2.2-4
        """
        sessions = {}

        d = Deferred()  # noqa
        h = BasicHandler(self.connect_code)
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)
        cp = MQTTClientParser()

        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertEqual(attr.asdict(events[0]), {
            'return_code': self.connect_code,
            'session_present': False,
        })
Exemple #6
0
    def test_lose_conn_on_reserved_qos3(self):
        """
        If we get, somehow, a QoS "3" Publish (one with both QoS bits set to
        3), we will drop the connection.

        Compliance statement: MQTT-3.3.1-4
        """
        h = BasicHandler()
        r, t, p, cp = make_test_items(h)

        conn = Connect(client_id=u"test123",
                       flags=ConnectFlags(clean_session=False))
        pub = Publish(duplicate=False,
                      qos_level=3,
                      retain=False,
                      topic_name=u"foo",
                      packet_identifier=1,
                      payload=b"bar")

        with LogCapturer("trace") as logs:
            p._handle_events([conn, pub])

        sent_logs = logs.get_category("MQ403")
        self.assertEqual(len(sent_logs), 1)
        self.assertEqual(sent_logs[0]["log_level"], LogLevel.error)

        self.assertTrue(t.disconnecting)
Exemple #7
0
    def test_subscribe_always_gets_packet(self):
        """
        Subscriptions always get a ConnACK, even if none of the subscriptions
        were successful.

        Compliance statements MQTT-3.8.4-1
        """
        sessions = {}

        class SubHandler(BasicHandler):
            def process_subscribe(self, event):
                return succeed([128])

        h = SubHandler()
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)
        cp = MQTTClientParser()

        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                Subscribe(packet_identifier=1234,
                          topic_requests=[SubscriptionTopicRequest(u"a", 0)
                                          ]).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 2)
        self.assertEqual(events[1].return_codes, [128])
Exemple #8
0
    def test_lose_conn_on_unimplemented_packet(self):
        """
        If we get a valid, but unimplemented for that role packet (e.g. SubACK,
        which we will only ever send, and getting it is a protocol violation),
        we will drop the connection.

        Compliance statement: MQTT-4.8.0-1
        """
        # This shouldn't normally happen, but just in case.
        from crossbar.adapter.mqtt import protocol
        protocol.server_packet_handlers[protocol.P_SUBACK] = SubACK
        self.addCleanup(
            lambda: protocol.server_packet_handlers.pop(protocol.P_SUBACK))

        h = BasicHandler()
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise() +
                SubACK(1, [1]).serialise())

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        sent_logs = logs.get_category("MQ402")
        self.assertEqual(len(sent_logs), 1)
        self.assertEqual(sent_logs[0]["log_level"], LogLevel.error)
        self.assertEqual(sent_logs[0]["packet_id"], "SubACK")

        self.assertTrue(t.disconnecting)
Exemple #9
0
    def test_subscribe_same_id(self):
        """
        SubACKs have the same packet IDs as the Subscription that it is
        replying to.

        Compliance statements MQTT-3.8.4-2
        """
        sessions = {}

        class SubHandler(BasicHandler):
            def process_subscribe(self, event):
                return succeed([0])

        h = SubHandler()
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)
        cp = MQTTClientParser()

        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                Subscribe(packet_identifier=1234,
                          topic_requests=[SubscriptionTopicRequest(u"a", 0)
                                          ]).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 2)
        self.assertEqual(events[1].return_codes, [0])
        self.assertEqual(events[1].packet_identifier, 1234)
Exemple #10
0
    def test_basic_publish(self):

        reactor, router, server_factory, session_factory = build_mqtt_server()

        session, pump = connect_application_session(
            server_factory, ObservingSession, component_config=ComponentConfig(realm=u"mqtt"))
        client_transport, client_protocol, mqtt_pump = connect_mqtt_server(server_factory)

        client_transport.write(
            Connect(client_id=u"testclient", username=u"test123", password=u"password",
                    flags=ConnectFlags(clean_session=False, username=True, password=True)).serialise())
        mqtt_pump.flush()

        # We get a CONNECT
        self.assertEqual(client_protocol.data,
                         ConnACK(session_present=False, return_code=0).serialise())
        client_protocol.data = b""

        client_transport.write(
            Publish(duplicate=False, qos_level=0, retain=False, topic_name=u"test", payload=b'{"kwargs": {"bar": "baz"}}').serialise())
        mqtt_pump.flush()
        pump.flush()

        # This needs to be replaced with the real deal, see https://github.com/crossbario/crossbar/issues/885
        self.assertEqual(len(session.events), 1)
        self.assertEqual(
            session.events,
            [{"args": tuple(),
              "kwargs": {u'bar': u'baz'}}])
Exemple #11
0
def test_self_subscribe(host, port):
    record = [
        Frame(send=True,
              data=Connect(client_id=u"test_selfsub",
                           flags=ConnectFlags(clean_session=True))),
        Frame(send=False, data=ConnACK(session_present=False, return_code=0)),
        Frame(send=True,
              data=Subscribe(
                  packet_identifier=1234,
                  topic_requests=[SubscriptionTopicRequest(u"foo", 2)])),
        Frame(send=False,
              data=SubACK(packet_identifier=1234, return_codes=[2])),
        Frame(send=True,
              data=Publish(duplicate=False,
                           qos_level=0,
                           topic_name=u"foo",
                           payload=b"abc",
                           retain=False)),
        Frame(send=False,
              data=Publish(duplicate=False,
                           qos_level=0,
                           topic_name=u"foo",
                           payload=b"abc",
                           retain=False)),
        Frame(send=True, data=Disconnect()),
        ConnectionLoss(),
    ]

    r = SelectReactor()
    f = ReplayClientFactory(r, record)
    e = TCP4ClientEndpoint(r, host, port)
    e.connect(f)
    r.run()

    return Result("self_subscribe", f.success, f.reason, f.client_transcript)
Exemple #12
0
    def test_qos_2_queues_message(self):
        """
        The WAMP layer calling send_publish will queue a message up for
        sending, and send it next time it has a chance.
        """
        h = BasicHandler()
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        # Connect has happened
        events = cp.data_received(t.value())
        t.clear()
        self.assertFalse(t.disconnecting)
        self.assertIsInstance(events[0], ConnACK)

        # WAMP layer calls send_publish, with QoS 2
        p.send_publish(u"hello", 2, b'some bytes', False)

        # Nothing should have been sent yet, it is queued
        self.assertEqual(t.value(), b'')

        # Advance the clock
        r.advance(0.1)

        # We should now get the sent Publish
        expected_publish = Publish(duplicate=False,
                                   qos_level=2,
                                   retain=False,
                                   packet_identifier=1,
                                   topic_name=u"hello",
                                   payload=b"some bytes")
        events = cp.data_received(t.value())
        t.clear()
        self.assertEqual(len(events), 1)
        self.assertEqual(events[0], expected_publish)

        # We send the PubREC, which we should get a PubREL back with
        pubrec = PubREC(packet_identifier=1)

        for x in iterbytes(pubrec.serialise()):
            p.dataReceived(x)

        events = cp.data_received(t.value())
        t.clear()
        self.assertEqual(len(events), 1)
        self.assertEqual(events[0], PubREL(packet_identifier=1))

        # We send the PubCOMP, which has no response
        pubcomp = PubCOMP(packet_identifier=1)

        for x in iterbytes(pubcomp.serialise()):
            p.dataReceived(x)

        self.assertFalse(t.disconnecting)
Exemple #13
0
    def _test_retained(self):
        """
        The MQTT client can set and receive retained messages.
        """
        reactor, router, server_factory, session_factory = build_mqtt_server()
        client_transport, client_protocol, mqtt_pump = connect_mqtt_server(
            server_factory)

        client_transport.write(
            Connect(client_id=u"testclient",
                    username=u"test123",
                    password=u"password",
                    flags=ConnectFlags(clean_session=False,
                                       username=True,
                                       password=True)).serialise())

        client_transport.write(
            Publish(duplicate=False,
                    qos_level=1,
                    retain=True,
                    topic_name=u"com/test/wamp",
                    packet_identifier=123,
                    payload=b'{}').serialise())

        mqtt_pump.flush()

        self.assertEqual(
            client_protocol.data,
            (ConnACK(session_present=False, return_code=0).serialise() +
             PubACK(packet_identifier=123).serialise()))
        client_protocol.data = b""

        client_transport.write(
            Subscribe(packet_identifier=1,
                      topic_requests=[
                          SubscriptionTopicRequest(
                              topic_filter=u"com/test/wamp", max_qos=0)
                      ]).serialise())

        mqtt_pump.flush()

        self.assertEqual(
            client_protocol.data,
            SubACK(packet_identifier=1, return_codes=[0]).serialise())
        client_protocol.data = b""

        reactor.advance(0.1)
        mqtt_pump.flush()

        # This needs to be replaced with the real deal, see https://github.com/crossbario/crossbar/issues/885
        self.assertEqual(
            client_protocol.data,
            Publish(duplicate=False,
                    qos_level=0,
                    retain=True,
                    topic_name=u"com/test/wamp",
                    payload=json.dumps(
                        {}, sort_keys=True).encode('utf8')).serialise())
Exemple #14
0
            def _(proto):
                p.append(proto)

                proto.transport.write(
                    Connect(client_id=u"test123",
                            flags=ConnectFlags(clean_session=False)).serialise())

                proto.transport.write(
                    Publish(duplicate=False, qos_level=1, retain=False, topic_name=u"test", payload=b"{}", packet_identifier=1).serialise())
Exemple #15
0
    def test_allow_connects_with_same_id_if_disconnected(self):
        """
        If a client connects and there is an existing session which is
        disconnected, it may connect.
        """
        sessions = {}

        h = BasicHandler()
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)
        cp = MQTTClientParser()

        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        self.assertFalse(t.disconnecting)
        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertEqual(attr.asdict(events[0]), {
            'return_code': 0,
            'session_present': False,
        })

        p.connectionLost(None)

        # New session
        r2 = Clock()
        t2 = StringTransport()
        p2 = MQTTServerTwistedProtocol(h, r2, sessions)
        cp2 = MQTTClientParser()

        p2.makeConnection(t2)

        # Send the same connect, with the same client ID
        for x in iterbytes(data):
            p2.dataReceived(x)

        # Connection allowed
        events = cp2.data_received(t2.value())
        self.assertEqual(len(events), 1)
        self.assertEqual(attr.asdict(events[0]), {
            'return_code': 0,
            'session_present': True,
        })

        # Same session
        self.assertEqual(p.session, p2.session)
Exemple #16
0
    def _test_basic_subscribe(self):
        """
        The MQTT client can subscribe to a WAMP topic and get messages.
        """
        reactor, router, server_factory, session_factory = build_mqtt_server()
        client_transport, client_protocol, mqtt_pump = connect_mqtt_server(
            server_factory)

        session, pump = connect_application_session(
            server_factory,
            ApplicationSession,
            component_config=ComponentConfig(realm=u"mqtt"))

        client_transport.write(
            Connect(client_id=u"testclient",
                    username=u"test123",
                    password=u"password",
                    flags=ConnectFlags(clean_session=False,
                                       username=True,
                                       password=True)).serialise())
        client_transport.write(
            Subscribe(packet_identifier=1,
                      topic_requests=[
                          SubscriptionTopicRequest(
                              topic_filter=u"com/test/wamp", max_qos=0)
                      ]).serialise())

        mqtt_pump.flush()

        self.assertEqual(
            client_protocol.data,
            (ConnACK(session_present=False, return_code=0).serialise() +
             SubACK(packet_identifier=1, return_codes=[0]).serialise()))
        client_protocol.data = b""

        session.publish(u"com.test.wamp", u"bar")
        pump.flush()

        reactor.advance(0.1)
        mqtt_pump.flush()

        self.assertEqual(
            client_protocol.data,
            Publish(duplicate=False,
                    qos_level=0,
                    retain=False,
                    topic_name=u"com/test/wamp",
                    payload=b'{"args":["bar"]}').serialise())
Exemple #17
0
    def test_qos_1_sends_ack(self):
        """
        When a QoS 1 Publish packet is recieved, we send a PubACK with the same
        packet identifier as the original Publish.

        Compliance statement MQTT-3.3.4-1
        Spec part 3.4
        """
        got_packets = []

        class PubHandler(BasicHandler):
            def process_publish_qos_1(self, event):
                got_packets.append(event)
                return succeed(None)

        h = PubHandler()
        r, t, p, cp = make_test_items(h)

        pub = Publish(duplicate=False,
                      qos_level=1,
                      retain=False,
                      topic_name=u"foo",
                      packet_identifier=1,
                      payload=b"bar").serialise()

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                pub)

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertFalse(t.disconnecting)

        # ConnACK + PubACK with the same packet ID
        self.assertEqual(len(events), 2)
        self.assertEqual(events[1], PubACK(packet_identifier=1))

        # The publish handler should have been called
        self.assertEqual(len(got_packets), 1)
        self.assertEqual(got_packets[0].serialise(), pub)

        # We should get a debug message saying we got the publish
        messages = logs.get_category("MQ202")
        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0]["publish"].serialise(), pub)
Exemple #18
0
def test_connect(host, port):
    record = [
        Frame(send=True,
              data=Connect(client_id=u"test_cleanconnect",
                           flags=ConnectFlags(clean_session=True))),
        Frame(send=False, data=ConnACK(session_present=False, return_code=0)),
        Frame(send=True, data=Disconnect()),
        ConnectionLoss(),
    ]

    r = SelectReactor()
    f = ReplayClientFactory(r, record)
    e = TCP4ClientEndpoint(r, host, port)
    e.connect(f)
    r.run()

    return Result("connect", f.success, f.reason, f.client_transcript)
Exemple #19
0
def test_qos1_send_wrong_confirm(host, port):
    record = [
        Frame(send=True,
              data=Connect(client_id=u"test_wrong_confirm_qos1",
                           flags=ConnectFlags(clean_session=True))),
        Frame(send=False, data=ConnACK(session_present=False, return_code=0)),
        Frame(send=True,
              data=Subscribe(
                  packet_identifier=1234,
                  topic_requests=[SubscriptionTopicRequest(u"foo", 2)])),
        Frame(send=False,
              data=SubACK(packet_identifier=1234, return_codes=[2])),
        Frame(send=True,
              data=Publish(duplicate=False,
                           qos_level=1,
                           topic_name=u"foo",
                           payload=b"abc",
                           retain=False,
                           packet_identifier=12)),
        Frame(send=False,
              data=[
                  PubACK(packet_identifier=12),
                  Publish(duplicate=False,
                          qos_level=1,
                          topic_name=u"foo",
                          payload=b"abc",
                          retain=False,
                          packet_identifier=1)
              ]),
        # We send a pubrel to the packet_id expecting a puback
        Frame(send=True, data=PubREL(packet_identifier=1)),
        # ..aaaaand we get a pubcomp back (even though mosquitto warns).
        Frame(send=False, data=PubCOMP(packet_identifier=1)),
        Frame(send=True, data=Disconnect()),
        ConnectionLoss(),
    ]

    r = SelectReactor()
    f = ReplayClientFactory(r, record)
    e = TCP4ClientEndpoint(r, host, port)
    e.connect(f)
    r.run()

    return Result("qos1_wrong_confirm", f.success, f.reason,
                  f.client_transcript)
Exemple #20
0
    def test_lastwill(self):
        """
        The MQTT client can set a last will message which will be published
        when it disconnects.
        """
        reactor, router, server_factory, session_factory = build_mqtt_server()
        session, pump = connect_application_session(
            server_factory,
            ObservingSession,
            component_config=ComponentConfig(realm=u"mqtt"))
        client_transport, client_protocol, mqtt_pump = connect_mqtt_server(
            server_factory)

        client_transport.write(
            Connect(client_id=u"testclient",
                    username=u"test123",
                    password=u"password",
                    will_topic=u"test",
                    will_message=b'{"args": ["foobar"]}',
                    flags=ConnectFlags(clean_session=False,
                                       username=True,
                                       password=True,
                                       will=True)).serialise())

        mqtt_pump.flush()

        # We get a CONNECT
        self.assertEqual(
            client_protocol.data,
            ConnACK(session_present=False, return_code=0).serialise())
        client_protocol.data = b""

        client_transport.write(Disconnect().serialise())

        mqtt_pump.flush()
        pump.flush()

        self.assertEqual(client_transport.disconnected, True)

        # This needs to be replaced with the real deal, see https://github.com/crossbario/crossbar/issues/885
        self.assertEqual(len(session.events), 1)
        self.assertEqual(session.events, [{
            "args": (u"foobar", ),
            "kwargs": {}
        }])
Exemple #21
0
    def test_unknown_connect_code_must_lose_connection(self):
        """
        A non-zero, and non-1-to-5 connect code from the handler must result in
        a lost connection, and no CONNACK.

        Compliance statements MQTT-3.2.2-4, MQTT-3.2.2-5
        """
        h = BasicHandler(6)
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        self.assertTrue(t.disconnecting)
        self.assertEqual(t.value(), b'')
Exemple #22
0
    def test_exception_in_subscribe_drops_connection(self):
        """
        Transient failures (like an exception from handler.process_subscribe)
        will cause the connection it happened on to be dropped.

        Compliance statement MQTT-4.8.0-2
        """
        sessions = {}

        class SubHandler(BasicHandler):
            @inlineCallbacks
            def process_subscribe(self, event):
                raise Exception("boom!")

        h = SubHandler()
        r = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)
        cp = MQTTClientParser()

        p.makeConnection(t)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                Subscribe(packet_identifier=1234,
                          topic_requests=[SubscriptionTopicRequest(u"a", 0)
                                          ]).serialise())

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        sent_logs = logs.get_category("MQ500")
        self.assertEqual(len(sent_logs), 1)
        self.assertEqual(sent_logs[0]["log_level"], LogLevel.critical)
        self.assertEqual(sent_logs[0]["log_failure"].value.args[0], "boom!")

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertTrue(t.disconnecting)

        # We got the error, we need to flush it so it doesn't make the test
        # error
        self.flushLoggedErrors()
Exemple #23
0
    def test_qos_0_sends_no_ack(self):
        """
        When a QoS 0 Publish packet is recieved, we don't send back a PubACK.
        """
        got_packets = []

        class PubHandler(BasicHandler):
            def process_publish_qos_0(self, event):
                got_packets.append(event)
                return succeed(None)

        h = PubHandler()
        r, t, p, cp = make_test_items(h)

        pub = Publish(duplicate=False,
                      qos_level=0,
                      retain=False,
                      topic_name=u"foo",
                      packet_identifier=None,
                      payload=b"bar").serialise()

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                pub)

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertFalse(t.disconnecting)

        # Just the connack, no puback.
        self.assertEqual(len(events), 1)

        # The publish handler should have been called
        self.assertEqual(len(got_packets), 1)
        self.assertEqual(got_packets[0].serialise(), pub)

        # We should get a debug message saying we got the publish
        messages = logs.get_category("MQ201")
        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0]["publish"].serialise(), pub)
Exemple #24
0
def test_uninvited_pubrel(host, port):
    record = [
        Frame(send=True,
              data=Connect(client_id=u"test_pubrel",
                           flags=ConnectFlags(clean_session=True))),
        Frame(send=False, data=ConnACK(session_present=False, return_code=0)),
        Frame(send=True, data=PubREL(packet_identifier=1234)),
        Frame(send=False, data=PubCOMP(packet_identifier=1234)),
        Frame(send=True, data=Disconnect()),
        ConnectionLoss(),
    ]

    r = SelectReactor()
    f = ReplayClientFactory(r, record)
    e = TCP4ClientEndpoint(r, host, port)
    e.connect(f)
    r.run()

    return Result("uninvited_pubrel", f.success, f.reason, f.client_transcript)
Exemple #25
0
def test_reserved_packet_15(host, port):
    record = [
        Frame(send=True,
              data=Connect(client_id=u"test_reserved15",
                           flags=ConnectFlags(clean_session=True))),
        Frame(send=False, data=ConnACK(session_present=False, return_code=0)),
        Frame(
            send=True,
            #        v pkt 15 right here
            data=b"\xf0\x13\x00\x04MQTT\x04\x02\x00\x02\x00\x07test123"),
        ConnectionLoss()
    ]

    r = SelectReactor()
    f = ReplayClientFactory(r, record)
    e = TCP4ClientEndpoint(r, host, port)
    e.connect(f)
    r.run()

    return Result("reserved_pkt15", f.success, f.reason, f.client_transcript)
Exemple #26
0
    def test_transport_paused_while_processing(self):
        """
        The transport is paused whilst the MQTT protocol is parsing/handling
        existing items.
        """
        d = Deferred()
        h = BasicHandler()
        h.process_connect = lambda x: d
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        self.assertEqual(t.producerState, 'producing')

        for x in iterbytes(data):
            p.dataReceived(x)

        self.assertEqual(t.producerState, 'paused')
        d.callback((0, False))
        self.assertEqual(t.producerState, 'producing')
Exemple #27
0
    def test_qos_0_failure_drops_connection(self):
        """
        Transient failures (like an exception from
        handler.process_publish_qos_0) will cause the connection it happened on
        to be dropped.

        Compliance statement MQTT-4.8.0-2
        """
        class PubHandler(BasicHandler):
            def process_publish_qos_0(self, event):
                raise Exception("boom!")

        h = PubHandler()
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                Publish(duplicate=False,
                        qos_level=0,
                        retain=False,
                        topic_name=u"foo",
                        packet_identifier=None,
                        payload=b"bar").serialise())

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        sent_logs = logs.get_category("MQ503")
        self.assertEqual(len(sent_logs), 1)
        self.assertEqual(sent_logs[0]["log_level"], LogLevel.critical)
        self.assertEqual(sent_logs[0]["log_failure"].value.args[0], "boom!")

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertTrue(t.disconnecting)

        # We got the error, we need to flush it so it doesn't make the test
        # error
        self.flushLoggedErrors()
Exemple #28
0
    def test_qos_0_queues_message(self):
        """
        The WAMP layer calling send_publish will queue a message up for
        sending, and send it next time it has a chance.
        """
        h = BasicHandler()
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        # Connect has happened
        events = cp.data_received(t.value())
        t.clear()
        self.assertFalse(t.disconnecting)
        self.assertIsInstance(events[0], ConnACK)

        # WAMP layer calls send_publish
        p.send_publish(u"hello", 0, b'some bytes', False)

        # Nothing should have been sent yet, it is queued
        self.assertEqual(t.value(), b'')

        # Advance the clock
        r.advance(0.1)

        # We should now get the sent Publish
        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertEqual(
            events[0],
            Publish(duplicate=False,
                    qos_level=0,
                    retain=False,
                    packet_identifier=None,
                    topic_name=u"hello",
                    payload=b"some bytes"))
Exemple #29
0
    def test_non_zero_connect_code_must_have_no_present_session(self):
        """
        A non-zero connect code in a CONNACK must be paired with no session
        present.

        Compliance statement MQTT-3.2.2-4
        """
        h = BasicHandler(self.connect_code)
        r, t, p, cp = make_test_items(h)

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

        for x in iterbytes(data):
            p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 1)
        self.assertEqual(attr.asdict(events[0]), {
            'return_code': self.connect_code,
            'session_present': False,
        })
Exemple #30
0
    def test_unsubscription_gets_unsuback_with_same_id(self):
        """
        When an unsubscription is processed, the UnsubACK has the same ID.
        Unsubscriptions are always processed.

        Compliance statements MQTT-3.10.4-4, MQTT-3.10.4-5, MQTT-3.12.4-1
        """
        got_packets = []

        class SubHandler(BasicHandler):
            def process_unsubscribe(self, event):
                got_packets.append(event)
                return succeed(None)

        h = SubHandler()
        r, t, p, cp = make_test_items(h)

        unsub = Unsubscribe(packet_identifier=1234,
                            topics=[u"foo"]).serialise()

        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=True)).serialise() +
                unsub)

        for x in iterbytes(data):
            p.dataReceived(x)

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 2)
        self.assertFalse(t.disconnecting)

        # UnsubACK that has the same ID
        self.assertIsInstance(events[1], UnsubACK)
        self.assertEqual(events[1].packet_identifier, 1234)

        # The unsubscribe handler should have been called
        self.assertEqual(len(got_packets), 1)
        self.assertEqual(got_packets[0].serialise(), unsub)