Skip to content

Instantly share code, notes, and snippets.

@djabraham
Last active March 5, 2016 06:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djabraham/19af4ea435dd94418af0 to your computer and use it in GitHub Desktop.
Save djabraham/19af4ea435dd94418af0 to your computer and use it in GitHub Desktop.
//
// THIS IS NO LONGER NECESSARY, VSCODE CAN DO REMOTE DEBUGGING NOW..
//
// Published @ http://code.xpotent.com/2015/12/debugging-nodejs-within-docker.html
//
// VSCode requests a PID at the onset of a debugging session and monitors the process
// to detect when it is no longer running. For some reason, VSCode does not rely on socket
// state to determine this. VSCode monitors the pid and will exit if it isn't active.
//
// This proxy detects the pid within one of the initial debug messages from a node instance
// on a remote server, then changes it to the local pid of this debug-proxy process.
//
// example request:
// {"command":"evaluate","arguments":{"expression":"process.pid","global":true},"type":"request","seq":1}
//
// example response:
// {"seq":59,"request_seq":1,"type":"response","command":"evaluate","success":true,"body":
// {"handle":0,"type":"number","value":<PID>,"text":"<PID>"},"refs":[],"running":true}
//
// Node must be started using the --debug flag, and preferably the --nolazy flag too.
//
// node --debug --nolazy app.js
// node --debug-brk --nolazy app.js
// node --debug=5858 --nolazy app.js
//
// Note that VSCode also emits absolute path to the remote node debugger; therefore, it is
// necessary to mount the source folder to the same location within the file system as the
// server's. Within OSX, this can be done using bind and osxfuse. Bind will need to be
// downloaded and built, while osxfuse can be installed.
//
// $ sudo mkdir /server-app
// $ sudo bindfs $(pwd)/local-source /server-app
//
// $ node debug-proxy 5860 5858
//
// VERSION
//
// 2015-11-30 Dan Abraham - Initial release.
// 2015-12-04 Dan Abraham - Bug fixes, during initialization.
//
var net = require("net");
var spawn = require("child_process").spawn;
if (process.argv.length < 4) {
console.log("\r\n\r\n usage: node debug-proxy.js <local-port> <remote-port>\r\n\r\n");
process.exit(1);
}
var localHost = "127.0.0.1";
var nodeAppName = process.argv[0];
var localClientPort = parseInt(process.argv[2]);
var remoteProcessPort = parseInt(process.argv[3]);
var outputPackets = (process.argv[4]);
console.log("");
console.log("Local Port: ", localClientPort);
console.log("Remote Port: ", remoteProcessPort);
console.log("");
var remoteProcessSocket = null;
var localEditorSocket = null;
var localChildProcess = null;
var transactionNumber = 0;
var transactionNumberPid = -1;
var transactionTriggerPid = '{"expression":"process.pid","global":true}';
process.on("SIGTERM", function (code) {
console.log("debugger-proxy: terminated (SIGTERM) (code: " + code + ")");
localEditorShutdown();
remoteProcessDetach();
});
process.once("uncaughtException", function (error) {
console.log("debugger-proxy: fatal error, shutdown initiated");
console.log(error.stack);
localEditorShutdown();
remoteProcessDetach();
process.exit();
});
function remoteProcessDetach() {
if (remoteProcessSocket) {
console.log("debugger-proxy: executed remoteProcessDetach");
remoteProcessSocket.end();
remoteProcessSocket.unref();
remoteProcessSocket = null;
}
}
// Attaching to remote node process
function remoteProcessAttach() {
console.log("debugger-proxy: attempting remoteProcessAttach");
if (remoteProcessSocket == null) {
remoteProcessSocket = new net.Socket();
remoteProcessSocket.on("data", function (data) { 
var outString = data.toString();
if (outputPackets) {
console.log("\r\n------------------- RESPONSE (trans: " + transactionNumber + ")\r\n");
console.log(outString);
}
// Confirming it's a JSON payload, PID is expected to be a small and complete response (1 packet)
var jsonDelimiterIndex = outString.indexOf('{');
if ((transactionNumberPid === transactionNumber) && (jsonDelimiterIndex > 0) &&
(outString.indexOf('type":"response","command":"evaluate","success":true') > 0)) {
var outObject = JSON.parse(outString.substr(jsonDelimiterIndex));
var originalPid = outObject.body.value;
outObject.body.value = process.pid;
outObject.body.text = process.pid.toString();
var outStringNew = JSON.stringify(outObject);
var outPacketNew = "Content-Length: " + (outStringNew.length) + "\r\n\r\n" + outStringNew;
localEditorSocket.write(new Buffer(outPacketNew));
console.log("\r\nPID Adjustment From (" + originalPid + ") To (" + outObject.body.value + ")\r\n");
console.log(outPacketNew);
} else {
if (localEditorSocket != null) {
localEditorSocket.write(data);
}
}
});
remoteProcessSocket.on("close", function () {
console.log("debugger-proxy: remoteProcessSocket closed");
remoteProcessSocket.unref();
remoteProcessSocket = null;
});
remoteProcessSocket.on("connect", function () {
console.log("debugger-proxy: remoteProcessSocket connected");
});
remoteProcessSocket.on("error", function (err) {
console.log("debugger-proxy: remoteProcessSocket error");
console.log(err);
remoteProcessSocket.unref();
});
remoteProcessSocket.connect(remoteProcessPort, localHost, function () {
console.log("debugger-proxy: remoteProcessSocket created (" + localHost + ":" + remoteProcessPort + ")");
});
}
return;
}
// Start socket server listening for local connection from editor
var localServer = net.createServer(function (socket) {
// Service only handles one editor connection at a time
if (localEditorSocket != null) {
console.log("debugger-proxy: localEditorSocket connection rejected - already connected");
return;
}
console.log("");
console.log("debugger-proxy: localEditorSocket connected, attaching to remoteProcessSocket");
if (!remoteProcessSocket) remoteProcessAttach();
// Wire up local socket server
transactionNumber = 0;
localEditorSocket = socket;
localEditorSocket.on("data", function (data) {
transactionNumber++;
var inString = data.toString();
if (outputPackets) {
console.log("\r\n------------------- REQUEST (trans: " + transactionNumber + ")\r\n");
console.log(inString);
}
// Flagging VSCode PID requests, so the can be changed on the inbound trip
if (inString.indexOf(transactionTriggerPid) > 0)
transactionNumberPid = transactionNumber;
if (remoteProcessSocket != null) {
remoteProcessSocket.write(data);
} else {
console.log("debugger-proxy: error connecting to remote process debugger");
}
});
localEditorSocket.on("close", function (data) {
console.log("debugger-proxy: localEditorSocket disconnected (trans: " + transactionNumber + ")");
localEditorSocket = null;
});
});
function localEditorListen() {
localServer.listen(localClientPort, localHost);
console.log("debugger-proxy: localEditorSocket listening for connection (" + localHost + ":" + localClientPort + ")");
}
function localEditorShutdown(restart) {
console.log("debugger-proxy: executed localServer shutdown");
localServer.close();
localServer.unref();
localEditorSocket = null;
if (restart) {
setTimeout(function() {
localEditorListen();
}, 5000)
}
}
localEditorListen();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment