// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity. // // SPDX-License-Identifier: BSD-3-Clause import EventEmitter from 'eventemitter3'; import net from 'net'; import Pino from '../utils/pino'; const pino = Pino(); /* * IpcChannel to send messages to and receive messages from the node via ipc. * * Usage: * ipcChannel.init('path/to/ipc'); * ipcChannel.send('{"jsonrpc":"2.0", ...}') * ipcChannel.on('message', x => { console.log(x) }); */ class IpcChannel extends EventEmitter { _queued = []; init (path) { return new Promise((resolve, reject) => { const socket = net.createConnection(path); socket.on('connect', () => { pino.info('Connected to IPC socket.'); this._socket = socket; this._sendQueued(); resolve(true); }); // We get data from the socket by chunks, and 1 chunk !== 1 Rpc message // Sometimes 1 chunk is multiple messages, sometimes we get partial // messages. // https://github.com/paritytech/fether/issues/562 // The last element in a '\n'-separate chunk. Will be `""` if the // last element was a correctly-formed message, or will hold the // ill-formed message otherwise. let lastData = ''; socket.on('data', data_ => { // If the last data was truncated, then we concatenate to the new data // we just received const data = `${lastData}${data_.toString()}`; // All messages are separated by a '\n' const messages = data.split(/\n/); lastData = messages.pop(); // Will hold "" or an ill-formed JSONRPC message messages.forEach(data => { this.emit('message', data); }); }); socket.on('error', err => { pino.error('Error connecting to IPC socket.', err); reject(err); }); }); } send (message) { if (this._socket) this._socket.write(message + '\r\n'); else this._queued.push(message); } _sendQueued () { this._queued.forEach(message => this.send(message)); } } export default new IpcChannel();