json-rpc-client-stream

JSON RPC 2.0 client implementation with streaming I/O and event driven API for endpoint implementors.

Usage no npm install needed!

<script type="module">
  import jsonRpcClientStream from 'https://cdn.skypack.dev/json-rpc-client-stream';
</script>

README

JSON RPC Client Stream

JSON RPC 2.0 client implementation with streaming I/O and event driven API for endpoint implementers. For corresponding server implementation see json-rpc-server-stream.

Line-delimited JSON

Several individual incoming JSON encoded request responses or batch responses are expected to be delimited with new line characters if sent in the same chunk.

JSON encoded messages (notifications, requests, and batches) are streamed with new line character \n as delimiter to the server, which enables sensible server side parsing even if several JSON encoded notification or request objects are streamed in the same chunk from the client.

For gereral info about new line encoded JSON, see http://jsonlines.org/.

JSON RPC 2.0 Architecture

The JSON RPC 2.0 protocol uses a client-server-architecture, in contrast to the peer-to-peer oriented 1.0 version, where peers act as both clients and server. However, it is still possible to use JSON RPC 2.0 in a peer-to-peer fashion.

Using JSON RPC 2.0 in a Peer-to-peer Fashion

A server and client pair must be installed on each node in order to use JSON RPC 2.0 in a peer-to-peer fashion. Additionally, full-duplex communication between node endpoint is required, for instance using WebSockets. The question then becomes: How to separate incoming requests from incoming responses?

This problem could be handled by the protocol implementation, since it is quite straight forward to separate well-formed requests from well-formed responses. Consequently, messages could be inspected by the implementation and either be handled as requests or as responses. However, such an approach is not optimal – It violates the idea of separation of concerns. Furthermore, there are malformed JSON RPC 2.0 messages, such as an empty array "[]", that cannot be distinguished as a malformed request that requires an error response or a malformed response that should be left unanswered.

A better approach when a JSON RPC 2.0 server and client on the same node share a common bidirectional channel is to multiplex and demultiplex (mux/demux) the transmission, so that each message only hits the intended endpoint implementation. The module mux-demux-stream can be used to achieve this.

Installation

npm install json-rpc-client-stream

Usage

Create a JSON RPC client stream and emit requests or notifications.

Do some stream plumbing, such as: Readable connection stream -> RPC client -> Writable connection stream.

As mentioned above, it is recommended to pipe the streaming JSON RPC 2.0 server and client through a mux/demux before piping it to a channel stream if using JSON RPC 2.0 in a peer-to-peer fashion.

jsonRpcClientStream()

The module exports a factory function that returns a JSON RPC client stream instance, which is a duplex stream.

jsonRpcClientStreamInstance.rpc.emit(method[, parameters][, callback])

Emit an RPC method with an optional parameters argument. If the RPC expects a response (i.e a request) pass in a response callback. The callback will be called with an error argument and a result argument. If the response callback is omitted, the RPC does not expect an response (i.e a notification).

Batch RPC call

Emitting several RPC request within the same event loop will result in a batch request, where all the requests are sent at the same time.

Basic Example

The following example shows the basic use cases for a JSON RPC 2.0 Client.

var jsonRpcClientStream = require('json-rpc-client-stream');

// A request
jsonRpcClientStream.rpc.emit('divide', { denominator: 2, numerator: 4 },
    function(err, result) {
  if (err) return console.error(err);

  console.log(result);
});

// A notification
jsonRpcClientStream.rpc.emit('log', 'All is good');

Advanced Example

The following example shows how to implement a JSON RPC 2.0 client and server in the browser where websockets are used as a shared bi-directional channel, with multiplexed and demultiplexed transmission. Additional modules json-rpc-server-stream, websocket-connection-stream, and mux-demux-stream are used.

var ws = new WebSocket('ws://localhost:8080');
var connectionStream = require('websocket-connection-stream')().attach(ws);

var jsonRpcServerStream = require('json-rpc-server-stream')();
var jsonRpcClientStream = require('json-rpc-client-stream')();
var mux = require('mux-demux-stream');

mux(jsonRpcServerStream, jsonRpcClientStream)
  .pipe(websocketConnectionStream)
  .demux(jsonRpcServerStream, jsonRpcClientStream);

jsonRpcClientStream.rpc.emit('join', { roomId: 'roomA'}, function(err, result) {
  if (err) return console.error(err)

  jsonRpcClientStream.rpc.emit('chat', {
    to: 'roomA',
    from: 'Alice',
    message: 'Hi there'
  });
})

jsonRpcServerStream.rpc.on('chat', function(params) {
  console.log(params.from + ' says: ' + params.message);
});

Related packages

Test

Run unit tests;

$ npm test

Create test coverage report:

$ npm run-script test-cov

License

MIT