rocket.chat.mqtt
Version:
It's a MQTT Server, using redis to scale horizontally.
176 lines (147 loc) • 3.88 kB
JavaScript
var write = require('../write')
var fastfall = require('fastfall')
var Packet = require('aedes-packet')
var through = require('through2')
var validateTopic = require('./validations').validateTopic
var topicActions = fastfall([
authorize,
storeSubscriptions,
subTopic
])
function SubscribeState (client, packet, finish, granted) {
this.client = client
this.packet = packet
this.finish = finish
this.granted = granted
}
function handleSubscribe (client, packet, done) {
var broker = client.broker
var subs = packet.subscriptions
var granted = []
broker._series(
new SubscribeState(client, packet, done, granted),
doSubscribe,
subs,
completeSubscribe)
}
function doSubscribe (sub, done) {
// TODO this function should not be needed
topicActions.call(this, sub, done)
}
function authorize (sub, done) {
var client = this.client
var err = validateTopic(sub.topic, 'SUBSCRIBE')
if (err) {
return done(err)
}
client.broker.authorizeSubscribe(client, sub, done)
}
function blockSys (func) {
return function deliverSharp (packet, cb) {
if (packet.topic.indexOf('$SYS') === 0) {
cb()
} else {
func(packet, cb)
}
}
}
function Subscription (qos, func) {
this.qos = qos
this.func = func
}
function storeSubscriptions (sub, done) {
var packet = this.packet
var client = this.client
var broker = client.broker
var perst = broker.persistence
if (sub && packet.subscriptions[0].topic !== sub.topic) {
// don't call addSubscriptions per topic,
// TODO change aedes subscribe handle, but this is a fast & dirty fix for now
return done(null, sub)
}
if (packet.restore) {
return done(null, sub)
}
if (client.clean) {
return done(null, sub)
}
perst.addSubscriptions(client, packet.subscriptions, function (err) {
done(err, sub)
})
}
function subTopic (sub, done) {
if (!sub) {
this.granted.push(128)
return done()
}
var client = this.client
var broker = client.broker
var func = nop
switch (sub.qos) {
case 2:
case 1:
func = client.deliverQoS
break
default:
func = client.deliver0
break
}
if (isWildcardThatMatchesSys(sub.topic)) {
func = blockSys(func)
}
this.granted.push(sub.qos)
if (!client.subscriptions[sub.topic]) {
client.subscriptions[sub.topic] = new Subscription(sub.qos, func)
broker.subscribe(sub.topic, func, done)
} else if (client.subscriptions[sub.topic].qos !== sub.qos) {
broker.unsubscribe(sub.topic, client.subscriptions[sub.topic].func)
client.subscriptions[sub.topic] = new Subscription(sub.qos, func)
broker.subscribe(sub.topic, func, done)
} else {
done()
}
}
// + is 43
// # is 35
function isWildcardThatMatchesSys (topic) {
var code = topic.charCodeAt(0)
return code === 43 || code === 35
}
function completeSubscribe (err) {
var packet = this.packet
var client = this.client
var broker = client.broker
var granted = this.granted
var done = this.finish
if (err) {
return done(err)
}
if (!packet.restore) {
broker.emit('subscribe', packet.subscriptions, client)
}
if (packet.messageId) {
write(client, new SubAck(packet, granted), done)
} else {
done()
}
var persistence = broker.persistence
var topics = []
for (var i = 0; i < packet.subscriptions.length; i++) {
topics.push(packet.subscriptions[i].topic)
}
var stream = persistence.createRetainedStreamCombi(topics)
stream.pipe(through.obj(function sendRetained (packet, enc, cb) {
packet = new Packet(packet)
// this should not be deduped
packet.brokerId = null
client.deliver0(packet, cb)
}))
}
function SubAck (packet, granted) {
this.cmd = 'suback'
this.messageId = packet.messageId
this.granted = granted
}
function nop () {}
module.exports = handleSubscribe