mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-27 10:37:38 +01:00
Merge pull request #103210 from TylerLeonhardt/add-debug-adapter-named-pipe-server
Add DebugAdapterNamedPipeServer
This commit is contained in:
Vendored
+16
-1
@@ -10899,6 +10899,21 @@ declare module 'vscode' {
|
||||
constructor(port: number, host?: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server.
|
||||
*/
|
||||
export class DebugAdapterNamedPipeServer {
|
||||
/**
|
||||
* The path to the NamedPipe/UNIX Domain Socket.
|
||||
*/
|
||||
readonly path: string;
|
||||
|
||||
/**
|
||||
* Create a description for a debug adapter running as a socket based server.
|
||||
*/
|
||||
constructor(path: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* A debug adapter that implements the Debug Adapter Protocol can be registered with VS Code if it implements the DebugAdapter interface.
|
||||
*/
|
||||
@@ -10937,7 +10952,7 @@ declare module 'vscode' {
|
||||
constructor(implementation: DebugAdapter);
|
||||
}
|
||||
|
||||
export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterInlineImplementation;
|
||||
export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation;
|
||||
|
||||
export interface DebugAdapterDescriptorFactory {
|
||||
/**
|
||||
|
||||
@@ -1019,6 +1019,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
ConfigurationTarget: extHostTypes.ConfigurationTarget,
|
||||
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
|
||||
DebugAdapterServer: extHostTypes.DebugAdapterServer,
|
||||
DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer,
|
||||
DebugAdapterInlineImplementation: extHostTypes.DebugAdapterInlineImplementation,
|
||||
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
|
||||
Diagnostic: extHostTypes.Diagnostic,
|
||||
|
||||
@@ -11,12 +11,12 @@ import {
|
||||
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
|
||||
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
|
||||
} from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode, DebugAdapterInlineImplementation } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor, IDebugAdapterImpl } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor, IDebugAdapterImpl, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration';
|
||||
@@ -737,6 +737,11 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
port: x.port,
|
||||
host: x.host
|
||||
};
|
||||
} else if (x instanceof DebugAdapterNamedPipeServer) {
|
||||
return <IDebugAdapterNamedPipeServer>{
|
||||
type: 'pipeServer',
|
||||
path: x.path
|
||||
};
|
||||
} else if (x instanceof DebugAdapterInlineImplementation) {
|
||||
return <IDebugAdapterImpl>{
|
||||
type: 'implementation',
|
||||
|
||||
@@ -2294,6 +2294,12 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer {
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class DebugAdapterNamedPipeServer implements vscode.DebugAdapterNamedPipeServer {
|
||||
constructor(public readonly path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class DebugAdapterInlineImplementation implements vscode.DebugAdapterInlineImplementation {
|
||||
readonly implementation: vscode.DebugAdapter;
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
@@ -49,6 +49,8 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
switch (adapter.type) {
|
||||
case 'server':
|
||||
return new SocketDebugAdapter(adapter);
|
||||
case 'pipeServer':
|
||||
return new NamedPipeDebugAdapter(adapter);
|
||||
case 'executable':
|
||||
return new ExecutableDebugAdapter(adapter, session.type);
|
||||
}
|
||||
|
||||
@@ -573,6 +573,11 @@ export interface IDebugAdapterServer {
|
||||
readonly host?: string;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterNamedPipeServer {
|
||||
readonly type: 'pipeServer';
|
||||
readonly path: string;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterInlineImpl extends IDisposable {
|
||||
readonly onDidSendMessage: Event<DebugProtocol.Message>;
|
||||
handleMessage(message: DebugProtocol.Message): void;
|
||||
@@ -583,7 +588,7 @@ export interface IDebugAdapterImpl {
|
||||
readonly implementation: IDebugAdapterInlineImpl;
|
||||
}
|
||||
|
||||
export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterImpl;
|
||||
export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterNamedPipeServer | IDebugAdapterImpl;
|
||||
|
||||
export interface IPlatformSpecificAdapterContribution {
|
||||
program?: string;
|
||||
|
||||
@@ -14,7 +14,7 @@ import * as objects from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { AbstractDebugAdapter } from '../common/abstractDebugAdapter';
|
||||
|
||||
@@ -91,25 +91,22 @@ export abstract class StreamDebugAdapter extends AbstractDebugAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation that connects to a debug adapter via a socket.
|
||||
*/
|
||||
export class SocketDebugAdapter extends StreamDebugAdapter {
|
||||
export abstract class NetworkDebugAdapter extends StreamDebugAdapter {
|
||||
|
||||
private socket?: net.Socket;
|
||||
protected socket?: net.Socket;
|
||||
|
||||
constructor(private adapterServer: IDebugAdapterServer) {
|
||||
super();
|
||||
}
|
||||
protected abstract createConnection(connectionListener: () => void): net.Socket;
|
||||
|
||||
startSession(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let connected = false;
|
||||
this.socket = net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', () => {
|
||||
|
||||
this.socket = this.createConnection(() => {
|
||||
this.connect(this.socket!, this.socket!);
|
||||
resolve();
|
||||
connected = true;
|
||||
});
|
||||
|
||||
this.socket.on('close', () => {
|
||||
if (connected) {
|
||||
this._onError.fire(new Error('connection closed'));
|
||||
@@ -117,6 +114,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter {
|
||||
reject(new Error('connection closed'));
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('error', error => {
|
||||
if (connected) {
|
||||
this._onError.fire(error);
|
||||
@@ -136,6 +134,34 @@ export class SocketDebugAdapter extends StreamDebugAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation that connects to a debug adapter via a socket.
|
||||
*/
|
||||
export class SocketDebugAdapter extends NetworkDebugAdapter {
|
||||
|
||||
constructor(private adapterServer: IDebugAdapterServer) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected createConnection(connectionListener: () => void): net.Socket {
|
||||
return net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', connectionListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation that connects to a debug adapter via a NamedPipe (on Windows)/UNIX Domain Socket (on non-Windows).
|
||||
*/
|
||||
export class NamedPipeDebugAdapter extends NetworkDebugAdapter {
|
||||
|
||||
constructor(private adapterServer: IDebugAdapterNamedPipeServer) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected createConnection(connectionListener: () => void): net.Socket {
|
||||
return net.createConnection(this.adapterServer.path, connectionListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation that launches the debug adapter as a separate process and communicates via stdin/stdout.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as crypto from 'crypto';
|
||||
import * as net from 'net';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { SocketDebugAdapter, NamedPipeDebugAdapter, StreamDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
|
||||
function rndPort(): number {
|
||||
const min = 8000;
|
||||
const max = 9000;
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
}
|
||||
|
||||
function sendInitializeRequest(debugAdapter: StreamDebugAdapter): Promise<DebugProtocol.Response> {
|
||||
return new Promise((resolve, reject) => {
|
||||
debugAdapter.sendRequest('initialize', { adapterID: 'test' }, (result) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function serverConnection(socket: net.Socket) {
|
||||
socket.on('data', (data: Buffer) => {
|
||||
const str = data.toString().split('\r\n')[2];
|
||||
const request = JSON.parse(str);
|
||||
const response: any = {
|
||||
seq: request.seq,
|
||||
request_seq: request.seq,
|
||||
type: 'response',
|
||||
command: request.command
|
||||
};
|
||||
if (request.arguments.adapterID === 'test') {
|
||||
response.success = true;
|
||||
} else {
|
||||
response.success = false;
|
||||
response.message = 'failed';
|
||||
}
|
||||
|
||||
const responsePayload = JSON.stringify(response);
|
||||
socket.write(`Content-Length: ${responsePayload.length}\r\n\r\n${responsePayload}`);
|
||||
});
|
||||
}
|
||||
|
||||
suite('Debug - StreamDebugAdapter', () => {
|
||||
const port = rndPort();
|
||||
const pipeName = crypto.randomBytes(10).toString('utf8');
|
||||
const pipePath = platform.isWindows ? join('\\\\.\\pipe\\', pipeName) : join(tmpdir(), pipeName);
|
||||
|
||||
const testCases: { testName: string, debugAdapter: StreamDebugAdapter, connectionDetail: string | number }[] = [
|
||||
{
|
||||
testName: 'NamedPipeDebugAdapter',
|
||||
debugAdapter: new NamedPipeDebugAdapter({
|
||||
type: 'pipeServer',
|
||||
path: pipePath
|
||||
}),
|
||||
connectionDetail: pipePath
|
||||
},
|
||||
{
|
||||
testName: 'SocketDebugAdapter',
|
||||
debugAdapter: new SocketDebugAdapter({
|
||||
type: 'server',
|
||||
port
|
||||
}),
|
||||
connectionDetail: port
|
||||
}
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
test(`StreamDebugAdapter (${testCase.testName}) can initialize a connection`, async () => {
|
||||
const server = net.createServer(serverConnection).listen(testCase.connectionDetail);
|
||||
const debugAdapter = testCase.debugAdapter;
|
||||
try {
|
||||
await debugAdapter.startSession();
|
||||
const response: DebugProtocol.Response = await sendInitializeRequest(debugAdapter);
|
||||
assert.strictEqual(response.command, 'initialize');
|
||||
assert.strictEqual(response.request_seq, 1);
|
||||
assert.strictEqual(response.success, true, response.message);
|
||||
} finally {
|
||||
await debugAdapter.stopSession();
|
||||
server.close();
|
||||
debugAdapter.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user