messaging.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // https://github.com/andy-portmen/native-client/blob/master/messaging.js
  2. // chrome-native-messaging module
  3. //
  4. // Defines three Transform streams:
  5. //
  6. // - Input - transform native messages to JavaScript objects
  7. // - Output - transform JavaScript objects to native messages
  8. // - Transform - transform message objects to reply objects
  9. // - Debug - transform JavaScript objects to lines of JSON (for debugging, obviously)
  10. const stream = require('stream');
  11. const util = require('util');
  12. function Input() {
  13. stream.Transform.call(this);
  14. // Transform bytes...
  15. this._writableState.objectMode = false;
  16. // ...into objects.
  17. this._readableState.objectMode = true;
  18. // Unparsed data.
  19. this.buf = Buffer.alloc(0);
  20. // Parsed length.
  21. this.len = null;
  22. }
  23. util.inherits(Input, stream.Transform);
  24. Input.prototype._transform = function(chunk, encoding, done) {
  25. // Save this chunk.
  26. this.buf = Buffer.concat([this.buf, chunk]);
  27. const self = this;
  28. function parseBuf() {
  29. // Do we have a length yet?
  30. if (typeof self.len !== 'number') {
  31. // Nope. Do we have enough bytes for the length?
  32. if (self.buf.length >= 4) {
  33. // Yep. Parse the bytes.
  34. self.len = self.buf.readUInt32LE(0);
  35. // Remove the length bytes from the buffer.
  36. self.buf = self.buf.slice(4);
  37. }
  38. }
  39. // Do we have a length yet? (We may have just parsed it.)
  40. if (typeof self.len === 'number') {
  41. // Yep. Do we have enough bytes for the message?
  42. if (self.buf.length >= self.len) {
  43. // Yep. Slice off the bytes we need.
  44. const message = self.buf.slice(0, self.len);
  45. // Remove the bytes for the message from the buffer.
  46. self.buf = self.buf.slice(self.len);
  47. // Clear the length so we know we need to parse it again.
  48. self.len = null;
  49. // Parse the message bytes.
  50. const obj = JSON.parse(message.toString());
  51. // Enqueue it for reading.
  52. self.push(obj);
  53. // We could have more messages in the buffer so check again.
  54. parseBuf();
  55. }
  56. }
  57. }
  58. // Check for a parsable buffer (both length and message).
  59. parseBuf();
  60. // We're done.
  61. done();
  62. };
  63. function Output() {
  64. stream.Transform.call(this);
  65. this._writableState.objectMode = true;
  66. this._readableState.objectMode = false;
  67. }
  68. util.inherits(Output, stream.Transform);
  69. Output.prototype._transform = function(chunk, encoding, done) {
  70. const len = Buffer.alloc(4);
  71. const buf = Buffer.from(JSON.stringify(chunk), 'utf8');
  72. len.writeUInt32LE(buf.length, 0);
  73. this.push(len);
  74. this.push(buf);
  75. done();
  76. };
  77. function Transform(handler) {
  78. stream.Transform.call(this);
  79. this._writableState.objectMode = true;
  80. this._readableState.objectMode = true;
  81. this.handler = handler;
  82. }
  83. util.inherits(Transform, stream.Transform);
  84. Transform.prototype._transform = function(msg, encoding, done) {
  85. this.handler(msg, this.push.bind(this), done);
  86. };
  87. function Debug() {
  88. stream.Transform.call(this);
  89. this._writableState.objectMode = true;
  90. this._readableState.objectMode = false;
  91. }
  92. util.inherits(Debug, stream.Transform);
  93. Debug.prototype._transform = function(chunk, encoding, done) {
  94. this.push(JSON.stringify(chunk) + '\n');
  95. done();
  96. };
  97. exports.Input = Input;
  98. exports.Output = Output;
  99. exports.Transform = Transform;
  100. exports.Debug = Debug;