async def test_coro(): async with create_broker( test_config, plugin_namespace="distmqtt.test.plugins" ) as broker: broker.plugins_manager._tg = broker._tg self.assertTrue(broker.transitions.is_started()) async with await anyio.connect_tcp("127.0.0.1", PORT) as conn: stream = StreamAdapter(conn) vh = ConnectVariableHeader() payload = ConnectPayload() vh.keep_alive = 10 vh.clean_session_flag = False vh.will_retain_flag = False payload.client_id = "test_id" connect = ConnectPacket(vh=vh, payload=payload) await connect.to_stream(stream) await ConnackPacket.from_stream(stream) publish_1 = PublishPacket.build("/test", b"data", 1, False, QOS_2, False) await publish_1.to_stream(stream) await PubrecPacket.from_stream(stream) publish_dup = PublishPacket.build("/test", b"data", 1, True, QOS_2, False) await publish_dup.to_stream(stream) await PubrecPacket.from_stream(stream) pubrel = PubrelPacket.build(1) await pubrel.to_stream(stream) # await PubcompPacket.from_stream(stream) disconnect = DisconnectPacket() await disconnect.to_stream(stream)
async def test_coro(): async with create_broker( test_config, plugin_namespace="distmqtt.test.plugins") as broker: broker.plugins_manager._tg = broker._tg self.assertTrue(broker.transitions.is_started()) async with await anyio.connect_tcp("127.0.0.1", 1883) as conn: stream = StreamAdapter(conn) vh = ConnectVariableHeader() payload = ConnectPayload() vh.keep_alive = 10 vh.clean_session_flag = False vh.will_retain_flag = False vh.will_flag = True vh.will_qos = QOS_0 payload.client_id = 'test_id' payload.will_message = b'test' payload.will_topic = '/topic' connect = ConnectPacket(vh=vh, payload=payload) await connect.to_stream(stream) await ConnackPacket.from_stream(stream) disconnect = DisconnectPacket() await disconnect.to_stream(stream) self.assertTrue(broker.transitions.is_stopped()) self.assertDictEqual(broker._sessions, {})
async def stream_connected(self, conn, listener_name): await self.client_connected(listener_name, StreamAdapter(conn))
async def _connect_coro(self): kwargs = dict() # Decode URI attributes uri_attributes = urlparse(self.session.broker_uri) scheme = uri_attributes.scheme secure = True if scheme in ("mqtts", "wss") else False self.session.username = (self.session.username if self.session.username else uri_attributes.username) self.session.password = (self.session.password if self.session.password else uri_attributes.password) self.session.remote_address = uri_attributes.hostname self.session.remote_port = uri_attributes.port if scheme in ("mqtt", "mqtts") and not self.session.remote_port: self.session.remote_port = 8883 if scheme == "mqtts" else 1883 if scheme in ("ws", "wss") and not self.session.remote_port: self.session.remote_port = 443 if scheme == "wss" else 80 if scheme in ("ws", "wss"): # Rewrite URI to conform to https://tools.ietf.org/html/rfc6455#section-3 uri = ( scheme, self.session.remote_address + ":" + str(self.session.remote_port), uri_attributes[2], uri_attributes[3], uri_attributes[4], uri_attributes[5], ) self.session.broker_uri = urlunparse(uri) # Init protocol handler # if not self._handler: self._handler = ClientProtocolHandler(self.plugins_manager) if secure: sc = ssl.create_default_context( ssl.Purpose.SERVER_AUTH, cafile=self.session.cafile, capath=self.session.capath, cadata=self.session.cadata, ) if "certfile" in self.config and "keyfile" in self.config: sc.load_cert_chain(self.config["certfile"], self.config["keyfile"]) if "check_hostname" in self.config and isinstance( self.config["check_hostname"], bool): sc.check_hostname = self.config["check_hostname"] kwargs["ssl_context"] = sc kwargs["autostart_tls"] = True try: adapter = None self._connected_state.clear() # Open connection if scheme in ("mqtt", "mqtts"): conn = await anyio.connect_tcp(self.session.remote_address, self.session.remote_port, **kwargs) if secure: await conn.start_tls() adapter = StreamAdapter(conn) elif scheme in ("ws", "wss"): if kwargs.pop("autostart_tls", False): kwargs["ssl"] = kwargs.pop("ssl_context") websocket = await create_websocket(self.session.broker_uri, subprotocols=["mqtt"], headers=self.extra_headers, **kwargs) adapter = WebSocketsAdapter(websocket) # Start MQTT protocol await self._handler.attach(self.session, adapter) try: return_code = await self._handler.mqtt_connect() except NoDataException: self.logger.warning("Connection broken by broker") raise ConnectException("Connection broken by broker") if return_code != CONNECTION_ACCEPTED: self.session.transitions.disconnect() self.logger.warning("Connection rejected with code '%s'", return_code) raise ConnectException("Connection rejected by broker", return_code) # Handle MQTT protocol await self._handler.start() self.session.transitions.connect() await self._connected_state.set() topics = [] if self._subscriptions is not None: for s in self._subscriptions.values(): topics.append(("/".join(s.topic), s.qos)) if topics: await self.subscribe(topics) self.logger.debug( "connected to %s:%s", self.session.remote_address, self.session.remote_port, ) return return_code except ProtocolError as exc: self.logger.warning( "connection failed: invalid websocket handshake") self.session.transitions.disconnect() raise ConnectException( "connection failed: invalid websocket handshake") from exc except (ProtocolHandlerException, ConnectionError, OSError) as exc: self.logger.warning("MQTT connection failed") self.session.transitions.disconnect() raise ConnectException from exc
async def _connect_coro(self): kwargs = dict() # Decode URI attributes uri_attributes = urlparse(self.session.broker_uri) scheme = uri_attributes.scheme secure = True if scheme in ('mqtts', 'wss') else False self.session.username = self.session.username if self.session.username else uri_attributes.username self.session.password = self.session.password if self.session.password else uri_attributes.password self.session.remote_address = uri_attributes.hostname self.session.remote_port = uri_attributes.port if scheme in ('mqtt', 'mqtts') and not self.session.remote_port: self.session.remote_port = 8883 if scheme == 'mqtts' else 1883 if scheme in ('ws', 'wss') and not self.session.remote_port: self.session.remote_port = 443 if scheme == 'wss' else 80 if scheme in ('ws', 'wss'): # Rewrite URI to conform to https://tools.ietf.org/html/rfc6455#section-3 uri = (scheme, self.session.remote_address + ":" + str(self.session.remote_port), uri_attributes[2], uri_attributes[3], uri_attributes[4], uri_attributes[5]) self.session.broker_uri = urlunparse(uri) # Init protocol handler #if not self._handler: self._handler = ClientProtocolHandler(self.plugins_manager) if secure: sc = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=self.session.cafile, capath=self.session.capath, cadata=self.session.cadata) if 'certfile' in self.config and 'keyfile' in self.config: sc.load_cert_chain(self.config['certfile'], self.config['keyfile']) if 'check_hostname' in self.config and isinstance( self.config['check_hostname'], bool): sc.check_hostname = self.config['check_hostname'] kwargs['ssl_context'] = sc kwargs['autostart_tls'] = True try: adapter = None self._connected_state.clear() # Open connection if scheme in ('mqtt', 'mqtts'): conn = \ await anyio.connect_tcp( self.session.remote_address, self.session.remote_port, **kwargs) if secure: await conn.start_tls() adapter = StreamAdapter(conn) elif scheme in ('ws', 'wss'): if kwargs.pop('autostart_tls', False): kwargs['ssl'] = kwargs.pop('ssl_context') websocket = await create_websocket(self.session.broker_uri, subprotocols=['mqtt'], headers=self.extra_headers, **kwargs) adapter = WebSocketsAdapter(websocket) # Start MQTT protocol await self._handler.attach(self.session, adapter) try: return_code = await self._handler.mqtt_connect() except NoDataException: self.logger.warning("Connection broken by broker") exc = ConnectException("Connection broken by broker") raise exc if return_code is not CONNECTION_ACCEPTED: self.session.transitions.disconnect() self.logger.warning("Connection rejected with code '%s'", return_code) exc = ConnectException("Connection rejected by broker") exc.return_code = return_code raise exc else: # Handle MQTT protocol await self._handler.start() self.session.transitions.connect() await self._connected_state.set() self.logger.debug("connected to %s:%s", self.session.remote_address, self.session.remote_port) return return_code except ProtocolError as exc: self.logger.warning( "connection failed: invalid websocket handshake") self.session.transitions.disconnect() raise ConnectException( "connection failed: invalid websocket handshake") from exc except (ProtocolHandlerException, ConnectionError, OSError) as exc: self.logger.warning("MQTT connection failed") self.session.transitions.disconnect() raise ConnectException from exc
async def init_from_connect(cls, stream: StreamAdapter, plugins_manager): """ :param stream: :param plugins_manager: :return: """ remote_address, remote_port = stream.get_peer_info() try: connect = await ConnectPacket.from_stream(stream) except NoDataException: raise MQTTException("Client closed the connection") logger.debug("< B %r", connect) await plugins_manager.fire_event(EVENT_MQTT_PACKET_RECEIVED, packet=connect) # this shouldn't be required anymore since broker generates for each client a random client_id if not provided # [MQTT-3.1.3-6] if connect.payload.client_id is None: raise MQTTException( "[[MQTT-3.1.3-3]] : Client identifier must be present") if connect.variable_header.will_flag: if connect.payload.will_topic is None or connect.payload.will_message is None: raise MQTTException( "will flag set, but will topic/message not present in payload" ) if connect.variable_header.reserved_flag: raise MQTTException( "[MQTT-3.1.2-3] CONNECT reserved flag must be set to 0") if connect.proto_name != "MQTT": raise MQTTException( '[MQTT-3.1.2-1] Incorrect protocol name: "%s"' % connect.proto_name) connack = None error_msg = None if connect.proto_level != 4: # only MQTT 3.1.1 supported error_msg = "Invalid protocol from %s: %d" % ( format_client_message(address=remote_address, port=remote_port), connect.proto_level, ) connack = ConnackPacket.build(0, UNACCEPTABLE_PROTOCOL_VERSION ) # [MQTT-3.2.2-4] session_parent=0 elif not connect.username_flag and connect.password_flag: connack = ConnackPacket.build( 0, BAD_USERNAME_PASSWORD) # [MQTT-3.1.2-22] elif connect.username_flag and not connect.password_flag: connack = ConnackPacket.build( 0, BAD_USERNAME_PASSWORD) # [MQTT-3.1.2-22] elif connect.username_flag and connect.username is None: error_msg = "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.password_flag and connect.password is None: error_msg = "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.clean_session_flag is False and ( connect.payload.client_id_is_random): error_msg = ( "[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) if connack is not None: logger.debug("B > %r", connack) await plugins_manager.fire_event(EVENT_MQTT_PACKET_SENT, packet=connack) await connack.to_stream(stream) await stream.close() raise MQTTException(error_msg) incoming_session = Session(plugins_manager) incoming_session.client_id = connect.client_id incoming_session.clean_session = connect.clean_session_flag incoming_session.will_flag = connect.will_flag incoming_session.will_retain = connect.will_retain_flag incoming_session.will_qos = connect.will_qos incoming_session.will_topic = connect.will_topic incoming_session.will_message = connect.will_message incoming_session.username = connect.username incoming_session.password = connect.password if connect.keep_alive > 0: incoming_session.keep_alive = connect.keep_alive else: incoming_session.keep_alive = 0 handler = cls(plugins_manager) return handler, incoming_session
def adapt(conn): return StreamAdapter(conn)