Skip to content

Commit

Permalink
feat: DBGp Proxy support (#685)
Browse files Browse the repository at this point in the history
* DBGP Proxy support.

* Change protocol encoding to utf-8.

* Fix package.json capabilities Untrusted Workspace.

* Compatibility hack between pydbgpproxy and official dbgpProxy.

* Changed setting title and keys to "php.debug". Populate ideKey in resolveDebugConfiguration.

* More detailed errors for debugging socket close cases.

Co-authored-by: Alvaro Pareja-Lecaros <[email protected]>

Co-authored-by: towhidabsar <[email protected]>
  • Loading branch information
zobo and towhidabsar authored Nov 4, 2021
1 parent 85a28a4 commit f5c9534
Show file tree
Hide file tree
Showing 9 changed files with 428 additions and 23 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [1.22.0]

### Added

- DBGp Proxy support.
- `php.debug.ideKey` setting to set the Proxy IDE key globally.

### Changed

- Renamed `php.executablePath` setting to `php.debug.executablePath` and do not fallback to `php.validate.executablePath`.
- Untrusted workspace settings.
- Default protocol encoding changed to utf-8.

## [1.21.1]

### Fixed
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ There are also configurations for Xdebug v2 (Legacy) installations.

More general information on debugging with VS Code can be found on https://code.visualstudio.com/docs/editor/debugging.

> _Note:_ You can even debug a script without `launch.json`. If no folder is open, and the VS Code status bar is purple, pressing `F5` will start the open script with Xdebug3 specific parameters. If the php executable is not in path, you can provide it with the setting `php.executablePath` or a fallback `php.validate.executablePath`. For debugging to work, Xdebug must still be correctly installed.
> _Note:_ You can even debug a script without `launch.json`. If no folder is open, and the VS Code status bar is purple, pressing `F5` will start the open script with Xdebug3 specific parameters. If the php executable is not in path, you can provide it with the setting `php.debug.executablePath`. For debugging to work, Xdebug must still be correctly installed.
#### Supported launch.json settings:

Expand All @@ -77,6 +77,13 @@ More general information on debugging with VS Code can be found on https://code.
- `log`: Whether to log all communication between VS Code and the adapter to the debug console. See _Troubleshooting_ further down.
- `ignore`: An optional array of glob patterns that errors should be ignored from (for example `**/vendor/**/*.php`)
- `maxConnections`: Accept only this number of parallel debugging sessions. Additional connections will be dropped and their execution will continue without debugging.
- `proxy`: DBGp Proxy settings
- `enable`: To enable proxy registration set to `true` (default is `false).
- `host`: The address of the proxy. Supports host name, IP address, or Unix domain socket (default: 127.0.0.1).
- `port`: The port where the adapter will register with the the proxy (default: `9001`).
- `key`: A unique key that allows the proxy to match requests to your editor (default: `vsc`). The default is taken from VSCode settings `php.debug.idekey`.
- `timeout`: The number of milliseconds to wait before giving up on the connection to proxy (default: `3000`).
- `allowMultipleSessions`: If the proxy should forward multiple sessions/connections at the same time or not (default: `true`).
- `xdebugSettings`: Allows you to override Xdebug's remote debugging settings to fine tuning Xdebug to your needs. For example, you can play with `max_children` and `max_depth` to change the max number of array and object children that are retrieved and the max depth in structures like arrays and objects. This can speed up the debugger on slow machines.
For a full list of feature names that can be set please refer to the [Xdebug documentation](https://xdebug.org/docs-dbgp.php#feature-names).
- `max_children`: max number of array or object children to initially retrieve
Expand Down Expand Up @@ -110,6 +117,7 @@ Options specific to CLI debugging:
- Watches
- Run as CLI
- Run without debugging
- DBGp Proxy registration and unregistration support

## Remote Host Debugging

Expand All @@ -127,6 +135,14 @@ To make VS Code map the files on the server to the right files on your local mac

Please also note that setting any of the CLI debugging options will not work with remote host debugging, because the script is always launched locally. If you want to debug a CLI script on a remote host, you need to launch it manually from the command line.

## Proxy support

The debugger can register itself to a DBGp proxy with a IDE Key. The proxy will then forward to the IDE only those DBGp sessions that have this specified IDE key. This is helpful in a multiuser environment where developers cannot use the same DBGp port at the same time. Careful setup is needed that requests to the web server contain the matching IDE key.

The official implementation of the [dbgpProxy](https://xdebug.org/docs/dbgpProxy).

A _Xdebug helper_ browser extension is also recommended. There the request side IDE key can be easily configured.

## Troubleshooting

- Ask a question on [StackOverflow](https://stackoverflow.com/)
Expand Down
63 changes: 52 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@
"onDebugResolve:php",
"onCommand:php.debug.debugPhpFile"
],
"capabilities": {
"untrustedWorkspaces": {
"supported": "limited",
"description": "%workspaceTrust%",
"restrictedConfigurations": [
"php.debug.executablePath"
]
}
},
"contributes": {
"breakpoints": [
{
Expand Down Expand Up @@ -250,6 +259,41 @@
"type": "boolean",
"description": "If true, will log all communication between VS Code and the adapter"
},
"proxy": {
"type": "object",
"properties": {
"allowMultipleSessions": {
"type": "boolean",
"description": "If the proxy should expect multiple sessions/connections or not.",
"default": true
},
"enable": {
"type": "boolean",
"description": "Whether to enable usage of a proxy",
"default": false
},
"host": {
"type": "string",
"description": "Selects the host where the debug client is running, you can either use a host name, IP address, or 'unix:///path/to/sock' for a Unix domain socket. This setting is ignored if xdebug.remote_connect_back is enabled.",
"default": "127.0.0.1"
},
"key": {
"type": "string",
"description": "A unique key that allows the proxy to match requests to your editor",
"default": "${config:php.debug.ideKey}"
},
"port": {
"type": "number",
"description": "The port where the adapter will register with the the proxy.",
"default": 9001
},
"timeout": {
"type": "number",
"description": "The port where the adapter will register with the the proxy.",
"default": 3000
}
}
},
"xdebugSettings": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -412,28 +456,25 @@
}
],
"configuration": {
"title": "PHP",
"title": "PHP Debug",
"properties": {
"php.executablePath": {
"php.debug.executablePath": {
"type": [
"string",
"null"
],
"default": null,
"description": "The path to a PHP executable.",
"scope": "machine-overridable"
},
"php.debug.ideKey": {
"type": "string",
"default": "vsc",
"description": "A unique key that allows the proxy to match requests to your editor. Only used when proxy configuration includes replacement.",
"scope": "machine-overridable"
}
}
},
"capabilities": {
"untrustedWorkspaces": {
"supported": "limited",
"description": "%workspaceTrust%",
"restrictedConfigurations": [
"php.executablePath"
]
}
},
"menus": {
"editor/title/run": [
{
Expand Down
2 changes: 1 addition & 1 deletion src/dbgp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as iconv from 'iconv-lite'
import { DOMParser } from 'xmldom'

/** The encoding all Xdebug messages are encoded with */
export const ENCODING = 'iso-8859-1'
export const ENCODING = 'utf-8'

/** The two states the connection switches between */
enum ParsingState {
Expand Down
19 changes: 14 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ export function activate(context: vscode.ExtensionContext) {
!debugConfiguration.runtimeExecutable
) {
// See if we have runtimeExecutable configured
const conf = vscode.workspace.getConfiguration('php')
const executablePath =
conf.get<string>('executablePath') || conf.get<string>('validate.executablePath')
const conf = vscode.workspace.getConfiguration('php.debug')
const executablePath = conf.get<string>('executablePath')
if (executablePath) {
debugConfiguration.runtimeExecutable = executablePath
}
Expand All @@ -45,18 +44,28 @@ export function activate(context: vscode.ExtensionContext) {
await which.default('php')
} catch (e) {
const selected = await vscode.window.showErrorMessage(
'PHP executable not found. Install PHP and add it to your PATH or set the php.executablePath setting',
'PHP executable not found. Install PHP and add it to your PATH or set the php.debug.executablePath setting',
'Open settings'
)
if (selected === 'Open settings') {
await vscode.commands.executeCommand('workbench.action.openGlobalSettings', {
query: 'php.executablePath',
query: 'php.debug.executablePath',
})
return undefined
}
}
}
}
if (debugConfiguration.proxy?.enable === true) {
// Proxy configuration
if (!debugConfiguration.proxy.key) {
const conf = vscode.workspace.getConfiguration('php.debug')
const ideKey = conf.get<string>('ideKey')
if (ideKey) {
debugConfiguration.proxy.key = ideKey
}
}
}
return debugConfiguration
},
})
Expand Down
40 changes: 40 additions & 0 deletions src/phpDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import minimatch = require('minimatch')
import { BreakpointManager, BreakpointAdapter } from './breakpoints'
import * as semver from 'semver'
import { LogPointManager } from './logpoint'
import { ProxyConnect } from './proxyConnect'

if (process.env['VSCODE_NLS_CONFIG']) {
try {
Expand Down Expand Up @@ -71,6 +72,15 @@ export interface LaunchRequestArguments extends VSCodeDebugProtocol.LaunchReques
ignore?: string[]
/** Xdebug configuration */
xdebugSettings?: { [featureName: string]: string | number }
/** proxy connection configuration */
proxy?: {
allowMultipleSessions: boolean
enable: boolean
host: string
key: string
port: number
timeout: number
}

// CLI options

Expand Down Expand Up @@ -158,6 +168,9 @@ class PhpDebugSession extends vscode.DebugSession {
*/
private _logPointManager = new LogPointManager()

/** The proxy initialization and termination connection. */
private _proxyConnect: ProxyConnect

/** the promise that gets resolved once we receive the done request */
private _donePromise: Promise<void>

Expand Down Expand Up @@ -447,6 +460,9 @@ class PhpDebugSession extends vscode.DebugSession {
let port = 0
if (!args.noDebug) {
port = await createServer()
if (args.proxy?.enable === true) {
await this.setupProxy(port)
}
}
if (args.program || args.runtimeArgs) {
await launchScript(port)
Expand All @@ -460,6 +476,26 @@ class PhpDebugSession extends vscode.DebugSession {
this.sendEvent(new vscode.InitializedEvent())
}

private async setupProxy(idePort: number): Promise<void> {
this._proxyConnect = new ProxyConnect(
this._args.proxy!.host,
this._args.proxy!.port,
idePort,
this._args.proxy!.allowMultipleSessions,
this._args.proxy!.key,
this._args.proxy!.timeout
)
const proxyConsole = (str: string) => this.sendEvent(new vscode.OutputEvent(str + '\n'), true)

this._proxyConnect.on('log_request', proxyConsole)
this._proxyConnect.on('log_response', proxyConsole)

this._proxyConnect.on('log_error', (error: Error) => {
this.sendEvent(new vscode.OutputEvent('PROXY ERROR: ' + error.message + '\n', 'stderr'))
})
return this._proxyConnect.sendProxyInitCommand()
}

/**
* Checks the status of a StatusResponse and notifies VS Code accordingly
* @param {xdebug.StatusResponse} response
Expand Down Expand Up @@ -1096,6 +1132,10 @@ class PhpDebugSession extends vscode.DebugSession {
if (this._phpProcess) {
this._phpProcess.kill()
}
// Unregister proxy
if (this._proxyConnect) {
await this._proxyConnect.sendProxyStopCommand()
}
} catch (error) {
this.sendErrorResponse(response, error)
return
Expand Down
Loading

0 comments on commit f5c9534

Please sign in to comment.