123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- const _ = require('../utils/underscore.js');
- const Q = require('bluebird');
- const oboe = require('oboe');
- const SocketBase = require('./base');
- const Socket = SocketBase.Socket;
- const STATE = SocketBase.STATE;
- module.exports = class Web3Socket extends Socket {
- constructor(socketMgr, id) {
- super(socketMgr, id);
- this._sendRequests = {};
- this._handleSocketResponse();
- }
- /**
- * Send an RPC call.
- * @param {Array|Object} single or array of payloads.
- * @param {Object} options Additional options.
- * @param {Boolean} [options.fullResult] If set then will return full result
- * JSON, not just result value.
- * @return {Promise}
- */
- send(payload, options) {
- return Q.try(() => {
- if (!this.isConnected) {
- throw new Error('Not connected');
- }
- const isBatch = _.isArray(payload);
- const finalPayload = isBatch
- ? _.map(payload, p => this._finalizeSinglePayload(p))
- : this._finalizeSinglePayload(payload);
- /*
- * For batch requests we use the id of the first request as the
- * id to refer to the batch as one. We can do this because the
- * response will also come back as a batch, in the same order as the
- * the requests within the batch were sent.
- */
- const id = isBatch ? finalPayload[0].id : finalPayload.id;
- this._log.trace(isBatch ? 'Batch request' : 'Request', id, finalPayload);
- this._sendRequests[id] = {
- options,
- /* Preserve the original id of the request so that we can
- update the response with it */
- origId: isBatch ? _.map(payload, p => p.id) : payload.id
- };
- this.write(JSON.stringify(finalPayload));
- return new Q((resolve, reject) => {
- _.extend(this._sendRequests[id], {
- resolve,
- reject
- });
- });
- });
- }
- /**
- * Construct a payload object.
- * @param {Object} payload Payload to send.
- * @param {String} payload.method Method name.
- * @param {Object} [payload.params] Method arguments.
- * @return {Object} final payload object
- */
- _finalizeSinglePayload(payload) {
- if (!payload.method) {
- throw new Error('Method required');
- }
- return {
- jsonrpc: '2.0',
- id: _.uuid(),
- method: payload.method,
- params: payload.params || []
- };
- }
- /**
- * Handle responses from Geth.
- * Responses are false, a single object, or an array of objects
- */
- _handleSocketResponse() {
- oboe(this)
- .done(result => {
- this._log.trace('JSON response', result);
- try {
- const isBatch = _.isArray(result);
- const firstItem = isBatch ? result[0] : result;
- const req = firstItem.id ? this._sendRequests[firstItem.id] : null;
- if (req) {
- this._log.trace(
- isBatch ? 'Batch response' : 'Response',
- firstItem.id,
- result
- );
- // if we don't want full JSON result, send just the result
- if (!_.get(req, 'options.fullResult')) {
- if (isBatch) {
- result = _.map(result, r => r.result);
- } else {
- result = result.result;
- }
- } else {
- // restore original ids
- if (isBatch) {
- req.origId.forEach((id, idx) => {
- if (result[idx]) {
- result[idx].id = id;
- }
- });
- } else {
- result.id = req.origId;
- }
- }
- req.resolve({
- isBatch,
- result
- });
- } else {
- // not a response to a request so pass it on as a notification
- this.emit('data-notification', result);
- }
- } catch (err) {
- this._log.error('Error handling socket response', err);
- }
- })
- .fail(err => {
- this._log.error('Socket response error', err);
- _.each(this._sendRequests, req => {
- if (req.reject) {
- req.reject(err);
- }
- });
- this._sendRequests = {};
- });
- }
- };
|