node-red-contrib-aedes
Version:
Node Red MQTT broker node based on aedes.js
270 lines (241 loc) • 8.35 kB
JavaScript
/**
* Copyright 2013,2014 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
module.exports = function (RED) {
'use strict';
const mongoPersistence = require('aedes-persistence-mongodb');
const aedes = require('aedes');
const net = require('net');
const tls = require('tls');
const http = require('http');
const https = require('https');
const ws = require('websocket-stream');
function AedesBrokerNode (config) {
RED.nodes.createNode(this, config);
this.mqtt_port = parseInt(config.mqtt_port, 10);
this.mqtt_ws_port = parseInt(config.mqtt_ws_port, 10);
this.usetls = config.usetls;
if (this.credentials) {
this.username = this.credentials.username;
this.password = this.credentials.password;
this.cert = this.credentials.certdata || '';
this.key = this.credentials.keydata || '';
}
if (typeof this.usetls === 'undefined') {
this.usetls = false;
}
const node = this;
const aedesSettings = {};
const serverOptions = {};
if (config.dburl) {
aedesSettings.persistence = mongoPersistence({
url: config.dburl
});
node.log('Start persistence to MongeDB');
}
if ((this.cert) && (this.key) && (this.usetls)) {
serverOptions.cert = this.cert;
serverOptions.key = this.key;
}
const broker = new aedes.Server(aedesSettings);
let server;
if (this.usetls) {
server = tls.createServer(serverOptions, broker.handle);
} else {
server = net.createServer(broker.handle);
}
let wss = null;
let httpServer = null;
if (this.mqtt_ws_port) {
// Awkward check since http or ws do not fire an error event in case the port is in use
const testServer = net.createServer();
testServer.once('error', function (err) {
if (err.code === 'EADDRINUSE') {
node.error('Error: Port ' + config.mqtt_ws_port + ' is already in use');
} else {
node.error('Error creating net server on port ' + config.mqtt_ws_port + ', ' + err.toString());
}
});
testServer.once('listening', function () {
testServer.close();
});
testServer.once('close', function () {
if (node.usetls) {
httpServer = https.createServer(serverOptions);
} else {
httpServer = http.createServer();
}
wss = ws.createServer({
server: httpServer
}, broker.handle);
httpServer.listen(config.mqtt_ws_port, function () {
node.log('Binding aedes mqtt server on ws port: ' + config.mqtt_ws_port);
});
});
testServer.listen(config.mqtt_ws_port, function () {
node.log('Checking ws port: ' + config.mqtt_ws_port);
});
}
server.once('error', function (err) {
if (err.code === 'EADDRINUSE') {
node.error('Error: Port ' + config.mqtt_port + ' is already in use');
node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' });
} else {
node.error('Error: Port ' + config.mqtt_port + ' ' + err.toString());
node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' });
}
});
if (this.mqtt_port) {
server.listen(this.mqtt_port, function () {
node.log('Binding aedes mqtt server on port: ' + config.mqtt_port);
node.status({ fill: 'green', shape: 'dot', text: 'node-red:common.status.connected' });
});
}
if (this.credentials && this.username && this.password) {
const authenticate = function (client, username, password, callback) {
var authorized = (username === node.username && password.toString() === node.password);
if (authorized) { client.user = username; }
callback(null, authorized);
};
broker.authenticate = authenticate;
}
broker.on('client', function (client) {
const msg = {
topic: 'client',
payload: {
client: client
}
};
node.send(msg);
});
broker.on('clientReady', function (client) {
const msg = {
topic: 'clientReady',
payload: {
client: client
}
};
node.status({ fill: 'green', shape: 'dot', text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients }) });
node.send(msg);
});
broker.on('clientDisconnect', function (client) {
const msg = {
topic: 'clientDisconnect',
payload: {
client: client
}
};
node.send(msg);
node.status({ fill: 'green', shape: 'dot', text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients }) });
});
broker.on('clientError', function (client, err) {
const msg = {
topic: 'clientError',
payload: {
client: client,
err: err
}
};
node.send(msg);
node.status({ fill: 'green', shape: 'dot', text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients }) });
});
broker.on('connectionError', function (client, err) {
const msg = {
topic: 'connectionError',
payload: {
client: client,
err: err
}
};
node.send(msg);
node.status({ fill: 'green', shape: 'dot', text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients }) });
});
broker.on('keepaliveTimeout', function (client) {
const msg = {
topic: 'keepaliveTimeout',
payload: {
client: client
}
};
node.send(msg);
node.status({ fill: 'green', shape: 'dot', text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients }) });
});
broker.on('subscribe', function (subscription, client) {
const msg = {
topic: 'subscribe',
payload: {
topic: subscription.topic,
qos: subscription.qos,
client: client
}
};
node.send(msg);
});
broker.on('unsubscribe', function (subscription, client) {
const msg = {
topic: 'unsubscribe',
payload: {
topic: subscription.topic,
qos: subscription.qos,
client: client
}
};
node.send(msg);
});
/*
broker.on('publish', function(packet,client) {
var msg = {
topic: 'publish',
payload: {
packet: packet,
client: client
}
};
node.send(msg);
});
*/
broker.on('closed', function () {
node.debug('Closed event');
});
this.on('close', function (done) {
broker.close(function () {
node.log('Unbinding aedes mqtt server from port: ' + config.mqtt_port);
server.close(function () {
node.debug('after server.close(): ');
if (wss) {
node.log('Unbinding aedes mqtt server from ws port: ' + config.mqtt_ws_port);
wss.close(function () {
node.debug('after wss.close(): ');
httpServer.close(function () {
node.debug('after httpServer.close(): ');
done();
});
});
} else {
done();
}
});
});
});
}
RED.nodes.registerType('aedes broker', AedesBrokerNode, {
credentials: {
username: { type: 'text' },
password: { type: 'password' },
certdata: { type: 'text' },
keydata: { type: 'text' }
}
});
};