1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | |
84 | (function ExportLibrary(root, factory) { |
85 | if(typeof exports === 'object' && typeof module === 'object'){ |
86 | module.exports = factory(); |
87 | } else if (typeof define === 'function' && define.amd){ |
88 | define(factory); |
89 | } else if (typeof exports === 'object'){ |
90 | exports = factory(); |
91 | } else { |
92 | if (typeof root.Paho === 'undefined'){ |
93 | root.Paho = {}; |
94 | } |
95 | root.Paho.MQTT = factory(); |
96 | } |
97 | })(this, function LibraryFactory(){ |
98 | |
99 | |
100 | var PahoMQTT = (function (global) { |
101 | |
102 | |
103 | |
104 | |
105 | var version = "@VERSION@"; |
106 | var buildLevel = "@BUILDLEVEL@"; |
107 | |
108 | |
109 | |
110 | |
111 | |
112 | |
113 | var MESSAGE_TYPE = { |
114 | CONNECT: 1, |
115 | CONNACK: 2, |
116 | PUBLISH: 3, |
117 | PUBACK: 4, |
118 | PUBREC: 5, |
119 | PUBREL: 6, |
120 | PUBCOMP: 7, |
121 | SUBSCRIBE: 8, |
122 | SUBACK: 9, |
123 | UNSUBSCRIBE: 10, |
124 | UNSUBACK: 11, |
125 | PINGREQ: 12, |
126 | PINGRESP: 13, |
127 | DISCONNECT: 14 |
128 | }; |
129 | |
130 | |
131 | |
132 | |
133 | |
134 | |
135 | |
136 | |
137 | |
138 | |
139 | |
140 | |
141 | |
142 | |
143 | var validate = function(obj, keys) { |
144 | for (var key in obj) { |
145 | if (obj.hasOwnProperty(key)) { |
146 | if (keys.hasOwnProperty(key)) { |
147 | if (typeof obj[key] !== keys[key]) |
148 | throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); |
149 | } else { |
150 | var errorStr = "Unknown property, " + key + ". Valid properties are:"; |
151 | for (var validKey in keys) |
152 | if (keys.hasOwnProperty(validKey)) |
153 | errorStr = errorStr+" "+validKey; |
154 | throw new Error(errorStr); |
155 | } |
156 | } |
157 | } |
158 | }; |
159 | |
160 | |
161 | |
162 | |
163 | |
164 | |
165 | |
166 | |
167 | |
168 | var scope = function (f, scope) { |
169 | return function () { |
170 | return f.apply(scope, arguments); |
171 | }; |
172 | }; |
173 | |
174 | |
175 | |
176 | |
177 | |
178 | |
179 | var ERROR = { |
180 | OK: {code:0, text:"AMQJSC0000I OK."}, |
181 | CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."}, |
182 | SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."}, |
183 | UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."}, |
184 | PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."}, |
185 | INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}"}, |
186 | CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."}, |
187 | SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."}, |
188 | SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."}, |
189 | MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, |
190 | UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."}, |
191 | INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."}, |
192 | INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."}, |
193 | INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."}, |
194 | UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."}, |
195 | INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."}, |
196 | INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."}, |
197 | MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."}, |
198 | BUFFER_FULL: {code:18, text:"AMQJS0018E Message buffer is full, maximum buffer size: {0}."}, |
199 | }; |
200 | |
201 | |
202 | var CONNACK_RC = { |
203 | 0:"Connection Accepted", |
204 | 1:"Connection Refused: unacceptable protocol version", |
205 | 2:"Connection Refused: identifier rejected", |
206 | 3:"Connection Refused: server unavailable", |
207 | 4:"Connection Refused: bad user name or password", |
208 | 5:"Connection Refused: not authorized" |
209 | }; |
210 | |
211 | |
212 | |
213 | |
214 | |
215 | |
216 | |
217 | |
218 | var format = function(error, substitutions) { |
219 | var text = error.text; |
220 | if (substitutions) { |
221 | var field,start; |
222 | for (var i=0; i<substitutions.length; i++) { |
223 | field = "{"+i+"}"; |
224 | start = text.indexOf(field); |
225 | if(start > 0) { |
226 | var part1 = text.substring(0,start); |
227 | var part2 = text.substring(start+field.length); |
228 | text = part1+substitutions[i]+part2; |
229 | } |
230 | } |
231 | } |
232 | return text; |
233 | }; |
234 | |
235 | |
236 | var MqttProtoIdentifierv3 = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03]; |
237 | |
238 | var MqttProtoIdentifierv4 = [0x00,0x04,0x4d,0x51,0x54,0x54,0x04]; |
239 | |
240 | |
241 | |
242 | |
243 | |
244 | |
245 | |
246 | |
247 | |
248 | |
249 | |
250 | |
251 | |
252 | |
253 | |
254 | |
255 | |
256 | |
257 | |
258 | |
259 | |
260 | |
261 | |
262 | |
263 | |
264 | var WireMessage = function (type, options) { |
265 | this.type = type; |
266 | for (var name in options) { |
267 | if (options.hasOwnProperty(name)) { |
268 | this[name] = options[name]; |
269 | } |
270 | } |
271 | }; |
272 | |
273 | WireMessage.prototype.encode = function() { |
274 | |
275 | var first = ((this.type & 0x0f) << 4); |
276 | |
277 | |
278 | |
279 | |
280 | |
281 | |
282 | var remLength = 0; |
283 | var topicStrLength = []; |
284 | var destinationNameLength = 0; |
285 | var willMessagePayloadBytes; |
286 | |
287 | |
288 | if (this.messageIdentifier !== undefined) |
289 | remLength += 2; |
290 | |
291 | switch(this.type) { |
292 | |
293 | case MESSAGE_TYPE.CONNECT: |
294 | switch(this.mqttVersion) { |
295 | case 3: |
296 | remLength += MqttProtoIdentifierv3.length + 3; |
297 | break; |
298 | case 4: |
299 | remLength += MqttProtoIdentifierv4.length + 3; |
300 | break; |
301 | } |
302 | |
303 | remLength += UTF8Length(this.clientId) + 2; |
304 | if (this.willMessage !== undefined) { |
305 | remLength += UTF8Length(this.willMessage.destinationName) + 2; |
306 | |
307 | willMessagePayloadBytes = this.willMessage.payloadBytes; |
308 | if (!(willMessagePayloadBytes instanceof Uint8Array)) |
309 | willMessagePayloadBytes = new Uint8Array(payloadBytes); |
310 | remLength += willMessagePayloadBytes.byteLength +2; |
311 | } |
312 | if (this.userName !== undefined) |
313 | remLength += UTF8Length(this.userName) + 2; |
314 | if (this.password !== undefined) |
315 | remLength += UTF8Length(this.password) + 2; |
316 | break; |
317 | |
318 | |
319 | case MESSAGE_TYPE.SUBSCRIBE: |
320 | first |= 0x02; |
321 | for ( var i = 0; i < this.topics.length; i++) { |
322 | topicStrLength[i] = UTF8Length(this.topics[i]); |
323 | remLength += topicStrLength[i] + 2; |
324 | } |
325 | remLength += this.requestedQos.length; |
326 | |
327 | break; |
328 | |
329 | case MESSAGE_TYPE.UNSUBSCRIBE: |
330 | first |= 0x02; |
331 | for ( var i = 0; i < this.topics.length; i++) { |
332 | topicStrLength[i] = UTF8Length(this.topics[i]); |
333 | remLength += topicStrLength[i] + 2; |
334 | } |
335 | break; |
336 | |
337 | case MESSAGE_TYPE.PUBREL: |
338 | first |= 0x02; |
339 | break; |
340 | |
341 | case MESSAGE_TYPE.PUBLISH: |
342 | if (this.payloadMessage.duplicate) first |= 0x08; |
343 | first = first |= (this.payloadMessage.qos << 1); |
344 | if (this.payloadMessage.retained) first |= 0x01; |
345 | destinationNameLength = UTF8Length(this.payloadMessage.destinationName); |
346 | remLength += destinationNameLength + 2; |
347 | var payloadBytes = this.payloadMessage.payloadBytes; |
348 | remLength += payloadBytes.byteLength; |
349 | if (payloadBytes instanceof ArrayBuffer) |
350 | payloadBytes = new Uint8Array(payloadBytes); |
351 | else if (!(payloadBytes instanceof Uint8Array)) |
352 | payloadBytes = new Uint8Array(payloadBytes.buffer); |
353 | break; |
354 | |
355 | case MESSAGE_TYPE.DISCONNECT: |
356 | break; |
357 | |
358 | default: |
359 | break; |
360 | } |
361 | |
362 | |
363 | |
364 | var mbi = encodeMBI(remLength); |
365 | var pos = mbi.length + 1; |
366 | var buffer = new ArrayBuffer(remLength + pos); |
367 | var byteStream = new Uint8Array(buffer); |
368 | |
369 | |
370 | byteStream[0] = first; |
371 | byteStream.set(mbi,1); |
372 | |
373 | |
374 | if (this.type == MESSAGE_TYPE.PUBLISH) |
375 | pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); |
376 | |
377 | |
378 | else if (this.type == MESSAGE_TYPE.CONNECT) { |
379 | switch (this.mqttVersion) { |
380 | case 3: |
381 | byteStream.set(MqttProtoIdentifierv3, pos); |
382 | pos += MqttProtoIdentifierv3.length; |
383 | break; |
384 | case 4: |
385 | byteStream.set(MqttProtoIdentifierv4, pos); |
386 | pos += MqttProtoIdentifierv4.length; |
387 | break; |
388 | } |
389 | var connectFlags = 0; |
390 | if (this.cleanSession) |
391 | connectFlags = 0x02; |
392 | if (this.willMessage !== undefined ) { |
393 | connectFlags |= 0x04; |
394 | connectFlags |= (this.willMessage.qos<<3); |
395 | if (this.willMessage.retained) { |
396 | connectFlags |= 0x20; |
397 | } |
398 | } |
399 | if (this.userName !== undefined) |
400 | connectFlags |= 0x80; |
401 | if (this.password !== undefined) |
402 | connectFlags |= 0x40; |
403 | byteStream[pos++] = connectFlags; |
404 | pos = writeUint16 (this.keepAliveInterval, byteStream, pos); |
405 | } |
406 | |
407 | |
408 | if (this.messageIdentifier !== undefined) |
409 | pos = writeUint16 (this.messageIdentifier, byteStream, pos); |
410 | |
411 | switch(this.type) { |
412 | case MESSAGE_TYPE.CONNECT: |
413 | pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); |
414 | if (this.willMessage !== undefined) { |
415 | pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); |
416 | pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); |
417 | byteStream.set(willMessagePayloadBytes, pos); |
418 | pos += willMessagePayloadBytes.byteLength; |
419 | |
420 | } |
421 | if (this.userName !== undefined) |
422 | pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); |
423 | if (this.password !== undefined) |
424 | pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); |
425 | break; |
426 | |
427 | case MESSAGE_TYPE.PUBLISH: |
428 | |
429 | byteStream.set(payloadBytes, pos); |
430 | |
431 | break; |
432 | |
433 | |
434 | |
435 | |
436 | |
437 | |
438 | case MESSAGE_TYPE.SUBSCRIBE: |
439 | |
440 | for (var i=0; i<this.topics.length; i++) { |
441 | pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos); |
442 | byteStream[pos++] = this.requestedQos[i]; |
443 | } |
444 | break; |
445 | |
446 | case MESSAGE_TYPE.UNSUBSCRIBE: |
447 | |
448 | for (var i=0; i<this.topics.length; i++) |
449 | pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos); |
450 | break; |
451 | |
452 | default: |
453 | |
454 | } |
455 | |
456 | return buffer; |
457 | }; |
458 | |
459 | function decodeMessage(input,pos) { |
460 | var startingPos = pos; |
461 | var first = input[pos]; |
462 | var type = first >> 4; |
463 | var messageInfo = first &= 0x0f; |
464 | pos += 1; |
465 | |
466 | |
467 | |
468 | |
469 | var digit; |
470 | var remLength = 0; |
471 | var multiplier = 1; |
472 | do { |
473 | if (pos == input.length) { |
474 | return [null,startingPos]; |
475 | } |
476 | digit = input[pos++]; |
477 | remLength += ((digit & 0x7F) * multiplier); |
478 | multiplier *= 128; |
479 | } while ((digit & 0x80) !== 0); |
480 | |
481 | var endPos = pos+remLength; |
482 | if (endPos > input.length) { |
483 | return [null,startingPos]; |
484 | } |
485 | |
486 | var wireMessage = new WireMessage(type); |
487 | switch(type) { |
488 | case MESSAGE_TYPE.CONNACK: |
489 | var connectAcknowledgeFlags = input[pos++]; |
490 | if (connectAcknowledgeFlags & 0x01) |
491 | wireMessage.sessionPresent = true; |
492 | wireMessage.returnCode = input[pos++]; |
493 | break; |
494 | |
495 | case MESSAGE_TYPE.PUBLISH: |
496 | var qos = (messageInfo >> 1) & 0x03; |
497 | |
498 | var len = readUint16(input, pos); |
499 | pos += 2; |
500 | var topicName = parseUTF8(input, pos, len); |
501 | pos += len; |
502 | |
503 | if (qos > 0) { |
504 | wireMessage.messageIdentifier = readUint16(input, pos); |
505 | pos += 2; |
506 | } |
507 | |
508 | var message = new Paho.MQTT.Message(input.subarray(pos, endPos)); |
509 | if ((messageInfo & 0x01) == 0x01) |
510 | message.retained = true; |
511 | if ((messageInfo & 0x08) == 0x08) |
512 | message.duplicate = true; |
513 | message.qos = qos; |
514 | message.destinationName = topicName; |
515 | wireMessage.payloadMessage = message; |
516 | break; |
517 | |
518 | case MESSAGE_TYPE.PUBACK: |
519 | case MESSAGE_TYPE.PUBREC: |
520 | case MESSAGE_TYPE.PUBREL: |
521 | case MESSAGE_TYPE.PUBCOMP: |
522 | case MESSAGE_TYPE.UNSUBACK: |
523 | wireMessage.messageIdentifier = readUint16(input, pos); |
524 | break; |
525 | |
526 | case MESSAGE_TYPE.SUBACK: |
527 | wireMessage.messageIdentifier = readUint16(input, pos); |
528 | pos += 2; |
529 | wireMessage.returnCode = input.subarray(pos, endPos); |
530 | break; |
531 | |
532 | default: |
533 | break; |
534 | } |
535 | |
536 | return [wireMessage,endPos]; |
537 | } |
538 | |
539 | function writeUint16(input, buffer, offset) { |
540 | buffer[offset++] = input >> 8; |
541 | buffer[offset++] = input % 256; |
542 | return offset; |
543 | } |
544 | |
545 | function writeString(input, utf8Length, buffer, offset) { |
546 | offset = writeUint16(utf8Length, buffer, offset); |
547 | stringToUTF8(input, buffer, offset); |
548 | return offset + utf8Length; |
549 | } |
550 | |
551 | function readUint16(buffer, offset) { |
552 | return 256*buffer[offset] + buffer[offset+1]; |
553 | } |
554 | |
555 | |
556 | |
557 | |
558 | |
559 | function encodeMBI(number) { |
560 | var output = new Array(1); |
561 | var numBytes = 0; |
562 | |
563 | do { |
564 | var digit = number % 128; |
565 | number = number >> 7; |
566 | if (number > 0) { |
567 | digit |= 0x80; |
568 | } |
569 | output[numBytes++] = digit; |
570 | } while ( (number > 0) && (numBytes<4) ); |
571 | |
572 | return output; |
573 | } |
574 | |
575 | |
576 | |
577 | |
578 | |
579 | function UTF8Length(input) { |
580 | var output = 0; |
581 | for (var i = 0; i<input.length; i++) |
582 | { |
583 | var charCode = input.charCodeAt(i); |
584 | if (charCode > 0x7FF) |
585 | { |
586 | |
587 | if (0xD800 <= charCode && charCode <= 0xDBFF) |
588 | { |
589 | i++; |
590 | output++; |
591 | } |
592 | output +=3; |
593 | } |
594 | else if (charCode > 0x7F) |
595 | output +=2; |
596 | else |
597 | output++; |
598 | } |
599 | return output; |
600 | } |
601 | |
602 | |
603 | |
604 | |
605 | |
606 | function stringToUTF8(input, output, start) { |
607 | var pos = start; |
608 | for (var i = 0; i<input.length; i++) { |
609 | var charCode = input.charCodeAt(i); |
610 | |
611 | |
612 | if (0xD800 <= charCode && charCode <= 0xDBFF) { |
613 | var lowCharCode = input.charCodeAt(++i); |
614 | if (isNaN(lowCharCode)) { |
615 | throw new Error(format(ERROR.MALFORMED_UNICODE, [charCode, lowCharCode])); |
616 | } |
617 | charCode = ((charCode - 0xD800)<<10) + (lowCharCode - 0xDC00) + 0x10000; |
618 | |
619 | } |
620 | |
621 | if (charCode <= 0x7F) { |
622 | output[pos++] = charCode; |
623 | } else if (charCode <= 0x7FF) { |
624 | output[pos++] = charCode>>6 & 0x1F | 0xC0; |
625 | output[pos++] = charCode & 0x3F | 0x80; |
626 | } else if (charCode <= 0xFFFF) { |
627 | output[pos++] = charCode>>12 & 0x0F | 0xE0; |
628 | output[pos++] = charCode>>6 & 0x3F | 0x80; |
629 | output[pos++] = charCode & 0x3F | 0x80; |
630 | } else { |
631 | output[pos++] = charCode>>18 & 0x07 | 0xF0; |
632 | output[pos++] = charCode>>12 & 0x3F | 0x80; |
633 | output[pos++] = charCode>>6 & 0x3F | 0x80; |
634 | output[pos++] = charCode & 0x3F | 0x80; |
635 | } |
636 | } |
637 | return output; |
638 | } |
639 | |
640 | function parseUTF8(input, offset, length) { |
641 | var output = ""; |
642 | var utf16; |
643 | var pos = offset; |
644 | |
645 | while (pos < offset+length) |
646 | { |
647 | var byte1 = input[pos++]; |
648 | if (byte1 < 128) |
649 | utf16 = byte1; |
650 | else |
651 | { |
652 | var byte2 = input[pos++]-128; |
653 | if (byte2 < 0) |
654 | throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""])); |
655 | if (byte1 < 0xE0) |
656 | utf16 = 64*(byte1-0xC0) + byte2; |
657 | else |
658 | { |
659 | var byte3 = input[pos++]-128; |
660 | if (byte3 < 0) |
661 | throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); |
662 | if (byte1 < 0xF0) |
663 | utf16 = 4096*(byte1-0xE0) + 64*byte2 + byte3; |
664 | else |
665 | { |
666 | var byte4 = input[pos++]-128; |
667 | if (byte4 < 0) |
668 | throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); |
669 | if (byte1 < 0xF8) |
670 | utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4; |
671 | else |
672 | throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); |
673 | } |
674 | } |
675 | } |
676 | |
677 | if (utf16 > 0xFFFF) |
678 | { |
679 | utf16 -= 0x10000; |
680 | output += String.fromCharCode(0xD800 + (utf16 >> 10)); |
681 | utf16 = 0xDC00 + (utf16 & 0x3FF); |
682 | } |
683 | output += String.fromCharCode(utf16); |
684 | } |
685 | return output; |
686 | } |
687 | |
688 | |
689 | |
690 | |
691 | |
692 | var Pinger = function(client, window, keepAliveInterval) { |
693 | this._client = client; |
694 | this._window = window; |
695 | this._keepAliveInterval = keepAliveInterval*1000; |
696 | this.isReset = false; |
697 | |
698 | var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); |
699 | |
700 | var doTimeout = function (pinger) { |
701 | return function () { |
702 | return doPing.apply(pinger); |
703 | }; |
704 | }; |
705 | |
706 | |
707 | var doPing = function() { |
708 | if (!this.isReset) { |
709 | this._client._trace("Pinger.doPing", "Timed out"); |
710 | this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT)); |
711 | } else { |
712 | this.isReset = false; |
713 | this._client._trace("Pinger.doPing", "send PINGREQ"); |
714 | this._client.socket.send(pingReq); |
715 | this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval); |
716 | } |
717 | }; |
718 | |
719 | this.reset = function() { |
720 | this.isReset = true; |
721 | this._window.clearTimeout(this.timeout); |
722 | if (this._keepAliveInterval > 0) |
723 | this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); |
724 | }; |
725 | |
726 | this.cancel = function() { |
727 | this._window.clearTimeout(this.timeout); |
728 | }; |
729 | }; |
730 | |
731 | |
732 | |
733 | |
734 | |
735 | var Timeout = function(client, window, timeoutSeconds, action, args) { |
736 | this._window = window; |
737 | if (!timeoutSeconds) |
738 | timeoutSeconds = 30; |
739 | |
740 | var doTimeout = function (action, client, args) { |
741 | return function () { |
742 | return action.apply(client, args); |
743 | }; |
744 | }; |
745 | this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); |
746 | |
747 | this.cancel = function() { |
748 | this._window.clearTimeout(this.timeout); |
749 | }; |
750 | }; |
751 | |
752 | |
753 | |
754 | |
755 | |
756 | |
757 | |
758 | |
759 | |
760 | var ClientImpl = function (uri, host, port, path, clientId) { |
761 | |
762 | if (!("WebSocket" in global && global.WebSocket !== null)) { |
763 | throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"])); |
764 | } |
765 | if (!("localStorage" in global && global.localStorage !== null)) { |
766 | throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"])); |
767 | } |
768 | if (!("ArrayBuffer" in global && global.ArrayBuffer !== null)) { |
769 | throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"])); |
770 | } |
771 | this._trace("Paho.MQTT.Client", uri, host, port, path, clientId); |
772 | |
773 | this.host = host; |
774 | this.port = port; |
775 | this.path = path; |
776 | this.uri = uri; |
777 | this.clientId = clientId; |
778 | this._wsuri = null; |
779 | |
780 | |
781 | |
782 | |
783 | |
784 | this._localKey=host+":"+port+(path!="/mqtt"?":"+path:"")+":"+clientId+":"; |
785 | |
786 | |
787 | |
788 | this._msg_queue = []; |
789 | this._buffered_msg_queue = []; |
790 | |
791 | |
792 | this._sentMessages = {}; |
793 | |
794 | |
795 | |
796 | this._receivedMessages = {}; |
797 | |
798 | |
799 | |
800 | |
801 | this._notify_msg_sent = {}; |
802 | |
803 | |
804 | |
805 | this._message_identifier = 1; |
806 | |
807 | |
808 | this._sequence = 0; |
809 | |
810 | |
811 | |
812 | for (var key in localStorage) |
813 | if ( key.indexOf("Sent:"+this._localKey) === 0 || key.indexOf("Received:"+this._localKey) === 0) |
814 | this.restore(key); |
815 | }; |
816 | |
817 | |
818 | ClientImpl.prototype.host = null; |
819 | ClientImpl.prototype.port = null; |
820 | ClientImpl.prototype.path = null; |
821 | ClientImpl.prototype.uri = null; |
822 | ClientImpl.prototype.clientId = null; |
823 | |
824 | |
825 | ClientImpl.prototype.socket = null; |
826 | |
827 | ClientImpl.prototype.connected = false; |
828 | |
829 | |
830 | |
831 | ClientImpl.prototype.maxMessageIdentifier = 65536; |
832 | ClientImpl.prototype.connectOptions = null; |
833 | ClientImpl.prototype.hostIndex = null; |
834 | ClientImpl.prototype.onConnected = null; |
835 | ClientImpl.prototype.onConnectionLost = null; |
836 | ClientImpl.prototype.onMessageDelivered = null; |
837 | ClientImpl.prototype.onMessageArrived = null; |
838 | ClientImpl.prototype.traceFunction = null; |
839 | ClientImpl.prototype._msg_queue = null; |
840 | ClientImpl.prototype._buffered_msg_queue = null; |
841 | ClientImpl.prototype._connectTimeout = null; |
842 | |
843 | ClientImpl.prototype.sendPinger = null; |
844 | |
845 | ClientImpl.prototype.receivePinger = null; |
846 | ClientImpl.prototype._reconnectInterval = 1; |
847 | ClientImpl.prototype._reconnecting = false; |
848 | ClientImpl.prototype._reconnectTimeout = null; |
849 | ClientImpl.prototype.disconnectedPublishing = false; |
850 | ClientImpl.prototype.disconnectedBufferSize = 5000; |
851 | |
852 | ClientImpl.prototype.receiveBuffer = null; |
853 | |
854 | ClientImpl.prototype._traceBuffer = null; |
855 | ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; |
856 | |
857 | ClientImpl.prototype.connect = function (connectOptions) { |
858 | var connectOptionsMasked = this._traceMask(connectOptions, "password"); |
859 | this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected); |
860 | |
861 | if (this.connected) |
862 | throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); |
863 | if (this.socket) |
864 | throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); |
865 | |
866 | if (this._reconnecting) { |
867 | |
868 | |
869 | this._reconnectTimeout.cancel(); |
870 | this._reconnectTimeout = null; |
871 | this._reconnecting = false; |
872 | } |
873 | |
874 | this.connectOptions = connectOptions; |
875 | this._reconnectInterval = 1; |
876 | this._reconnecting = false; |
877 | if (connectOptions.uris) { |
878 | this.hostIndex = 0; |
879 | this._doConnect(connectOptions.uris[0]); |
880 | } else { |
881 | this._doConnect(this.uri); |
882 | } |
883 | |
884 | }; |
885 | |
886 | ClientImpl.prototype.subscribe = function (filter, subscribeOptions) { |
887 | this._trace("Client.subscribe", filter, subscribeOptions); |
888 | |
889 | if (!this.connected) |
890 | throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); |
891 | |
892 | var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); |
893 | wireMessage.topics=[filter]; |
894 | if (subscribeOptions.qos !== undefined) |
895 | wireMessage.requestedQos = [subscribeOptions.qos]; |
896 | else |
897 | wireMessage.requestedQos = [0]; |
898 | |
899 | if (subscribeOptions.onSuccess) { |
900 | wireMessage.onSuccess = function(grantedQos) {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext,grantedQos:grantedQos});}; |
901 | } |
902 | |
903 | if (subscribeOptions.onFailure) { |
904 | wireMessage.onFailure = function(errorCode) {subscribeOptions.onFailure({invocationContext:subscribeOptions.invocationContext,errorCode:errorCode, errorMessage:format(errorCode)});}; |
905 | } |
906 | |
907 | if (subscribeOptions.timeout) { |
908 | wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure, |
909 | [{invocationContext:subscribeOptions.invocationContext, |
910 | errorCode:ERROR.SUBSCRIBE_TIMEOUT.code, |
911 | errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]); |
912 | } |
913 | |
914 | |
915 | this._requires_ack(wireMessage); |
916 | this._schedule_message(wireMessage); |
917 | }; |
918 | |
919 | |
920 | ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { |
921 | this._trace("Client.unsubscribe", filter, unsubscribeOptions); |
922 | |
923 | if (!this.connected) |
924 | throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); |
925 | |
926 | var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); |
927 | wireMessage.topics = [filter]; |
928 | |
929 | if (unsubscribeOptions.onSuccess) { |
930 | wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});}; |
931 | } |
932 | if (unsubscribeOptions.timeout) { |
933 | wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure, |
934 | [{invocationContext:unsubscribeOptions.invocationContext, |
935 | errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code, |
936 | errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]); |
937 | } |
938 | |
939 | |
940 | this._requires_ack(wireMessage); |
941 | this._schedule_message(wireMessage); |
942 | }; |
943 | |
944 | ClientImpl.prototype.send = function (message) { |
945 | this._trace("Client.send", message); |
946 | |
947 | wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); |
948 | wireMessage.payloadMessage = message; |
949 | |
950 | if (this.connected) { |
951 | |
952 | |
953 | |
954 | if (message.qos > 0) { |
955 | this._requires_ack(wireMessage); |
956 | } else if (this.onMessageDelivered) { |
957 | this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); |
958 | } |
959 | this._schedule_message(wireMessage); |
960 | } else { |
961 | |
962 | |
963 | if (this._reconnecting && this.disconnectedPublishing) { |
964 | |
965 | var messageCount = Object.keys(this._sentMessages).length + this._buffered_msg_queue.length; |
966 | if (messageCount > this.disconnectedBufferSize) { |
967 | throw new Error(format(ERROR.BUFFER_FULL, [this.disconnectedBufferSize])); |
968 | } else { |
969 | if (message.qos > 0) { |
970 | |
971 | this._requires_ack(wireMessage); |
972 | } else { |
973 | wireMessage.sequence = ++this._sequence; |
974 | this._buffered_msg_queue.push(wireMessage); |
975 | } |
976 | } |
977 | } else { |
978 | throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); |
979 | } |
980 | } |
981 | }; |
982 | |
983 | ClientImpl.prototype.disconnect = function () { |
984 | this._trace("Client.disconnect"); |
985 | |
986 | if (this._reconnecting) { |
987 | |
988 | |
989 | this._reconnectTimeout.cancel(); |
990 | this._reconnectTimeout = null; |
991 | this._reconnecting = false; |
992 | } |
993 | |
994 | if (!this.socket) |
995 | throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); |
996 | |
997 | wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); |
998 | |
999 | |
1000 | |
1001 | |
1002 | this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); |
1003 | |
1004 | this._schedule_message(wireMessage); |
1005 | }; |
1006 | |
1007 | ClientImpl.prototype.getTraceLog = function () { |
1008 | if ( this._traceBuffer !== null ) { |
1009 | this._trace("Client.getTraceLog", new Date()); |
1010 | this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); |
1011 | for (var key in this._sentMessages) |
1012 | this._trace("_sentMessages ",key, this._sentMessages[key]); |
1013 | for (var key in this._receivedMessages) |
1014 | this._trace("_receivedMessages ",key, this._receivedMessages[key]); |
1015 | |
1016 | return this._traceBuffer; |
1017 | } |
1018 | }; |
1019 | |
1020 | ClientImpl.prototype.startTrace = function () { |
1021 | if ( this._traceBuffer === null ) { |
1022 | this._traceBuffer = []; |
1023 | } |
1024 | this._trace("Client.startTrace", new Date(), version); |
1025 | }; |
1026 | |
1027 | ClientImpl.prototype.stopTrace = function () { |
1028 | delete this._traceBuffer; |
1029 | }; |
1030 | |
1031 | ClientImpl.prototype._doConnect = function (wsurl) { |
1032 | |
1033 | if (this.connectOptions.useSSL) { |
1034 | var uriParts = wsurl.split(":"); |
1035 | uriParts[0] = "wss"; |
1036 | wsurl = uriParts.join(":"); |
1037 | } |
1038 | this._wsuri = wsurl; |
1039 | this.connected = false; |
1040 | |
1041 | |
1042 | |
1043 | if (this.connectOptions.mqttVersion < 4) { |
1044 | this.socket = new WebSocket(wsurl, ["mqttv3.1"]); |
1045 | } else { |
1046 | this.socket = new WebSocket(wsurl, ["mqtt"]); |
1047 | } |
1048 | this.socket.binaryType = 'arraybuffer'; |
1049 | this.socket.onopen = scope(this._on_socket_open, this); |
1050 | this.socket.onmessage = scope(this._on_socket_message, this); |
1051 | this.socket.onerror = scope(this._on_socket_error, this); |
1052 | this.socket.onclose = scope(this._on_socket_close, this); |
1053 | |
1054 | this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); |
1055 | this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); |
1056 | if (this._connectTimeout) { |
1057 | this._connectTimeout.cancel(); |
1058 | this._connectTimeout = null; |
1059 | } |
1060 | this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); |
1061 | }; |
1062 | |
1063 | |
1064 | |
1065 | |
1066 | |
1067 | |
1068 | |
1069 | ClientImpl.prototype._schedule_message = function (message) { |
1070 | this._msg_queue.push(message); |
1071 | |
1072 | if (this.connected) { |
1073 | this._process_queue(); |
1074 | } |
1075 | }; |
1076 | |
1077 | ClientImpl.prototype.store = function(prefix, wireMessage) { |
1078 | var storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1}; |
1079 | |
1080 | switch(wireMessage.type) { |
1081 | case MESSAGE_TYPE.PUBLISH: |
1082 | if(wireMessage.pubRecReceived) |
1083 | storedMessage.pubRecReceived = true; |
1084 | |
1085 | |
1086 | storedMessage.payloadMessage = {}; |
1087 | var hex = ""; |
1088 | var messageBytes = wireMessage.payloadMessage.payloadBytes; |
1089 | for (var i=0; i<messageBytes.length; i++) { |
1090 | if (messageBytes[i] <= 0xF) |
1091 | hex = hex+"0"+messageBytes[i].toString(16); |
1092 | else |
1093 | hex = hex+messageBytes[i].toString(16); |
1094 | } |
1095 | storedMessage.payloadMessage.payloadHex = hex; |
1096 | |
1097 | storedMessage.payloadMessage.qos = wireMessage.payloadMessage.qos; |
1098 | storedMessage.payloadMessage.destinationName = wireMessage.payloadMessage.destinationName; |
1099 | if (wireMessage.payloadMessage.duplicate) |
1100 | storedMessage.payloadMessage.duplicate = true; |
1101 | if (wireMessage.payloadMessage.retained) |
1102 | storedMessage.payloadMessage.retained = true; |
1103 | |
1104 | |
1105 | if ( prefix.indexOf("Sent:") === 0 ) { |
1106 | if ( wireMessage.sequence === undefined ) |
1107 | wireMessage.sequence = ++this._sequence; |
1108 | storedMessage.sequence = wireMessage.sequence; |
1109 | } |
1110 | break; |
1111 | |
1112 | default: |
1113 | throw Error(format(ERROR.INVALID_STORED_DATA, [key, storedMessage])); |
1114 | } |
1115 | localStorage.setItem(prefix+this._localKey+wireMessage.messageIdentifier, JSON.stringify(storedMessage)); |
1116 | }; |
1117 | |
1118 | ClientImpl.prototype.restore = function(key) { |
1119 | var value = localStorage.getItem(key); |
1120 | var storedMessage = JSON.parse(value); |
1121 | |
1122 | var wireMessage = new WireMessage(storedMessage.type, storedMessage); |
1123 | |
1124 | switch(storedMessage.type) { |
1125 | case MESSAGE_TYPE.PUBLISH: |
1126 | |
1127 | var hex = storedMessage.payloadMessage.payloadHex; |
1128 | var buffer = new ArrayBuffer((hex.length)/2); |
1129 | var byteStream = new Uint8Array(buffer); |
1130 | var i = 0; |
1131 | while (hex.length >= 2) { |
1132 | var x = parseInt(hex.substring(0, 2), 16); |
1133 | hex = hex.substring(2, hex.length); |
1134 | byteStream[i++] = x; |
1135 | } |
1136 | var payloadMessage = new Paho.MQTT.Message(byteStream); |
1137 | |
1138 | payloadMessage.qos = storedMessage.payloadMessage.qos; |
1139 | payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; |
1140 | if (storedMessage.payloadMessage.duplicate) |
1141 | payloadMessage.duplicate = true; |
1142 | if (storedMessage.payloadMessage.retained) |
1143 | payloadMessage.retained = true; |
1144 | wireMessage.payloadMessage = payloadMessage; |
1145 | |
1146 | break; |
1147 | |
1148 | default: |
1149 | throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); |
1150 | } |
1151 | |
1152 | if (key.indexOf("Sent:"+this._localKey) === 0) { |
1153 | wireMessage.payloadMessage.duplicate = true; |
1154 | this._sentMessages[wireMessage.messageIdentifier] = wireMessage; |
1155 | } else if (key.indexOf("Received:"+this._localKey) === 0) { |
1156 | this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; |
1157 | } |
1158 | }; |
1159 | |
1160 | ClientImpl.prototype._process_queue = function () { |
1161 | var message = null; |
1162 | |
1163 | var fifo = this._msg_queue.reverse(); |
1164 | |
1165 | |
1166 | while ((message = fifo.pop())) { |
1167 | this._socket_send(message); |
1168 | |
1169 | if (this._notify_msg_sent[message]) { |
1170 | this._notify_msg_sent[message](); |
1171 | delete this._notify_msg_sent[message]; |
1172 | } |
1173 | } |
1174 | }; |
1175 | |
1176 | |
1177 | |
1178 | |
1179 | |
1180 | |
1181 | ClientImpl.prototype._requires_ack = function (wireMessage) { |
1182 | var messageCount = Object.keys(this._sentMessages).length; |
1183 | if (messageCount > this.maxMessageIdentifier) |
1184 | throw Error ("Too many messages:"+messageCount); |
1185 | |
1186 | while(this._sentMessages[this._message_identifier] !== undefined) { |
1187 | this._message_identifier++; |
1188 | } |
1189 | wireMessage.messageIdentifier = this._message_identifier; |
1190 | this._sentMessages[wireMessage.messageIdentifier] = wireMessage; |
1191 | if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { |
1192 | this.store("Sent:", wireMessage); |
1193 | } |
1194 | if (this._message_identifier === this.maxMessageIdentifier) { |
1195 | this._message_identifier = 1; |
1196 | } |
1197 | }; |
1198 | |
1199 | |
1200 | |
1201 | |
1202 | |
1203 | ClientImpl.prototype._on_socket_open = function () { |
1204 | |
1205 | var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); |
1206 | wireMessage.clientId = this.clientId; |
1207 | this._socket_send(wireMessage); |
1208 | }; |
1209 | |
1210 | |
1211 | |
1212 | |
1213 | |
1214 | ClientImpl.prototype._on_socket_message = function (event) { |
1215 | this._trace("Client._on_socket_message", event.data); |
1216 | var messages = this._deframeMessages(event.data); |
1217 | for (var i = 0; i < messages.length; i+=1) { |
1218 | this._handleMessage(messages[i]); |
1219 | } |
1220 | }; |
1221 | |
1222 | ClientImpl.prototype._deframeMessages = function(data) { |
1223 | var byteArray = new Uint8Array(data); |
1224 | var messages = []; |
1225 | if (this.receiveBuffer) { |
1226 | var newData = new Uint8Array(this.receiveBuffer.length+byteArray.length); |
1227 | newData.set(this.receiveBuffer); |
1228 | newData.set(byteArray,this.receiveBuffer.length); |
1229 | byteArray = newData; |
1230 | delete this.receiveBuffer; |
1231 | } |
1232 | try { |
1233 | var offset = 0; |
1234 | while(offset < byteArray.length) { |
1235 | var result = decodeMessage(byteArray,offset); |
1236 | var wireMessage = result[0]; |
1237 | offset = result[1]; |
1238 | if (wireMessage !== null) { |
1239 | messages.push(wireMessage); |
1240 | } else { |
1241 | break; |
1242 | } |
1243 | } |
1244 | if (offset < byteArray.length) { |
1245 | this.receiveBuffer = byteArray.subarray(offset); |
1246 | } |
1247 | } catch (error) { |
1248 | var errorStack = ((error.hasOwnProperty('stack') == 'undefined') ? error.stack.toString() : "No Error Stack Available"); |
1249 | this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message,errorStack])); |
1250 | return; |
1251 | } |
1252 | return messages; |
1253 | }; |
1254 | |
1255 | ClientImpl.prototype._handleMessage = function(wireMessage) { |
1256 | |
1257 | this._trace("Client._handleMessage", wireMessage); |
1258 | |
1259 | try { |
1260 | switch(wireMessage.type) { |
1261 | case MESSAGE_TYPE.CONNACK: |
1262 | this._connectTimeout.cancel(); |
1263 | if (this._reconnectTimeout) |
1264 | this._reconnectTimeout.cancel(); |
1265 | |
1266 | |
1267 | if (this.connectOptions.cleanSession) { |
1268 | for (var key in this._sentMessages) { |
1269 | var sentMessage = this._sentMessages[key]; |
1270 | localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier); |
1271 | } |
1272 | this._sentMessages = {}; |
1273 | |
1274 | for (var key in this._receivedMessages) { |
1275 | var receivedMessage = this._receivedMessages[key]; |
1276 | localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier); |
1277 | } |
1278 | this._receivedMessages = {}; |
1279 | } |
1280 | |
1281 | if (wireMessage.returnCode === 0) { |
1282 | |
1283 | this.connected = true; |
1284 | |
1285 | |
1286 | if (this.connectOptions.uris) |
1287 | this.hostIndex = this.connectOptions.uris.length; |
1288 | |
1289 | } else { |
1290 | this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); |
1291 | break; |
1292 | } |
1293 | |
1294 | |
1295 | var sequencedMessages = []; |
1296 | for (var msgId in this._sentMessages) { |
1297 | if (this._sentMessages.hasOwnProperty(msgId)) |
1298 | sequencedMessages.push(this._sentMessages[msgId]); |
1299 | } |
1300 | |
1301 | |
1302 | if (this._buffered_msg_queue.length > 0) { |
1303 | var msg = null; |
1304 | var fifo = this._buffered_msg_queue.reverse(); |
1305 | while ((msg = fifo.pop())) { |
1306 | sequencedMessages.push(msg); |
1307 | if (this.onMessageDelivered) |
1308 | this._notify_msg_sent[msg] = this.onMessageDelivered(msg.payloadMessage); |
1309 | } |
1310 | } |
1311 | |
1312 | |
1313 | var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} ); |
1314 | for (var i=0, len=sequencedMessages.length; i<len; i++) { |
1315 | var sentMessage = sequencedMessages[i]; |
1316 | if (sentMessage.type == MESSAGE_TYPE.PUBLISH && sentMessage.pubRecReceived) { |
1317 | var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, {messageIdentifier:sentMessage.messageIdentifier}); |
1318 | this._schedule_message(pubRelMessage); |
1319 | } else { |
1320 | this._schedule_message(sentMessage); |
1321 | } |
1322 | } |
1323 | |
1324 | |
1325 | |
1326 | |
1327 | if (this.connectOptions.onSuccess) { |
1328 | this.connectOptions.onSuccess({invocationContext:this.connectOptions.invocationContext}); |
1329 | } |
1330 | |
1331 | var reconnected = false; |
1332 | if (this._reconnecting) { |
1333 | reconnected = true; |
1334 | this._reconnectInterval = 1; |
1335 | this._reconnecting = false; |
1336 | } |
1337 | |
1338 | |
1339 | this._connected(reconnected, this._wsuri); |
1340 | |
1341 | |
1342 | this._process_queue(); |
1343 | break; |
1344 | |
1345 | case MESSAGE_TYPE.PUBLISH: |
1346 | this._receivePublish(wireMessage); |
1347 | break; |
1348 | |
1349 | case MESSAGE_TYPE.PUBACK: |
1350 | var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; |
1351 | |
1352 | if (sentMessage) { |
1353 | delete this._sentMessages[wireMessage.messageIdentifier]; |
1354 | localStorage.removeItem("Sent:"+this._localKey+wireMessage.messageIdentifier); |
1355 | if (this.onMessageDelivered) |
1356 | this.onMessageDelivered(sentMessage.payloadMessage); |
1357 | } |
1358 | break; |
1359 | |
1360 | case MESSAGE_TYPE.PUBREC: |
1361 | var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; |
1362 | |
1363 | if (sentMessage) { |
1364 | sentMessage.pubRecReceived = true; |
1365 | var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, {messageIdentifier:wireMessage.messageIdentifier}); |
1366 | this.store("Sent:", sentMessage); |
1367 | this._schedule_message(pubRelMessage); |
1368 | } |
1369 | break; |
1370 | |
1371 | case MESSAGE_TYPE.PUBREL: |
1372 | var receivedMessage = this._receivedMessages[wireMessage.messageIdentifier]; |
1373 | localStorage.removeItem("Received:"+this._localKey+wireMessage.messageIdentifier); |
1374 | |
1375 | if (receivedMessage) { |
1376 | this._receiveMessage(receivedMessage); |
1377 | delete this._receivedMessages[wireMessage.messageIdentifier]; |
1378 | } |
1379 | |
1380 | var pubCompMessage = new WireMessage(MESSAGE_TYPE.PUBCOMP, {messageIdentifier:wireMessage.messageIdentifier}); |
1381 | this._schedule_message(pubCompMessage); |
1382 | |
1383 | |
1384 | break; |
1385 | |
1386 | case MESSAGE_TYPE.PUBCOMP: |
1387 | var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; |
1388 | delete this._sentMessages[wireMessage.messageIdentifier]; |
1389 | localStorage.removeItem("Sent:"+this._localKey+wireMessage.messageIdentifier); |
1390 | if (this.onMessageDelivered) |
1391 | this.onMessageDelivered(sentMessage.payloadMessage); |
1392 | break; |
1393 | |
1394 | case MESSAGE_TYPE.SUBACK: |
1395 | var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; |
1396 | if (sentMessage) { |
1397 | if(sentMessage.timeOut) |
1398 | sentMessage.timeOut.cancel(); |
1399 | |
1400 | if (wireMessage.returnCode[0] === 0x80) { |
1401 | if (sentMessage.onFailure) { |
1402 | sentMessage.onFailure(wireMessage.returnCode); |
1403 | } |
1404 | } else if (sentMessage.onSuccess) { |
1405 | sentMessage.onSuccess(wireMessage.returnCode); |
1406 | } |
1407 | delete this._sentMessages[wireMessage.messageIdentifier]; |
1408 | } |
1409 | break; |
1410 | |
1411 | case MESSAGE_TYPE.UNSUBACK: |
1412 | var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; |
1413 | if (sentMessage) { |
1414 | if (sentMessage.timeOut) |
1415 | sentMessage.timeOut.cancel(); |
1416 | if (sentMessage.callback) { |
1417 | sentMessage.callback(); |
1418 | } |
1419 | delete this._sentMessages[wireMessage.messageIdentifier]; |
1420 | } |
1421 | |
1422 | break; |
1423 | |
1424 | case MESSAGE_TYPE.PINGRESP: |
1425 | |
1426 | this.sendPinger.reset(); |
1427 | break; |
1428 | |
1429 | case MESSAGE_TYPE.DISCONNECT: |
1430 | |
1431 | this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code , format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type])); |
1432 | break; |
1433 | |
1434 | default: |
1435 | this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code , format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type])); |
1436 | } |
1437 | } catch (error) { |
1438 | var errorStack = ((error.hasOwnProperty('stack') == 'undefined') ? error.stack.toString() : "No Error Stack Available"); |
1439 | this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message,errorStack])); |
1440 | return; |
1441 | } |
1442 | }; |
1443 | |
1444 | |
1445 | ClientImpl.prototype._on_socket_error = function (error) { |
1446 | if (!this._reconnecting) { |
1447 | this._disconnected(ERROR.SOCKET_ERROR.code , format(ERROR.SOCKET_ERROR, [error.data])); |
1448 | } |
1449 | }; |
1450 | |
1451 | |
1452 | ClientImpl.prototype._on_socket_close = function () { |
1453 | if (!this._reconnecting) { |
1454 | this._disconnected(ERROR.SOCKET_CLOSE.code , format(ERROR.SOCKET_CLOSE)); |
1455 | } |
1456 | }; |
1457 | |
1458 | |
1459 | ClientImpl.prototype._socket_send = function (wireMessage) { |
1460 | |
1461 | if (wireMessage.type == 1) { |
1462 | var wireMessageMasked = this._traceMask(wireMessage, "password"); |
1463 | this._trace("Client._socket_send", wireMessageMasked); |
1464 | } |
1465 | else this._trace("Client._socket_send", wireMessage); |
1466 | |
1467 | this.socket.send(wireMessage.encode()); |
1468 | |
1469 | this.sendPinger.reset(); |
1470 | }; |
1471 | |
1472 | |
1473 | ClientImpl.prototype._receivePublish = function (wireMessage) { |
1474 | switch(wireMessage.payloadMessage.qos) { |
1475 | case "undefined": |
1476 | case 0: |
1477 | this._receiveMessage(wireMessage); |
1478 | break; |
1479 | |
1480 | case 1: |
1481 | var pubAckMessage = new WireMessage(MESSAGE_TYPE.PUBACK, {messageIdentifier:wireMessage.messageIdentifier}); |
1482 | this._schedule_message(pubAckMessage); |
1483 | this._receiveMessage(wireMessage); |
1484 | break; |
1485 | |
1486 | case 2: |
1487 | this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; |
1488 | this.store("Received:", wireMessage); |
1489 | var pubRecMessage = new WireMessage(MESSAGE_TYPE.PUBREC, {messageIdentifier:wireMessage.messageIdentifier}); |
1490 | this._schedule_message(pubRecMessage); |
1491 | |
1492 | break; |
1493 | |
1494 | default: |
1495 | throw Error("Invaild qos="+wireMmessage.payloadMessage.qos); |
1496 | } |
1497 | }; |
1498 | |
1499 | |
1500 | ClientImpl.prototype._receiveMessage = function (wireMessage) { |
1501 | if (this.onMessageArrived) { |
1502 | this.onMessageArrived(wireMessage.payloadMessage); |
1503 | } |
1504 | }; |
1505 | |
1506 | |
1507 | |
1508 | |
1509 | |
1510 | |
1511 | ClientImpl.prototype._connected = function (reconnect, uri) { |
1512 | |
1513 | if (this.onConnected) |
1514 | this.onConnected(reconnect, uri); |
1515 | }; |
1516 | |
1517 | |
1518 | |
1519 | |
1520 | |
1521 | |
1522 | ClientImpl.prototype._reconnect = function () { |
1523 | this._trace("Client._reconnect"); |
1524 | if (!this.connected) { |
1525 | this._reconnecting = true; |
1526 | this.sendPinger.cancel(); |
1527 | this.receivePinger.cancel(); |
1528 | if (this._reconnectInterval < 128) |
1529 | this._reconnectInterval = this._reconnectInterval * 2; |
1530 | if (this.connectOptions.uris) { |
1531 | this.hostIndex = 0; |
1532 | this._doConnect(this.connectOptions.uris[0]); |
1533 | } else { |
1534 | this._doConnect(this.uri); |
1535 | } |
1536 | } |
1537 | }; |
1538 | |
1539 | |
1540 | |
1541 | |
1542 | |
1543 | |
1544 | |
1545 | |
1546 | ClientImpl.prototype._disconnected = function (errorCode, errorText) { |
1547 | this._trace("Client._disconnected", errorCode, errorText); |
1548 | |
1549 | if (errorCode !== undefined && this._reconnecting) { |
1550 | |
1551 | this._reconnectTimeout = new Timeout(this, window, this._reconnectInterval, this._reconnect); |
1552 | return; |
1553 | } |
1554 | |
1555 | this.sendPinger.cancel(); |
1556 | this.receivePinger.cancel(); |
1557 | if (this._connectTimeout) { |
1558 | this._connectTimeout.cancel(); |
1559 | this._connectTimeout = null; |
1560 | } |
1561 | |
1562 | |
1563 | this._msg_queue = []; |
1564 | this._buffered_msg_queue = []; |
1565 | this._notify_msg_sent = {}; |
1566 | |
1567 | if (this.socket) { |
1568 | |
1569 | this.socket.onopen = null; |
1570 | this.socket.onmessage = null; |
1571 | this.socket.onerror = null; |
1572 | this.socket.onclose = null; |
1573 | if (this.socket.readyState === 1) |
1574 | this.socket.close(); |
1575 | delete this.socket; |
1576 | } |
1577 | |
1578 | if (this.connectOptions.uris && this.hostIndex < this.connectOptions.uris.length-1) { |
1579 | |
1580 | this.hostIndex++; |
1581 | this._doConnect(this.connectOptions.uris[this.hostIndex]); |
1582 | } else { |
1583 | |
1584 | if (errorCode === undefined) { |
1585 | errorCode = ERROR.OK.code; |
1586 | errorText = format(ERROR.OK); |
1587 | } |
1588 | |
1589 | |
1590 | if (this.connected) { |
1591 | this.connected = false; |
1592 | |
1593 | if (this.onConnectionLost) { |
1594 | this.onConnectionLost({errorCode:errorCode, errorMessage:errorText, reconnect:this.connectOptions.reconnect, uri:this._wsuri}); |
1595 | } |
1596 | if (errorCode !== ERROR.OK.code && this.connectOptions.reconnect) { |
1597 | |
1598 | this._reconnectInterval = 1; |
1599 | this._reconnect(); |
1600 | return; |
1601 | } |
1602 | } else { |
1603 | |
1604 | if (this.connectOptions.mqttVersion === 4 && this.connectOptions.mqttVersionExplicit === false) { |
1605 | this._trace("Failed to connect V4, dropping back to V3"); |
1606 | this.connectOptions.mqttVersion = 3; |
1607 | if (this.connectOptions.uris) { |
1608 | this.hostIndex = 0; |
1609 | this._doConnect(this.connectOptions.uris[0]); |
1610 | } else { |
1611 | this._doConnect(this.uri); |
1612 | } |
1613 | } else if(this.connectOptions.onFailure) { |
1614 | this.connectOptions.onFailure({invocationContext:this.connectOptions.invocationContext, errorCode:errorCode, errorMessage:errorText}); |
1615 | } |
1616 | } |
1617 | } |
1618 | }; |
1619 | |
1620 | |
1621 | ClientImpl.prototype._trace = function () { |
1622 | |
1623 | if (this.traceFunction) { |
1624 | for (var i in arguments) |
1625 | { |
1626 | if (typeof arguments[i] !== "undefined") |
1627 | arguments.splice(i, 1, JSON.stringify(arguments[i])); |
1628 | } |
1629 | var record = Array.prototype.slice.call(arguments).join(""); |
1630 | this.traceFunction ({severity: "Debug", message: record }); |
1631 | } |
1632 | |
1633 | |
1634 | if ( this._traceBuffer !== null ) { |
1635 | for (var i = 0, max = arguments.length; i < max; i++) { |
1636 | if ( this._traceBuffer.length == this._MAX_TRACE_ENTRIES ) { |
1637 | this._traceBuffer.shift(); |
1638 | } |
1639 | if (i === 0) this._traceBuffer.push(arguments[i]); |
1640 | else if (typeof arguments[i] === "undefined" ) this._traceBuffer.push(arguments[i]); |
1641 | else this._traceBuffer.push(" "+JSON.stringify(arguments[i])); |
1642 | } |
1643 | } |
1644 | }; |
1645 | |
1646 | |
1647 | ClientImpl.prototype._traceMask = function (traceObject, masked) { |
1648 | var traceObjectMasked = {}; |
1649 | for (var attr in traceObject) { |
1650 | if (traceObject.hasOwnProperty(attr)) { |
1651 | if (attr == masked) |
1652 | traceObjectMasked[attr] = "******"; |
1653 | else |
1654 | traceObjectMasked[attr] = traceObject[attr]; |
1655 | } |
1656 | } |
1657 | return traceObjectMasked; |
1658 | }; |
1659 | |
1660 | |
1661 | |
1662 | |
1663 | |
1664 | |
1665 | |
1666 | |
1667 | |
1668 | |
1669 | |
1670 | |
1671 | |
1672 | |
1673 | |
1674 | |
1675 | |
1676 | |
1677 | |
1678 | |
1679 | |
1680 | |
1681 | |
1682 | |
1683 | |
1684 | |
1685 | |
1686 | |
1687 | |
1688 | |
1689 | |
1690 | |
1691 | |
1692 | |
1693 | |
1694 | |
1695 | |
1696 | |
1697 | |
1698 | |
1699 | |
1700 | |
1701 | |
1702 | |
1703 | |
1704 | |
1705 | |
1706 | |
1707 | |
1708 | |
1709 | |
1710 | |
1711 | |
1712 | |
1713 | |
1714 | |
1715 | |
1716 | |
1717 | |
1718 | |
1719 | |
1720 | |
1721 | |
1722 | |
1723 | |
1724 | |
1725 | |
1726 | |
1727 | |
1728 | |
1729 | |
1730 | |
1731 | |
1732 | |
1733 | var Client = function (host, port, path, clientId) { |
1734 | |
1735 | var uri; |
1736 | |
1737 | if (typeof host !== "string") |
1738 | throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); |
1739 | |
1740 | if (arguments.length == 2) { |
1741 | |
1742 | |
1743 | clientId = port; |
1744 | uri = host; |
1745 | var match = uri.match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/); |
1746 | if (match) { |
1747 | host = match[4]||match[2]; |
1748 | port = parseInt(match[7]); |
1749 | path = match[8]; |
1750 | } else { |
1751 | throw new Error(format(ERROR.INVALID_ARGUMENT,[host,"host"])); |
1752 | } |
1753 | } else { |
1754 | if (arguments.length == 3) { |
1755 | clientId = path; |
1756 | path = "/mqtt"; |
1757 | } |
1758 | if (typeof port !== "number" || port < 0) |
1759 | throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); |
1760 | if (typeof path !== "string") |
1761 | throw new Error(format(ERROR.INVALID_TYPE, [typeof path, "path"])); |
1762 | |
1763 | var ipv6AddSBracket = (host.indexOf(":") !== -1 && host.slice(0,1) !== "[" && host.slice(-1) !== "]"); |
1764 | uri = "ws://"+(ipv6AddSBracket?"["+host+"]":host)+":"+port+path; |
1765 | } |
1766 | |
1767 | var clientIdLength = 0; |
1768 | for (var i = 0; i<clientId.length; i++) { |
1769 | var charCode = clientId.charCodeAt(i); |
1770 | if (0xD800 <= charCode && charCode <= 0xDBFF) { |
1771 | i++; |
1772 | } |
1773 | clientIdLength++; |
1774 | } |
1775 | if (typeof clientId !== "string" || clientIdLength > 65535) |
1776 | throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); |
1777 | |
1778 | var client = new ClientImpl(uri, host, port, path, clientId); |
1779 | this._getHost = function() { return host; }; |
1780 | this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; |
1781 | |
1782 | this._getPort = function() { return port; }; |
1783 | this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; |
1784 | |
1785 | this._getPath = function() { return path; }; |
1786 | this._setPath = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; |
1787 | |
1788 | this._getURI = function() { return uri; }; |
1789 | this._setURI = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; |
1790 | |
1791 | this._getClientId = function() { return client.clientId; }; |
1792 | this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; |
1793 | |
1794 | this._getOnConnected = function() { return client.onConnected; }; |
1795 | this._setOnConnected = function(newOnConnected) { |
1796 | if (typeof newOnConnected === "function") |
1797 | client.onConnected = newOnConnected; |
1798 | else |
1799 | throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnected, "onConnected"])); |
1800 | }; |
1801 | |
1802 | this._getDisconnectedPublishing = function() { return client.disconnectedPublishing; }; |
1803 | this._setDisconnectedPublishing = function(newDisconnectedPublishing) { |
1804 | client.disconnectedPublishing = newDisconnectedPublishing; |
1805 | }; |
1806 | |
1807 | this._getDisconnectedBufferSize = function() { return client.disconnectedBufferSize; }; |
1808 | this._setDisconnectedBufferSize = function(newDisconnectedBufferSize) { |
1809 | client.disconnectedBufferSize = newDisconnectedBufferSize; |
1810 | }; |
1811 | |
1812 | this._getOnConnectionLost = function() { return client.onConnectionLost; }; |
1813 | this._setOnConnectionLost = function(newOnConnectionLost) { |
1814 | if (typeof newOnConnectionLost === "function") |
1815 | client.onConnectionLost = newOnConnectionLost; |
1816 | else |
1817 | throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); |
1818 | }; |
1819 | |
1820 | this._getOnMessageDelivered = function() { return client.onMessageDelivered; }; |
1821 | this._setOnMessageDelivered = function(newOnMessageDelivered) { |
1822 | if (typeof newOnMessageDelivered === "function") |
1823 | client.onMessageDelivered = newOnMessageDelivered; |
1824 | else |
1825 | throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); |
1826 | }; |
1827 | |
1828 | this._getOnMessageArrived = function() { return client.onMessageArrived; }; |
1829 | this._setOnMessageArrived = function(newOnMessageArrived) { |
1830 | if (typeof newOnMessageArrived === "function") |
1831 | client.onMessageArrived = newOnMessageArrived; |
1832 | else |
1833 | throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); |
1834 | }; |
1835 | |
1836 | this._getTrace = function() { return client.traceFunction; }; |
1837 | this._setTrace = function(trace) { |
1838 | if(typeof trace === "function"){ |
1839 | client.traceFunction = trace; |
1840 | }else{ |
1841 | throw new Error(format(ERROR.INVALID_TYPE, [typeof trace, "onTrace"])); |
1842 | } |
1843 | }; |
1844 | |
1845 | |
1846 | |
1847 | |
1848 | |
1849 | |
1850 | |
1851 | |
1852 | |
1853 | |
1854 | |
1855 | |
1856 | |
1857 | |
1858 | |
1859 | |
1860 | |
1861 | |
1862 | |
1863 | |
1864 | |
1865 | |
1866 | |
1867 | |
1868 | |
1869 | |
1870 | |
1871 | |
1872 | |
1873 | |
1874 | |
1875 | |
1876 | |
1877 | |
1878 | |
1879 | |
1880 | |
1881 | |
1882 | |
1883 | |
1884 | |
1885 | |
1886 | |
1887 | |
1888 | |
1889 | |
1890 | |
1891 | |
1892 | |
1893 | |
1894 | |
1895 | |
1896 | |
1897 | |
1898 | |
1899 | |
1900 | |
1901 | |
1902 | |
1903 | |
1904 | |
1905 | |
1906 | |
1907 | this.connect = function (connectOptions) { |
1908 | connectOptions = connectOptions || {} ; |
1909 | validate(connectOptions, {timeout:"number", |
1910 | userName:"string", |
1911 | password:"string", |
1912 | willMessage:"object", |
1913 | keepAliveInterval:"number", |
1914 | cleanSession:"boolean", |
1915 | useSSL:"boolean", |
1916 | invocationContext:"object", |
1917 | onSuccess:"function", |
1918 | onFailure:"function", |
1919 | hosts:"object", |
1920 | ports:"object", |
1921 | reconnect:"boolean", |
1922 | mqttVersion:"number", |
1923 | mqttVersionExplicit:"boolean", |
1924 | uris: "object"}); |
1925 | |
1926 | |
1927 | if (connectOptions.keepAliveInterval === undefined) |
1928 | connectOptions.keepAliveInterval = 60; |
1929 | |
1930 | if (connectOptions.mqttVersion > 4 || connectOptions.mqttVersion < 3) { |
1931 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.mqttVersion, "connectOptions.mqttVersion"])); |
1932 | } |
1933 | |
1934 | if (connectOptions.mqttVersion === undefined) { |
1935 | connectOptions.mqttVersionExplicit = false; |
1936 | connectOptions.mqttVersion = 4; |
1937 | } else { |
1938 | connectOptions.mqttVersionExplicit = true; |
1939 | } |
1940 | |
1941 | |
1942 | if (connectOptions.password !== undefined && connectOptions.userName === undefined) |
1943 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.password, "connectOptions.password"])); |
1944 | |
1945 | if (connectOptions.willMessage) { |
1946 | if (!(connectOptions.willMessage instanceof Message)) |
1947 | throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); |
1948 | |
1949 | |
1950 | connectOptions.willMessage.stringPayload = null; |
1951 | |
1952 | if (typeof connectOptions.willMessage.destinationName === "undefined") |
1953 | throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); |
1954 | } |
1955 | if (typeof connectOptions.cleanSession === "undefined") |
1956 | connectOptions.cleanSession = true; |
1957 | if (connectOptions.hosts) { |
1958 | |
1959 | if (!(connectOptions.hosts instanceof Array) ) |
1960 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); |
1961 | if (connectOptions.hosts.length <1 ) |
1962 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); |
1963 | |
1964 | var usingURIs = false; |
1965 | for (var i = 0; i<connectOptions.hosts.length; i++) { |
1966 | if (typeof connectOptions.hosts[i] !== "string") |
1967 | throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.hosts[i], "connectOptions.hosts["+i+"]"])); |
1968 | if (/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/.test(connectOptions.hosts[i])) { |
1969 | if (i === 0) { |
1970 | usingURIs = true; |
1971 | } else if (!usingURIs) { |
1972 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts["+i+"]"])); |
1973 | } |
1974 | } else if (usingURIs) { |
1975 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts["+i+"]"])); |
1976 | } |
1977 | } |
1978 | |
1979 | if (!usingURIs) { |
1980 | if (!connectOptions.ports) |
1981 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); |
1982 | if (!(connectOptions.ports instanceof Array) ) |
1983 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); |
1984 | if (connectOptions.hosts.length !== connectOptions.ports.length) |
1985 | throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); |
1986 | |
1987 | connectOptions.uris = []; |
1988 | |
1989 | for (var i = 0; i<connectOptions.hosts.length; i++) { |
1990 | if (typeof connectOptions.ports[i] !== "number" || connectOptions.ports[i] < 0) |
1991 | throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.ports[i], "connectOptions.ports["+i+"]"])); |
1992 | var host = connectOptions.hosts[i]; |
1993 | var port = connectOptions.ports[i]; |
1994 | |
1995 | var ipv6 = (host.indexOf(":") !== -1); |
1996 | uri = "ws://"+(ipv6?"["+host+"]":host)+":"+port+path; |
1997 | connectOptions.uris.push(uri); |
1998 | } |
1999 | } else { |
2000 | connectOptions.uris = connectOptions.hosts; |
2001 | } |
2002 | } |
2003 | |
2004 | client.connect(connectOptions); |
2005 | }; |
2006 | |
2007 | |
2008 | |
2009 | |
2010 | |
2011 | |
2012 | |
2013 | |
2014 | |
2015 | |
2016 | |
2017 | |
2018 | |
2019 | |
2020 | |
2021 | |
2022 | |
2023 | |
2024 | |
2025 | |
2026 | |
2027 | |
2028 | |
2029 | |
2030 | |
2031 | |
2032 | |
2033 | |
2034 | |
2035 | |
2036 | |
2037 | |
2038 | |
2039 | this.subscribe = function (filter, subscribeOptions) { |
2040 | if (typeof filter !== "string") |
2041 | throw new Error("Invalid argument:"+filter); |
2042 | subscribeOptions = subscribeOptions || {} ; |
2043 | validate(subscribeOptions, {qos:"number", |
2044 | invocationContext:"object", |
2045 | onSuccess:"function", |
2046 | onFailure:"function", |
2047 | timeout:"number" |
2048 | }); |
2049 | if (subscribeOptions.timeout && !subscribeOptions.onFailure) |
2050 | throw new Error("subscribeOptions.timeout specified with no onFailure callback."); |
2051 | if (typeof subscribeOptions.qos !== "undefined" && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 )) |
2052 | throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); |
2053 | client.subscribe(filter, subscribeOptions); |
2054 | }; |
2055 | |
2056 | |
2057 | |
2058 | |
2059 | |
2060 | |
2061 | |
2062 | |
2063 | |
2064 | |
2065 | |
2066 | |
2067 | |
2068 | |
2069 | |
2070 | |
2071 | |
2072 | |
2073 | |
2074 | |
2075 | |
2076 | |
2077 | |
2078 | |
2079 | |
2080 | |
2081 | |
2082 | |
2083 | |
2084 | this.unsubscribe = function (filter, unsubscribeOptions) { |
2085 | if (typeof filter !== "string") |
2086 | throw new Error("Invalid argument:"+filter); |
2087 | unsubscribeOptions = unsubscribeOptions || {} ; |
2088 | validate(unsubscribeOptions, {invocationContext:"object", |
2089 | onSuccess:"function", |
2090 | onFailure:"function", |
2091 | timeout:"number" |
2092 | }); |
2093 | if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) |
2094 | throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); |
2095 | client.unsubscribe(filter, unsubscribeOptions); |
2096 | }; |
2097 | |
2098 | |
2099 | |
2100 | |
2101 | |
2102 | |
2103 | |
2104 | |
2105 | |
2106 | |
2107 | |
2108 | |
2109 | |
2110 | |
2111 | |
2112 | |
2113 | |
2114 | |
2115 | |
2116 | |
2117 | |
2118 | |
2119 | |
2120 | this.send = function (topic,payload,qos,retained) { |
2121 | var message ; |
2122 | |
2123 | if(arguments.length === 0){ |
2124 | throw new Error("Invalid argument."+"length"); |
2125 | |
2126 | }else if(arguments.length == 1) { |
2127 | |
2128 | if (!(topic instanceof Message) && (typeof topic !== "string")) |
2129 | throw new Error("Invalid argument:"+ typeof topic); |
2130 | |
2131 | message = topic; |
2132 | if (typeof message.destinationName === "undefined") |
2133 | throw new Error(format(ERROR.INVALID_ARGUMENT,[message.destinationName,"Message.destinationName"])); |
2134 | client.send(message); |
2135 | |
2136 | }else { |
2137 | |
2138 | message = new Message(payload); |
2139 | message.destinationName = topic; |
2140 | if(arguments.length >= 3) |
2141 | message.qos = qos; |
2142 | if(arguments.length >= 4) |
2143 | message.retained = retained; |
2144 | client.send(message); |
2145 | } |
2146 | }; |
2147 | |
2148 | |
2149 | |
2150 | |
2151 | |
2152 | |
2153 | |
2154 | |
2155 | |
2156 | |
2157 | |
2158 | |
2159 | |
2160 | |
2161 | |
2162 | |
2163 | |
2164 | |
2165 | |
2166 | |
2167 | |
2168 | |
2169 | |
2170 | |
2171 | this.publish = function(topic,payload,qos,retained) { |
2172 | console.log("Publising message to: ", topic); |
2173 | var message ; |
2174 | |
2175 | if(arguments.length === 0){ |
2176 | throw new Error("Invalid argument."+"length"); |
2177 | |
2178 | }else if(arguments.length == 1) { |
2179 | |
2180 | if (!(topic instanceof Message) && (typeof topic !== "string")) |
2181 | throw new Error("Invalid argument:"+ typeof topic); |
2182 | |
2183 | message = topic; |
2184 | if (typeof message.destinationName === "undefined") |
2185 | throw new Error(format(ERROR.INVALID_ARGUMENT,[message.destinationName,"Message.destinationName"])); |
2186 | client.send(message); |
2187 | |
2188 | }else { |
2189 | |
2190 | message = new Message(payload); |
2191 | message.destinationName = topic; |
2192 | if(arguments.length >= 3) |
2193 | message.qos = qos; |
2194 | if(arguments.length >= 4) |
2195 | message.retained = retained; |
2196 | client.send(message); |
2197 | } |
2198 | }; |
2199 | |
2200 | |
2201 | |
2202 | |
2203 | |
2204 | |
2205 | |
2206 | |
2207 | this.disconnect = function () { |
2208 | client.disconnect(); |
2209 | }; |
2210 | |
2211 | |
2212 | |
2213 | |
2214 | |
2215 | |
2216 | |
2217 | |
2218 | this.getTraceLog = function () { |
2219 | return client.getTraceLog(); |
2220 | }; |
2221 | |
2222 | |
2223 | |
2224 | |
2225 | |
2226 | |
2227 | |
2228 | this.startTrace = function () { |
2229 | client.startTrace(); |
2230 | }; |
2231 | |
2232 | |
2233 | |
2234 | |
2235 | |
2236 | |
2237 | |
2238 | this.stopTrace = function () { |
2239 | client.stopTrace(); |
2240 | }; |
2241 | |
2242 | this.isConnected = function() { |
2243 | return client.connected; |
2244 | }; |
2245 | }; |
2246 | |
2247 | Client.prototype = { |
2248 | get host() { return this._getHost(); }, |
2249 | set host(newHost) { this._setHost(newHost); }, |
2250 | |
2251 | get port() { return this._getPort(); }, |
2252 | set port(newPort) { this._setPort(newPort); }, |
2253 | |
2254 | get path() { return this._getPath(); }, |
2255 | set path(newPath) { this._setPath(newPath); }, |
2256 | |
2257 | get clientId() { return this._getClientId(); }, |
2258 | set clientId(newClientId) { this._setClientId(newClientId); }, |
2259 | |
2260 | get onConnected() { return this._getOnConnected(); }, |
2261 | set onConnected(newOnConnected) { this._setOnConnected(newOnConnected); }, |
2262 | |
2263 | get disconnectedPublishing() { return this._getDisconnectedPublishing(); }, |
2264 | set disconnectedPublishing(newDisconnectedPublishing) { this._setDisconnectedPublishing(newDisconnectedPublishing); }, |
2265 | |
2266 | get disconnectedBufferSize() { return this._getDisconnectedBufferSize(); }, |
2267 | set disconnectedBufferSize(newDisconnectedBufferSize) { this._setDisconnectedBufferSize(newDisconnectedBufferSize); }, |
2268 | |
2269 | get onConnectionLost() { return this._getOnConnectionLost(); }, |
2270 | set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); }, |
2271 | |
2272 | get onMessageDelivered() { return this._getOnMessageDelivered(); }, |
2273 | set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); }, |
2274 | |
2275 | get onMessageArrived() { return this._getOnMessageArrived(); }, |
2276 | set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); }, |
2277 | |
2278 | get trace() { return this._getTrace(); }, |
2279 | set trace(newTraceFunction) { this._setTrace(newTraceFunction); } |
2280 | |
2281 | }; |
2282 | |
2283 | |
2284 | |
2285 | |
2286 | |
2287 | |
2288 | |
2289 | |
2290 | |
2291 | |
2292 | |
2293 | |
2294 | |
2295 | |
2296 | |
2297 | |
2298 | |
2299 | |
2300 | |
2301 | |
2302 | |
2303 | |
2304 | |
2305 | |
2306 | |
2307 | |
2308 | |
2309 | |
2310 | |
2311 | |
2312 | |
2313 | |
2314 | |
2315 | |
2316 | |
2317 | var Message = function (newPayload) { |
2318 | var payload; |
2319 | if ( typeof newPayload === "string" || |
2320 | newPayload instanceof ArrayBuffer || |
2321 | newPayload instanceof Int8Array || |
2322 | newPayload instanceof Uint8Array || |
2323 | newPayload instanceof Int16Array || |
2324 | newPayload instanceof Uint16Array || |
2325 | newPayload instanceof Int32Array || |
2326 | newPayload instanceof Uint32Array || |
2327 | newPayload instanceof Float32Array || |
2328 | newPayload instanceof Float64Array |
2329 | ) { |
2330 | payload = newPayload; |
2331 | } else { |
2332 | throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); |
2333 | } |
2334 | |
2335 | this._getPayloadString = function () { |
2336 | if (typeof payload === "string") |
2337 | return payload; |
2338 | else |
2339 | return parseUTF8(payload, 0, payload.length); |
2340 | }; |
2341 | |
2342 | this._getPayloadBytes = function() { |
2343 | if (typeof payload === "string") { |
2344 | var buffer = new ArrayBuffer(UTF8Length(payload)); |
2345 | var byteStream = new Uint8Array(buffer); |
2346 | stringToUTF8(payload, byteStream, 0); |
2347 | |
2348 | return byteStream; |
2349 | } else { |
2350 | return payload; |
2351 | } |
2352 | }; |
2353 | |
2354 | var destinationName; |
2355 | this._getDestinationName = function() { return destinationName; }; |
2356 | this._setDestinationName = function(newDestinationName) { |
2357 | if (typeof newDestinationName === "string") |
2358 | destinationName = newDestinationName; |
2359 | else |
2360 | throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); |
2361 | }; |
2362 | |
2363 | var qos = 0; |
2364 | this._getQos = function() { return qos; }; |
2365 | this._setQos = function(newQos) { |
2366 | if (newQos === 0 || newQos === 1 || newQos === 2 ) |
2367 | qos = newQos; |
2368 | else |
2369 | throw new Error("Invalid argument:"+newQos); |
2370 | }; |
2371 | |
2372 | var retained = false; |
2373 | this._getRetained = function() { return retained; }; |
2374 | this._setRetained = function(newRetained) { |
2375 | if (typeof newRetained === "boolean") |
2376 | retained = newRetained; |
2377 | else |
2378 | throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); |
2379 | }; |
2380 | |
2381 | var duplicate = false; |
2382 | this._getDuplicate = function() { return duplicate; }; |
2383 | this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; }; |
2384 | }; |
2385 | |
2386 | Message.prototype = { |
2387 | get payloadString() { return this._getPayloadString(); }, |
2388 | get payloadBytes() { return this._getPayloadBytes(); }, |
2389 | |
2390 | get destinationName() { return this._getDestinationName(); }, |
2391 | set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); }, |
2392 | |
2393 | get topic() { return this._getDestinationName(); }, |
2394 | set topic(newTopic) { this._setDestinationName(newTopic); }, |
2395 | |
2396 | get qos() { return this._getQos(); }, |
2397 | set qos(newQos) { this._setQos(newQos); }, |
2398 | |
2399 | get retained() { return this._getRetained(); }, |
2400 | set retained(newRetained) { this._setRetained(newRetained); }, |
2401 | |
2402 | get duplicate() { return this._getDuplicate(); }, |
2403 | set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); } |
2404 | }; |
2405 | |
2406 | |
2407 | return { |
2408 | Client: Client, |
2409 | Message: Message |
2410 | }; |
2411 | })(window); |
2412 | return PahoMQTT; |
2413 | }); |
2414 | |