UNPKG

rocket.chat.mqtt

Version:

It's a MQTT Server, using redis to scale horizontally.

349 lines (282 loc) 8.33 kB
'use strict' var from2 = require('from2') var QlobberSub = require('qlobber/aedes/qlobber-sub') var QlobberTrue = require('qlobber').QlobberTrue var Packet = require('aedes-packet') var QlobberOpts = { wildcard_one: '+', wildcard_some: '#', separator: '/' } function MemoryPersistence () { if (!(this instanceof MemoryPersistence)) { return new MemoryPersistence() } this._retained = [] // clientId -> topic -> qos this._subscriptions = new Map() this._clientsCount = 0 this._trie = new QlobberSub(QlobberOpts) this._outgoing = {} this._incoming = {} this._wills = {} } function matchTopic (p) { return p.topic !== this.topic } MemoryPersistence.prototype.storeRetained = function (packet, cb) { packet = Object.assign({}, packet) this._retained = this._retained.filter(matchTopic, packet) if (packet.payload.length > 0) this._retained.push(packet) cb(null) } function matchingStream (current, pattern) { var matcher = new QlobberTrue(QlobberOpts) if (Array.isArray(pattern)) { for (var i = 0; i < pattern.length; i += 1) { matcher.add(pattern[i]) } } else { matcher.add(pattern) } return from2.obj(function match (size, next) { var entry while ((entry = current.shift()) != null) { if (matcher.test(entry.topic)) { setImmediate(next, null, entry) return } } if (!entry) this.push(null) }) } MemoryPersistence.prototype.createRetainedStream = function (pattern) { return matchingStream([].concat(this._retained), pattern) } MemoryPersistence.prototype.createRetainedStreamCombi = function (patterns) { return matchingStream([].concat(this._retained), patterns) } MemoryPersistence.prototype.addSubscriptions = function (client, subs, cb) { var stored = this._subscriptions.get(client.id) var trie = this._trie if (!stored) { stored = new Map() this._subscriptions.set(client.id, stored) this._clientsCount++ } for (var i = 0; i < subs.length; i += 1) { var sub = subs[i] var qos = stored.get(sub.topic) var hasQoSGreaterThanZero = (qos !== undefined) && (qos > 0) if (sub.qos > 0) { trie.add(sub.topic, { clientId: client.id, topic: sub.topic, qos: sub.qos }) } else if (hasQoSGreaterThanZero) { trie.remove(sub.topic, { clientId: client.id, topic: sub.topic }) } stored.set(sub.topic, sub.qos) } cb(null, client) } MemoryPersistence.prototype.removeSubscriptions = function (client, subs, cb) { var stored = this._subscriptions.get(client.id) var trie = this._trie if (stored) { for (var i = 0; i < subs.length; i += 1) { var topic = subs[i] var qos = stored.get(topic) if (qos !== undefined) { if (qos > 0) { trie.remove(topic, { clientId: client.id, topic: topic }) } stored.delete(topic) } } if (stored.size === 0) { this._clientsCount-- this._subscriptions.delete(client.id) } } cb(null, client) } MemoryPersistence.prototype.subscriptionsByClient = function (client, cb) { var subs = null var stored = this._subscriptions.get(client.id) if (stored) { subs = [] for (var topicAndQos of stored) { subs.push({ topic: topicAndQos[0], qos: topicAndQos[1] }) } } cb(null, subs, client) } MemoryPersistence.prototype.countOffline = function (cb) { return cb(null, this._trie.subscriptionsCount, this._clientsCount) } MemoryPersistence.prototype.subscriptionsByTopic = function (pattern, cb) { cb(null, this._trie.match(pattern)) } MemoryPersistence.prototype.cleanSubscriptions = function (client, cb) { var trie = this._trie var stored = this._subscriptions.get(client.id) if (stored) { for (var topicAndQos of stored) { if (topicAndQos[1] > 0) { var topic = topicAndQos[0] trie.remove(topic, { clientId: client.id, topic: topic }) } } this._clientsCount-- this._subscriptions.delete(client.id) } cb(null, client) } MemoryPersistence.prototype.outgoingEnqueue = function (sub, packet, cb) { _outgoingEnqueue.call(this, sub, packet) process.nextTick(cb) } MemoryPersistence.prototype.outgoingEnqueueCombi = function (subs, packet, cb) { for (var i = 0; i < subs.length; i++) { _outgoingEnqueue.call(this, subs[i], packet) } process.nextTick(cb) } function _outgoingEnqueue (sub, packet) { var id = sub.clientId var queue = this._outgoing[id] || [] this._outgoing[id] = queue queue[queue.length] = new Packet(packet) } MemoryPersistence.prototype.outgoingUpdate = function (client, packet, cb) { var i var clientId = client.id var outgoing = this._outgoing[clientId] || [] var temp this._outgoing[clientId] = outgoing for (i = 0; i < outgoing.length; i++) { temp = outgoing[i] if (temp.brokerId === packet.brokerId && temp.brokerCounter === packet.brokerCounter) { temp.messageId = packet.messageId return cb(null, client, packet) } else if (temp.messageId === packet.messageId) { outgoing[i] = packet return cb(null, client, packet) } } cb(new Error('no such packet'), client, packet) } MemoryPersistence.prototype.outgoingClearMessageId = function (client, packet, cb) { var i var clientId = client.id var outgoing = this._outgoing[clientId] || [] var temp this._outgoing[clientId] = outgoing for (i = 0; i < outgoing.length; i++) { temp = outgoing[i] if (temp.messageId === packet.messageId) { outgoing.splice(i, 1) return cb(null, temp) } } cb() } MemoryPersistence.prototype.outgoingStream = function (client) { var queue = [].concat(this._outgoing[client.id] || []) return from2.obj(function match (size, next) { var entry while ((entry = queue.shift()) != null) { setImmediate(next, null, entry) return } if (!entry) this.push(null) }) } MemoryPersistence.prototype.incomingStorePacket = function (client, packet, cb) { var id = client.id var store = this._incoming[id] || {} this._incoming[id] = store store[packet.messageId] = new Packet(packet) store[packet.messageId].messageId = packet.messageId cb(null) } MemoryPersistence.prototype.incomingGetPacket = function (client, packet, cb) { var id = client.id var store = this._incoming[id] || {} var err = null this._incoming[id] = store if (!store[packet.messageId]) { err = new Error('no such packet') } cb(err, store[packet.messageId]) } MemoryPersistence.prototype.incomingDelPacket = function (client, packet, cb) { var id = client.id var store = this._incoming[id] || {} var toDelete = store[packet.messageId] var err = null if (!toDelete) { err = new Error('no such packet') } else { delete store[packet.messageId] } cb(err) } MemoryPersistence.prototype.putWill = function (client, packet, cb) { packet.brokerId = this.broker.id packet.clientId = client.id this._wills[client.id] = packet cb(null, client) } MemoryPersistence.prototype.getWill = function (client, cb) { cb(null, this._wills[client.id], client) } MemoryPersistence.prototype.delWill = function (client, cb) { var will = this._wills[client.id] delete this._wills[client.id] cb(null, will, client) } MemoryPersistence.prototype.streamWill = function (brokers) { var clients = Object.keys(this._wills) var wills = this._wills brokers = brokers || {} return from2.obj(function match (size, next) { var entry while ((entry = clients.shift()) != null) { if (!brokers[wills[entry].brokerId]) { setImmediate(next, null, wills[entry]) return } } if (!entry) { this.push(null) } }) } MemoryPersistence.prototype.getClientList = function (topic) { var clientSubs = this._subscriptions var entries = clientSubs.entries(clientSubs) return from2.obj(function match (size, next) { var entry while (!(entry = entries.next()).done) { if (entry.value[1].has(topic)) { setImmediate(next, null, entry.value[0]) return } } next(null, null) }) } MemoryPersistence.prototype.destroy = function (cb) { this._retained = null if (cb) { cb(null) } } module.exports = MemoryPersistence