diff options
| author | Eric Urban <hydrogen18@gmail.com> | 2013-10-06 21:23:20 -0400 |
|---|---|---|
| committer | Sergey Shepelev <temotor@gmail.com> | 2015-02-21 18:12:48 +0300 |
| commit | e536cfaea7616b5e76b311bc0ae87858f3409422 (patch) | |
| tree | e78aa223d0397e3dfb903a960d8c3fc455968b2d | |
| parent | 52d42dd741fc8da7f1b1ac525ff6c9f2f5739ee7 (diff) | |
| download | eventlet-tm3.tar.gz | |
backdoor: support UNIX sockets and IPv6tm3
https://bitbucket.org/eventlet/eventlet/issue/157
| -rw-r--r-- | eventlet/backdoor.py | 46 | ||||
| -rw-r--r-- | tests/backdoor_test.py | 69 |
2 files changed, 98 insertions, 17 deletions
diff --git a/eventlet/backdoor.py b/eventlet/backdoor.py index 2067772..6cfd89e 100644 --- a/eventlet/backdoor.py +++ b/eventlet/backdoor.py @@ -4,6 +4,7 @@ from code import InteractiveConsole import errno import socket import sys +import traceback import eventlet from eventlet import hubs @@ -40,20 +41,35 @@ class FileProxy(object): return getattr(self.f, attr) -# @@tavis: the `locals` args below mask the built-in function. Should -# be renamed. +def format_addr(a): + if len(a) >= 2: + # IP + return '{0}:{1}'.format(*a) + if len(a) == 1: + # UNIX socket / named pipe + return str(a[0]) + # unknown + return str(a) + + class SocketConsole(greenlets.greenlet): - def __init__(self, desc, hostport, locals): + def __init__(self, desc, hostport, init_locals): self.hostport = hostport - self.locals = locals + self.init_locals = init_locals # mangle the socket self.desc = FileProxy(desc) greenlets.greenlet.__init__(self) + # place to store exception if InteractiveConsole terminates + self.exc_info = None def run(self): try: - console = InteractiveConsole(self.locals) + console = InteractiveConsole(self.init_locals) console.interact() + except SystemExit: + pass + except BaseException: + self.exc_info = sys.exc_info() finally: self.switch_out() self.finalize() @@ -69,24 +85,27 @@ class SocketConsole(greenlets.greenlet): def finalize(self): # restore the state of the socket self.desc = None - print("backdoor closed to %s:%s" % self.hostport) + print("backdoor closed to {0}".format(format_addr(self.hostport))) + + if self.exc_info is not None: + traceback.print_exception(*self.exc_info) -def backdoor_server(sock, locals=None): +def backdoor_server(sock, init_locals=None): """ Blocking function that runs a backdoor server on the socket *sock*, accepting connections and running backdoor consoles for each client that connects. - The *locals* argument is a dictionary that will be included in the locals() + The *init_locals* argument is a dictionary that will be included in the locals() of the interpreters. It can be convenient to stick important application variables in here. """ - print("backdoor server listening on %s:%s" % sock.getsockname()) + print("backdoor server listening on {0}".format(format_addr(sock.getsockname()))) try: try: while True: socketpair = sock.accept() - backdoor(socketpair, locals) + backdoor(socketpair, init_locals) except socket.error as e: # Broken pipe means it was shutdown if get_errno(e) != errno.EPIPE: @@ -95,17 +114,16 @@ def backdoor_server(sock, locals=None): sock.close() -def backdoor(conn_info, locals=None): +def backdoor(conn_info, init_locals=None): """Sets up an interactive console on a socket with a single connected client. This does not block the caller, as it spawns a new greenlet to handle the console. This is meant to be called from within an accept loop (such as backdoor_server). """ conn, addr = conn_info - host, port = addr - print("backdoor to %s:%s" % (host, port)) + print("backdoor to {0}".format(format_addr(addr))) fl = conn.makefile("rw") - console = SocketConsole(fl, (host, port), locals) + console = SocketConsole(fl, addr, init_locals) hub = hubs.get_hub() hub.schedule_call_global(0, console.switch) diff --git a/tests/backdoor_test.py b/tests/backdoor_test.py index 6facffe..e91bbbc 100644 --- a/tests/backdoor_test.py +++ b/tests/backdoor_test.py @@ -1,11 +1,25 @@ +import os + import eventlet from eventlet import backdoor from eventlet.green import socket +import tests + + +SOCKET_PATH = '/tmp/eventlet_backdoor_test.socket' + -from tests import LimitedTestCase, main +def silent_unlink(path): + try: + os.unlink(SOCKET_PATH) + except OSError: + pass -class BackdoorTest(LimitedTestCase): +class BackdoorTest(tests.LimitedTestCase): + def tearDown(self): + silent_unlink(SOCKET_PATH) + def test_server(self): listener = socket.socket() listener.bind(('localhost', 0)) @@ -29,6 +43,55 @@ class BackdoorTest(LimitedTestCase): # wait for the console to discover that it's dead eventlet.sleep(0.1) + def test_server_on_ipv6(self): + listener = socket.socket(socket.AF_INET6) + listener.bind(('::1', 0)) + listener.listen(5) + serv = eventlet.spawn(backdoor.backdoor_server, listener) + client = socket.socket(socket.AF_INET6) + client.connect(listener.getsockname()) + f = client.makefile('rw') + self.assert_('Python' in f.readline()) + f.readline() # build info + f.readline() # help info + self.assert_('InteractiveConsole' in f.readline()) + self.assertEquals('>>> ', f.read(4)) + f.write('print("hi")\n') + f.flush() + self.assertEquals('hi\n', f.readline()) + self.assertEquals('>>> ', f.read(4)) + f.write('exit()\n') + f.close() + client.close() + serv.kill() + # wait for the console to discover that it's dead + eventlet.sleep(0.1) + + def test_server_on_unix_socket(self): + silent_unlink(SOCKET_PATH) + listener = socket.socket(socket.AF_UNIX) + listener.bind(SOCKET_PATH) + listener.listen(5) + serv = eventlet.spawn(backdoor.backdoor_server, listener) + client = socket.socket(socket.AF_UNIX) + client.connect(SOCKET_PATH) + f = client.makefile('rw') + self.assert_('Python' in f.readline()) + f.readline() # build info + f.readline() # help info + self.assert_('InteractiveConsole' in f.readline()) + self.assertEquals('>>> ', f.read(4)) + f.write('print("hi")\n') + f.flush() + self.assertEquals('hi\n', f.readline()) + self.assertEquals('>>> ', f.read(4)) + f.write('exit()\n') + f.close() + client.close() + serv.kill() + # wait for the console to discover that it's dead + eventlet.sleep(0.1) + if __name__ == '__main__': - main() + tests.main() |
