UNPKG

mqtt-reqres-broker

Version:

mqtt request/response websocket broker

210 lines (159 loc) 20.1 kB
<!DOCTYPE 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(&#39;constructor()&#39;);</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 &gt; <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>] &amp;&amp; topicParts[<span class="hljs-number">2</span>] === <span class="hljs-string">'+'</span> &amp;&amp; topicParts.length === <span class="hljs-number">3</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>subscribe -t connect/&lt;device-id&gt;/+</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>] &amp;&amp; topicParts[<span class="hljs-number">2</span>] &amp;&amp; topicParts[<span class="hljs-number">3</span>] === <span class="hljs-string">'#'</span> &amp;&amp; topicParts.length === <span class="hljs-number">4</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>subscribe -t message/&lt;from-client-id&gt;/&lt;to-channel-id&gt;/#</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 &gt; <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 &gt; 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>] &amp;&amp; topicParts[<span class="hljs-number">2</span>] &amp;&amp; topicParts.length === <span class="hljs-number">3</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>publish -t connect/&lt;to-device-id&gt;/&lt;from-client-id&gt;</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>] &amp;&amp; topicParts[<span class="hljs-number">2</span>] &amp;&amp; topicParts.length &gt;= <span class="hljs-number">3</span>) {</div></div></div><div class="segment"><div class="comments "><div class="wrapper"><p>publish -t message/&lt;from-client-id&gt;/&lt;to-channel-id&gt;</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) =&gt; { <span class="hljs-keyword">if</span> (client) { debug( <span class="hljs-string">'%s on subscribe topics'</span>, client.id ); topics.forEach(topic =&gt; { 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>, () =&gt; {</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 &#39;close&#39; event. The optional callback will be called once the &#39;close&#39; 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>