class TelnetClient(ClientInterface): def __init__(self, reader, writer): self.reader = reader self.writer = writer self.remote_ip = writer.get_extra_info('peername')[0] self.input_queue = asyncio.Queue() self.output_queue = asyncio.Queue() self.parser = TelnetParser(self.input_queue, self.output_queue, self.remote_ip) self.kill_switch = asyncio.Future() @asyncio.coroutine def close(self): self.kill_switch.set_result(True) @asyncio.coroutine def _close(self): """ A coroutine that gracefully kicks the client """ self.writer.close() @asyncio.coroutine def get_input_async(self): """ A coroutine that returns the next message from the client The message must be a Dictionary that at least specifies a command for the controller. """ # { # 'command': command, # 'other_params': other_params # } msg = yield from self.parser.get_input_async() return msg @asyncio.coroutine def put_output_async(self, msg): """ A coroutine that sends the specified message to the client The message will be a Dictionary that echoes the client's command and sends the results as a sub-item """ # { # 'command': command, # 'data': results # } yield from self.parser.put_output_async(msg) @asyncio.coroutine def communicate_until_closed(self): logger.info("[{}] New telnet client.".format(self.remote_ip)) communication_tasks = [ asyncio. async (self._receive_to_queue()), asyncio. async (self.parser.run_parser()), asyncio. async (self._send_from_queue()), self.kill_switch ] done, pending = yield from asyncio.wait(communication_tasks, return_when=FIRST_COMPLETED) logger.info("[{}] Cleaning up client...".format(self.remote_ip)) got_exception = None for task in done: e = task.exception() if isinstance(e, TelnetExit): # No need to handle this here; the client will be closed anyway. pass elif isinstance(e, Exception): # If any of our tasks threw a different exception, re-raise it # instead of failing silently. got_exception = e # Make sure we cancel the tasks in order, so that last minute # messages can still get sent. for task in communication_tasks: if not task.done(): task.cancel() # self.kill_switch is a simple Future; it doesn't need to # clean up. if task != self.kill_switch: yield from task yield from self._close() logger.info("[{}] Cleanup complete.".format(self.remote_ip)) if got_exception is not None: print(got_exception) raise got_exception @asyncio.coroutine def _receive_to_queue(self): try: while True: msg = yield from self.reader.readline() # "If the EOF was received and the internal buffer is empty, # return an empty bytes object." if msg == b'': logger.info("[{}] Client connection closed.".format( self.remote_ip)) break logger.info("[{}] [RECV] {}".format(self.remote_ip, msg)) yield from self.input_queue.put(msg) except CancelledError: logger.debug("[{}] Cancelling receiver...".format(self.remote_ip)) @asyncio.coroutine def _send_from_queue(self): preview_length = 80 # ====================================== @asyncio.coroutine def execute(msg): msg_preview = (msg[0:preview_length].replace('\n', '\\n').replace( '\r', '\\r')) if len(msg) > preview_length: msg_preview += "..." self.writer.write(msg.encode()) yield from self.writer.drain() logger.info("[{}] [SEND] {}".format(self.remote_ip, msg_preview)) # ====================================== try: while True: msg = yield from self.output_queue.get() yield from execute(msg) except CancelledError: logger.debug("[{}] Cancelling sender...".format(self.remote_ip)) # Goodbye, client yield from self.output_queue.put("Server closing connection -- " "Goodbye.") while self.output_queue.qsize() > 0: msg = self.output_queue.get_nowait() yield from execute(msg)
class TelnetClient(ClientInterface): def __init__(self, reader, writer): self.reader = reader self.writer = writer self.remote_ip = writer.get_extra_info("peername")[0] self.input_queue = asyncio.Queue() self.output_queue = asyncio.Queue() self.parser = TelnetParser(self.input_queue, self.output_queue, self.remote_ip) self.kill_switch = asyncio.Future() @asyncio.coroutine def close(self): self.kill_switch.set_result(True) @asyncio.coroutine def _close(self): """ A coroutine that gracefully kicks the client """ self.writer.close() @asyncio.coroutine def get_input_async(self): """ A coroutine that returns the next message from the client The message must be a Dictionary that at least specifies a command for the controller. """ # { # 'command': command, # 'other_params': other_params # } msg = yield from self.parser.get_input_async() return msg @asyncio.coroutine def put_output_async(self, msg): """ A coroutine that sends the specified message to the client The message will be a Dictionary that echoes the client's command and sends the results as a sub-item """ # { # 'command': command, # 'data': results # } yield from self.parser.put_output_async(msg) @asyncio.coroutine def communicate_until_closed(self): logger.info("[{}] New telnet client.".format(self.remote_ip)) communication_tasks = [ asyncio.async(self._receive_to_queue()), asyncio.async(self.parser.run_parser()), asyncio.async(self._send_from_queue()), self.kill_switch, ] done, pending = yield from asyncio.wait(communication_tasks, return_when=FIRST_COMPLETED) logger.info("[{}] Cleaning up client...".format(self.remote_ip)) got_exception = None for task in done: e = task.exception() if isinstance(e, TelnetExit): # No need to handle this here; the client will be closed anyway. pass elif isinstance(e, Exception): # If any of our tasks threw a different exception, re-raise it # instead of failing silently. got_exception = e # Make sure we cancel the tasks in order, so that last minute # messages can still get sent. for task in communication_tasks: if not task.done(): task.cancel() # self.kill_switch is a simple Future; it doesn't need to # clean up. if task != self.kill_switch: yield from task yield from self._close() logger.info("[{}] Cleanup complete.".format(self.remote_ip)) if got_exception is not None: print(got_exception) raise got_exception @asyncio.coroutine def _receive_to_queue(self): try: while True: msg = yield from self.reader.readline() # "If the EOF was received and the internal buffer is empty, # return an empty bytes object." if msg == b"": logger.info("[{}] Client connection closed.".format(self.remote_ip)) break logger.info("[{}] [RECV] {}".format(self.remote_ip, msg)) yield from self.input_queue.put(msg) except CancelledError: logger.debug("[{}] Cancelling receiver...".format(self.remote_ip)) @asyncio.coroutine def _send_from_queue(self): preview_length = 80 # ====================================== @asyncio.coroutine def execute(msg): msg_preview = msg[0:preview_length].replace("\n", "\\n").replace("\r", "\\r") if len(msg) > preview_length: msg_preview += "..." self.writer.write(msg.encode()) yield from self.writer.drain() logger.info("[{}] [SEND] {}".format(self.remote_ip, msg_preview)) # ====================================== try: while True: msg = yield from self.output_queue.get() yield from execute(msg) except CancelledError: logger.debug("[{}] Cancelling sender...".format(self.remote_ip)) # Goodbye, client yield from self.output_queue.put("Server closing connection -- " "Goodbye.") while self.output_queue.qsize() > 0: msg = self.output_queue.get_nowait() yield from execute(msg)