legacyWeb3IpcProvider.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. This file is part of web3.js.
  3. web3.js is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. web3.js is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with web3.js. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. /** @file ipcprovider.js
  15. * @authors:
  16. * Fabian Vogelsteller <fabian@ethdev.com>
  17. * @date 2015
  18. */
  19. 'use strict';
  20. var _ = require('underscore');
  21. var errors = {
  22. InvalidConnection: function(host) {
  23. return new Error(
  24. "CONNECTION ERROR: Couldn't connect to node " + host + '.'
  25. );
  26. },
  27. InvalidResponse: function(result) {
  28. var message =
  29. !!result && !!result.error && !!result.error.message
  30. ? result.error.message
  31. : 'Invalid JSON RPC response: ' + JSON.stringify(result);
  32. return new Error(message);
  33. }
  34. };
  35. var IpcProvider = function(path, net) {
  36. var _this = this;
  37. this.responseCallbacks = {};
  38. this.path = path;
  39. this.connection = net.connect({ path: this.path });
  40. this.connection.on('error', function(e) {
  41. console.error('IPC Connection Error', e);
  42. _this._timeout();
  43. });
  44. this.connection.on('end', function() {
  45. _this._timeout();
  46. });
  47. // LISTEN FOR CONNECTION RESPONSES
  48. this.connection.on('data', function(data) {
  49. /*jshint maxcomplexity: 6 */
  50. _this._parseResponse(data.toString()).forEach(function(result) {
  51. var id = null;
  52. // get the id which matches the returned id
  53. if (_.isArray(result)) {
  54. result.forEach(function(load) {
  55. if (_this.responseCallbacks[load.id]) id = load.id;
  56. });
  57. } else {
  58. id = result.id;
  59. }
  60. // fire the callback
  61. if (_this.responseCallbacks[id]) {
  62. _this.responseCallbacks[id](null, result);
  63. delete _this.responseCallbacks[id];
  64. }
  65. });
  66. });
  67. };
  68. /**
  69. Will parse the response and make an array out of it.
  70. @method _parseResponse
  71. @param {String} data
  72. */
  73. IpcProvider.prototype._parseResponse = function(data) {
  74. var _this = this,
  75. returnValues = [];
  76. // DE-CHUNKER
  77. var dechunkedData = data
  78. .replace(/\}[\n\r]?\{/g, '}|--|{') // }{
  79. .replace(/\}\][\n\r]?\[\{/g, '}]|--|[{') // }][{
  80. .replace(/\}[\n\r]?\[\{/g, '}|--|[{') // }[{
  81. .replace(/\}\][\n\r]?\{/g, '}]|--|{') // }]{
  82. .split('|--|');
  83. dechunkedData.forEach(function(data) {
  84. // prepend the last chunk
  85. if (_this.lastChunk) data = _this.lastChunk + data;
  86. var result = null;
  87. try {
  88. result = JSON.parse(data);
  89. } catch (e) {
  90. _this.lastChunk = data;
  91. // start timeout to cancel all requests
  92. clearTimeout(_this.lastChunkTimeout);
  93. _this.lastChunkTimeout = setTimeout(function() {
  94. _this._timeout();
  95. throw errors.InvalidResponse(data);
  96. }, 1000 * 15);
  97. return;
  98. }
  99. // cancel timeout and set chunk to null
  100. clearTimeout(_this.lastChunkTimeout);
  101. _this.lastChunk = null;
  102. if (result) returnValues.push(result);
  103. });
  104. return returnValues;
  105. };
  106. /**
  107. Get the adds a callback to the responseCallbacks object,
  108. which will be called if a response matching the response Id will arrive.
  109. @method _addResponseCallback
  110. */
  111. IpcProvider.prototype._addResponseCallback = function(payload, callback) {
  112. var id = payload.id || payload[0].id;
  113. var method = payload.method || payload[0].method;
  114. this.responseCallbacks[id] = callback;
  115. this.responseCallbacks[id].method = method;
  116. };
  117. /**
  118. Timeout all requests when the end/error event is fired
  119. @method _timeout
  120. */
  121. IpcProvider.prototype._timeout = function() {
  122. for (var key in this.responseCallbacks) {
  123. if (this.responseCallbacks.hasOwnProperty(key)) {
  124. this.responseCallbacks[key](errors.InvalidConnection('on IPC'));
  125. delete this.responseCallbacks[key];
  126. }
  127. }
  128. };
  129. /**
  130. Check if the current connection is still valid.
  131. @method isConnected
  132. */
  133. IpcProvider.prototype.isConnected = function() {
  134. var _this = this;
  135. // try reconnect, when connection is gone
  136. if (!_this.connection.writable)
  137. _this.connection.connect({ path: _this.path });
  138. return !!this.connection.writable;
  139. };
  140. IpcProvider.prototype.send = function(payload) {
  141. if (this.connection.writeSync) {
  142. var result;
  143. // try reconnect, when connection is gone
  144. if (!this.connection.writable) this.connection.connect({ path: this.path });
  145. var data = this.connection.writeSync(JSON.stringify(payload));
  146. try {
  147. result = JSON.parse(data);
  148. } catch (e) {
  149. throw errors.InvalidResponse(data);
  150. }
  151. return result;
  152. } else {
  153. throw new Error(
  154. 'You tried to send "' +
  155. payload.method +
  156. '" synchronously. Synchronous requests are not supported by the IPC provider.'
  157. );
  158. }
  159. };
  160. IpcProvider.prototype.sendAsync = function(payload, callback) {
  161. // try reconnect, when connection is gone
  162. if (!this.connection.writable) this.connection.connect({ path: this.path });
  163. this.connection.write(JSON.stringify(payload));
  164. this._addResponseCallback(payload, callback);
  165. };
  166. module.exports = IpcProvider;