diff --git a/src/nodeDebugAdapter.ts b/src/nodeDebugAdapter.ts index 49df05f9..c4e1e31c 100644 --- a/src/nodeDebugAdapter.ts +++ b/src/nodeDebugAdapter.ts @@ -21,6 +21,9 @@ import * as wsl from './wslSupport'; import * as nls from 'vscode-nls'; import { FinishedStartingUpEventArguments } from 'vscode-chrome-debug-core/lib/src/executionTimingsReporter'; + +import * as NiMS from './nodeInspectMetadataServer'; + let localize = nls.loadMessageBundle(); const DefaultSourceMapPathOverrides: ISourceMapPathOverrides = { @@ -387,6 +390,9 @@ export class NodeDebugAdapter extends ChromeDebugAdapter { } } + NiMS.addSession(launchArgs); + logger.warn('NiMS listening on ' + NiMS.getSocket()); + this.logLaunchCommand(runtimeExecutable, launchArgs); spawnOpts.detached = this.supportsTerminateRequest; // https://github.com/Microsoft/vscode/issues/57018 const nodeProcess = cp.spawn(runtimeExecutable, launchArgs, spawnOpts); @@ -397,6 +403,7 @@ export class NodeDebugAdapter extends ChromeDebugAdapter { const msg = `Node process error: ${error}`; logger.error(msg); this.terminateSession(msg); + NiMS.removeSession(launchArgs); }); nodeProcess.on('exit', () => { const msg = 'Target exited'; @@ -404,6 +411,7 @@ export class NodeDebugAdapter extends ChromeDebugAdapter { if (!this.isExtensionHost()) { this.terminateSession(msg); } + NiMS.removeSession(launchArgs); }); nodeProcess.on('close', (code) => { const msg = 'Target closed'; @@ -411,6 +419,7 @@ export class NodeDebugAdapter extends ChromeDebugAdapter { if (!this.isExtensionHost()) { this.terminateSession(msg); } + NiMS.removeSession(launchArgs); }); const noDebugMode = (this._launchAttachArgs).noDebug; @@ -492,7 +501,7 @@ export class NodeDebugAdapter extends ChromeDebugAdapter { const r = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/); if (r !== null) { const key = r[1]; - if (!process.env[key]) { // .env variables never overwrite existing variables (see #21169) + if (!process.env[key]) { // .env variables never overwrite existing variables (see #21169) let value = r[2] || ''; if (value.length > 0 && value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') { value = value.replace(/\\n/gm, '\n'); diff --git a/src/nodeInspectMetadataServer.ts b/src/nodeInspectMetadataServer.ts new file mode 100644 index 00000000..cbfad616 --- /dev/null +++ b/src/nodeInspectMetadataServer.ts @@ -0,0 +1,116 @@ +import { logger } from 'vscode-chrome-debug-core'; +const http = require('http'), + os = require('os'), + HOST = '127.0.0.1', + PORT = 6607; + +class NodeInspectMetaServer { + sessions: any[]; + + constructor() { + let self = this; + self.sessions = []; + + const socketStatusServer = http.createServer((req, res) => { + let out = '', + contentType = 'text/html'; + + if (req.url === '/json') { + contentType = 'application/json'; + out = JSON.stringify(self.sessions); + } else { + let json = JSON.stringify(self.sessions); + out = ` + + + Node.js --inspect Metadata Server + + +
${ (self.sessions.length == 0) ? json :
+                json.replace(/\[/g, '[' + os.EOL)
+                .replace(/\]/g, os.EOL + ']')
+                .replace(/\]/g, os.EOL + ']')
+                .replace(/\{/g, '  {' + os.EOL + '    ')
+                .replace(/\}/g, os.EOL + '  }')
+                .replace(/,/g, ',' + os.EOL + '    ')
+              }
+              
+ + + `; + } + res.writeHead(200, { 'Content-Type': contentType + '; charset=UTF-8' }); + res.end(out); + }); + socketStatusServer.listen(PORT, HOST, () => { + logger.log('Node --inspect Metadata Server listening on ' + HOST + ':' + PORT); + }); + } + getSessionMeta(inspectPort) { + return new Promise((resolve, reject) => { + http.get('http://127.0.0.1:' + inspectPort + '/json', (res) => { + const { statusCode } = res; + const contentType = res.headers['content-type']; + + let error; + if (statusCode !== 200) { + error = new Error('Request Failed.\n' + + `Status Code: ${statusCode}`); + } else if (!/^application\/json/.test(contentType)) { + error = new Error('Invalid content-type.\n' + + `Expected application/json but received ${contentType}`); + } + if (error) { + logger.error(error.message); + // consume response data to free up memory + res.resume(); + return; + } + + res.setEncoding('utf8'); + let rawData = ''; + res.on('data', (chunk) => { rawData += chunk; }); + res.on('end', () => { + try { + const parsedData = JSON.parse(rawData); + resolve(parsedData[0]); + } catch (e) { + reject(e.message); + } + }); + }).on('error', (e) => { + reject(`Got error: ${e.message}`); + }); + }); + } + getSocket() { + return HOST + ':' + PORT; + } + getInspectPort(launchArgs) { + return launchArgs.find(arg => arg.includes('--inspect')).split('=')[1]; + } + addSession(launchArgs) { + let self = this; + + let inspectPort = self.getInspectPort(launchArgs); + self.getSessionMeta(inspectPort) + .then((meta:any) => { + Object.assign(meta, { + inspectSocket: '127.0.0.1:' + inspectPort, + nodeExeRunner: 'vscode' + }); + let index = self.sessions.findIndex(session => session.id === meta.id); + if (index == -1) self.sessions.push(meta); + else self.sessions[index] = meta; + }); + } + removeSession(launchArgs) { + let self = this; + let inspectPort = self.getInspectPort(launchArgs); + let index = self.sessions.findIndex(session => session.inspectSocket === '127.0.0.1:' + inspectPort); + self.sessions.splice(index, 1); + } +} + +let NiMS = new NodeInspectMetaServer(); +export = NiMS; \ No newline at end of file