From ba8a23bb08c5293326290275ed55f8c7e0517c31 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Wed, 30 Dec 2020 16:26:55 +1100 Subject: [PATCH 1/7] docs: fix simple typo, detatch -> detach There is a small typo in tests/test_dbgp_api.py. Should read `detach` rather than `detatch`. --- tests/test_dbgp_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dbgp_api.py b/tests/test_dbgp_api.py index 26382d57..4e998059 100644 --- a/tests/test_dbgp_api.py +++ b/tests/test_dbgp_api.py @@ -127,10 +127,10 @@ def test_stop_retval(self): assert str(status_res) == "stopping" def test_detatch_retval(self): - """Test that the detatch command receives a message from the api.""" + """Test that the detach command receives a message from the api.""" self.p.conn.recv_msg.return_value = """\n - Date: Tue, 31 Aug 2021 13:36:21 +0200 Subject: [PATCH 2/7] Added Xdebug v3 instructions to helpfile --- doc/Vdebug.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/Vdebug.txt b/doc/Vdebug.txt index f7f30f0a..e0be1b58 100644 --- a/doc/Vdebug.txt +++ b/doc/Vdebug.txt @@ -206,7 +206,7 @@ it as a zend extension. This can be done in the PHP INI file, but in Ubuntu I can add a new file to /etc/php5/conf.d/ that contains all the configuration options and gets loaded automatically by PHP. -Add these options to the INI file: > +For Xdebug v2, add these options to the INI file: > zend_extension=/path/to/xdebug.so xdebug.remote_enable=on @@ -214,6 +214,16 @@ Add these options to the INI file: > xdebug.remote_host=localhost xdebug.remote_port=9000 < + +For Xdebug v3, add tese options to the INI file: > + + zend_extension=/path/to/xdebug.so + xdebug.mode=debug + xdebug.client_host=localhost + xdebug.client_port=9000 +< +Please refer to https://xdebug.org/docs/upgrade_guide for instructions on how to upgrade from v2 to v3. + If using Apache, restart it to enable the new library. The command line interface should be ready to go - type "php -v" and you should see the line > with Xdebug v2.2.0, Copyright (c) 2002-2012, by Derick Rethans From 21dbc4d93ea397d7369f96dccf5da8cfa999a75f Mon Sep 17 00:00:00 2001 From: Aleix Quintana Date: Wed, 17 Nov 2021 21:50:51 +0100 Subject: [PATCH 3/7] Add dbgpproxy support --- plugin/vdebug.vim | 2 + python3/vdebug/connection.py | 101 ++++++++++++++++++++++++++++++++--- python3/vdebug/listener.py | 8 ++- 3 files changed, 104 insertions(+), 7 deletions(-) diff --git a/plugin/vdebug.vim b/plugin/vdebug.vim index 7c605d5a..216fe681 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 89b34dad..d5ce6f4f 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,18 @@ 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: + self.proxystop() 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 +165,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 +236,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,9 +247,11 @@ 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: + await self.proxystop() self.log("Error: %s" % str(sys.exc_info())) self.log("Stopping server") @@ -207,16 +259,53 @@ async def run_async(self): self.log("Address already in use") print("Socket is already in use") except asyncio.CancelledError as e: + await self.proxystop() self.log("Stopping server") self.__socket_task = None except Exception as e: + await self.proxystop() print("Exception caught") 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 +325,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 15775904..0c39ad2d 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): From 6efc0aefef95ea6abfe06f1053f172b3b4574a1d Mon Sep 17 00:00:00 2001 From: Aleix Quintana Alsius Date: Thu, 18 Nov 2021 11:38:57 +0100 Subject: [PATCH 4/7] Remove unnecessary stops --- python3/vdebug/connection.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python3/vdebug/connection.py b/python3/vdebug/connection.py index d5ce6f4f..f7494e23 100644 --- a/python3/vdebug/connection.py +++ b/python3/vdebug/connection.py @@ -130,7 +130,6 @@ def start(self, host='', proxy_host = '', proxy_port = 9001, idekey = None, port self.proxyinit(proxy_host, proxy_port, port, idekey) self.__sock = self.accept(serv, timeout) except socket.timeout: - self.proxystop() raise TimeoutError("Timeout waiting for connection") finally: self.proxystop(proxy_host, proxy_port, idekey) @@ -251,7 +250,6 @@ async def run_async(self): # No connection pass except socket.error as socket_error: - await self.proxystop() self.log("Error: %s" % str(sys.exc_info())) self.log("Stopping server") @@ -259,11 +257,9 @@ async def run_async(self): self.log("Address already in use") print("Socket is already in use") except asyncio.CancelledError as e: - await self.proxystop() self.log("Stopping server") self.__socket_task = None except Exception as e: - await self.proxystop() print("Exception caught") self.log("Error: %s" % str(sys.exc_info())) self.log("Stopping server") From 3484f1aa73d26c40bfb6144aec97438009c2fed4 Mon Sep 17 00:00:00 2001 From: Aleix Quintana Alsius Date: Fri, 19 Nov 2021 00:55:10 +0100 Subject: [PATCH 5/7] FeatureGetResponse string cast always return str --- python3/vdebug/dbgp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vdebug/dbgp.py b/python3/vdebug/dbgp.py index b8f1a108..053f0186 100644 --- a/python3/vdebug/dbgp.py +++ b/python3/vdebug/dbgp.py @@ -181,7 +181,7 @@ def is_supported(self): def __str__(self): if self.is_supported(): xml = self.as_xml() - return xml.text + return xml.text if xml.text else "" return "* Feature not supported *" From 617c7e02b219b8e5002c43dda9b9f9ee536a7273 Mon Sep 17 00:00:00 2001 From: BoilingSoup <84747244+BoilingSoup@users.noreply.github.com> Date: Sat, 26 Feb 2022 21:11:24 -0800 Subject: [PATCH 6/7] Update Vdebug.txt --- doc/Vdebug.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Vdebug.txt b/doc/Vdebug.txt index e0be1b58..495d8830 100644 --- a/doc/Vdebug.txt +++ b/doc/Vdebug.txt @@ -215,7 +215,7 @@ For Xdebug v2, add these options to the INI file: > xdebug.remote_port=9000 < -For Xdebug v3, add tese options to the INI file: > +For Xdebug v3, add these options to the INI file: > zend_extension=/path/to/xdebug.so xdebug.mode=debug From 66517871178779ac54e19ed0f34d66805b820664 Mon Sep 17 00:00:00 2001 From: Lucas Hoffmann Date: Thu, 2 May 2024 22:20:58 +0200 Subject: [PATCH 7/7] Fix python syntax warning This fixes #524. --- python3/vdebug/event.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python3/vdebug/event.py b/python3/vdebug/event.py index 7e4acb3a..87842e4c 100644 --- a/python3/vdebug/event.py +++ b/python3/vdebug/event.py @@ -44,16 +44,16 @@ class CursorEvalEvent(Event): """Evaluate the variable currently under the cursor. """ char_regex = { - "default": "a-zA-Z0-9_.\[\]'\"", - "ruby": "$@a-zA-Z0-9_.\[\]'\"", + "default": "a-zA-Z0-9_.\\[\\]'\"", + "ruby": "$@a-zA-Z0-9_.\\[\\]'\"", "perl": "$a-zA-Z0-9_{}'\"", - "php": "$@%a-zA-Z0-9_\[\]'\"\->" + "php": "$@%a-zA-Z0-9_\\[\\]'\">-" } var_regex = { "default": "^[a-zA-Z_]", "ruby": "^[$@a-zA-Z_]", - "php": "^[\$A-Z]", + "php": r"^[\$A-Z]", "perl": "^[$@%]" } @@ -559,7 +559,7 @@ def run(self): line = self.ui.windows.breakpoints().line_at(lineno - 1) # Match on ID - id = re.findall('^[\s][0-9]*[\s]', line) + id = re.findall('^[\\s][0-9]*[\\s]', line) if not id: return False @@ -877,9 +877,9 @@ def _get_window_name(): @staticmethod def _get_breakpoint_id_breakpoint_window(line): # Match on ID - id = re.findall('^[\s][0-9]*[\s]', line) + id = re.findall(r'^[\s][0-9]*[\s]', line) if not id: - log.Log("No breakpoint founr at current cursor position", + log.Log("No breakpoint found at current cursor position", log.Logger.DEBUG) return False