示例#1
0
文件: client.py 项目: nfsnfs/amqtt
    def __init__(self, client_id=None, config=None, loop=None):
        self.logger = logging.getLogger(__name__)
        self.config = copy.deepcopy(_defaults)
        if config is not None:
            self.config.update(config)
        if client_id is not None:
            self.client_id = client_id
        else:
            from amqtt.utils import gen_client_id

            self.client_id = gen_client_id()
            self.logger.debug("Using generated client ID : %s" %
                              self.client_id)

        if loop is not None:
            self._loop = loop
        else:
            self._loop = asyncio.get_event_loop()
        self.session = None
        self._handler = None
        self._disconnect_task = None
        self._connected_state = asyncio.Event(loop=self._loop)
        self._no_more_connections = asyncio.Event(loop=self._loop)
        self.extra_headers = {}

        # Init plugins manager
        context = ClientContext()
        context.config = self.config
        self.plugins_manager = PluginManager("amqtt.client.plugins",
                                             context,
                                             loop=self._loop)
        self.client_tasks = deque()
示例#2
0
文件: connect.py 项目: nfsnfs/amqtt
    async def from_stream(
        cls,
        reader: ReaderAdapter,
        fixed_header: MQTTFixedHeader,
        variable_header: ConnectVariableHeader,
    ):
        payload = cls()
        #  Client identifier
        try:
            payload.client_id = await decode_string(reader)
        except NoDataException:
            payload.client_id = None

        if payload.client_id is None or payload.client_id == "":
            # A Server MAY allow a Client to supply a ClientId that has a length of zero bytes
            # [MQTT-3.1.3-6]
            payload.client_id = gen_client_id()
            # indicator to trow exception in case CLEAN_SESSION_FLAG is set to False
            payload.client_id_is_random = True

        # Read will topic, username and password
        if variable_header.will_flag:
            try:
                payload.will_topic = await decode_string(reader)
                payload.will_message = await decode_data_with_length(reader)
            except NoDataException:
                payload.will_topic = None
                payload.will_message = None

        if variable_header.username_flag:
            try:
                payload.username = await decode_string(reader)
            except NoDataException:
                payload.username = None

        if variable_header.password_flag:
            try:
                payload.password = await decode_string(reader)
            except NoDataException:
                payload.password = None

        return payload
