diff --git a/plugin/vdebug.vim b/plugin/vdebug.vim index 7c605d5..216fe68 100644 --- a/plugin/vdebug.vim +++ b/plugin/vdebug.vim @@ -79,6 +79,8 @@ let g:vdebug_options_defaults = { \ 'port' : 9000, \ 'timeout' : 20, \ 'server' : '', +\ "proxy_host" : '', +\ "proxy_port" : 9001, \ 'on_close' : 'stop', \ 'break_on_open' : 1, \ 'ide_key' : '', diff --git a/python3/vdebug/connection.py b/python3/vdebug/connection.py index 89b34da..f7494e2 100644 --- a/python3/vdebug/connection.py +++ b/python3/vdebug/connection.py @@ -5,6 +5,7 @@ import threading import time import asyncio +import xml.etree.ElementTree as ET from . import log @@ -103,13 +104,17 @@ def __init__(self, input_stream=None): """ self.__sock = None self.input_stream = input_stream + self.proxy_success = False - def start(self, host='', port=9000, timeout=30): + def start(self, host='', proxy_host = '', proxy_port = 9001, idekey = None, port=9000, timeout=30): """Listen for a connection from the debugger. Listening for the actual connection is handled by self.listen() host -- host name where debugger is running (default '') port -- port number which debugger is listening on (default 9000) + proxy_host -- If using a DBGp Proxy, host name where the proxy is running (default None to disable) + proxy_port -- If using a DBGp Proxy, port where the proxy is listening for debugger connections (default 9001) + idekey -- The idekey that our Api() wrapper is expecting. Only required if using a proxy timeout -- time in seconds to wait for a debugger connection before giving up (default 30) """ print('Waiting for a connection (Ctrl-C to cancel, this message will ' @@ -120,13 +125,17 @@ def start(self, host='', port=9000, timeout=30): serv.setblocking(1) serv.bind((host, port)) serv.listen(5) - self.__sock = self.listen(serv, timeout) + if proxy_host and proxy_port: + # Register ourselves with the proxy server + self.proxyinit(proxy_host, proxy_port, port, idekey) + self.__sock = self.accept(serv, timeout) except socket.timeout: raise TimeoutError("Timeout waiting for connection") finally: + self.proxystop(proxy_host, proxy_port, idekey) serv.close() - def listen(self, serv, timeout): + def accept(self, serv, timeout): """Non-blocking listener. Provides support for keyboard interrupts from the user. Although it's non-blocking, the user interface will still block until the timeout is reached. @@ -155,13 +164,50 @@ def socket(self): def has_socket(self): return self.__sock is not None + def proxyinit(self, proxy_host, proxy_port, port, idekey): + """Register ourselves with the proxy.""" + if not proxy_host or not proxy_port: + return + + self.log("Connecting to DBGp proxy [%s:%d]" % (proxy_host, proxy_port)) + proxy_conn = socket.create_connection((proxy_host, proxy_port), 30) + + self.log("Sending proxyinit command") + msg = 'proxyinit -p %d -k %s -m 0' % (port, idekey) + proxy_conn.send(msg.encode()) + proxy_conn.shutdown(socket.SHUT_WR) + + # Parse proxy response + response = proxy_conn.recv(8192) + proxy_conn.close() + response = ET.fromstring(response) + self.proxy_success = bool(response.get("success")) + + def proxystop(self, proxy_host, proxy_port, idekey): + """De-register ourselves from the proxy.""" + if not self.proxy_success: + return + + proxy_conn = socket.create_connection((proxy_host, proxy_port), 30) + + self.log("Sending proxystop command") + msg = 'proxystop -k %s' % str(idekey) + proxy_conn.send(msg.encode()) + proxy_conn.close() + self.proxy_success = False + + class BackgroundSocketCreator(threading.Thread): - def __init__(self, host, port, output_q): + def __init__(self, host, port, proxy_host, proxy_port, idekey, output_q): self.__output_q = output_q self.__host = host self.__port = port + self.__proxy_host = proxy_host + self.__proxy_port = proxy_port + self.__idekey = idekey + self.proxy_success = False self.__socket_task = None self.__loop = None threading.Thread.__init__(self) @@ -189,6 +235,9 @@ async def run_async(self): try: # using ensure_future here since before 3.7, this is not a coroutine, but returns a future self.__socket_task = asyncio.ensure_future(self.__loop.sock_accept(s)) + if self.__proxy_host and self.__proxy_port: + # Register ourselves with the proxy server + await self.proxyinit() client, address = await self.__socket_task # set resulting socket to blocking client.setblocking(True) @@ -197,6 +246,7 @@ async def run_async(self): self.__output_q.put((client, address)) break except socket.error: + await self.proxystop() # No connection pass except socket.error as socket_error: @@ -214,9 +264,44 @@ async def run_async(self): self.log("Error: %s" % str(sys.exc_info())) self.log("Stopping server") finally: + await self.proxystop() self.log("Finishing socket server") s.close() + async def proxyinit(self): + """Register ourselves with the proxy.""" + if not self.__proxy_host or not self.__proxy_port: + return + + self.log("Connecting to DBGp proxy [%s:%d]" % (self.__proxy_host, self.__proxy_port)) + proxy_conn = socket.create_connection((self.__proxy_host, self.__proxy_port), 30) + + self.log("Sending proxyinit command") + msg = 'proxyinit -p %d -k %s -m 0' % (self.__port, self.__idekey) + proxy_conn.send(msg.encode()) + proxy_conn.shutdown(socket.SHUT_WR) + + # Parse proxy response + response = proxy_conn.recv(8192) + proxy_conn.close() + response = ET.fromstring(response) + self.proxy_success = bool(response.get("success")) + + async def proxystop(self): + """De-register ourselves from the proxy.""" + if not self.proxy_success: + return + + proxy_conn = socket.create_connection((self.__proxy_host, self.__proxy_port), 30) + + self.log("Sending proxystop command") + msg = 'proxystop -k %s' % str(self.__idekey) + proxy_conn.send(msg.encode()) + proxy_conn.close() + self.proxy_success = False + + + def _exit(self): if self.__socket_task: # this will raise asyncio.CancelledError @@ -236,10 +321,10 @@ def __init__(self): def __del__(self): self.stop() - def start(self, host, port): + def start(self, host, port, proxy_host, proxy_port, ide_key): if not self.is_alive(): self.__thread = BackgroundSocketCreator( - host, port, self.__socket_q) + host, port, proxy_host, proxy_port, ide_key, self.__socket_q) self.__thread.start() def is_alive(self): diff --git a/python3/vdebug/listener.py b/python3/vdebug/listener.py index 1577590..0c39ad2 100644 --- a/python3/vdebug/listener.py +++ b/python3/vdebug/listener.py @@ -22,6 +22,9 @@ def __init__(self): def start(self): self.__server.start(opts.Options.get('server'), opts.Options.get('port', int), + opts.Options.get('proxy_host'), + opts.Options.get('proxy_port', int), + opts.Options.get('ide_key'), opts.Options.get('timeout', int)) def stop(self): @@ -51,7 +54,10 @@ def start(self): if opts.Options.get("auto_start", int): vim.command('autocmd Vdebug CursorHold,CursorHoldI,CursorMoved,CursorMovedI,FocusGained,FocusLost * python3 debugger.start_if_ready()') self.__server.start(opts.Options.get('server'), - opts.Options.get('port', int)) + opts.Options.get('port', int), + opts.Options.get('proxy_host'), + opts.Options.get('proxy_port', int), + opts.Options.get('ide_key')) def stop(self): if opts.Options.get("auto_start", bool):