# # Implement an asyncio.Protocol for Electrum (clients) # # import asyncio import json import logging import socket logger = logging.getLogger(__name__) class StratumProtocol(asyncio.Protocol): client = None closed = False transport = None buf = b"" connection_lost_called = False def connection_made(self, transport): self.transport = transport logger.debug("Transport connected ok") # To detect if server disconnected us sock = self.transport.get_extra_info("socket") if sock is not None: sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) def connection_lost(self, exc): if not self.closed: self.closed = True self.close() if not self.connection_lost_called: self.connection_lost_called = True self.client.connection_lost(self) def data_received(self, data): self.buf += data # Unframe the mesage. Expecting JSON. *lines, self.buf = self.buf.split(b"\n") for line in lines: if not line: continue try: msg = line.decode("utf-8", "error").strip() except UnicodeError as exc: logger.exception("Encoding issue on %r", line) self.connection_lost(exc) return try: msg = json.loads(msg) except ValueError as exc: logger.exception("Bad JSON received from server: %r", msg) self.connection_lost(exc) return # logger.debug("RX:\n%s", json.dumps(msg, indent=2)) # pylint: disable=protected-access try: self.client._got_response(msg) except Exception as e: logger.exception("Trouble handling response! (%s)", e) continue def send_data(self, message): """ Given an object, encode as JSON and transmit to the server. """ # logger.debug("TX:\n%s", json.dumps(message, indent=2)) data = json.dumps(message).encode("utf-8") + b"\n" self.transport.write(data) def close(self): if not self.closed: try: self.transport.close() finally: self.closed = True # EOF