Ejemplo n.º 1
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())
Ejemplo n.º 2
0
        class TestProtocol(Protocol):
            data = b""
            expected = (ConnACK(session_present=False, return_code=0).serialise() + PubACK(packet_identifier=1).serialise())

            def dataReceived(self_, data):
                self_.data = self_.data + data

                if len(self_.data) == len(self_.expected):
                    self.assertEqual(self_.data, self_.expected)
                    real_reactor.stop()
Ejemplo n.º 3
0
    def test_qos_1_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 1
        p.send_publish(u"hello", 1, 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=1,
                                   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 PubACK, which we don't get a response to
        puback = PubACK(packet_identifier=1)

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

        events = cp.data_received(t.value())
        self.assertEqual(len(events), 0)

        self.assertFalse(t.disconnecting)
Ejemplo n.º 4
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)
Ejemplo n.º 5
0
    def _test_tls_auth(self):
        """
        A MQTT client can connect using mutually authenticated TLS
        authentication.
        """
        reactor, router, server_factory, session_factory = build_mqtt_server()
        real_reactor = selectreactor.SelectReactor()
        logger = make_logger()

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

        endpoint = create_listening_endpoint_from_config({
            "type": "tcp",
            "port": 1099,
            "interface": "0.0.0.0",
            "tls": {
                "certificate": "server.crt",
                "key": "server.key",
                "dhparam": "dhparam",
                "ca_certificates": [
                    "ca.cert.pem",
                    "intermediate.cert.pem"
                ]},
        }, FilePath(__file__).sibling('certs').path, real_reactor, logger)

        client_endpoint = create_connecting_endpoint_from_config({
            "type": "tcp",
            "host": "127.0.0.1",
            "port": 1099,
            "tls": {
                "certificate": "client.crt",
                "hostname": u"localhost",
                "key": "client.key",
                "ca_certificates": [
                    "ca.cert.pem",
                    "intermediate.cert.pem"
                ]},
        }, FilePath(__file__).sibling('certs').path, real_reactor, logger)

        p = []
        l = endpoint.listen(server_factory)

        class TestProtocol(Protocol):
            data = b""
            expected = (ConnACK(session_present=False, return_code=0).serialise() + PubACK(packet_identifier=1).serialise())

            def dataReceived(self_, data):
                self_.data = self_.data + data

                if len(self_.data) == len(self_.expected):
                    self.assertEqual(self_.data, self_.expected)
                    real_reactor.stop()

        @l.addCallback
        def _listening(factory):
            d = client_endpoint.connect(Factory.forProtocol(TestProtocol))

            @d.addCallback
            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())

        lc = LoopingCall(pump.flush)
        lc.clock = real_reactor
        lc.start(0.01)

        def timeout():
            print("Timing out :(")
            real_reactor.stop()
            print(self.logs.log_text.getvalue())

        # Timeout, just in case
        real_reactor.callLater(10, timeout)
        real_reactor.run()

        client_protocol = p[0]

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

        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": {}}])
Ejemplo n.º 6
0
    def test_qos_1_resent_on_disconnect(self):
        """
        If we send a QoS1 Publish and we did not get a PubACK from the client
        before it disconnected, we will resend the Publish packet if it
        connects with a non-clean session.

        Compliance statements: MQTT-4.4.0-1, MQTT-3.3.1-1
        """
        h = BasicHandler()
        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)

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

        # Advance the clock
        r.advance(0.1)

        # We should now get the sent Publish
        expected_publish = Publish(duplicate=False,
                                   qos_level=1,
                                   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), 2)
        self.assertEqual(events[1], expected_publish)

        # Disconnect the client
        t.connected = False
        t.loseConnection()
        p.connectionLost(None)

        r2, t2, p2, cp2 = make_test_items(h)

        # We must NOT have a clean session
        data = (Connect(client_id=u"test123",
                        flags=ConnectFlags(clean_session=False)).serialise())

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

        # The flushing is queued, so we'll have to spin the reactor
        r2.advance(0.1)

        # We should have two events; the ConnACK, and the Publish. The ConnACK
        # MUST come first.
        events = cp2.data_received(t2.value())
        t2.clear()
        self.assertEqual(len(events), 2)
        self.assertIsInstance(events[0], ConnACK)
        self.assertIsInstance(events[1], Publish)

        # The Publish packet must have DUP set to True.
        resent_publish = Publish(duplicate=True,
                                 qos_level=1,
                                 retain=False,
                                 packet_identifier=1,
                                 topic_name=u"hello",
                                 payload=b"some bytes")
        self.assertEqual(events[1], resent_publish)

        # We send the PubACK to this Publish
        puback = PubACK(packet_identifier=1)

        for x in iterbytes(puback.serialise()):
            p2.dataReceived(x)

        events = cp2.data_received(t2.value())
        self.assertEqual(len(events), 0)

        self.assertFalse(t2.disconnecting)