iotivity 0.9.0
[platform/upstream/iotivity.git] / service / protocol-plugin / plugins / mqtt-light / lib / jsws / mosquitto.js
1 /*
2 Copyright (c) 2012 Roger Light <roger@atchoo.org>
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9    this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. Neither the name of mosquitto nor the names of its
14    contributors may be used to endorse or promote products derived from
15    this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /* Mosquitto MQTT Javascript/Websocket client */
31 /* Provides complete support for QoS 0. 
32  * Will not cause an error on QoS 1/2 packets.
33  */
34
35 var CONNECT = 0x10;
36 var CONNACK = 0x20;
37 var PUBLISH = 0x30;
38 var PUBACK = 0x40;
39 var PUBREC = 0x50;
40 var PUBREL = 0x60;
41 var PUBCOMP = 0x70;
42 var SUBSCRIBE = 0x80;
43 var SUBACK = 0x90;
44 var UNSUBSCRIBE = 0xA0;
45 var UNSUBACK = 0xB0;
46 var PINGREQ = 0xC0;
47 var PINGRESP = 0xD0;
48 var DISCONNECT = 0xE0;
49
50 function AB2S(buffer) {
51         var binary = '';
52         var bytes = new Uint8Array(buffer);
53         var len = bytes.byteLength;
54         for(var i=0; i<len; i++){
55                 binary += String.fromCharCode(bytes[i]);
56         }
57         return binary;
58 }
59
60 function Mosquitto()
61 {
62         this.ws = null;
63         this.onconnect = null;
64         this.ondisconnect = null;
65         this.onmessage = null;
66 }
67
68 Mosquitto.prototype = {
69         mqtt_ping : function()
70         {
71                 var buffer = new ArrayBuffer(2);
72                 var i8V = new Int8Array(buffer);
73                 i8V[0] = PINGREQ;
74                 i8V[1] = 0;
75                 if(this.ws.readyState == 1){
76                         this.ws.send(buffer);
77                 }else{
78                         this.queue(buffer);
79                 }
80                 setTimeout(function(_this){_this.mqtt_ping();}, 60000, this);
81         },
82
83         connect : function(url, keepalive){
84
85                 this.url = url;
86                 this.keepalive = keepalive;
87                 this.mid = 1;
88                 this.out_queue = new Array();
89
90                 this.ws = new WebSocket(url, 'mqttv3.1');
91                 this.ws.binaryType = "arraybuffer";
92                 this.ws.onopen = this.ws_onopen;
93                 this.ws.onclose = this.ws_onclose;
94                 this.ws.onmessage = this.ws_onmessage;
95                 this.ws.m = this;
96                 this.ws.onerror = function(evt){
97                         alert(evt.data);
98                 }
99         },
100
101         disconnect : function(){
102                 if(this.ws.readyState == 1){
103                         var buffer = new ArrayBuffer(2);
104                         var i8V = new Int8Array(buffer);
105
106                         i8V[0] = DISCONNECT;
107                         i8V[1] = 0;
108                         this.ws.send(buffer);
109                         this.ws.close();
110                 }
111         },
112
113         ws_onopen : function(evt) {
114                 var buffer = new ArrayBuffer(1+1+12+2+20);
115                 var i8V = new Int8Array(buffer);
116
117                 i=0;
118                 i8V[i++] = CONNECT;
119                 i8V[i++] = 12+2+20;
120                 i8V[i++] = 0;
121                 i8V[i++] = 6;
122                 str = "MQIsdp";
123                 for(var j=0; j<str.length; j++){
124                         i8V[i++] = str.charCodeAt(j);
125                 }
126                 i8V[i++] = 3;
127                 i8V[i++] = 2;
128                 i8V[i++] = 0;
129                 i8V[i++] = 60;
130                 i8V[i++] = 0;
131                 i8V[i++] = 20;
132                 var str = "mjsws/";
133                 for(var j=0; j<str.length; j++){
134                         i8V[i++] = str.charCodeAt(j);
135                 }
136                 var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
137                 for(var j=0; j<14; j++){
138                         i8V[i++] = chars.charCodeAt(Math.floor(Math.random()*chars.length));
139                 }
140
141                 this.send(buffer);
142                 while(this.m.out_queue.length > 0){
143                         this.send(this.m.out_queue.pop());
144                 }
145                 setTimeout(function(_this){_this.mqtt_ping();}, 60000, this.m);
146         },
147
148         ws_onclose : function(evt) {
149                 if(this.m.ondisconnect){
150                         this.m.ondisconnect(evt.data);
151                 }
152         },
153
154         ws_onmessage : function(evt) {
155                 var i8V = new Int8Array(evt.data);
156                 buffer = evt.data;
157                 var q=0;
158                 while(i8V.length > 0 && q < 1000){
159                         q++;
160                         switch(i8V[0] & 0xF0){
161                                 case CONNACK:
162                                         var rl = i8V[1];
163                                         var rc = i8V[2];
164                                         if(this.m.onconnect){
165                                                 this.m.onconnect(rc);
166                                         }
167                                         buffer = buffer.slice(rl+2);
168                                         i8V = new Int8Array(buffer);
169                                         break;
170                                 case PUBLISH:
171                                         var i=1;
172                                         var mult = 1;
173                                         var rl = 0;
174                                         var count = 0;
175                                         var digit;
176                                         var qos = (i8V[0] & 0x06) >> 1;
177                                         var retain = (i8V[0] & 0x01);
178                                         var mid = 0;
179                                         do{
180                                                 count++;
181                                                 digit = i8V[i++];
182                                                 rl += (digit & 127)*mult;
183                                                 mult *= 128;
184                                         }while((digit & 128) != 0);
185
186                                         var topiclen = i8V[i++]*256 + i8V[i++];
187                                         var atopic = buffer.slice(i, i+topiclen);
188                                         i+=topiclen;
189                                         var topic = AB2S(atopic);
190                                         if(qos > 0){
191                                                 mid = i8V[i++]*256 + i8V[i++];
192                                         }
193                                         var apayload = buffer.slice(i, rl+count+1);
194                                         var payload = AB2S(apayload);
195
196                                         buffer = buffer.slice(rl+1+count);
197                                         i8V = new Int8Array(buffer);
198
199                                         if(this.m.onmessage){
200                                                 this.m.onmessage(topic, payload, qos, retain);
201                                         }
202                                         break;
203                                 case PUBREC:
204                                 case PUBREL:
205                                 case PUBACK:
206                                 case PUBCOMP:
207                                 case SUBACK:
208                                 case UNSUBACK:
209                                 case PINGRESP:
210                                         var rl = i8V[1];
211                                         buffer = buffer.slice(rl+2);
212                                         i8V = new Int8Array(buffer);
213                                         break;
214                         }
215                 }
216         },
217
218         get_remaining_count : function(remaining_length)
219         {
220                 if(remaining_length >= 0 && remaining_length < 128){
221                         return 1;
222                 }else if(remaining_length >= 128 && remaining_length < 16384){
223                         return 2;
224                 }else if(remaining_length >= 16384 && remaining_length < 2097152){
225                         return 3;
226                 }else if(remaining_length >= 2097152 && remaining_length < 268435456){
227                         return 4;
228                 }else{
229                         return -1;
230                 }
231         },
232
233         generate_mid : function()
234         {
235                 var mid = this.mid;
236                 this.mid++;
237                 if(this.mid == 256) this.mid = 0;
238                 return mid;
239         },
240
241         queue : function(buffer)
242         {
243                 this.out_queue.push(buffer);
244         },
245
246         send_cmd_with_mid : function(cmd, mid)
247         {
248                 var buffer = new ArrayBuffer(4);
249                 var i8V = new Int8Array(buffer);
250                 i8V[0] = cmd;
251                 i8V[1] = 2;
252                 i8V[2] = mid%128;
253                 i8V[3] = mid/128;
254                 if(this.ws.readyState == 1){
255                         this.ws.send(buffer);
256                 }else{
257                         this.queue(buffer);
258                 }
259         },
260
261         unsubscribe : function(topic)
262         {
263                 var rl = 2+2+topic.length;
264                 var remaining_count = this.get_remaining_count(rl);
265                 var buffer = new ArrayBuffer(1+remaining_count+rl);
266                 var i8V = new Int8Array(buffer);
267
268                 var i=0;
269                 i8V[i++] = UNSUBSCRIBE | 0x02;
270                 do{
271                         digit = Math.floor(rl % 128);
272                         rl = Math.floor(rl / 128);
273                         if(rl > 0){
274                                 digit = digit | 0x80;
275                         }
276                         i8V[i++] = digit;
277                 }while(rl > 0);
278                 i8V[i++] = 0;
279                 i8V[i++] = this.generate_mid();
280                 i8V[i++] = 0;
281                 i8V[i++] = topic.length;
282                 for(var j=0; j<topic.length; j++){
283                         i8V[i++] = topic.charCodeAt(j);
284                 }
285
286                 if(this.ws.readyState == 1){
287                         this.ws.send(buffer);
288                 }else{
289                         this.queue(buffer);
290                 }
291         },
292
293         subscribe : function(topic, qos)
294         {
295                 if(qos != 0){
296                         return 1;
297                 }
298                 var rl = 2+2+topic.length+1;
299                 var remaining_count = this.get_remaining_count(rl);
300                 var buffer = new ArrayBuffer(1+remaining_count+rl);
301                 var i8V = new Int8Array(buffer);
302
303                 var i=0;
304                 i8V[i++] = SUBSCRIBE | 0x02;
305                 do{
306                         digit = Math.floor(rl % 128);
307                         rl = Math.floor(rl / 128);
308                         if(rl > 0){
309                                 digit = digit | 0x80;
310                         }
311                         i8V[i++] = digit;
312                 }while(rl > 0);
313                 i8V[i++] = 0;
314                 i8V[i++] = this.generate_mid();
315                 i8V[i++] = 0;
316                 i8V[i++] = topic.length;
317                 for(var j=0; j<topic.length; j++){
318                         i8V[i++] = topic.charCodeAt(j);
319                 }
320                 i8V[i++] = qos;
321
322                 if(this.ws.readyState == 1){
323                         this.ws.send(buffer);
324                 }else{
325                         this.queue(buffer);
326                 }
327         },
328
329         publish : function(topic, payload, qos, retain){
330                 if(qos != 0) return 1;
331                 var rl = 2+topic.length+payload.length;
332                 var remaining_count = this.get_remaining_count(rl);
333                 var buffer = new ArrayBuffer(1+remaining_count+rl);
334                 var i8V = new Int8Array(buffer);
335
336                 var i=0;
337                 retain = retain?1:0;
338                 i8V[i++] = PUBLISH | (qos<<1) | retain;
339                 do{
340                         digit = Math.floor(rl % 128);
341                         rl = Math.floor(rl / 128);
342                         if(rl > 0){
343                                 digit = digit | 0x80;
344                         }
345                         i8V[i++] = digit;
346                 }while(rl > 0);
347                 i8V[i++] = 0;
348                 i8V[i++] = topic.length;
349                 for(var j=0; j<topic.length; j++){
350                         i8V[i++] = topic.charCodeAt(j);
351                 }
352                 for(var j=0; j<payload.length; j++){
353                         i8V[i++] = payload.charCodeAt(j);
354                 }
355
356                 if(this.ws.readyState == 1){
357                         this.ws.send(buffer);
358                 }else{
359                         this.queue(buffer);
360                 }
361         }
362 }
363