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())
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()
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)
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)
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": {}}])
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)