async def ws_connect(self): """Connect to the websocket server.""" if self.connected: raise TransportError('Connection already open.') try: self._client = await self.session.ws_connect( self._url, **self._connect_kwargs) except (ClientError, HttpProcessingError, asyncio.TimeoutError) as exc: raise TransportError('Error connecting to server', None, exc) return self.session.loop.create_task(self._ws_loop())
async def _ws_loop(self): """Listen for messages from the websocket server.""" msg = None try: while True: msg = await self._client.receive() if msg.type == aiohttp.WSMsgType.CLOSED: break if msg.type == aiohttp.WSMsgType.ERROR: break if msg.type == aiohttp.WSMsgType.BINARY: try: # If we get a binary message, try and decode it as a # UTF-8 JSON string, in case the server is sending # binary websocket messages. If it doens't decode we'll # ignore it since we weren't expecting binary messages # anyway data = json.loads(msg.data.decode()) except ValueError: continue elif msg.type == aiohttp.WSMsgType.TEXT: try: data = msg.json() except ValueError as exc: raise TransportError('Error Parsing JSON', None, exc) else: # This is tested with test_message_ping_ignored, but # cpython's optimizations prevent coveragepy from detecting # that it's run # https://bitbucket.org/ned/coveragepy/issues/198/continue-marked-as-not-covered continue # pragma: no cover if 'method' in data: request = jsonrpc_base.Request.parse(data) response = self.receive_request(request) if response: await self.send_message(response) else: self._pending_messages[data['id']].response = data except (ClientError, HttpProcessingError, asyncio.TimeoutError) as exc: raise TransportError('Transport Error', None, exc) finally: await self.close() if msg and msg.type == aiohttp.WSMsgType.ERROR: raise TransportError( 'Websocket error detected. Connection closed.')
async def send_message(self, message): """Send the HTTP message to the server and return the message response. No result is returned if message is a notification. """ if self._client is None: raise TransportError('Client is not connected.', message) try: await self._client.send_str(message.serialize()) if message.response_id: pending_message = PendingMessage(loop=self.session.loop) self._pending_messages[message.response_id] = pending_message response = await pending_message.wait(self._timeout) del self._pending_messages[message.response_id] else: response = None return message.parse_response(response) except (ClientError, HttpProcessingError, asyncio.TimeoutError) as exc: raise TransportError('Transport Error', message, exc)
def send_message(self, message): """Issue the request to the server and return the method result (if not a notification)""" try: if isinstance(message, jsonrpc_base.Request): data = jsonrpc_base.Request.parse( json.loads(message.serialize())) else: data = message.serialize() response = json.loads(json.dumps(self._handler(data))) except Exception as requests_exception: raise TransportError('Transport Error', message, requests_exception) return message.parse_response(response)
def send_message(self, message): """Send the HTTP message to the server and return the message response. No result is returned if message is a notification. """ try: response = yield from self._request(data=message.serialize()) except (aiohttp.ClientError, asyncio.TimeoutError) as exc: raise TransportError('Transport Error', message, exc) if response.status != 200: raise TransportError('HTTP %d %s' % (response.status, response.reason), message) if message.response_id is None: # Message is notification, so no response is expcted. return None try: response_data = yield from response.json() except ValueError as value_error: raise TransportError('Cannot deserialize response body', message, value_error) return message.parse_response(response_data)
def test_transport_error_constructor(self): with self.assertRaisesRegex(TransportError, 'Test Message'): raise TransportError('Test Message')
def test_transport_error_constructor(server): with pytest.raises(TransportError, match='Test Message'): raise TransportError('Test Message')