mqtt-reqres-broker
Version:
mqtt request/response websocket broker
210 lines (159 loc) • 20.1 kB
HTML
<html lang="en"><head><title>lib/mqtt-reqres-broker</title></head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"><meta name="groc-relative-root" content="../"><meta name="groc-document-path" content="lib/mqtt-reqres-broker"><meta name="groc-project-path" content="lib/mqtt-reqres-broker.js"><link rel="stylesheet" type="text/css" media="all" href="../assets/style.css"><script type="text/javascript" src="../assets/behavior.js"></script><body><div id="meta"><div class="file-path">lib/mqtt-reqres-broker.js</div></div><div id="document"><div class="segment"><div class="comments "><div class="wrapper"><h2 id="mqttreqresbroker">MqttReqResBroker</h2></div></div></div><div class="segment"><div class="code"><div class="wrapper"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
<span class="hljs-pi">
'use strict'</span>;</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>dependencies</p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">const</span> debug = <span class="hljs-built_in">require</span>(<span class="hljs-string">'debug'</span>)(<span class="hljs-string">'mqtt-reqres-broker'</span>);
<span class="hljs-keyword">const</span> aedes = <span class="hljs-built_in">require</span>(<span class="hljs-string">'aedes'</span>);
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
<span class="hljs-keyword">const</span> ws = <span class="hljs-built_in">require</span>(<span class="hljs-string">'websocket-stream'</span>);
<span class="hljs-keyword">const</span> noop = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{};</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>limit the maximum payload size</p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">const</span> MAX_PAYLOAD_LENGTH = <span class="hljs-number">1024</span> * <span class="hljs-number">512</span>;</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><h2 id="broker-class">broker class</h2></div></div></div><div class="segment"><div class="code"><div class="wrapper"> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MqttReqResBroker</span> </span>{</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><h3 id="constructor">constructor</h3></div></div></div><div class="segment"><div class="code"><div class="wrapper"> constructor () {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>debug('constructor()');</p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">this</span>.hostname = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">this</span>.port = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">this</span>.mqtt = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">this</span>.httpServer = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">this</span>.wss = <span class="hljs-literal">null</span>;
} <span class="hljs-comment">// ctor</span></div></div></div><div class="segment"><div class="comments "><div class="wrapper"><h3 id="initializeoptions-callback">initialize(options, callback)</h3></div></div></div><div class="segment"><div class="code"><div class="wrapper"> initialize (options, callback) {
debug(<span class="hljs-string">'initialize'</span>, options);
<span class="hljs-keyword">this</span>.hostname = options.hostname;
<span class="hljs-keyword">this</span>.port = options.port;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.mqtt) {
<span class="hljs-keyword">this</span>.mqtt.close();
}</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>for options see: <a href="https://github.com/mcollina/aedes#aedesopts">https://github.com/mcollina/aedes#aedesopts</a></p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">this</span>.mqtt = aedes();</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p> authorizeSubscribe
It will be called when a client publishes a message.
see: <a href="https://github.com/mcollina/aedes#instanceauthorizesubscribeclient-pattern-doneerr-pattern">https://github.com/mcollina/aedes#instanceauthorizesubscribeclient-pattern-doneerr-pattern</a></p></div></div><div class="code"><div class="wrapper">
<span class="hljs-keyword">this</span>.mqtt.authorizeSubscribe = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">client, sub, cb</span>) </span>{
debug(<span class="hljs-string">'%s authorizeSubscribe(%s, %s)'</span>, client.id, client.id, sub.topic);
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">if</span> (sub.topic.length > <span class="hljs-number">500</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'ESUBTOPIC'</span>);
}
<span class="hljs-keyword">const</span> topicParts = sub.topic.split(<span class="hljs-string">'/'</span>);
<span class="hljs-keyword">if</span> (sub.qos !== <span class="hljs-number">0</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'ESUBQOS'</span>);
}
<span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">0</span>] === <span class="hljs-string">'connect'</span>) {
<span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">1</span>] && topicParts[<span class="hljs-number">2</span>] === <span class="hljs-string">'+'</span> && topicParts.length === <span class="hljs-number">3</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>subscribe -t connect/<device-id>/+</p></div></div><div class="code"><div class="wrapper"> debug(<span class="hljs-string">'%s authorizeSubscribe() subscribed connect/%s/+'</span>, client.id, topicParts[<span class="hljs-number">1</span>]);
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'ESUBCONNECT'</span>));
}
}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">0</span>] === <span class="hljs-string">'message'</span>) {
<span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">1</span>] && topicParts[<span class="hljs-number">2</span>] && topicParts[<span class="hljs-number">3</span>] === <span class="hljs-string">'#'</span> && topicParts.length === <span class="hljs-number">4</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>subscribe -t message/<from-client-id>/<to-channel-id>/#</p></div></div><div class="code"><div class="wrapper"> debug(<span class="hljs-string">'%s authorizeSubscribe() subscribed message/%s/%s/#'</span>, client.id, topicParts[<span class="hljs-number">1</span>], topicParts[<span class="hljs-number">2</span>]);
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'ESUBMESSAGE'</span>));
}
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'ESUBTOPIC'</span>));
}
}
<span class="hljs-keyword">catch</span> (error) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>To negate a subscription, set the subscription to null</p></div></div><div class="code"><div class="wrapper"> sub = <span class="hljs-literal">null</span>;
debug(<span class="hljs-string">'%s authorizeSubscribe() error:'</span>, client.id, error);
}
cb(<span class="hljs-literal">null</span>, sub);
};</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p> authorizePublish
It will be called when a client publishes a message.
see: <a href="https://github.com/mcollina/aedes#instanceauthorizesubscribeclient-pattern-doneerr-pattern">https://github.com/mcollina/aedes#instanceauthorizesubscribeclient-pattern-doneerr-pattern</a></p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">this</span>.mqtt.authorizePublish = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">client, packet, cb</span>) </span>{
debug(<span class="hljs-string">'%s authorizePublish(%s, %s)'</span>, client.id, client.id, packet.topic);
<span class="hljs-keyword">var</span> error = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">if</span> (packet.topic.length > <span class="hljs-number">500</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBTOPIC'</span>);
}
<span class="hljs-keyword">if</span> (packet.payload.length > MAX_PAYLOAD_LENGTH) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBPAYLOAD'</span>);
}
<span class="hljs-keyword">const</span> topicParts = packet.topic.split(<span class="hljs-string">'/'</span>);
<span class="hljs-keyword">if</span> (packet.qos !== <span class="hljs-number">0</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBQOS'</span>);
}
<span class="hljs-keyword">if</span> (packet.retain) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBRETAIN'</span>);
}
<span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">0</span>] === <span class="hljs-string">'connect'</span>) {
<span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">1</span>] && topicParts[<span class="hljs-number">2</span>] && topicParts.length === <span class="hljs-number">3</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>publish -t connect/<to-device-id>/<from-client-id></p></div></div><div class="code"><div class="wrapper"> debug(<span class="hljs-string">'%s authorizePublish() published connect/%s/%s'</span>, client.id, topicParts[<span class="hljs-number">1</span>], topicParts[<span class="hljs-number">2</span>]);
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBCONNECT'</span>));
}
}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">0</span>] === <span class="hljs-string">'message'</span>) {
<span class="hljs-keyword">if</span> (topicParts[<span class="hljs-number">1</span>] && topicParts[<span class="hljs-number">2</span>] && topicParts.length >= <span class="hljs-number">3</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>publish -t message/<from-client-id>/<to-channel-id></p></div></div><div class="code"><div class="wrapper"> debug(<span class="hljs-string">'%s authorizePublish() published message/%s/%s'</span>, client.id, topicParts[<span class="hljs-number">1</span>], topicParts[<span class="hljs-number">2</span>]);
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBMESSAGE'</span>));
}
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'EPUBTOPIC'</span>));
}
}
<span class="hljs-keyword">catch</span> (e) {
error = e;
debug(<span class="hljs-string">'%s authorizePublish() error:'</span>, client.id, error);
}
cb(error);
};</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>initialize done</p></div></div><div class="code"><div class="wrapper"> callback();
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
} <span class="hljs-comment">// initialize</span></div></div></div><div class="segment"><div class="comments "><div class="wrapper"><h3 id="initializemqtteventhandlers-options-callback">initializeMQTTEventHandlers (options, callback)</h3></div></div></div><div class="segment"><div class="code"><div class="wrapper"> initializeMQTTEventHandlers (options, callback) {
debug(<span class="hljs-string">'initializeMQTTEventHandlers()'</span>);
<span class="hljs-keyword">const</span> mqtt = <span class="hljs-keyword">this</span>.mqtt;</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>for event descriptions see <a href="https://github.com/mcollina/aedes#aedesopts">https://github.com/mcollina/aedes#aedesopts</a></p></div></div><div class="code"><div class="wrapper"> mqtt.on(<span class="hljs-string">'subscribe'</span>, (topics, client) => {
<span class="hljs-keyword">if</span> (client) {
debug(
<span class="hljs-string">'%s on subscribe topics'</span>,
client.id
);
topics.forEach(topic => {
debug(
<span class="hljs-string">'%s topic "%s" qos: %s'</span>,
topic.topic,
topic.qos,
client.id
);
});
}
});
mqtt.on(<span class="hljs-string">'publish'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">packet, client</span>) </span>{
<span class="hljs-keyword">if</span> (client) {
debug(
<span class="hljs-string">'%s on publish message %s topic "%s" qos %s'</span>,
client.id,
packet.topic,
packet.qos</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>packet.payload.toString()</p></div></div><div class="code"><div class="wrapper"> );
}
});
mqtt.on(<span class="hljs-string">'clientError'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">client, err</span>) </span>{
debug(<span class="hljs-string">'%s client error'</span>, client.id, err.message, err.stack);
});
mqtt.on(<span class="hljs-string">'client'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">client</span>) </span>{
debug(<span class="hljs-string">'%s on client - a new Client connects'</span>, client.id);
});
callback();
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
} <span class="hljs-comment">// initializeMQTTEventHandlers</span></div></div></div><div class="segment"><div class="comments "><div class="wrapper"><h3 id="start-options-callback">start (options, callback)</h3></div></div></div><div class="segment"><div class="code"><div class="wrapper"> start (options, callback) {
debug(<span class="hljs-string">'start()'</span>);
<span class="hljs-keyword">this</span>.httpServer = http.createServer();
<span class="hljs-keyword">this</span>.wss = ws.createServer({
server: <span class="hljs-keyword">this</span>.httpServer
}, <span class="hljs-keyword">this</span>.mqtt.handle);</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>init mqtt event handlers</p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">this</span>.initializeMQTTEventHandlers(<span class="hljs-literal">null</span>, () => {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>app.listen(port, [hostname], [backlog], [callback])</p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">this</span>.httpServer.listen(<span class="hljs-keyword">this</span>.port, <span class="hljs-keyword">this</span>.hostname, callback);
});
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
} <span class="hljs-comment">// start</span></div></div></div><div class="segment"><div class="comments "><div class="wrapper"><h3 id="close-callback">close (callback)</h3></div></div></div><div class="segment"><div class="code"><div class="wrapper"> close (callback) {
callback = callback || noop;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.mqtt) {
<span class="hljs-keyword">this</span>.mqtt.close();
}
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.wss) {
<span class="hljs-keyword">this</span>.wss.close();
}
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.httpServer) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>this.httpServer.unref();
Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event. The optional callback will be called once the 'close' event occurs. Unlike that event, it will be called with an Error as its only argument if the server was not open when it was closed.
see: <a href="https://nodejs.org/dist/latest-v6.x/docs/api/net.html#net_server_close_callback">https://nodejs.org/dist/latest-v6.x/docs/api/net.html#net_server_close_callback</a></p></div></div><div class="code"><div class="wrapper"> <span class="hljs-keyword">this</span>.httpServer.close(callback);
}
<span class="hljs-keyword">else</span> {
callback();
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
} <span class="hljs-comment">// close</span>
} <span class="hljs-comment">// MqttReqResBroker</span>
<span class="hljs-keyword">return</span> MqttReqResBroker;
}();</div></div></div></div></body></html>