364 lines
8.4 KiB
JavaScript
364 lines
8.4 KiB
JavaScript
|
/*
|
||
|
Copyright (c) 2012 Roger Light <roger@atchoo.org>
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
|
||
|
1. Redistributions of source code must retain the above copyright notice,
|
||
|
this list of conditions and the following disclaimer.
|
||
|
2. Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
3. Neither the name of mosquitto nor the names of its
|
||
|
contributors may be used to endorse or promote products derived from
|
||
|
this software without specific prior written permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
/* Mosquitto MQTT Javascript/Websocket client */
|
||
|
/* Provides complete support for QoS 0.
|
||
|
* Will not cause an error on QoS 1/2 packets.
|
||
|
*/
|
||
|
|
||
|
var CONNECT = 0x10;
|
||
|
var CONNACK = 0x20;
|
||
|
var PUBLISH = 0x30;
|
||
|
var PUBACK = 0x40;
|
||
|
var PUBREC = 0x50;
|
||
|
var PUBREL = 0x60;
|
||
|
var PUBCOMP = 0x70;
|
||
|
var SUBSCRIBE = 0x80;
|
||
|
var SUBACK = 0x90;
|
||
|
var UNSUBSCRIBE = 0xA0;
|
||
|
var UNSUBACK = 0xB0;
|
||
|
var PINGREQ = 0xC0;
|
||
|
var PINGRESP = 0xD0;
|
||
|
var DISCONNECT = 0xE0;
|
||
|
|
||
|
function AB2S(buffer) {
|
||
|
var binary = '';
|
||
|
var bytes = new Uint8Array(buffer);
|
||
|
var len = bytes.byteLength;
|
||
|
for(var i=0; i<len; i++){
|
||
|
binary += String.fromCharCode(bytes[i]);
|
||
|
}
|
||
|
return binary;
|
||
|
}
|
||
|
|
||
|
function Mosquitto()
|
||
|
{
|
||
|
this.ws = null;
|
||
|
this.onconnect = null;
|
||
|
this.ondisconnect = null;
|
||
|
this.onmessage = null;
|
||
|
}
|
||
|
|
||
|
Mosquitto.prototype = {
|
||
|
mqtt_ping : function()
|
||
|
{
|
||
|
var buffer = new ArrayBuffer(2);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
i8V[0] = PINGREQ;
|
||
|
i8V[1] = 0;
|
||
|
if(this.ws.readyState == 1){
|
||
|
this.ws.send(buffer);
|
||
|
}else{
|
||
|
this.queue(buffer);
|
||
|
}
|
||
|
setTimeout(function(_this){_this.mqtt_ping();}, 60000, this);
|
||
|
},
|
||
|
|
||
|
connect : function(url, keepalive){
|
||
|
|
||
|
this.url = url;
|
||
|
this.keepalive = keepalive;
|
||
|
this.mid = 1;
|
||
|
this.out_queue = new Array();
|
||
|
|
||
|
this.ws = new WebSocket(url, 'mqttv3.1');
|
||
|
this.ws.binaryType = "arraybuffer";
|
||
|
this.ws.onopen = this.ws_onopen;
|
||
|
this.ws.onclose = this.ws_onclose;
|
||
|
this.ws.onmessage = this.ws_onmessage;
|
||
|
this.ws.m = this;
|
||
|
this.ws.onerror = function(evt){
|
||
|
alert(evt.data);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
disconnect : function(){
|
||
|
if(this.ws.readyState == 1){
|
||
|
var buffer = new ArrayBuffer(2);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
|
||
|
i8V[0] = DISCONNECT;
|
||
|
i8V[1] = 0;
|
||
|
this.ws.send(buffer);
|
||
|
this.ws.close();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
ws_onopen : function(evt) {
|
||
|
var buffer = new ArrayBuffer(1+1+12+2+20);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
|
||
|
i=0;
|
||
|
i8V[i++] = CONNECT;
|
||
|
i8V[i++] = 12+2+20;
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = 6;
|
||
|
str = "MQIsdp";
|
||
|
for(var j=0; j<str.length; j++){
|
||
|
i8V[i++] = str.charCodeAt(j);
|
||
|
}
|
||
|
i8V[i++] = 3;
|
||
|
i8V[i++] = 2;
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = 60;
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = 20;
|
||
|
var str = "mjsws/";
|
||
|
for(var j=0; j<str.length; j++){
|
||
|
i8V[i++] = str.charCodeAt(j);
|
||
|
}
|
||
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||
|
for(var j=0; j<14; j++){
|
||
|
i8V[i++] = chars.charCodeAt(Math.floor(Math.random()*chars.length));
|
||
|
}
|
||
|
|
||
|
this.send(buffer);
|
||
|
while(this.m.out_queue.length > 0){
|
||
|
this.send(this.m.out_queue.pop());
|
||
|
}
|
||
|
setTimeout(function(_this){_this.mqtt_ping();}, 60000, this.m);
|
||
|
},
|
||
|
|
||
|
ws_onclose : function(evt) {
|
||
|
if(this.m.ondisconnect){
|
||
|
this.m.ondisconnect(evt.data);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
ws_onmessage : function(evt) {
|
||
|
var i8V = new Int8Array(evt.data);
|
||
|
buffer = evt.data;
|
||
|
var q=0;
|
||
|
while(i8V.length > 0 && q < 1000){
|
||
|
q++;
|
||
|
switch(i8V[0] & 0xF0){
|
||
|
case CONNACK:
|
||
|
var rl = i8V[1];
|
||
|
var rc = i8V[2];
|
||
|
if(this.m.onconnect){
|
||
|
this.m.onconnect(rc);
|
||
|
}
|
||
|
buffer = buffer.slice(rl+2);
|
||
|
i8V = new Int8Array(buffer);
|
||
|
break;
|
||
|
case PUBLISH:
|
||
|
var i=1;
|
||
|
var mult = 1;
|
||
|
var rl = 0;
|
||
|
var count = 0;
|
||
|
var digit;
|
||
|
var qos = (i8V[0] & 0x06) >> 1;
|
||
|
var retain = (i8V[0] & 0x01);
|
||
|
var mid = 0;
|
||
|
do{
|
||
|
count++;
|
||
|
digit = i8V[i++];
|
||
|
rl += (digit & 127)*mult;
|
||
|
mult *= 128;
|
||
|
}while((digit & 128) != 0);
|
||
|
|
||
|
var topiclen = i8V[i++]*256 + i8V[i++];
|
||
|
var atopic = buffer.slice(i, i+topiclen);
|
||
|
i+=topiclen;
|
||
|
var topic = AB2S(atopic);
|
||
|
if(qos > 0){
|
||
|
mid = i8V[i++]*256 + i8V[i++];
|
||
|
}
|
||
|
var apayload = buffer.slice(i, rl+count+1);
|
||
|
var payload = AB2S(apayload);
|
||
|
|
||
|
buffer = buffer.slice(rl+1+count);
|
||
|
i8V = new Int8Array(buffer);
|
||
|
|
||
|
if(this.m.onmessage){
|
||
|
this.m.onmessage(topic, payload, qos, retain);
|
||
|
}
|
||
|
break;
|
||
|
case PUBREC:
|
||
|
case PUBREL:
|
||
|
case PUBACK:
|
||
|
case PUBCOMP:
|
||
|
case SUBACK:
|
||
|
case UNSUBACK:
|
||
|
case PINGRESP:
|
||
|
var rl = i8V[1];
|
||
|
buffer = buffer.slice(rl+2);
|
||
|
i8V = new Int8Array(buffer);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
get_remaining_count : function(remaining_length)
|
||
|
{
|
||
|
if(remaining_length >= 0 && remaining_length < 128){
|
||
|
return 1;
|
||
|
}else if(remaining_length >= 128 && remaining_length < 16384){
|
||
|
return 2;
|
||
|
}else if(remaining_length >= 16384 && remaining_length < 2097152){
|
||
|
return 3;
|
||
|
}else if(remaining_length >= 2097152 && remaining_length < 268435456){
|
||
|
return 4;
|
||
|
}else{
|
||
|
return -1;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
generate_mid : function()
|
||
|
{
|
||
|
var mid = this.mid;
|
||
|
this.mid++;
|
||
|
if(this.mid == 256) this.mid = 0;
|
||
|
return mid;
|
||
|
},
|
||
|
|
||
|
queue : function(buffer)
|
||
|
{
|
||
|
this.out_queue.push(buffer);
|
||
|
},
|
||
|
|
||
|
send_cmd_with_mid : function(cmd, mid)
|
||
|
{
|
||
|
var buffer = new ArrayBuffer(4);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
i8V[0] = cmd;
|
||
|
i8V[1] = 2;
|
||
|
i8V[2] = mid%128;
|
||
|
i8V[3] = mid/128;
|
||
|
if(this.ws.readyState == 1){
|
||
|
this.ws.send(buffer);
|
||
|
}else{
|
||
|
this.queue(buffer);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
unsubscribe : function(topic)
|
||
|
{
|
||
|
var rl = 2+2+topic.length;
|
||
|
var remaining_count = this.get_remaining_count(rl);
|
||
|
var buffer = new ArrayBuffer(1+remaining_count+rl);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
|
||
|
var i=0;
|
||
|
i8V[i++] = UNSUBSCRIBE | 0x02;
|
||
|
do{
|
||
|
digit = Math.floor(rl % 128);
|
||
|
rl = Math.floor(rl / 128);
|
||
|
if(rl > 0){
|
||
|
digit = digit | 0x80;
|
||
|
}
|
||
|
i8V[i++] = digit;
|
||
|
}while(rl > 0);
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = this.generate_mid();
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = topic.length;
|
||
|
for(var j=0; j<topic.length; j++){
|
||
|
i8V[i++] = topic.charCodeAt(j);
|
||
|
}
|
||
|
|
||
|
if(this.ws.readyState == 1){
|
||
|
this.ws.send(buffer);
|
||
|
}else{
|
||
|
this.queue(buffer);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
subscribe : function(topic, qos)
|
||
|
{
|
||
|
if(qos != 0){
|
||
|
return 1;
|
||
|
}
|
||
|
var rl = 2+2+topic.length+1;
|
||
|
var remaining_count = this.get_remaining_count(rl);
|
||
|
var buffer = new ArrayBuffer(1+remaining_count+rl);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
|
||
|
var i=0;
|
||
|
i8V[i++] = SUBSCRIBE | 0x02;
|
||
|
do{
|
||
|
digit = Math.floor(rl % 128);
|
||
|
rl = Math.floor(rl / 128);
|
||
|
if(rl > 0){
|
||
|
digit = digit | 0x80;
|
||
|
}
|
||
|
i8V[i++] = digit;
|
||
|
}while(rl > 0);
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = this.generate_mid();
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = topic.length;
|
||
|
for(var j=0; j<topic.length; j++){
|
||
|
i8V[i++] = topic.charCodeAt(j);
|
||
|
}
|
||
|
i8V[i++] = qos;
|
||
|
|
||
|
if(this.ws.readyState == 1){
|
||
|
this.ws.send(buffer);
|
||
|
}else{
|
||
|
this.queue(buffer);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
publish : function(topic, payload, qos, retain){
|
||
|
if(qos != 0) return 1;
|
||
|
var rl = 2+topic.length+payload.length;
|
||
|
var remaining_count = this.get_remaining_count(rl);
|
||
|
var buffer = new ArrayBuffer(1+remaining_count+rl);
|
||
|
var i8V = new Int8Array(buffer);
|
||
|
|
||
|
var i=0;
|
||
|
retain = retain?1:0;
|
||
|
i8V[i++] = PUBLISH | (qos<<1) | retain;
|
||
|
do{
|
||
|
digit = Math.floor(rl % 128);
|
||
|
rl = Math.floor(rl / 128);
|
||
|
if(rl > 0){
|
||
|
digit = digit | 0x80;
|
||
|
}
|
||
|
i8V[i++] = digit;
|
||
|
}while(rl > 0);
|
||
|
i8V[i++] = 0;
|
||
|
i8V[i++] = topic.length;
|
||
|
for(var j=0; j<topic.length; j++){
|
||
|
i8V[i++] = topic.charCodeAt(j);
|
||
|
}
|
||
|
for(var j=0; j<payload.length; j++){
|
||
|
i8V[i++] = payload.charCodeAt(j);
|
||
|
}
|
||
|
|
||
|
if(this.ws.readyState == 1){
|
||
|
this.ws.send(buffer);
|
||
|
}else{
|
||
|
this.queue(buffer);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|