Skip to content

Commit

Permalink
feat: Unix sockets (#777)
Browse files Browse the repository at this point in the history
* Experimental PIPE and Unix Domain listen.

* Docs.

* Use hostname instead of port, same format as Xdebug.
  • Loading branch information
zobo committed Apr 11, 2022
1 parent 401feaa commit d2bf23f
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ More general information on debugging with VS Code can be found on https://code.
#### Supported launch.json settings:
- `request`: Always `"launch"`
- `hostname`: The address to bind to when listening for Xdebug (default: all IPv6 connections if available, else all IPv4 connections)
- `hostname`: The address to bind to when listening for Xdebug (default: all IPv6 connections if available, else all IPv4 connections) or Unix Domain socket (prefix with `unix://`) or Windows Pipe (`\\?\pipe\name`) - cannot be combined with port
- `port`: The port on which to listen for Xdebug (default: `9003`). If port is set to `0` a random port is chosen by the system and a placeholder `${port}` is replaced with the chosen port in `env` and `runtimeArgs`.
- `stopOnEntry`: Whether to break at the beginning of the script (default: `false`)
- `pathMappings`: A list of server paths mapping to the local source paths on your machine, see "Remote Host Debugging" below
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@
},
"hostname": {
"type": "string",
"description": "Address to bind to when listening for Xdebug",
"description": "Address to bind to when listening for Xdebug or Unix domain socket (start with unix://)",
"default": "::"
},
"port": {
Expand Down
46 changes: 37 additions & 9 deletions src/phpDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function formatPropertyValue(property: xdebug.BaseProperty): string {
* This interface should always match the schema found in the mock-debug extension manifest.
*/
export interface LaunchRequestArguments extends VSCodeDebugProtocol.LaunchRequestArguments {
/** The address to bind to for listening for Xdebug connections (default: all IPv6 connections if available, else all IPv4 connections) */
/** The address to bind to for listening for Xdebug connections (default: all IPv6 connections if available, else all IPv4 connections) or unix socket */
hostname?: string
/** The port where the adapter should listen for Xdebug connections (default: 9003) */
port?: number
Expand Down Expand Up @@ -249,7 +249,7 @@ class PhpDebugSession extends vscode.DebugSession {
})

/** launches the script as CLI */
const launchScript = async (port: number) => {
const launchScript = async (port: number | string) => {
// check if program exists
if (args.program) {
await new Promise<void>((resolve, reject) =>
Expand Down Expand Up @@ -306,7 +306,7 @@ class PhpDebugSession extends vscode.DebugSession {
}
/** sets up a TCP server to listen for Xdebug connections */
const createServer = () =>
new Promise<number>((resolve, reject) => {
new Promise<number | string>((resolve, reject) => {
const server = (this._server = net.createServer())
server.on('connection', async (socket: net.Socket) => {
try {
Expand Down Expand Up @@ -461,17 +461,45 @@ class PhpDebugSession extends vscode.DebugSession {
if (args.log) {
this.sendEvent(new vscode.OutputEvent(`Listening on ${util.inspect(server.address())}\n`), true)
}
const port = (server.address() as net.AddressInfo).port
resolve(port)
if (typeof server.address() === 'string') {
resolve(<string>server.address())
} else {
const port = (server.address() as net.AddressInfo).port
resolve(port)
}
})
const listenPort = args.port === undefined ? 9003 : args.port
server.listen(listenPort, args.hostname)
if (
args.port !== undefined &&
(args.hostname?.toLowerCase()?.startsWith('unix://') === true ||
args.hostname?.startsWith('\\\\') === true)
) {
throw new Error('Cannot have port and socketPath set at the same time')
}
if (args.hostname?.toLowerCase()?.startsWith('unix://') === true) {
server.listen(args.hostname.substring(7))
} else if (args.hostname?.startsWith('\\\\') === true) {
server.listen(args.hostname)
} else {
const listenPort = args.port === undefined ? 9003 : args.port
server.listen(listenPort, args.hostname)
}
})
try {
let port = 0
// Some checks
if (args.env !== undefined && args.program === undefined && args.runtimeArgs === undefined) {
throw new Error('Cannot set env without running a program (or ')
}
if (
(args.hostname?.toLowerCase()?.startsWith('unix://') === true ||
args.hostname?.startsWith('\\\\') === true) &&
args.proxy?.enable === true
) {
throw new Error('Proxy does not support socket path listen, only port.')
}
let port = <number | string>0
if (!args.noDebug) {
port = await createServer()
if (args.proxy?.enable === true) {
if (typeof port === 'number' && args.proxy?.enable === true) {
await this.setupProxy(port)
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/test/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ describe('PHP Debug Adapter', () => {
Promise.all([client.launch({ program: 'thisfiledoesnotexist.php' }), client.configurationSequence()])
))

it('should error on env without program', () =>
assert.isRejected(Promise.all([client.launch({ env: {} }), client.configurationSequence()])))

it('should run program to the end', () =>
Promise.all([
client.launch({ program }),
Expand All @@ -63,6 +66,33 @@ describe('PHP Debug Adapter', () => {
]))
})

describe('socket path listen', () => {
const program = path.join(TEST_PROJECT, 'hello_world.php')

it('should error on port and socketPath', () =>
assert.isRejected(
Promise.all([client.launch({ port: 9003, hostname: 'unix:///test' }), client.configurationSequence()])
))
;(process.platform === 'win32' ? it : it.skip)('should listen on windows pipe', async () => {
await Promise.all([
client.launch({ program, hostname: '\\\\?\\pipe\\test' }),
client.configurationSequence(),
])
await client.disconnectRequest()
})
;(process.platform === 'win32' ? it.skip : it)('should listen on unix pipe', () => {
Promise.all([
client.launch({
program,
hostname: 'unix:///tmp/test',
runtimeArgs: ['-dxdebug.client_host=unix:///tmp/text'],
}),
client.configurationSequence(),
client.waitForEvent('terminated'),
])
})
})

describe('continuation commands', () => {
const program = path.join(TEST_PROJECT, 'function.php')

Expand Down

0 comments on commit d2bf23f

Please sign in to comment.