123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /*
- This file is part of web3.js.
- web3.js is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- web3.js is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with web3.js. If not, see <http://www.gnu.org/licenses/>.
- */
- /** @file ipcprovider.js
- * @authors:
- * Fabian Vogelsteller <fabian@ethdev.com>
- * @date 2015
- */
- 'use strict';
- var _ = require('underscore');
- var errors = {
- InvalidConnection: function(host) {
- return new Error(
- "CONNECTION ERROR: Couldn't connect to node " + host + '.'
- );
- },
- InvalidResponse: function(result) {
- var message =
- !!result && !!result.error && !!result.error.message
- ? result.error.message
- : 'Invalid JSON RPC response: ' + JSON.stringify(result);
- return new Error(message);
- }
- };
- var IpcProvider = function(path, net) {
- var _this = this;
- this.responseCallbacks = {};
- this.path = path;
- this.connection = net.connect({ path: this.path });
- this.connection.on('error', function(e) {
- console.error('IPC Connection Error', e);
- _this._timeout();
- });
- this.connection.on('end', function() {
- _this._timeout();
- });
- // LISTEN FOR CONNECTION RESPONSES
- this.connection.on('data', function(data) {
- /*jshint maxcomplexity: 6 */
- _this._parseResponse(data.toString()).forEach(function(result) {
- var id = null;
- // get the id which matches the returned id
- if (_.isArray(result)) {
- result.forEach(function(load) {
- if (_this.responseCallbacks[load.id]) id = load.id;
- });
- } else {
- id = result.id;
- }
- // fire the callback
- if (_this.responseCallbacks[id]) {
- _this.responseCallbacks[id](null, result);
- delete _this.responseCallbacks[id];
- }
- });
- });
- };
- /**
- Will parse the response and make an array out of it.
- @method _parseResponse
- @param {String} data
- */
- IpcProvider.prototype._parseResponse = function(data) {
- var _this = this,
- returnValues = [];
- // DE-CHUNKER
- var dechunkedData = data
- .replace(/\}[\n\r]?\{/g, '}|--|{') // }{
- .replace(/\}\][\n\r]?\[\{/g, '}]|--|[{') // }][{
- .replace(/\}[\n\r]?\[\{/g, '}|--|[{') // }[{
- .replace(/\}\][\n\r]?\{/g, '}]|--|{') // }]{
- .split('|--|');
- dechunkedData.forEach(function(data) {
- // prepend the last chunk
- if (_this.lastChunk) data = _this.lastChunk + data;
- var result = null;
- try {
- result = JSON.parse(data);
- } catch (e) {
- _this.lastChunk = data;
- // start timeout to cancel all requests
- clearTimeout(_this.lastChunkTimeout);
- _this.lastChunkTimeout = setTimeout(function() {
- _this._timeout();
- throw errors.InvalidResponse(data);
- }, 1000 * 15);
- return;
- }
- // cancel timeout and set chunk to null
- clearTimeout(_this.lastChunkTimeout);
- _this.lastChunk = null;
- if (result) returnValues.push(result);
- });
- return returnValues;
- };
- /**
- Get the adds a callback to the responseCallbacks object,
- which will be called if a response matching the response Id will arrive.
- @method _addResponseCallback
- */
- IpcProvider.prototype._addResponseCallback = function(payload, callback) {
- var id = payload.id || payload[0].id;
- var method = payload.method || payload[0].method;
- this.responseCallbacks[id] = callback;
- this.responseCallbacks[id].method = method;
- };
- /**
- Timeout all requests when the end/error event is fired
- @method _timeout
- */
- IpcProvider.prototype._timeout = function() {
- for (var key in this.responseCallbacks) {
- if (this.responseCallbacks.hasOwnProperty(key)) {
- this.responseCallbacks[key](errors.InvalidConnection('on IPC'));
- delete this.responseCallbacks[key];
- }
- }
- };
- /**
- Check if the current connection is still valid.
- @method isConnected
- */
- IpcProvider.prototype.isConnected = function() {
- var _this = this;
- // try reconnect, when connection is gone
- if (!_this.connection.writable)
- _this.connection.connect({ path: _this.path });
- return !!this.connection.writable;
- };
- IpcProvider.prototype.send = function(payload) {
- if (this.connection.writeSync) {
- var result;
- // try reconnect, when connection is gone
- if (!this.connection.writable) this.connection.connect({ path: this.path });
- var data = this.connection.writeSync(JSON.stringify(payload));
- try {
- result = JSON.parse(data);
- } catch (e) {
- throw errors.InvalidResponse(data);
- }
- return result;
- } else {
- throw new Error(
- 'You tried to send "' +
- payload.method +
- '" synchronously. Synchronous requests are not supported by the IPC provider.'
- );
- }
- };
- IpcProvider.prototype.sendAsync = function(payload, callback) {
- // try reconnect, when connection is gone
- if (!this.connection.writable) this.connection.connect({ path: this.path });
- this.connection.write(JSON.stringify(payload));
- this._addResponseCallback(payload, callback);
- };
- module.exports = IpcProvider;
|