diff --git a/.vscode/launch.json b/.vscode/launch.json index 0741e05..37ae871 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -90,15 +90,7 @@ "NODE_DEBUG": "", "ENABLE_LONG_RUNNING_TESTS": "" } - }, - { - "type": "node", - "request": "launch", - "name": "Server", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/resources/debug/debugAdapter.js", - "args": [ "--server=4711" ] - }, + } ], "compounds": [ { diff --git a/src/commands/debugPolicies/debugPolicy.ts b/src/commands/debugPolicies/debugPolicy.ts index 5e89e0d..2aa152d 100644 --- a/src/commands/debugPolicies/debugPolicy.ts +++ b/src/commands/debugPolicies/debugPolicy.ts @@ -11,6 +11,7 @@ import { IOperationTreeRoot } from '../../explorer/IOperationTreeRoot'; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; import { nameUtil } from '../../utils/nameUtil'; +import { IServiceTreeRoot } from '../../explorer/IServiceTreeRoot'; export async function debugPolicy(context: IActionContext, node?: ApiOperationTreeItem): Promise { if (!node) { @@ -18,6 +19,11 @@ export async function debugPolicy(context: IActionContext, node?: ApiOperationTr node = await ext.tree.showTreeItemPicker(ApiOperationTreeItem.contextValue, context); } + if (!await isDebuggingSupported(node.root)) { + vscode.window.showInformationMessage(localize("onlyDevDebugging", "Debugging is only supported on Developer SKU of API Management services.")); + return; + } + const operationData = await node.getOperationDebugInfo(); const debugConfig: vscode.DebugConfiguration = { type: "apim-policy", @@ -56,6 +62,11 @@ export async function debugPolicy(context: IActionContext, node?: ApiOperationTr } } +async function isDebuggingSupported(service: IServiceTreeRoot): Promise { + const svc = await service.client.apiManagementService.get(service.resourceGroupName, service.serviceName/*, { customHeaders: { Authorization: credentials.tokenCache._entries[0].accessToken } }*/); + return svc.sku.name === "Developer"; +} + function getManagementUrl(root: IOperationTreeRoot): string { return `${root.environment.resourceManagerEndpointUrl}/subscriptions/${root.subscriptionId}/resourceGroups/${root.resourceGroupName}/providers/microsoft.apimanagement/service/${root.serviceName}`; } diff --git a/src/debugger/apimDebug.ts b/src/debugger/apimDebug.ts index 86d7dc0..7e11969 100644 --- a/src/debugger/apimDebug.ts +++ b/src/debugger/apimDebug.ts @@ -45,7 +45,7 @@ export class ApimDebugSession extends LoggingDebugSession { private policySource: PolicySource; private variablesHandles = new Handles(); - public constructor() { + public constructor(private sessionKey: string) { super(); this.setDebuggerLinesStartAt1(false); @@ -81,6 +81,16 @@ export class ApimDebugSession extends LoggingDebugSession { this.configurationDone.notify(); } + public handleMessage(request: DebugProtocol.Request): void { + // If request came from VSCode there must be session key present, if not - ignore the message + if (this.sessionKey !== request.arguments?._sessionKey) { + return; + } + + delete request.arguments._sessionKey; + super.handleMessage(request); + } + protected async launchRequest(response: DebugProtocol.LaunchResponse, args: ILaunchRequestArguments): Promise { logger.setup(Logger.LogLevel.Verbose, false); let masterKey; diff --git a/src/debugger/debugAdapter.ts b/src/debugger/debugAdapter.ts deleted file mode 100644 index f5432c1..0000000 --- a/src/debugger/debugAdapter.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - - import { ApimDebugSession } from './apimDebug'; - - ApimDebugSession.run(ApimDebugSession); diff --git a/src/debugger/extension.ts b/src/debugger/extension.ts index c018fcd..d0da5ae 100644 --- a/src/debugger/extension.ts +++ b/src/debugger/extension.ts @@ -4,6 +4,7 @@ import * as Net from 'net'; import * as vscode from 'vscode'; import { CancellationToken, DebugConfiguration, ProviderResult, WorkspaceFolder } from 'vscode'; import { ApimDebugSession } from './apimDebug'; +import * as Crypto from 'crypto'; // tslint:disable: no-unsafe-any // tslint:disable: indent @@ -21,6 +22,8 @@ export function activate(context: vscode.ExtensionContext) { const factory = new ApimPolicyDebugAdapterDescriptorFactory(); context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('apim-policy', factory)); context.subscriptions.push(factory); + + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('apim-policy', new ApimPolicyDebugSessionKeyAppenderFactory())); } export function deactivate() {} @@ -36,24 +39,53 @@ class ApimPolicyConfigurationProvider implements vscode.DebugConfigurationProvid config.stopOnEntry = true; } } + return config; } } -class ApimPolicyDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { +class ApimPolicyDebugSessionKeyAppenderFactory implements vscode.DebugAdapterTrackerFactory { + createDebugAdapterTracker(session: vscode.DebugSession): vscode.ProviderResult { + return new ApimPolicyDebugSessionKeyAppender(session); + } +} + +class ApimPolicyDebugSessionKeyAppender implements vscode.DebugAdapterTracker { + private sessionKey: string; + + constructor(_session: vscode.DebugSession) { + this.sessionKey = _session.configuration.key; + } + onWillReceiveMessage(message: any) { + if (!message.arguments) { + message.arguments = {}; + } + + //Append the session key to every message we send + message.arguments._sessionKey = this.sessionKey; + } +} + +class ApimPolicyDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { private server?: Net.Server; + private serverKey: string; public createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult { if (!this.server) { + // generate a random key for further the debug sessions + this.serverKey = Crypto.randomBytes(16).toString('hex'); + // start listening on a random port this.server = Net.createServer(socket => { - const session = new ApimDebugSession(); + const session = new ApimDebugSession(this.serverKey); session.setRunAsServer(true); session.start(socket, socket); }).listen(0); } + _session.configuration.key = this.serverKey; + // make VS Code connect to debug server return new vscode.DebugAdapterServer(this.server.address().port); }