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()
    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()
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)