def __init__( self, run_mode: RunMode = RunMode.THREAD, loop: Optional[asyncio.AbstractEventLoop] = None, ssl_verify: Optional[Union[bool, str]] = None, ssl_cert: Optional[Union[str, Sequence[str]]] = None, ) -> None: """Instantiate a client using the given run_mode. If you do not pass in an event loop, then either a shared loop in a separate thread (THREAD mode) or the default asyncio event loop (FULL_ASYNCIO mode) will be used. Not passing in an event loop will make sure we share the :py:class:`aiohttp.ClientSession` object between AsyncioClient instances. :param ssl_verify: Set to False to disable SSL certificate validation. Provide the path to a CA bundle if you need to use a custom one. :param ssl_cert: Provide a client-side certificate to use. Either a sequence of strings pointing to the certificate (1) and the private key (2), or a string pointing to the combined certificate and key. """ self.run_mode = run_mode if self.run_mode == RunMode.THREAD: self.loop = loop or get_thread_loop() self.run_coroutine_func = asyncio.run_coroutine_threadsafe # type: Callable self.response_adapter = AioHTTPResponseAdapter self.bravado_future_class = HttpFuture self.future_adapter = FutureAdapter # type: Type[BaseFutureAdapter] elif run_mode == RunMode.FULL_ASYNCIO: from aiobravado.http_future import HttpFuture as AsyncioHttpFuture self.loop = loop or asyncio.get_event_loop() self.run_coroutine_func = asyncio.ensure_future self.response_adapter = AsyncioHTTPResponseAdapter self.bravado_future_class = AsyncioHttpFuture self.future_adapter = AsyncioFutureAdapter else: raise ValueError("Don't know how to handle run mode {}".format( str(run_mode))) # don't use the shared client_session if we've been passed an explicit loop argument if loop: self.client_session = aiohttp.ClientSession(loop=loop) else: self.client_session = get_client_session(self.loop) # translate the requests-type SSL options to a ssl.SSLContext object as used by aiohttp. # see https://aiohttp.readthedocs.io/en/stable/client_advanced.html#ssl-control-for-tcp-sockets if isinstance(ssl_verify, str) or ssl_cert: self.ssl_verify = None # type: Optional[bool] cafile = None if isinstance(ssl_verify, str): cafile = ssl_verify self.ssl_context = ssl.create_default_context( cafile=cafile) # type: Optional[ssl.SSLContext] if ssl_cert: if isinstance(ssl_cert, str): ssl_cert = [ssl_cert] self.ssl_context.load_cert_chain(*ssl_cert) else: self.ssl_verify = ssl_verify self.ssl_context = None
def test_client_from_asyncio(integration_server): """Let's make sure that the event loop for our HTTP client that runs in a different thread behaves properly with the 'standard' asyncio loop that people would normally use when doing asynchronous programming. While we're at it, let's also make sure two instances of AsyncioClient work well together.""" # recreate the separate event loop and client session for the HTTP client so we start with a clean slate # this is important since we measure the time this test takes, and the test_timeout() tasks might # interfere with it loop = thread_loop.get_thread_loop() if http_client.client_session: run_coroutine_threadsafe(http_client.client_session.close(), loop) http_client.client_session = None # not going to properly shut down the running loop, this will be cleaned up on exit thread_loop.event_loop = None # get a second event loop running in the current thread; we'll use this one to run the test loop = asyncio.get_event_loop() start_time = time.time() loop.run_until_complete(_test_asyncio_client(integration_server)) end_time = time.time() # There are three things being executed asynchronously: # 1. sleep 1 second in the main event loop # 2. fetch the response for client1 (the server sleeps 1 second) # 3. fetch the response for client2 (the server sleeps 1 second) # All of this combined should take only a bit more than one second. # While this assertion could become flaky depending on how busy the system that runs the test # is for now it's a nice confirmation that things work as expected. We can remove it later if # it becomes a problem. assert end_time - start_time < 2
def loop(self) -> asyncio.AbstractEventLoop: if self._loop is not None: return self._loop elif self.run_mode == RunMode.THREAD: return get_thread_loop() elif self.run_mode == RunMode.FULL_ASYNCIO: return asyncio.get_event_loop() else: # pragma: no cover # should be impossible because this is validated by __init__ raise ValueError(self.run_mode)