PageRenderTime 54ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/deps/debugger-agent/lib/_debugger_agent.js

https://gitlab.com/GeekSir/node
JavaScript | 193 lines | 141 code | 42 blank | 10 comment | 12 complexity | e5d9560ee05b65d1ac059c769e7362cf MD5 | raw file
Possible License(s): 0BSD, Apache-2.0, MPL-2.0-no-copyleft-exception, JSON, WTFPL, CC-BY-SA-3.0, Unlicense, ISC, BSD-3-Clause, MIT, AGPL-3.0
  1. 'use strict';
  2. var assert = require('assert');
  3. var net = require('net');
  4. var util = require('util');
  5. var Buffer = require('buffer').Buffer;
  6. var Transform = require('stream').Transform;
  7. exports.start = function start() {
  8. var agent = new Agent();
  9. // Do not let `agent.listen()` request listening from cluster master
  10. var cluster = require('cluster');
  11. cluster.isWorker = false;
  12. cluster.isMaster = true;
  13. agent.on('error', function(err) {
  14. process._rawDebug(err.stack || err);
  15. });
  16. agent.listen(process._debugAPI.port, function() {
  17. var addr = this.address();
  18. process._rawDebug('Debugger listening on port %d', addr.port);
  19. process._debugAPI.notifyListen();
  20. });
  21. // Just to spin-off events
  22. // TODO(indutny): Figure out why node.cc isn't doing this
  23. setImmediate(function() {
  24. });
  25. process._debugAPI.onclose = function() {
  26. // We don't care about it, but it prevents loop from cleaning up gently
  27. // NOTE: removeAllListeners won't work, as it doesn't call `removeListener`
  28. process.listeners('SIGWINCH').forEach(function(fn) {
  29. process.removeListener('SIGWINCH', fn);
  30. });
  31. agent.close();
  32. };
  33. // Not used now, but anyway
  34. return agent;
  35. };
  36. function Agent() {
  37. net.Server.call(this, this.onConnection);
  38. this.first = true;
  39. this.binding = process._debugAPI;
  40. var self = this;
  41. this.binding.onmessage = function(msg) {
  42. self.clients.forEach(function(client) {
  43. client.send({}, msg);
  44. });
  45. };
  46. this.clients = [];
  47. assert(this.binding, 'Debugger agent running without bindings!');
  48. }
  49. util.inherits(Agent, net.Server);
  50. Agent.prototype.onConnection = function onConnection(socket) {
  51. var c = new Client(this, socket);
  52. c.start();
  53. this.clients.push(c);
  54. var self = this;
  55. c.once('close', function() {
  56. var index = self.clients.indexOf(c);
  57. assert(index !== -1);
  58. self.clients.splice(index, 1);
  59. });
  60. };
  61. Agent.prototype.notifyWait = function notifyWait() {
  62. if (this.first)
  63. this.binding.notifyWait();
  64. this.first = false;
  65. };
  66. function Client(agent, socket) {
  67. Transform.call(this);
  68. this._readableState.objectMode = true;
  69. this.agent = agent;
  70. this.binding = this.agent.binding;
  71. this.socket = socket;
  72. // Parse incoming data
  73. this.state = 'headers';
  74. this.headers = {};
  75. this.buffer = '';
  76. socket.pipe(this);
  77. this.on('data', this.onCommand);
  78. var self = this;
  79. this.socket.on('close', function() {
  80. self.destroy();
  81. });
  82. }
  83. util.inherits(Client, Transform);
  84. Client.prototype.destroy = function destroy(msg) {
  85. this.socket.destroy();
  86. this.emit('close');
  87. };
  88. Client.prototype._transform = function _transform(data, enc, cb) {
  89. cb();
  90. this.buffer += data;
  91. while (true) {
  92. if (this.state === 'headers') {
  93. // Not enough data
  94. if (!/\r\n/.test(this.buffer))
  95. break;
  96. if (/^\r\n/.test(this.buffer)) {
  97. this.buffer = this.buffer.slice(2);
  98. this.state = 'body';
  99. continue;
  100. }
  101. // Match:
  102. // Header-name: header-value\r\n
  103. var match = this.buffer.match(/^([^:\s\r\n]+)\s*:\s*([^\s\r\n]+)\r\n/);
  104. if (!match)
  105. return this.destroy('Expected header, but failed to parse it');
  106. this.headers[match[1].toLowerCase()] = match[2];
  107. this.buffer = this.buffer.slice(match[0].length);
  108. } else {
  109. var len = this.headers['content-length'];
  110. if (len === undefined)
  111. return this.destroy('Expected content-length');
  112. len = len | 0;
  113. if (Buffer.byteLength(this.buffer) < len)
  114. break;
  115. this.push(new Command(this.headers, this.buffer.slice(0, len)));
  116. this.state = 'headers';
  117. this.buffer = this.buffer.slice(len);
  118. this.headers = {};
  119. }
  120. }
  121. };
  122. Client.prototype.send = function send(headers, data) {
  123. if (!data)
  124. data = '';
  125. var out = [];
  126. Object.keys(headers).forEach(function(key) {
  127. out.push(key + ': ' + headers[key]);
  128. });
  129. out.push('Content-Length: ' + Buffer.byteLength(data), '');
  130. this.socket.cork();
  131. this.socket.write(out.join('\r\n') + '\r\n');
  132. if (data.length > 0)
  133. this.socket.write(data);
  134. this.socket.uncork();
  135. };
  136. Client.prototype.start = function start() {
  137. this.send({
  138. Type: 'connect',
  139. 'V8-Version': process.versions.v8,
  140. 'Protocol-Version': 1,
  141. 'Embedding-Host': 'node ' + process.version
  142. });
  143. };
  144. Client.prototype.onCommand = function onCommand(cmd) {
  145. this.binding.sendCommand(cmd.body);
  146. this.agent.notifyWait();
  147. };
  148. function Command(headers, body) {
  149. this.headers = headers;
  150. this.body = body;
  151. }