xcraft-axon
Version:
High-level messaging & socket patterns implemented in pure js
170 lines (140 loc) • 3.18 kB
JavaScript
/**
* Module dependencies.
*/
var debug = require('debug')('axon:sub');
var escape = require('escape-regexp');
var Message = require('xcraft-amp-message');
var Socket = require('./sock');
const {Cache} = require('xcraft-core-transport');
/**
* Expose `SubSocket`.
*/
module.exports = SubSocket;
/**
* Initialize a new `SubSocket`.
*
* @api private
*/
function SubSocket() {
Socket.call(this);
this.subscriptions = {};
this._subscriptionsSize = 0;
this._cache = new Cache();
}
/**
* Inherits from `Socket.prototype`.
*/
SubSocket.prototype.__proto__ = Socket.prototype;
/**
* Check if this socket has subscriptions.
*
* @return {Boolean}
* @api public
*/
SubSocket.prototype.hasSubscriptions = function() {
return this._subscriptionsSize > 0;
};
/**
* Check if any subscriptions match `topic`.
*
* @param {String} topic
* @return {Boolean}
* @api public
*/
SubSocket.prototype.matches = function(topic, ids) {
return this._cache.matches(topic, ids);
};
/**
* Message handler.
*
* @param {net.Socket} sock
* @return {Function} closure(msg, mulitpart)
* @api private
*/
SubSocket.prototype.onmessage = function(sock) {
var subs = this.hasSubscriptions();
var self = this;
return function(buf) {
var msg = new Message(buf);
if (subs) {
var topic = msg.args[0];
if (!self.matches(topic)) {
return debug('not subscribed to "%s"', topic);
}
}
self.emit.apply(self, ['message'].concat(msg.args));
};
};
SubSocket.prototype._unsub = function(id, reS) {
this._cache.del(id, reS);
delete this.subscriptions[reS];
--this._subscriptionsSize;
};
/**
* Subscribe with the given `re`.
*
* @param {RegExp|String} re
* @return {RegExp}
* @api public
*/
SubSocket.prototype.subscribe = function(re, ids = ['_']) {
debug('subscribe to "%s"', re);
const id = ids[ids.length - 1] ;
re = toRegExp(re);
const reS = re.toString();
if (!this.subscriptions[reS]) {
this._cache.set(id, reS, re);
this.subscriptions[reS] = {
regex: re,
unsub: reS => this._unsub(id, reS),
};
++this._subscriptionsSize;
}
return this.subscriptions[reS].regex;
};
/**
* Unsubscribe with the given `re`.
*
* @param {RegExp|String} re
* @api public
*/
SubSocket.prototype.unsubscribe = function(re) {
debug('unsubscribe from "%s"', re);
re = toRegExp(re);
var s = re.toString();
if (this.subscriptions[s]) {
this.subscriptions[s].unsub(s);
}
};
/**
* Clear current subscriptions.
*
* @api public
*/
SubSocket.prototype.clearSubscriptions = function() {
for (const sub in this.subscriptions) {
this.subscriptions[sub].unsub(sub);
}
this.subscriptions = {};
this._subscriptionsSize = 0;
this._cache.clear();
};
/**
* Subscribers should not send messages.
*/
SubSocket.prototype.send = function() {
throw new Error('subscribers cannot send messages');
};
/**
* Convert `str` to a `RegExp`.
*
* @param {String} str
* @return {RegExp}
* @api private
*/
function toRegExp(str) {
if (str instanceof RegExp) return str;
str = escape(str);
str = str.replace(/\\\*/g, '(.+)');
return new RegExp('^' + str + '$');
}