@inst/vscode-bin-darwin
Version:
BINARY ONLY - VSCode binary deployment for macOS
191 lines (189 loc) • 7.7 kB
JavaScript
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
;
Object.defineProperty(exports, "__esModule", { value: true });
const cp = require("child_process");
const utilities_1 = require("./utilities");
const net = require("net");
const WSL = require("../wslSupport");
exports.INSPECTOR_PORT_DEFAULT = 9229;
exports.LEGACY_PORT_DEFAULT = 5858;
// For launch, use inspector protocol starting with v8 because it's stable after that version.
const InspectorMinNodeVersionLaunch = 80000;
function detectDebugType(config) {
switch (config.request) {
case 'attach':
return detectProtocolForAttach(config).then(protocol => {
return protocol === 'inspector' ? 'node2' : 'node';
});
case 'launch':
return Promise.resolve(detectProtocolForLaunch(config) === 'inspector' ? 'node2' : 'node');
default:
// should not happen
break;
}
return Promise.resolve(null);
}
exports.detectDebugType = detectDebugType;
/**
* Detect which debug protocol is being used for a running node process.
*/
function detectProtocolForAttach(config) {
const address = config.address || '127.0.0.1';
const port = config.port;
if (config.processId) {
// this is only supported for legacy protocol
utilities_1.log(utilities_1.localize('protocol.switch.attach.process', "Debugging with legacy protocol because attaching to a process by ID is only supported for legacy protocol."));
return Promise.resolve('legacy');
}
const socket = new net.Socket();
const cleanup = () => {
try {
socket.write(`"Content-Length: 50\r\n\r\n{"command":"disconnect","type":"request","seq":2}"`);
socket.end();
}
catch (e) {
// ignore failure
}
};
return new Promise((resolve, reject) => {
socket.once('data', data => {
let reason;
let protocol;
const dataStr = data.toString();
if (dataStr.indexOf('WebSockets request was expected') >= 0) {
reason = utilities_1.localize('protocol.switch.inspector.detected', "Debugging with inspector protocol because it was detected.");
protocol = 'inspector';
}
else {
reason = utilities_1.localize('protocol.switch.legacy.detected', "Debugging with legacy protocol because it was detected.");
protocol = 'legacy';
}
resolve({ reason, protocol });
});
socket.once('error', err => {
reject(err);
});
socket.connect(port, address);
socket.on('connect', () => {
// Send a safe request to trigger a response from the inspector protocol
socket.write(`Content-Length: 102\r\n\r\n{"command":"evaluate","arguments":{"expression":"process.pid","global":true},"type":"request","seq":1}`);
});
setTimeout(() => {
// No data or error received? Bail and let the debug adapter handle it.
reject(new Error('timeout'));
}, 2000);
}).catch(err => {
return {
reason: utilities_1.localize('protocol.switch.unknown.error', "Debugging with legacy protocol because Node.js version could not be determined ({0})", err.toString()),
protocol: 'legacy'
};
}).then(result => {
cleanup();
utilities_1.log(result.reason);
return result.protocol;
});
}
function detectProtocolForLaunch(config) {
if (config.runtimeExecutable) {
utilities_1.log(utilities_1.localize('protocol.switch.runtime.set', "Debugging with inspector protocol because a runtime executable is set."));
return 'inspector';
}
else {
// only determine version if no runtimeExecutable is set (and 'node' on PATH is used)
const result = WSL.spawnSync(config.useWSL, 'node', ['--version']);
const semVerString = result.stdout ? result.stdout.toString() : undefined;
if (semVerString) {
config.__nodeVersion = semVerString.trim();
if (semVerStringToInt(config.__nodeVersion) >= InspectorMinNodeVersionLaunch) {
utilities_1.log(utilities_1.localize('protocol.switch.inspector.version', "Debugging with inspector protocol because Node.js {0} was detected.", config.__nodeVersion));
return 'inspector';
}
else {
utilities_1.log(utilities_1.localize('protocol.switch.legacy.version', "Debugging with legacy protocol because Node.js {0} was detected.", config.__nodeVersion));
}
}
else {
utilities_1.log(utilities_1.localize('protocol.switch.unknown.version', "Debugging with legacy protocol because Node.js version could not be determined."));
}
}
return undefined;
}
/**
* convert the 3 parts of a semVer string into a single number
*/
function semVerStringToInt(vString) {
const match = vString.match(/v(\d+)\.(\d+)\.(\d+)/);
if (match && match.length === 4) {
return (parseInt(match[1]) * 100 + parseInt(match[2])) * 100 + parseInt(match[3]);
}
return -1;
}
function detectProtocolForPid(pid) {
return process.platform === 'win32' ?
detectProtocolForPidWin(pid) :
detectProtocolForPidUnix(pid);
}
exports.detectProtocolForPid = detectProtocolForPid;
function detectProtocolForPidWin(pid) {
return getOpenPortsForPidWin(pid).then(ports => {
return ports.indexOf(exports.INSPECTOR_PORT_DEFAULT) >= 0 ? 'inspector' :
ports.indexOf(exports.LEGACY_PORT_DEFAULT) >= 0 ? 'legacy' : null;
});
}
/**
* Netstat output is like:
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 812
*/
function getOpenPortsForPidWin(pid) {
return new Promise(resolve => {
cp.exec('netstat -a -n -o -p TCP', (err, stdout) => {
if (err || !stdout) {
resolve([]);
}
const ports = stdout
.split(/\r?\n/)
.map(line => line.trim().split(/\s+/))
.filter(lineParts => {
// Filter to just `pid` rows
return lineParts[4] && lineParts[4] === String(pid);
})
.map(lineParts => {
const address = lineParts[1];
return parseInt(address.split(':')[1]);
});
resolve(ports);
});
});
}
function detectProtocolForPidUnix(pid) {
return getPidListeningOnPortUnix(exports.INSPECTOR_PORT_DEFAULT).then(inspectorProtocolPid => {
if (inspectorProtocolPid === pid) {
return 'inspector';
}
else {
return getPidListeningOnPortUnix(exports.LEGACY_PORT_DEFAULT)
.then(legacyProtocolPid => legacyProtocolPid === pid ? 'legacy' : null);
}
});
}
function getPidListeningOnPortUnix(port) {
return new Promise(resolve => {
cp.exec(`lsof -i:${port} -F p`, (err, stdout) => {
if (err || !stdout) {
resolve(-1);
return;
}
const pidMatch = stdout.match(/p(\d+)/);
if (pidMatch && pidMatch[1]) {
resolve(Number(pidMatch[1]));
}
else {
resolve(-1);
}
});
});
}
//# sourceMappingURL=../../../out/node/extension/protocolDetection.js.map