async def test_p2p_api_disconnect_fn(bob, alice): async with P2PAPI().as_behavior().apply(alice): async with P2PAPI().as_behavior().apply(bob): alice_p2p_api = alice.get_logic('p2p', P2PAPI) bob_p2p_api = bob.get_logic('p2p', P2PAPI) alice_p2p_api.disconnect(DisconnectReason.CLIENT_QUITTING) assert alice_p2p_api.local_disconnect_reason is DisconnectReason.CLIENT_QUITTING assert bob_p2p_api.local_disconnect_reason is None
async def test_p2p_api_disconnect_fn_nowait(bob, alice): async with P2PAPI().as_behavior().apply(alice): async with P2PAPI().as_behavior().apply(bob): alice_p2p_api = alice.get_logic('p2p', P2PAPI) bob_p2p_api = bob.get_logic('p2p', P2PAPI) alice_p2p_api.disconnect_nowait(DisconnectReason.CLIENT_QUITTING) await asyncio.wait_for(bob.events.cancelled.wait(), timeout=1) assert alice_p2p_api.remote_disconnect_reason is None assert alice_p2p_api.local_disconnect_reason is DisconnectReason.CLIENT_QUITTING assert bob_p2p_api.remote_disconnect_reason is DisconnectReason.CLIENT_QUITTING assert bob_p2p_api.local_disconnect_reason is None
async def _run(self) -> None: async with AsyncExitStack() as stack: await stack.enter_async_context(P2PAPI().as_behavior().apply( self.connection)) self.p2p_api = self.connection.get_logic('p2p', P2PAPI) for behavior in self.get_behaviors(): if behavior.should_apply_to(self.connection): await stack.enter_async_context( behavior.apply(self.connection)) # setup handler for protocol messages to pass messages to subscribers for protocol in self.connection.get_protocols(): self.connection.add_protocol_handler( type(protocol), self._handle_subscriber_message, ) self.setup_protocol_handlers() # The `boot` process is run in the background to allow the `run` loop # to continue so that all of the Peer APIs can be used within the # `boot` task. self.run_child_service(self.boot_manager) # Trigger the connection to start feeding messages though the handlers self.connection.start_protocol_streams() self.ready.set() await self.cancellation()
async def test_p2p_api_applies_DisconnectIfIdle(bob, alice): p2p_api = P2PAPI() for behavior in p2p_api._behaviors: if isinstance(behavior.logic, DisconnectIfIdle): break else: raise AssertionError("DisconnectIfIdle not among P2PAPI behaviors")
async def test_p2p_api_properties(bob, alice): async with P2PAPI().as_behavior().apply(alice): assert alice.has_logic('p2p') p2p_api = alice.get_logic('p2p', P2PAPI) assert p2p_api.client_version_string == 'bob' assert p2p_api.safe_client_version_string == 'bob'
async def test_p2p_api_triggers_cancellation_on_disconnect(bob, alice): async with P2PAPI().as_behavior().apply(alice): p2p_api = alice.get_logic('p2p', P2PAPI) bob.get_base_protocol().send_disconnect(DisconnectReason.client_quitting) await asyncio.wait_for(alice.events.cancelled.wait(), timeout=1) assert p2p_api.remote_disconnect_reason is DisconnectReason.client_quitting assert p2p_api.local_disconnect_reason is None
async def test_p2p_api_pongs_when_pinged(bob, alice): async with P2PAPI().as_behavior().apply(alice): got_pong = asyncio.Event() async def handle_pong(connection, msg): got_pong.set() bob.add_command_handler(Pong, handle_pong) bob.get_base_protocol().send_ping() await asyncio.wait_for(got_pong.wait(), timeout=1)
async def run(self) -> None: self._start_time = time.monotonic() self.connection.add_command_handler(Disconnect, cast(HandlerFn, self._handle_disconnect)) try: async with contextlib.AsyncExitStack() as stack: fut = await stack.enter_async_context(P2PAPI().as_behavior().apply(self.connection)) futures = [fut] self.p2p_api = self.connection.get_logic('p2p', P2PAPI) for behavior in self.get_behaviors(): if behavior.should_apply_to(self.connection): future = await stack.enter_async_context(behavior.apply(self.connection)) futures.append(future) self.connection.add_msg_handler(self._handle_subscriber_message) self.setup_protocol_handlers() # The `boot` process is run in the background to allow the `run` loop # to continue so that all of the Peer APIs can be used within the # `boot` task. self.manager.run_child_service(self.boot_manager) # Trigger the connection to start feeding messages though the handlers self.connection.start_protocol_streams() self.ready.set() try: await wait_first(futures) except asyncio.CancelledError: raise except BaseException: self.logger.exception("Behavior finished before us, cancelling ourselves") self.manager.cancel() finally: for callback in self._finished_callbacks: callback(self) if (self.p2p_api.local_disconnect_reason is None and self.p2p_api.remote_disconnect_reason is None): self._send_disconnect(DisconnectReason.CLIENT_QUITTING) # We run as a child service of the connection, but we don't want to leave a connection # open if somebody cancels just us, so this ensures the connection gets closed as well. if not self.connection.get_manager().is_cancelled: self.logger.debug("Connection hasn't been cancelled yet, doing so now") self.connection.get_manager().cancel()
def get_behaviors(self) -> Tuple[BehaviorAPI, ...]: return (P2PAPI().as_behavior(), )