Stability: 1 - Experimental
NodeJS wrapper for Discover, a distributed master-less node discovery mechanism that enables locating any entity (server, worker, drone, actor) based on node id.
npm install discover
npm test
Discover implements the Discover transport protocol consisting of a stripped down version of the Kademlia DHT protocol, PING and FIND-NODE. The TCP transport reports a node as unreachable if a connection cannot be established with it.
NOTE: Unreachability of nodes depends on the transport. For example, other transports (like TLS transport) could use other criteria (like invalid certificate) for reporting unreachable nodes.
WARNING: Using TCP transport is meant primarily for development in a development environment. TCP transport exists because it is a low hanging fruit. It is most likely that it should be replaced with DTLS transport in production (maybe TLS if DTLS is not viable). There may also be a use-case for using UDP transport if communicating nodes are on a VPN/VPC. Only if UDP on a VPN/VPC seems not viable, should TCP transport be considered.
Public API
- TcpTransport.listen(options, callback)
- new TcpTransport(options)
- tcpTransport.close(callback)
- tcpTransport.findNode(contact, nodeId, sender)
- tcpTransport.listen(callback)
- tcpTransport.ping(contact, sender)
- Event 'findNode'
- Event 'node'
- Event 'ping'
- Event 'reached'
- Event 'unreachable'
options
: Seenew TcpTransport(options)
options
.callback
: SeetcpTransport.listen(callback)
callback
.- Return: Object An instance of TcpTransport with server running.
Creates new TCP transport and starts the server.
options
:host
: String (Default: 'localhost')port
: Integer (Default: 6742) A port value of zero will assign a random port.
Creates a new TCP transport.
callback
: Function (Default: undefined) Optional callback to call once the server is stopped.
Stops the server from listening to requests from other nodes.
contact
: Object The node to contact with request to findnodeId
.id
: String (base64) Base64 encoded contact node id.transport
: Object TCP transport data.host
: String IP address to connect to.port
: Integer Port to connect to.
nodeId
: String (base64) Base64 encoded string representation of the node id to find.sender
: Object The node making the requestid
: String (base64) Base64 encoded sender node id.data
: Any Sender node data.transport
: Object TCP transport data.host
: String Host to connect to.port
: Integer Port to connect to.
Issues a FIND-NODE request to the contact
. In other words, sends FIND-NODE request to the contact at contact.host
and contact.port
using TCP. The transport will emit node
event when a response is processed (or times out).
callback
: Function (Default: undefined) Optional callback to call once the server is up.
Starts the server to listen to requests from other nodes.
contact
: Object Contact to ping.id
: String (base64) Base64 encoded contact node id.transport
: Object TCP transport data.host
: String Host to connect to.port
: Integer Port to connect to.
sender
: Object The contact making the request.id
: String (base64) Base64 encoded sender node id.data
: Any Sender node data.transport
: Object TCP transport data.host
: String Host of the sender.port
: Integer Port of the sender.
Issues a PING request to the contact
. In other words, pings the contact at the contact.transport.host
and contact.transport.port
using TCP. The transport will emit unreachable
event if the contact is deemed to be unreachable, or reached
event otherwise.
CAUTION: reserved for internal use
contact
: Object Contact to ping.id
: String (base64) Base64 encoded contact node id.transport
: Object TCP transport data.host
: String Host to connect to.port
: Integer Port to connect to.
payload
: String or Object Payload to send on the wire. If an Object is provided, it will beJSON.stringify()
'ed.callback
: Function Callback to call with an error or response.
An internal common implementation for tcpTransport.findNode(...)
and tcpTransport.ping(...)
.
nodeId
: String (base64) Base64 encoded string representation of the node id to find.sender
: Object The contact making the request.id
: String (base64) Base64 encoded sender node id.data
: Any Sender node data.transport
: Object TCP transport data.host
: String Host of the sender.port
: Integer Port of the sender.
callback
: Function The callback to call with the result of processing the FIND-NODE request.error
: Error An error, if any.response
: Object or Array The response to FIND-NODE request.
Emitted when another node issues a FIND-NODE request to this node.
var Discover = require('discover');
var tcpTransport = new Discover.TcpTransport();
tcpTransport.on('findNode', function (nodeId, sender, callback) {
// ... find closestNodes to the desired nodeId
return callback(null, closestNodes);
});
In the above example closestNodes
is an Array of contacts that are closest known to the desired nodeId
.
If the node handling the request itself contains the nodeId
, then it sends only itself back.
var Discover = require('discover');
var tcpTransport = new Discover.TcpTransport();
tcpTransport.on('findNode', function (nodeId, sender, callback) {
// ... this node knows the node with nodeId or is itself node with nodeId
return callback(null, nodeWithNodeId);
});
In the above example, nodeWithNodeId
is not an array, but an individual contact
representing the answer to findNode
query.
error
: Error An error, if one occurred.contact
: Object The node that FIND-NODE request was sent to.nodeId
: String (base64) The original base64 encoded node id requested to be found.response
: Object or Array The response from the queriedcontact
.
If error
occurs, the transport encountered an error when issuing the findNode
request to the contact
. contact
and nodeId
will also be provided in case of an error. response
is undefined if an error
occurs.
response
will be an Array if the contact
does not contain the nodeId
requested. In this case response
will be a contact
list of nodes closer to the nodeId
that the queried node is aware of. The usual step is to next query the returned contacts with the FIND-NODE request.
response
will be an Object if the contact
contains the nodeId
. In other words, the node has been found.
nodeId
: String (base64) Base64 encoded string representation of the node id being pinged.sender
: Object The contact making the request.id
: String (base64) Base64 encoded sender node id.data
: Any Sender node data.transport
: Object TCP transport data.host
: String Host of the sender.port
: Integer Port of the sender.
callback
: Function The callback to call with the result of processing the PING request.error
: Error An error, if any.response
: Object The response to PING request, if any.
Emitted when another node issues a PING request to this node.
var Discover = require('discover');
var tcpTransport = new Discover.TcpTransport();
tcpTransport.on('ping', function (nodeId, sender, callback) {
// ... verify that we have the exact node specified by nodeId
return callback(null, contact);
});
In the above example contact
is an Object representing the answer to ping
query.
If the exact node specified by nodeId does not exist, an error shall be returned as shown below:
var Discover = require('discover');
var tcpTransport = new Discover.TcpTransport();
tcpTransport.on('ping', function (nodeId, sender, callback) {
// ...we don't have the nodeId specified
return callback(true);
});
contact
: Object The contact that was reached when pinged.id
: String (base64) Base64 encoded contact node id.data
: Any Data included with the contact.transport
: Object TCP transport data.host
: String Host of reached contact.port
: Integer port of reached contact.
Emitted when a previously pinged contact
is deemed reachable by the transport.
contact
: Object The contact that was unreachable when pinged.id
: String (base64) Base64 encoded contact node id.transport
: Object TCP transport data.host
: String Host of unreachable contact.port
: Integer port of unreachable contact.
Emitted when a previously pinged contact
is deemed unreachable by the transport.
Wire protocol for TCP transport is simple one-line \r\n terminated ASCII.
{"request":{"findNode":"Zm9v"},"sender":{"id":"YmF6","data":"some data","transport":{"host":"127.0.0.1","port":6742}}}\r\n
FIND-NODE request consists of a JSON object with base64 encoded node id and a sender followed by \r\n as shown above.
{"id":"Zm9v","data":"some data","transport":{"host":"127.0.0.1","port":6742}}\r\n
An Object response is JSON representation of the contact followed by \r\n.
[{"id":"YmFy","data":"some data","transport":{"host":"192.168.0.1","port":6742}},{"id":"YmF6","data":"some data","transport":{"host":"192.168.0.2","port":6742}}]\r\n
An Array response is JSON representation of an array of closest contacts followed by \r\n.
{"request":{"ping":"Zm9v"},"sender":{"id":"YmF6","data":"some data","transport":{"host":"127.0.0.1","port":6742}}}\r\n
PING request consists of a JSON object with base64 encoded node id and a sender followed by \r\n as shown above.
{"id":"Zm9v","data":"some data","transport":{"host":"127.0.0.1","port":6742}}\r\n
An Object response is JSON representation of the pinged contact followed by \r\n.
Closing the connection without an object response or inability to connect in the first place indicates a PING failure.