dual-protocol
v0.4.6
Published
Protocol layer for dual-api
Downloads
4
Maintainers
Readme
Dual-protocol
This is the protocol layer for dualapi.
Dual-protocol extends my hierarchical event emitter, HevEmitter, by constraining the schema of the event bodies to create an HTTP-like layered system.
Messages
Dual protocol messages (assuming an instance named ctxt
here) are similar
in concept to HTTP requests. Dual protocol messages consist in:
- A destination address:
ctxt.to
. - An optional source address:
ctxt.from
. - An optional body:
ctxt.body
. - And an optional hash of meta data (like headers):
ctxt.options
.
The destination address names a resource controlled by a host function mounted on the dual-protocol domain. Under the hood, the destination address is a HevEmitter event, which is a list of strings representing the heirarchical event.
The entity associated with the message is expressed by the optional body. Messages without bodies can be interpreted like HTTP GET or HEAD requests; similarly messages with bodies may be interpreted like HTTP POST or PUT. The event body should be JSON serializable. In order for the message to cross interprocess boundaries, the body MUST be JSON serializable.
The optional source address provides information orthogonal to the body information, which the destination host can use to create a layered system in the RESTful sense. The source address should also be a HevEmitter event. Practically, hosts may use the source address to affect message processing in a manner distinct from the expected function of the body (e.g., source address filtering, responses, proxy).
Finally, the optional hash of meta data is similar to headers in HTTP.
ctxt.options
express information about the message orthogonal to
both the source and body, providing information which aid in
processing, but do not affect the function of the host (e.g.,
authorization tokens, body schema, classification).
Constructing dual-protocol domains
Any process holding a dual-protocol domain instance may send messages to any host mounted on the domain. Host functions receive a reference to the domain on which the message was sent on each request.
The dual-protocol module is the constructor for domain instances:
var dualproto = require('dual-protocol');
var domain = dualproto();
Mounting a host
A dual protocol host is a function which accepts dual protocol
messages. Hosts are mounted on the domain using the mount
method.
In addition to the attributes associated with dual protocol messages,
the dual-protocol
object will automatically parse parameters
declared in the destination address (strings prefixed with :
), and
provide these in the ctxt.params
object. Strings prefixed with ::
will match the tail of the address.
For example the following host will record all messages it receives in a list.
var db = {};
domain.mount(['database', ':collection'], function (body, ctxt) {
var collection = ctxt.params.collection;
if (!db.hasOwnProperty(collection)) {
db[collection] = [];
}
db[collection].push(ctxt.body);
});
Sending messages with domain.send
Messages are sent to hosts mounted on the domain via domain.send(to, from, body, options)
.
domain.send(['database', 'message'], [], 'Hello Alice!');
Here we are using an empty source address. Because 'database'
is not
attempting to make a reply, no source address is necessary.
In addition to the usual message properties, dual-protocol also
attaches a reference to the domain on which the host is mounted
at ctxt.domain
. The host could use the domain to send additional
messages and/or replies. For example, the following host outputs
information about the message to the console, and forwards a copy of
the message to the database host:
domain.mount(['message', ':name'], function (body, ctxt) {
console.log(ctxt.from.join('/') + ' sent a message to ' + ctxt.params.name);
console.log('The message was received by ' + ctxt.from.join('/'));
console.log('The message is: ', ctxt.body);
ctxt.domain.send(['database', 'message'], [], ctxt.body);
});
Then, a message such as:
domain.send(['message', 'alice'], ['user', 'bob'], 'Hello Alice!');
would result in:
user/bob sent a message to alice
The message was received by message/alice
The message is: Hello Alice!
Creating temporary hosts with domain.waitFor
A common pattern is to create a temporary host to wait for a
single message, such as a ready event or a request response, which is removed immediately after the event. Dual-protocol
provides the domain.waitFor(event, options)
method for this purpose.
domain.waitFor
returns a promise that will resolve when the event
has been received. For instance, we create a listener for 'ready'
:
domain.waitFor(['ready'], { timeout: 60 })
.then(function (ctxt) {
console.log('We are ready!');
});
Then we can trigger this host once, and only once, by using
domain.send
. If the timeout
option is provided, the host will be
cancelled after timeout
seconds.
In order to wait for a response to a specific message, we need to
create a unique host name known only by the request
originator and receiver. For this purpose, dual-protocol includes a unique id generator at
domain.uid
.
domain.uid().then(function (mailbox) {
domain.waitFor([mailbox])
.then(function (ctxt) {
console.log('My temporary mailbox just received: ', ctxt.body);
});
domain.send([mailbox], [], 'mail!');
});
Extending dual-protocol
Dual-protocol strives to be simple, without constraining the behavior of the hosts or senders. Additional constraints, convenience functions and automated behaviors can be added by customizing dual-protocol; creating an API:
var dualproto = require('dual-protocol');
var api = dualproto.use(function (Domain) {
Domain.prototype.Message.prototype.reply = function (body) {
this.domain.send(this.from, [], body);
};
});
var domain = api();
domain.mount(['responder'], function (ctxt) {
ctxt.reply('Hello');
});