messaging.js 3.5 KB

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