mirror of https://github.com/lukechilds/node.git
Ryan Dahl
14 years ago
3 changed files with 300 additions and 7 deletions
@ -0,0 +1,221 @@ |
|||||
|
var net = require('net'); |
||||
|
var readline = require('readline'); |
||||
|
var inherits = require('util').inherits; |
||||
|
|
||||
|
exports.port = 5858; |
||||
|
|
||||
|
exports.start = function (pid) { |
||||
|
if (pid) { |
||||
|
process.kill(pid, "SIGUSR1"); |
||||
|
setTimeout(tryConnect, 100); |
||||
|
} else { |
||||
|
tryConnect(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var c; |
||||
|
|
||||
|
function tryConnect() { |
||||
|
c = new Client(); |
||||
|
|
||||
|
process.stdout.write("connecting..."); |
||||
|
c.connect(exports.port, function () { |
||||
|
process.stdout.write("ok\r\n"); |
||||
|
startInterface(); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//
|
||||
|
// Parser/Serializer for V8 debugger protocol
|
||||
|
// http://code.google.com/p/v8/wiki/DebuggerProtocol
|
||||
|
//
|
||||
|
// Usage:
|
||||
|
// p = new Protocol();
|
||||
|
//
|
||||
|
// p.onResponse = function (res) {
|
||||
|
// // do stuff with response from V8
|
||||
|
// };
|
||||
|
//
|
||||
|
// socket.setEncoding('utf8');
|
||||
|
// socket.on('data', function (s) {
|
||||
|
// // Pass strings into the protocol
|
||||
|
// p.execute(s);
|
||||
|
// });
|
||||
|
//
|
||||
|
//
|
||||
|
function Protocol() { |
||||
|
this._newRes(); |
||||
|
} |
||||
|
exports.Protocol = Protocol; |
||||
|
|
||||
|
|
||||
|
Protocol.prototype._newRes = function(raw) { |
||||
|
this.res = { raw: raw || '', headers: {} }; |
||||
|
this.state = 'headers'; |
||||
|
this.reqSeq = 1; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Protocol.prototype.execute = function(d) { |
||||
|
var res = this.res; |
||||
|
res.raw += d; |
||||
|
|
||||
|
switch (this.state) { |
||||
|
case 'headers': |
||||
|
var endHeaderIndex = res.raw.indexOf('\r\n\r\n'); |
||||
|
|
||||
|
if (endHeaderIndex < 0) break; |
||||
|
|
||||
|
var lines = res.raw.slice(0, endHeaderIndex).split('\r\n'); |
||||
|
for (var i = 0; i < lines.length; i++) { |
||||
|
var kv = lines[i].split(/: +/); |
||||
|
res.headers[kv[0]] = kv[1]; |
||||
|
} |
||||
|
|
||||
|
this.contentLength = +res.headers['Content-Length']; |
||||
|
this.bodyStartIndex = endHeaderIndex + 4; |
||||
|
|
||||
|
this.state = 'body'; |
||||
|
if (res.raw.length - this.bodyStartIndex < this.contentLength) break; |
||||
|
// pass thru
|
||||
|
|
||||
|
case 'body': |
||||
|
if (res.raw.length - this.bodyStartIndex >= this.contentLength) { |
||||
|
res.body = |
||||
|
res.raw.slice(this.bodyStartIndex, |
||||
|
this.bodyStartIndex + this.contentLength); |
||||
|
// JSON parse body?
|
||||
|
res.body = res.body.length ? JSON.parse(res.body) : {}; |
||||
|
|
||||
|
// Done!
|
||||
|
this.onResponse(res); |
||||
|
|
||||
|
this._newRes(res.raw.slice(this.bodyStartIndex + this.contentLength)); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
throw new Error("Unknown state"); |
||||
|
break; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Protocol.prototype.serialize = function(req) { |
||||
|
req.type = 'request'; |
||||
|
req.seq = this.reqSeq++; |
||||
|
var json = JSON.stringify(req); |
||||
|
return 'Content-Length: ' + json.length + '\r\n\r\n' + json; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
function Client() { |
||||
|
net.Stream.call(this); |
||||
|
var protocol = this.protocol = new Protocol(c); |
||||
|
this._reqCallbacks = []; |
||||
|
var socket = this; |
||||
|
|
||||
|
// Note that 'Protocol' requires strings instead of Buffers.
|
||||
|
socket.setEncoding('utf8'); |
||||
|
socket.on('data', function(d) { |
||||
|
protocol.execute(d); |
||||
|
}); |
||||
|
|
||||
|
protocol.onResponse = this._onResponse.bind(this); |
||||
|
}; |
||||
|
inherits(Client, net.Stream); |
||||
|
exports.Client = Client; |
||||
|
|
||||
|
|
||||
|
Client.prototype._onResponse = function(res) { |
||||
|
console.error(res); |
||||
|
for (var i = 0; i < this._reqCallbacks.length; i++) { |
||||
|
var cb = this._reqCallbacks[i]; |
||||
|
if (this._reqCallbacks[i].request_seq == cb.request_seq) break; |
||||
|
} |
||||
|
|
||||
|
if (cb) { |
||||
|
this._reqCallbacks.splice(i, 1); |
||||
|
cb(res.body); |
||||
|
} else if (res.headers.Type == 'connect') { |
||||
|
// do nothing
|
||||
|
} else { |
||||
|
console.error("unhandled res: %j", res.body); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Client.prototype.req = function(req, cb) { |
||||
|
this.write(this.protocol.serialize(req)); |
||||
|
cb.request_seq = req.seq; |
||||
|
this._reqCallbacks.push(cb); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Client.prototype.reqVersion = function(cb) { |
||||
|
this.req({ command: 'version' } , function (res) { |
||||
|
if (cb) cb(res.body.V8Version, res.running); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
var helpMessage = "Commands: version, eval, help, quit"; |
||||
|
|
||||
|
|
||||
|
function startInterface() { |
||||
|
|
||||
|
var i = readline.createInterface(process.stdout); |
||||
|
var stdin = process.openStdin(); |
||||
|
stdin.addListener('data', function(chunk) { |
||||
|
i.write(chunk); |
||||
|
}); |
||||
|
|
||||
|
var prompt = '> '; |
||||
|
|
||||
|
i.setPrompt(prompt); |
||||
|
i.prompt(); |
||||
|
|
||||
|
i.on('SIGINT', function() { |
||||
|
i.close(); |
||||
|
}); |
||||
|
|
||||
|
i.on('line', function(cmd) { |
||||
|
if (cmd == 'quit') { |
||||
|
process.exit(0); |
||||
|
} else if (/^help/.test(cmd)) { |
||||
|
console.log(helpMessage); |
||||
|
i.prompt(); |
||||
|
|
||||
|
} else if ('version' == cmd) { |
||||
|
c.reqVersion(function (v) { |
||||
|
console.log(v); |
||||
|
i.prompt(); |
||||
|
}); |
||||
|
|
||||
|
} else if (/^eval/.test(cmd)) { |
||||
|
var req = { |
||||
|
command: 'evaluate', |
||||
|
arguments: { 'expression': cmd.slice(5) } |
||||
|
}; |
||||
|
|
||||
|
c.req(req, function (res) { |
||||
|
console.log(res); |
||||
|
i.prompt(); |
||||
|
}); |
||||
|
|
||||
|
} else { |
||||
|
if (!/^\s*$/.test(cmd)) { |
||||
|
// If it's not all white-space print this error message.
|
||||
|
console.log('Unknown command "%s". Try "help"', cmd); |
||||
|
} |
||||
|
i.prompt(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
i.on('close', function() { |
||||
|
stdin.destroy(); |
||||
|
}); |
||||
|
} |
@ -0,0 +1,63 @@ |
|||||
|
var common = require('../common'); |
||||
|
var assert = require('assert'); |
||||
|
var d = require('_debugger'); |
||||
|
|
||||
|
var spawn = require('child_process').spawn; |
||||
|
|
||||
|
|
||||
|
var resCount = 0; |
||||
|
var p = new d.Protocol(); |
||||
|
p.onResponse = function (res) { |
||||
|
resCount++; |
||||
|
}; |
||||
|
|
||||
|
p.execute("Type: connect\r\n" + |
||||
|
"V8-Version: 3.0.4.1\r\n" + |
||||
|
"Protocol-Version: 1\r\n" + |
||||
|
"Embedding-Host: node v0.3.3-pre\r\n" + |
||||
|
"Content-Length: 0\r\n\r\n"); |
||||
|
assert.equal(1, resCount); |
||||
|
|
||||
|
var n = spawn(process.execPath, |
||||
|
['-e', 'setInterval(function () { console.log("blah"); }, 1000);']); |
||||
|
|
||||
|
|
||||
|
var connected = false; |
||||
|
|
||||
|
n.stdout.once('data', function () { |
||||
|
console.log("new node process: %d", n.pid); |
||||
|
process.kill(n.pid, "SIGUSR1"); |
||||
|
console.log("signaling it with SIGUSR1"); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
var didTryConnect = false; |
||||
|
n.stderr.setEncoding('utf8'); |
||||
|
n.stderr.on('data', function (d) { |
||||
|
if (didTryConnect == false && /debugger/.test(d)) { |
||||
|
didTryConnect = true; |
||||
|
tryConnect(); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
function tryConnect() { |
||||
|
// Wait for some data before trying to connect
|
||||
|
var c = new d.Client(); |
||||
|
process.stdout.write("connecting..."); |
||||
|
c.connect(d.port, function () { |
||||
|
connected = true; |
||||
|
console.log("connected!"); |
||||
|
}); |
||||
|
|
||||
|
c.reqVersion(function (v) { |
||||
|
assert.equal(process.versions.v8, v); |
||||
|
n.kill(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
process.on('exit', function() { |
||||
|
assert.ok(connected); |
||||
|
}); |
||||
|
|
Loading…
Reference in new issue