Transport Adapters

Kadence does not impose any particular transport layer, which makes it very flexible for applying to many use cases. As far as Kadence is concerned, a valid transport adapter is any objectMode DuplexStream that exposes a listen() method.

Kadence ships with UDP and HTTP(S) transports so you don't need to implement a transport adapter yourself to get started. If your network layer needs are not met by these, check out the interface for AbstractNode~transport.

API for Transport Implementers

The transport adapter interface has been designed to make implementing any given networking or communication layer easy using JavaScript's inheritance model.

First, a developer would declare a new JavaScript class that extends the DuplexStream class, and implements the _read, _write, and listen methods. This architecture makes it simple to implement any type of transport layer.

When a consumer reads from the stream, they shall expect to receive a raw buffer representing a received message which is processed by a Messenger instance. When a consumer writes to the stream, they shall expect the adapter to dispatch the message to the target. Calling listen on the stream should perform any initialization needed, like binding to a port.

Transport streams must be placed in objectMode. The _read method must push the received messages as raw buffers to be parsed by the deserializer used by the Messenger class (which by default is JSON-RPC). The _write method receives an array object as it's first argument which contains the following:

[
  // String: unique identifier for the message, can be a request or a response
  messageId,
  // Buffer: raw payload to be delivered to the target
  messagePayload,
  [
    // String: target contact's identity key
    identityKey,
    // Object: target contact's address information (transport-specific)
    contactInfo
  ]
]

Example: UDP Transport

Implementing a UDP based transport adapter is very simple given that no state must be maintained between requests and responses, so we will use it as a simple example of how you might implement a transport.

const { Duplex: DuplexStream } = require('stream');
const dgram = require('dgram');

class UDPTransport extends DuplexStream {

  constructor(options) {
    super({ objectMode: true });
    this.socket = dgram.createSocket();
  }

  _write([id, buffer, target], encoding, callback) {
    let [, contact] = target;
    this.socket.send(buffer, 0, buffer.length, contact.port, contact.hostname,
                     callback);
  }

  _read() {
    this.socket.once('message', (buffer) => {
      this.push(buffer);
    });
  }

  listen() {
    this.socket.bind(...arguments);
  }

}