def client_connected(self, listener_name, reader: ReaderAdapter, writer: WriterAdapter): # Wait for connection available server = self._servers[listener_name] yield from server.acquire_connection() remote_address, remote_port = writer.get_peer_info() self.logger.debug("Connection from %s:%d on listener '%s'" % (remote_address, remote_port, listener_name)) # Wait for first packet and expect a CONNECT connect = None try: connect = yield from ConnectPacket.from_stream(reader) self.logger.debug(" <-in-- " + repr(connect)) self.check_connect(connect) except HBMQTTException as exc: self.logger.warn("[MQTT-3.1.0-1] %s: Can't read first packet an CONNECT: %s" % (format_client_message(address=remote_address, port=remote_port), exc)) yield from writer.close() self.logger.debug("Connection closed") return except BrokerException as be: self.logger.error('Invalid connection from %s : %s' % (format_client_message(address=remote_address, port=remote_port), be)) yield from writer.close() self.logger.debug("Connection closed") return connack = None if connect.variable_header.proto_level != 4: # only MQTT 3.1.1 supported self.logger.error('Invalid protocol from %s: %d' % (format_client_message(address=remote_address, port=remote_port), connect.variable_header.protocol_level)) connack = ConnackPacket.build(0, UNACCEPTABLE_PROTOCOL_VERSION) # [MQTT-3.2.2-4] session_parent=0 elif connect.variable_header.username_flag and connect.payload.username is None: self.logger.error('Invalid username from %s' % (format_client_message(address=remote_address, port=remote_port))) connack = ConnackPacket.build(0, BAD_USERNAME_PASSWORD) # [MQTT-3.2.2-4] session_parent=0 elif connect.variable_header.password_flag and connect.payload.password is None: self.logger.error('Invalid password %s' % (format_client_message(address=remote_address, port=remote_port))) connack = ConnackPacket.build(0, BAD_USERNAME_PASSWORD) # [MQTT-3.2.2-4] session_parent=0 elif connect.variable_header.clean_session_flag is False and connect.payload.client_id is None: self.logger.error('[MQTT-3.1.3-8] [MQTT-3.1.3-9] %s: No client Id provided (cleansession=0)' % format_client_message(address=remote_address, port=remote_port)) connack = ConnackPacket.build(0, IDENTIFIER_REJECTED) self.logger.debug(" -out-> " + repr(connack)) if connack is not None: self.logger.debug(" -out-> " + repr(connack)) yield from connack.to_stream(writer) yield from writer.close() return client_session = None self.logger.debug("Clean session={0}".format(connect.variable_header.clean_session_flag)) self.logger.debug("known sessions={0}".format(self._sessions)) client_id = connect.payload.client_id if connect.variable_header.clean_session_flag: # Delete existing session and create a new one if client_id is not None: self.delete_session(client_id) client_session = Session() client_session.parent = 0 client_session.client_id = client_id self._sessions[client_id] = client_session else: # Get session from cache if client_id in self._sessions: self.logger.debug("Found old session %s" % repr(self._sessions[client_id])) client_session = self._sessions[client_id] client_session.parent = 1 else: client_session = Session() client_session.client_id = client_id self._sessions[client_id] = client_session client_session.parent = 0 if client_session.client_id is None: # Generate client ID client_session.client_id = gen_client_id() client_session.remote_address = remote_address client_session.remote_port = remote_port client_session.clean_session = connect.variable_header.clean_session_flag client_session.will_flag = connect.variable_header.will_flag client_session.will_retain = connect.variable_header.will_retain_flag client_session.will_qos = connect.variable_header.will_qos client_session.will_topic = connect.payload.will_topic client_session.will_message = connect.payload.will_message client_session.username = connect.payload.username client_session.password = connect.payload.password client_session.client_id = connect.payload.client_id if connect.variable_header.keep_alive > 0: client_session.keep_alive = connect.variable_header.keep_alive + self.config['timeout-disconnect-delay'] else: client_session.keep_alive = 0 client_session.publish_retry_delay = self.config['publish-retry-delay'] client_session.reader = reader client_session.writer = writer if self.authenticate(client_session): connack = ConnackPacket.build(client_session.parent, CONNECTION_ACCEPTED) self.logger.info('%s : connection accepted' % format_client_message(session=client_session)) self.logger.debug(" -out-> " + repr(connack)) yield from connack.to_stream(writer) else: connack = ConnackPacket.build(client_session.parent, NOT_AUTHORIZED) self.logger.info('%s : connection refused' % format_client_message(session=client_session)) self.logger.debug(" -out-> " + repr(connack)) yield from connack.to_stream(writer) yield from writer.close() return client_session.transitions.connect() handler = self._init_handler(reader, writer, client_session) self.logger.debug("%s Start messages handling" % client_session.client_id) yield from handler.start() self.logger.debug("Retained messages queue size: %d" % client_session.retained_messages.qsize()) yield from self.publish_session_retained_messages(client_session) self.logger.debug("%s Wait for disconnect" % client_session.client_id) connected = True wait_disconnect = asyncio.Task(handler.wait_disconnect()) wait_subscription = asyncio.Task(handler.get_next_pending_subscription()) wait_unsubscription = asyncio.Task(handler.get_next_pending_unsubscription()) wait_deliver = asyncio.Task(handler.mqtt_deliver_next_message()) while connected: done, pending = yield from asyncio.wait( [wait_disconnect, wait_subscription, wait_unsubscription, wait_deliver], return_when=asyncio.FIRST_COMPLETED) if wait_disconnect in done: result = wait_disconnect.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)) yield from self.broadcast_application_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) connected = False if wait_unsubscription in done: self.logger.debug("%s handling unsubscription" % client_session.client_id) unsubscription = wait_unsubscription.result() for topic in unsubscription['topics']: self.del_subscription(topic, client_session) yield from handler.mqtt_acknowledge_unsubscription(unsubscription['packet_id']) wait_unsubscription = asyncio.Task(handler.get_next_pending_unsubscription()) if wait_subscription in done: self.logger.debug("%s handling subscription" % client_session.client_id) subscriptions = wait_subscription.result() return_codes = [] for subscription in subscriptions['topics']: return_codes.append(self.add_subscription(subscription, client_session)) yield from handler.mqtt_acknowledge_subscription(subscriptions['packet_id'], return_codes) for index, subscription in enumerate(subscriptions['topics']): if return_codes[index] != 0x80: yield from self.publish_retained_messages_for_subscription(subscription, client_session) wait_subscription = asyncio.Task(handler.get_next_pending_subscription()) self.logger.debug(repr(self._subscriptions)) if wait_deliver in done: self.logger.debug("%s handling message delivery" % client_session.client_id) publish_packet = wait_deliver.result() packet_id = publish_packet.variable_header.packet_id topic_name = publish_packet.variable_header.topic_name data = publish_packet.payload.data yield from self.broadcast_application_message(client_session, topic_name, data) if publish_packet.retain_flag: self.retain_message(client_session, topic_name, data) # Acknowledge message delivery yield from handler.mqtt_acknowledge_delivery(packet_id) wait_deliver = asyncio.Task(handler.mqtt_deliver_next_message()) wait_subscription.cancel() wait_unsubscription.cancel() wait_deliver.cancel() self.logger.debug("%s Client disconnecting" % client_session.client_id) yield from self._stop_handler(handler) client_session.transitions.disconnect() yield from writer.close() self.logger.debug("%s Session disconnected" % client_session.client_id) server.release_connection()
def client_connected(self, listener_name, reader: ReaderAdapter, writer: WriterAdapter): # Wait for connection available server = self._servers[listener_name] yield from server.acquire_connection() remote_address, remote_port = writer.get_peer_info() self.logger.debug("Connection from %s:%d on listener '%s'" % (remote_address, remote_port, listener_name)) # Wait for first packet and expect a CONNECT connect = None try: connect = yield from ConnectPacket.from_stream(reader) self.logger.debug(" <-in-- " + repr(connect)) self.check_connect(connect) except HBMQTTException as exc: self.logger.warn( "[MQTT-3.1.0-1] %s: Can't read first packet an CONNECT: %s" % (format_client_message(address=remote_address, port=remote_port), exc)) yield from writer.close() self.logger.debug("Connection closed") return except BrokerException as be: self.logger.error('Invalid connection from %s : %s' % (format_client_message(address=remote_address, port=remote_port), be)) yield from writer.close() self.logger.debug("Connection closed") return connack = None if connect.variable_header.proto_level != 4: # only MQTT 3.1.1 supported self.logger.error( 'Invalid protocol from %s: %d' % (format_client_message( address=remote_address, port=remote_port), connect.variable_header.protocol_level)) connack = ConnackPacket.build(0, UNACCEPTABLE_PROTOCOL_VERSION ) # [MQTT-3.2.2-4] session_parent=0 elif connect.variable_header.username_flag and connect.payload.username is None: self.logger.error('Invalid username from %s' % (format_client_message(address=remote_address, port=remote_port))) connack = ConnackPacket.build( 0, BAD_USERNAME_PASSWORD) # [MQTT-3.2.2-4] session_parent=0 elif connect.variable_header.password_flag and connect.payload.password is None: self.logger.error('Invalid password %s' % (format_client_message( address=remote_address, port=remote_port))) connack = ConnackPacket.build( 0, BAD_USERNAME_PASSWORD) # [MQTT-3.2.2-4] session_parent=0 elif connect.variable_header.clean_session_flag is False and connect.payload.client_id is None: self.logger.error( '[MQTT-3.1.3-8] [MQTT-3.1.3-9] %s: No client Id provided (cleansession=0)' % format_client_message(address=remote_address, port=remote_port)) connack = ConnackPacket.build(0, IDENTIFIER_REJECTED) self.logger.debug(" -out-> " + repr(connack)) if connack is not None: self.logger.debug(" -out-> " + repr(connack)) yield from connack.to_stream(writer) yield from writer.close() return client_session = None self.logger.debug("Clean session={0}".format( connect.variable_header.clean_session_flag)) self.logger.debug("known sessions={0}".format(self._sessions)) client_id = connect.payload.client_id if connect.variable_header.clean_session_flag: # Delete existing session and create a new one if client_id is not None: self.delete_session(client_id) client_session = Session() client_session.parent = 0 client_session.client_id = client_id self._sessions[client_id] = client_session else: # Get session from cache if client_id in self._sessions: self.logger.debug("Found old session %s" % repr(self._sessions[client_id])) client_session = self._sessions[client_id] client_session.parent = 1 else: client_session = Session() client_session.client_id = client_id self._sessions[client_id] = client_session client_session.parent = 0 if client_session.client_id is None: # Generate client ID client_session.client_id = gen_client_id() client_session.remote_address = remote_address client_session.remote_port = remote_port client_session.clean_session = connect.variable_header.clean_session_flag client_session.will_flag = connect.variable_header.will_flag client_session.will_retain = connect.variable_header.will_retain_flag client_session.will_qos = connect.variable_header.will_qos client_session.will_topic = connect.payload.will_topic client_session.will_message = connect.payload.will_message client_session.username = connect.payload.username client_session.password = connect.payload.password client_session.client_id = connect.payload.client_id if connect.variable_header.keep_alive > 0: client_session.keep_alive = connect.variable_header.keep_alive + self.config[ 'timeout-disconnect-delay'] else: client_session.keep_alive = 0 client_session.publish_retry_delay = self.config['publish-retry-delay'] client_session.reader = reader client_session.writer = writer if self.authenticate(client_session): connack = ConnackPacket.build(client_session.parent, CONNECTION_ACCEPTED) self.logger.info('%s : connection accepted' % format_client_message(session=client_session)) self.logger.debug(" -out-> " + repr(connack)) yield from connack.to_stream(writer) else: connack = ConnackPacket.build(client_session.parent, NOT_AUTHORIZED) self.logger.info('%s : connection refused' % format_client_message(session=client_session)) self.logger.debug(" -out-> " + repr(connack)) yield from connack.to_stream(writer) yield from writer.close() return client_session.transitions.connect() handler = self._init_handler(reader, writer, client_session) self.logger.debug("%s Start messages handling" % client_session.client_id) yield from handler.start() self.logger.debug("Retained messages queue size: %d" % client_session.retained_messages.qsize()) yield from self.publish_session_retained_messages(client_session) self.logger.debug("%s Wait for disconnect" % client_session.client_id) connected = True wait_disconnect = asyncio.Task(handler.wait_disconnect()) wait_subscription = asyncio.Task( handler.get_next_pending_subscription()) wait_unsubscription = asyncio.Task( handler.get_next_pending_unsubscription()) wait_deliver = asyncio.Task(handler.mqtt_deliver_next_message()) while connected: done, pending = yield from asyncio.wait( [ wait_disconnect, wait_subscription, wait_unsubscription, wait_deliver ], return_when=asyncio.FIRST_COMPLETED) if wait_disconnect in done: result = wait_disconnect.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)) yield from self.broadcast_application_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) connected = False if wait_unsubscription in done: self.logger.debug("%s handling unsubscription" % client_session.client_id) unsubscription = wait_unsubscription.result() for topic in unsubscription['topics']: self.del_subscription(topic, client_session) yield from handler.mqtt_acknowledge_unsubscription( unsubscription['packet_id']) wait_unsubscription = asyncio.Task( handler.get_next_pending_unsubscription()) if wait_subscription in done: self.logger.debug("%s handling subscription" % client_session.client_id) subscriptions = wait_subscription.result() return_codes = [] for subscription in subscriptions['topics']: return_codes.append( self.add_subscription(subscription, client_session)) yield from handler.mqtt_acknowledge_subscription( subscriptions['packet_id'], return_codes) for index, subscription in enumerate(subscriptions['topics']): if return_codes[index] != 0x80: yield from self.publish_retained_messages_for_subscription( subscription, client_session) wait_subscription = asyncio.Task( handler.get_next_pending_subscription()) self.logger.debug(repr(self._subscriptions)) if wait_deliver in done: self.logger.debug("%s handling message delivery" % client_session.client_id) publish_packet = wait_deliver.result() packet_id = publish_packet.variable_header.packet_id topic_name = publish_packet.variable_header.topic_name data = publish_packet.payload.data yield from self.broadcast_application_message( client_session, topic_name, data) if publish_packet.retain_flag: self.retain_message(client_session, topic_name, data) # Acknowledge message delivery yield from handler.mqtt_acknowledge_delivery(packet_id) wait_deliver = asyncio.Task( handler.mqtt_deliver_next_message()) wait_subscription.cancel() wait_unsubscription.cancel() wait_deliver.cancel() self.logger.debug("%s Client disconnecting" % client_session.client_id) yield from self._stop_handler(handler) client_session.transitions.disconnect() yield from writer.close() self.logger.debug("%s Session disconnected" % client_session.client_id) server.release_connection()