示例#3
0
文件: broker.py 项目: nfsnfs/amqtt
    async def client_connected(
        self, listener_name, reader: ReaderAdapter, writer: WriterAdapter
    ):
        # Wait for connection available on listener
        server = self._servers.get(listener_name, None)
        if not server:
            raise BrokerException("Invalid listener name '%s'" % listener_name)
        await server.acquire_connection()

        remote_address, remote_port = writer.get_peer_info()
        self.logger.info(
            "Connection from %s:%d on listener '%s'"
            % (remote_address, remote_port, listener_name)
        )

        # Wait for first packet and expect a CONNECT
        try:
            handler, client_session = await BrokerProtocolHandler.init_from_connect(
                reader, writer, self.plugins_manager, loop=self._loop
            )
        except HBMQTTException as exc:
            self.logger.warning(
                "[MQTT-3.1.0-1] %s: Can't read first packet an CONNECT: %s"
                % (format_client_message(address=remote_address, port=remote_port), exc)
            )
            # await writer.close()
            self.logger.debug("Connection closed")
            return
        except MQTTException as me:
            self.logger.error(
                "Invalid connection from %s : %s"
                % (format_client_message(address=remote_address, port=remote_port), me)
            )
            await writer.close()
            self.logger.debug("Connection closed")
            return

        if client_session.clean_session:
            # Delete existing session and create a new one
            if client_session.client_id is not None and client_session.client_id != "":
                self.delete_session(client_session.client_id)
            else:
                client_session.client_id = gen_client_id()
            client_session.parent = 0
        else:
            # Get session from cache
            if client_session.client_id in self._sessions:
                self.logger.debug(
                    "Found old session %s"
                    % repr(self._sessions[client_session.client_id])
                )
                (client_session, h) = self._sessions[client_session.client_id]
                client_session.parent = 1
            else:
                client_session.parent = 0
        if client_session.keep_alive > 0:
            client_session.keep_alive += self.config["timeout-disconnect-delay"]
        self.logger.debug("Keep-alive timeout=%d" % client_session.keep_alive)

        handler.attach(client_session, reader, writer)
        self._sessions[client_session.client_id] = (client_session, handler)

        authenticated = await self.authenticate(
            client_session, self.listeners_config[listener_name]
        )
        if not authenticated:
            await writer.close()
            server.release_connection()  # Delete client from connections list
            return

        while True:
            try:
                client_session.transitions.connect()
                break
            except (MachineError, ValueError):
                # Backwards compat: MachineError is raised by transitions < 0.5.0.
                self.logger.warning(
                    "Client %s is reconnecting too quickly, make it wait"
                    % client_session.client_id
                )
                # Wait a bit may be client is reconnecting too fast
                await asyncio.sleep(1, loop=self._loop)
        await handler.mqtt_connack_authorize(authenticated)

        await self.plugins_manager.fire_event(
            EVENT_BROKER_CLIENT_CONNECTED, client_id=client_session.client_id
        )

        self.logger.debug("%s Start messages handling" % client_session.client_id)
        await handler.start()
        self.logger.debug(
            "Retained messages queue size: %d"
            % client_session.retained_messages.qsize()
        )
        await self.publish_session_retained_messages(client_session)

        # Init and start loop for handling client messages (publish, subscribe/unsubscribe, disconnect)
        disconnect_waiter = asyncio.ensure_future(
            handler.wait_disconnect(), loop=self._loop
        )
        subscribe_waiter = asyncio.ensure_future(
            handler.get_next_pending_subscription(), loop=self._loop
        )
        unsubscribe_waiter = asyncio.ensure_future(
            handler.get_next_pending_unsubscription(), loop=self._loop
        )
        wait_deliver = asyncio.ensure_future(
            handler.mqtt_deliver_next_message(), loop=self._loop
        )
        connected = True
        while connected:
            try:
                done, pending = await asyncio.wait(
                    [
                        disconnect_waiter,
                        subscribe_waiter,
                        unsubscribe_waiter,
                        wait_deliver,
                    ],
                    return_when=asyncio.FIRST_COMPLETED,
                    loop=self._loop,
                )
                if disconnect_waiter in done:
                    result = disconnect_waiter.result()
                    self.logger.debug(
                        "%s Result from wait_diconnect: %s"
                        % (client_session.client_id, result)
                    )
                    if result is None:
                        self.logger.debug("Will flag: %s" % client_session.will_flag)
                        # Connection closed anormally, send will message
                        if client_session.will_flag:
                            self.logger.debug(
                                "Client %s disconnected abnormally, sending will message"
                                % format_client_message(client_session)
                            )
                            await self._broadcast_message(
                                client_session,
                                client_session.will_topic,
                                client_session.will_message,
                                client_session.will_qos,
                            )
                            if client_session.will_retain:
                                self.retain_message(
                                    client_session,
                                    client_session.will_topic,
                                    client_session.will_message,
                                    client_session.will_qos,
                                )
                    self.logger.debug(
                        "%s Disconnecting session" % client_session.client_id
                    )
                    await self._stop_handler(handler)
                    client_session.transitions.disconnect()
                    await self.plugins_manager.fire_event(
                        EVENT_BROKER_CLIENT_DISCONNECTED,
                        client_id=client_session.client_id,
                    )
                    connected = False
                if unsubscribe_waiter in done:
                    self.logger.debug(
                        "%s handling unsubscription" % client_session.client_id
                    )
                    unsubscription = unsubscribe_waiter.result()
                    for topic in unsubscription["topics"]:
                        self._del_subscription(topic, client_session)
                        await self.plugins_manager.fire_event(
                            EVENT_BROKER_CLIENT_UNSUBSCRIBED,
                            client_id=client_session.client_id,
                            topic=topic,
                        )
                    await handler.mqtt_acknowledge_unsubscription(
                        unsubscription["packet_id"]
                    )
                    unsubscribe_waiter = asyncio.Task(
                        handler.get_next_pending_unsubscription(), loop=self._loop
                    )
                if subscribe_waiter in done:
                    self.logger.debug(
                        "%s handling subscription" % client_session.client_id
                    )
                    subscriptions = subscribe_waiter.result()
                    return_codes = []
                    for subscription in subscriptions["topics"]:
                        result = await self.add_subscription(
                            subscription, client_session
                        )
                        return_codes.append(result)
                    await handler.mqtt_acknowledge_subscription(
                        subscriptions["packet_id"], return_codes
                    )
                    for index, subscription in enumerate(subscriptions["topics"]):
                        if return_codes[index] != 0x80:
                            await self.plugins_manager.fire_event(
                                EVENT_BROKER_CLIENT_SUBSCRIBED,
                                client_id=client_session.client_id,
                                topic=subscription[0],
                                qos=subscription[1],
                            )
                            await self.publish_retained_messages_for_subscription(
                                subscription, client_session
                            )
                    subscribe_waiter = asyncio.Task(
                        handler.get_next_pending_subscription(), loop=self._loop
                    )
                    self.logger.debug(repr(self._subscriptions))
                if wait_deliver in done:
                    if self.logger.isEnabledFor(logging.DEBUG):
                        self.logger.debug(
                            "%s handling message delivery" % client_session.client_id
                        )
                    app_message = wait_deliver.result()
                    if not app_message.topic:
                        self.logger.warning(
                            "[MQTT-4.7.3-1] - %s invalid TOPIC sent in PUBLISH message, closing connection"
                            % client_session.client_id
                        )
                        break
                    if "#" in app_message.topic or "+" in app_message.topic:
                        self.logger.warning(
                            "[MQTT-3.3.2-2] - %s invalid TOPIC sent in PUBLISH message, closing connection"
                            % client_session.client_id
                        )
                        break
                    await self.plugins_manager.fire_event(
                        EVENT_BROKER_MESSAGE_RECEIVED,
                        client_id=client_session.client_id,
                        message=app_message,
                    )
                    await self._broadcast_message(
                        client_session, app_message.topic, app_message.data
                    )
                    if app_message.publish_packet.retain_flag:
                        self.retain_message(
                            client_session,
                            app_message.topic,
                            app_message.data,
                            app_message.qos,
                        )
                    wait_deliver = asyncio.Task(
                        handler.mqtt_deliver_next_message(), loop=self._loop
                    )
            except asyncio.CancelledError:
                self.logger.debug("Client loop cancelled")
                break
        disconnect_waiter.cancel()
        subscribe_waiter.cancel()
        unsubscribe_waiter.cancel()
        wait_deliver.cancel()

        self.logger.debug("%s Client disconnected" % client_session.client_id)
        server.release_connection()
示例#4
0
def test_client_id():
    client_id = utils.gen_client_id()
    assert isinstance(client_id, str)
    assert client_id.startswith("amqtt/")