3 const assert = require('assert');
4 const net = require('net');
5 const util = require('util');
6 const Buffer = require('buffer').Buffer;
7 const Transform = require('stream').Transform;
9 exports.start = function start() {
10 var agent = new Agent();
12 // Do not let `agent.listen()` request listening from cluster master
13 const cluster = require('cluster');
14 cluster.isWorker = false;
15 cluster.isMaster = true;
17 agent.on('error', function(err) {
18 process._rawDebug(err.stack || err);
21 agent.listen(process._debugAPI.port, function() {
22 var addr = this.address();
23 process._rawDebug('Debugger listening on port %d', addr.port);
24 process._debugAPI.notifyListen();
27 // Just to spin-off events
28 // TODO(indutny): Figure out why node.cc isn't doing this
29 setImmediate(function() {
32 process._debugAPI.onclose = function() {
33 // We don't care about it, but it prevents loop from cleaning up gently
34 // NOTE: removeAllListeners won't work, as it doesn't call `removeListener`
35 process.listeners('SIGWINCH').forEach(function(fn) {
36 process.removeListener('SIGWINCH', fn);
42 // Not used now, but anyway
47 net.Server.call(this, this.onConnection);
50 this.binding = process._debugAPI;
53 this.binding.onmessage = function(msg) {
54 self.clients.forEach(function(client) {
60 assert(this.binding, 'Debugger agent running without bindings!');
62 util.inherits(Agent, net.Server);
64 Agent.prototype.onConnection = function onConnection(socket) {
65 var c = new Client(this, socket);
71 c.once('close', function() {
72 var index = self.clients.indexOf(c);
74 self.clients.splice(index, 1);
78 Agent.prototype.notifyWait = function notifyWait() {
80 this.binding.notifyWait();
84 function Client(agent, socket) {
85 Transform.call(this, {
86 readableObjectMode: true
90 this.binding = this.agent.binding;
93 // Parse incoming data
94 this.state = 'headers';
99 this.on('data', this.onCommand);
102 this.socket.on('close', function() {
106 util.inherits(Client, Transform);
108 Client.prototype.destroy = function destroy(msg) {
109 this.socket.destroy();
114 Client.prototype._transform = function _transform(data, enc, cb) {
120 if (this.state === 'headers') {
122 if (!/\r\n/.test(this.buffer))
125 if (/^\r\n/.test(this.buffer)) {
126 this.buffer = this.buffer.slice(2);
132 // Header-name: header-value\r\n
133 var match = this.buffer.match(/^([^:\s\r\n]+)\s*:\s*([^\s\r\n]+)\r\n/);
135 return this.destroy('Expected header, but failed to parse it');
137 this.headers[match[1].toLowerCase()] = match[2];
139 this.buffer = this.buffer.slice(match[0].length);
141 var len = this.headers['content-length'];
142 if (len === undefined)
143 return this.destroy('Expected content-length');
146 if (Buffer.byteLength(this.buffer) < len)
149 this.push(new Command(this.headers, this.buffer.slice(0, len)));
150 this.state = 'headers';
151 this.buffer = this.buffer.slice(len);
157 Client.prototype.send = function send(headers, data) {
162 Object.keys(headers).forEach(function(key) {
163 out.push(key + ': ' + headers[key]);
165 out.push('Content-Length: ' + Buffer.byteLength(data), '');
168 this.socket.write(out.join('\r\n') + '\r\n');
171 this.socket.write(data);
172 this.socket.uncork();
175 Client.prototype.start = function start() {
178 'V8-Version': process.versions.v8,
179 'Protocol-Version': 1,
180 'Embedding-Host': 'node ' + process.version
184 Client.prototype.onCommand = function onCommand(cmd) {
185 this.binding.sendCommand(cmd.body);
187 this.agent.notifyWait();
190 function Command(headers, body) {
191 this.headers = headers;