|
@@ -14,7 +14,7 @@
|
|
* read binary data off of the receive queue.
|
|
* read binary data off of the receive queue.
|
|
*/
|
|
*/
|
|
|
|
|
|
-/*jslint browser: true, bitwise: false, plusplus: false */
|
|
|
|
|
|
+/*jslint browser: true, bitwise: true */
|
|
/*global Util, Base64 */
|
|
/*global Util, Base64 */
|
|
|
|
|
|
|
|
|
|
@@ -43,382 +43,334 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
|
}
|
|
}
|
|
Util.load_scripts(["web-socket-js/swfobject.js",
|
|
Util.load_scripts(["web-socket-js/swfobject.js",
|
|
"web-socket-js/web_socket.js"]);
|
|
"web-socket-js/web_socket.js"]);
|
|
- }());
|
|
|
|
|
|
+ })();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
function Websock() {
|
|
function Websock() {
|
|
-"use strict";
|
|
|
|
-
|
|
|
|
-var api = {}, // Public API
|
|
|
|
- websocket = null, // WebSocket object
|
|
|
|
- mode = 'base64', // Current WebSocket mode: 'binary', 'base64'
|
|
|
|
- rQ = [], // Receive queue
|
|
|
|
- rQi = 0, // Receive queue index
|
|
|
|
- rQmax = 10000, // Max receive queue size before compacting
|
|
|
|
- sQ = [], // Send queue
|
|
|
|
-
|
|
|
|
- eventHandlers = {
|
|
|
|
- 'message' : function() {},
|
|
|
|
- 'open' : function() {},
|
|
|
|
- 'close' : function() {},
|
|
|
|
- 'error' : function() {}
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- test_mode = false;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-//
|
|
|
|
-// Queue public functions
|
|
|
|
-//
|
|
|
|
-
|
|
|
|
-function get_sQ() {
|
|
|
|
- return sQ;
|
|
|
|
|
|
+ "use strict";
|
|
|
|
+
|
|
|
|
+ this._websocket = null; // WebSocket object
|
|
|
|
+ this._rQ = []; // Receive queue
|
|
|
|
+ this._rQi = 0; // Receive queue index
|
|
|
|
+ this._rQmax = 10000; // Max receive queue size before compacting
|
|
|
|
+ this._sQ = []; // Send queue
|
|
|
|
+
|
|
|
|
+ this._mode = 'base64'; // Current WebSocket mode: 'binary', 'base64'
|
|
|
|
+ this.maxBufferedAmount = 200;
|
|
|
|
+
|
|
|
|
+ this._eventHandlers = {
|
|
|
|
+ 'message': function () {},
|
|
|
|
+ 'open': function () {},
|
|
|
|
+ 'close': function () {},
|
|
|
|
+ 'error': function () {}
|
|
|
|
+ };
|
|
}
|
|
}
|
|
|
|
|
|
-function get_rQ() {
|
|
|
|
- return rQ;
|
|
|
|
-}
|
|
|
|
-function get_rQi() {
|
|
|
|
- return rQi;
|
|
|
|
-}
|
|
|
|
-function set_rQi(val) {
|
|
|
|
- rQi = val;
|
|
|
|
-}
|
|
|
|
|
|
+(function () {
|
|
|
|
+ "use strict";
|
|
|
|
+ Websock.prototype = {
|
|
|
|
+ // Getters and Setters
|
|
|
|
+ get_sQ: function () {
|
|
|
|
+ return this._sQ;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ get_rQ: function () {
|
|
|
|
+ return this._rQ;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ get_rQi: function () {
|
|
|
|
+ return this._rQi;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ set_rQi: function (val) {
|
|
|
|
+ this._rQi = val;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // Receive Queue
|
|
|
|
+ rQlen: function () {
|
|
|
|
+ return this._rQ.length - this._rQi;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQpeek8: function () {
|
|
|
|
+ return this._rQ[this._rQi];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQshift8: function () {
|
|
|
|
+ return this._rQ[this._rQi++];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQunshift8: function (num) {
|
|
|
|
+ if (this._rQi === 0) {
|
|
|
|
+ this._rQ.unshift(num);
|
|
|
|
+ } else {
|
|
|
|
+ this._rQi--;
|
|
|
|
+ this._rQ[this._rQi] = num;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQshift16: function () {
|
|
|
|
+ return (this._rQ[this._rQi++] << 8) +
|
|
|
|
+ this._rQ[this._rQi++];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQshift32: function () {
|
|
|
|
+ return (this._rQ[this._rQi++] << 24) +
|
|
|
|
+ (this._rQ[this._rQi++] << 16) +
|
|
|
|
+ (this._rQ[this._rQi++] << 8) +
|
|
|
|
+ this._rQ[this._rQi++];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQshiftStr: function (len) {
|
|
|
|
+ if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
|
|
|
+ var arr = this._rQ.slice(this._rQi, this._rQi + len);
|
|
|
|
+ this._rQi += len;
|
|
|
|
+ return String.fromCharCode.apply(null, arr);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQshiftBytes: function (len) {
|
|
|
|
+ if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
|
|
|
+ this._rQi += len;
|
|
|
|
+ return this._rQ.slice(this._rQi - len, this._rQi);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ rQslice: function (start, end) {
|
|
|
|
+ if (end) {
|
|
|
|
+ return this._rQ.slice(this._rQi + start, this._rQi + end);
|
|
|
|
+ } else {
|
|
|
|
+ return this._rQ.slice(this._rQi + start);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
|
|
|
+ // to be available in the receive queue. Return true if we need to
|
|
|
|
+ // wait (and possibly print a debug message), otherwise false.
|
|
|
|
+ rQwait: function (msg, num, goback) {
|
|
|
|
+ var rQlen = this._rQ.length - this._rQi; // Skip rQlen() function call
|
|
|
|
+ if (rQlen < num) {
|
|
|
|
+ if (goback) {
|
|
|
|
+ if (this._rQi < goback) {
|
|
|
|
+ throw new Error("rQwait cannot backup " + goback + " bytes");
|
|
|
|
+ }
|
|
|
|
+ this._rQi -= goback;
|
|
|
|
+ }
|
|
|
|
+ return true; // true means need more data
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ },
|
|
|
|
|
|
-function rQlen() {
|
|
|
|
- return rQ.length - rQi;
|
|
|
|
-}
|
|
|
|
|
|
+ // Send Queue
|
|
|
|
|
|
-function rQpeek8() {
|
|
|
|
- return (rQ[rQi] );
|
|
|
|
-}
|
|
|
|
-function rQshift8() {
|
|
|
|
- return (rQ[rQi++] );
|
|
|
|
-}
|
|
|
|
-function rQunshift8(num) {
|
|
|
|
- if (rQi === 0) {
|
|
|
|
- rQ.unshift(num);
|
|
|
|
- } else {
|
|
|
|
- rQi -= 1;
|
|
|
|
- rQ[rQi] = num;
|
|
|
|
- }
|
|
|
|
|
|
+ flush: function () {
|
|
|
|
+ if (this._websocket.bufferedAmount !== 0) {
|
|
|
|
+ Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
|
-function rQshift16() {
|
|
|
|
- return (rQ[rQi++] << 8) +
|
|
|
|
- (rQ[rQi++] );
|
|
|
|
-}
|
|
|
|
-function rQshift32() {
|
|
|
|
- return (rQ[rQi++] << 24) +
|
|
|
|
- (rQ[rQi++] << 16) +
|
|
|
|
- (rQ[rQi++] << 8) +
|
|
|
|
- (rQ[rQi++] );
|
|
|
|
-}
|
|
|
|
-function rQshiftStr(len) {
|
|
|
|
- if (typeof(len) === 'undefined') { len = rQlen(); }
|
|
|
|
- var arr = rQ.slice(rQi, rQi + len);
|
|
|
|
- rQi += len;
|
|
|
|
- return String.fromCharCode.apply(null, arr);
|
|
|
|
-}
|
|
|
|
-function rQshiftBytes(len) {
|
|
|
|
- if (typeof(len) === 'undefined') { len = rQlen(); }
|
|
|
|
- rQi += len;
|
|
|
|
- return rQ.slice(rQi-len, rQi);
|
|
|
|
-}
|
|
|
|
|
|
+ if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
|
|
|
|
+ if (this._sQ.length > 0) {
|
|
|
|
+ this._websocket.send(this._encode_message());
|
|
|
|
+ this._sQ = [];
|
|
|
|
+ }
|
|
|
|
|
|
-function rQslice(start, end) {
|
|
|
|
- if (end) {
|
|
|
|
- return rQ.slice(rQi + start, rQi + end);
|
|
|
|
- } else {
|
|
|
|
- return rQ.slice(rQi + start);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ return true;
|
|
|
|
+ } else {
|
|
|
|
+ Util.Info("Delaying send, bufferedAmount: " +
|
|
|
|
+ this._websocket.bufferedAmount);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ send: function (arr) {
|
|
|
|
+ this._sQ = this._sQ.concat(arr);
|
|
|
|
+ return this.flush();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ send_string: function (str) {
|
|
|
|
+ this.send(str.split('').map(function (chr) {
|
|
|
|
+ return chr.charCodeAt(0);
|
|
|
|
+ }));
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // Event Handlers
|
|
|
|
+ on: function (evt, handler) {
|
|
|
|
+ this._eventHandlers[evt] = handler;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ init: function (protocols, ws_schema) {
|
|
|
|
+ this._rQ = [];
|
|
|
|
+ this._rQi = 0;
|
|
|
|
+ this._sQ = [];
|
|
|
|
+ this._websocket = null;
|
|
|
|
+
|
|
|
|
+ // Check for full typed array support
|
|
|
|
+ var bt = false;
|
|
|
|
+ if (('Uint8Array' in window) &&
|
|
|
|
+ ('set' in Uint8Array.prototype)) {
|
|
|
|
+ bt = true;
|
|
|
|
+ }
|
|
|
|
|
|
-// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
|
|
|
-// to be available in the receive queue. Return true if we need to
|
|
|
|
-// wait (and possibly print a debug message), otherwise false.
|
|
|
|
-function rQwait(msg, num, goback) {
|
|
|
|
- var rQlen = rQ.length - rQi; // Skip rQlen() function call
|
|
|
|
- if (rQlen < num) {
|
|
|
|
- if (goback) {
|
|
|
|
- if (rQi < goback) {
|
|
|
|
- throw("rQwait cannot backup " + goback + " bytes");
|
|
|
|
|
|
+ // Check for full binary type support in WebSockets
|
|
|
|
+ // Inspired by:
|
|
|
|
+ // https://github.com/Modernizr/Modernizr/issues/370
|
|
|
|
+ // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js
|
|
|
|
+ var wsbt = false;
|
|
|
|
+ try {
|
|
|
|
+ if (bt && ('binaryType' in WebSocket.prototype ||
|
|
|
|
+ !!(new WebSocket(ws_schema + '://.').binaryType))) {
|
|
|
|
+ Util.Info("Detected binaryType support in WebSockets");
|
|
|
|
+ wsbt = true;
|
|
|
|
+ }
|
|
|
|
+ } catch (exc) {
|
|
|
|
+ // Just ignore failed test localhost connection
|
|
}
|
|
}
|
|
- rQi -= goback;
|
|
|
|
- }
|
|
|
|
- //Util.Debug(" waiting for " + (num-rQlen) +
|
|
|
|
- // " " + msg + " byte(s)");
|
|
|
|
- return true; // true means need more data
|
|
|
|
- }
|
|
|
|
- return false;
|
|
|
|
-}
|
|
|
|
|
|
|
|
-//
|
|
|
|
-// Private utility routines
|
|
|
|
-//
|
|
|
|
-
|
|
|
|
-function encode_message() {
|
|
|
|
- if (mode === 'binary') {
|
|
|
|
- // Put in a binary arraybuffer
|
|
|
|
- return (new Uint8Array(sQ)).buffer;
|
|
|
|
- } else {
|
|
|
|
- // base64 encode
|
|
|
|
- return Base64.encode(sQ);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ // Default protocols if not specified
|
|
|
|
+ if (typeof(protocols) === "undefined") {
|
|
|
|
+ if (wsbt) {
|
|
|
|
+ protocols = ['binary', 'base64'];
|
|
|
|
+ } else {
|
|
|
|
+ protocols = 'base64';
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
-function decode_message(data) {
|
|
|
|
- //Util.Debug(">> decode_message: " + data);
|
|
|
|
- if (mode === 'binary') {
|
|
|
|
- // push arraybuffer values onto the end
|
|
|
|
- var u8 = new Uint8Array(data);
|
|
|
|
- for (var i = 0; i < u8.length; i++) {
|
|
|
|
- rQ.push(u8[i]);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- // base64 decode and concat to the end
|
|
|
|
- rQ = rQ.concat(Base64.decode(data, 0));
|
|
|
|
- }
|
|
|
|
- //Util.Debug(">> decode_message, rQ: " + rQ);
|
|
|
|
-}
|
|
|
|
|
|
+ if (!wsbt) {
|
|
|
|
+ if (protocols === 'binary') {
|
|
|
|
+ throw new Error('WebSocket binary sub-protocol requested but not supported');
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ if (typeof(protocols) === 'object') {
|
|
|
|
+ var new_protocols = [];
|
|
|
|
+
|
|
|
|
+ for (var i = 0; i < protocols.length; i++) {
|
|
|
|
+ if (protocols[i] === 'binary') {
|
|
|
|
+ Util.Error('Skipping unsupported WebSocket binary sub-protocol');
|
|
|
|
+ } else {
|
|
|
|
+ new_protocols.push(protocols[i]);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (new_protocols.length > 0) {
|
|
|
|
+ protocols = new_protocols;
|
|
|
|
+ } else {
|
|
|
|
+ throw new Error("Only WebSocket binary sub-protocol was requested and is not supported.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
-//
|
|
|
|
-// Public Send functions
|
|
|
|
-//
|
|
|
|
-
|
|
|
|
-function flush() {
|
|
|
|
- if (websocket.bufferedAmount !== 0) {
|
|
|
|
- Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
|
|
|
|
- }
|
|
|
|
- if (websocket.bufferedAmount < api.maxBufferedAmount) {
|
|
|
|
- //Util.Debug("arr: " + arr);
|
|
|
|
- //Util.Debug("sQ: " + sQ);
|
|
|
|
- if (sQ.length > 0) {
|
|
|
|
- websocket.send(encode_message(sQ));
|
|
|
|
- sQ = [];
|
|
|
|
- }
|
|
|
|
- return true;
|
|
|
|
- } else {
|
|
|
|
- Util.Info("Delaying send, bufferedAmount: " +
|
|
|
|
- websocket.bufferedAmount);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ return protocols;
|
|
|
|
+ },
|
|
|
|
|
|
-// overridable for testing
|
|
|
|
-function send(arr) {
|
|
|
|
- //Util.Debug(">> send_array: " + arr);
|
|
|
|
- sQ = sQ.concat(arr);
|
|
|
|
- return flush();
|
|
|
|
-}
|
|
|
|
|
|
+ open: function (uri, protocols) {
|
|
|
|
+ var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
|
|
|
|
+ protocols = this.init(protocols, ws_schema);
|
|
|
|
|
|
-function send_string(str) {
|
|
|
|
- //Util.Debug(">> send_string: " + str);
|
|
|
|
- api.send(str.split('').map(
|
|
|
|
- function (chr) { return chr.charCodeAt(0); } ) );
|
|
|
|
-}
|
|
|
|
|
|
+ this._websocket = new WebSocket(uri, protocols);
|
|
|
|
|
|
-//
|
|
|
|
-// Other public functions
|
|
|
|
-
|
|
|
|
-function recv_message(e) {
|
|
|
|
- //Util.Debug(">> recv_message: " + e.data.length);
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- decode_message(e.data);
|
|
|
|
- if (rQlen() > 0) {
|
|
|
|
- eventHandlers.message();
|
|
|
|
- // Compact the receive queue
|
|
|
|
- if (rQ.length > rQmax) {
|
|
|
|
- //Util.Debug("Compacting receive queue");
|
|
|
|
- rQ = rQ.slice(rQi);
|
|
|
|
- rQi = 0;
|
|
|
|
|
|
+ if (protocols.indexOf('binary') >= 0) {
|
|
|
|
+ this._websocket.binaryType = 'arraybuffer';
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- Util.Debug("Ignoring empty message");
|
|
|
|
- }
|
|
|
|
- } catch (exc) {
|
|
|
|
- if (typeof exc.stack !== 'undefined') {
|
|
|
|
- Util.Warn("recv_message, caught exception: " + exc.stack);
|
|
|
|
- } else if (typeof exc.description !== 'undefined') {
|
|
|
|
- Util.Warn("recv_message, caught exception: " + exc.description);
|
|
|
|
- } else {
|
|
|
|
- Util.Warn("recv_message, caught exception:" + exc);
|
|
|
|
- }
|
|
|
|
- if (typeof exc.name !== 'undefined') {
|
|
|
|
- eventHandlers.error(exc.name + ": " + exc.message);
|
|
|
|
- } else {
|
|
|
|
- eventHandlers.error(exc);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- //Util.Debug("<< recv_message");
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
|
|
|
|
-// Set event handlers
|
|
|
|
-function on(evt, handler) {
|
|
|
|
- eventHandlers[evt] = handler;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function init(protocols, ws_schema) {
|
|
|
|
- rQ = [];
|
|
|
|
- rQi = 0;
|
|
|
|
- sQ = [];
|
|
|
|
- websocket = null;
|
|
|
|
-
|
|
|
|
- var bt = false,
|
|
|
|
- wsbt = false,
|
|
|
|
- try_binary = false;
|
|
|
|
-
|
|
|
|
- // Check for full typed array support
|
|
|
|
- if (('Uint8Array' in window) &&
|
|
|
|
- ('set' in Uint8Array.prototype)) {
|
|
|
|
- bt = true;
|
|
|
|
- }
|
|
|
|
- // Check for full binary type support in WebSocket
|
|
|
|
- // Inspired by:
|
|
|
|
- // https://github.com/Modernizr/Modernizr/issues/370
|
|
|
|
- // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js
|
|
|
|
- try {
|
|
|
|
- if (bt && ('binaryType' in WebSocket.prototype ||
|
|
|
|
- !!(new WebSocket(ws_schema + '://.').binaryType))) {
|
|
|
|
- Util.Info("Detected binaryType support in WebSockets");
|
|
|
|
- wsbt = true;
|
|
|
|
- }
|
|
|
|
- } catch (exc) {
|
|
|
|
- // Just ignore failed test localhost connections
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Default protocols if not specified
|
|
|
|
- if (typeof(protocols) === "undefined") {
|
|
|
|
- if (wsbt) {
|
|
|
|
- protocols = ['binary', 'base64'];
|
|
|
|
- } else {
|
|
|
|
- protocols = 'base64';
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // If no binary support, make sure it was not requested
|
|
|
|
- if (!wsbt) {
|
|
|
|
- if (protocols === 'binary') {
|
|
|
|
- throw("WebSocket binary sub-protocol requested but not supported");
|
|
|
|
- }
|
|
|
|
- if (typeof(protocols) === "object") {
|
|
|
|
- var new_protocols = [];
|
|
|
|
- for (var i = 0; i < protocols.length; i++) {
|
|
|
|
- if (protocols[i] === 'binary') {
|
|
|
|
- Util.Error("Skipping unsupported WebSocket binary sub-protocol");
|
|
|
|
|
|
+ this._websocket.onmessage = this._recv_message.bind(this);
|
|
|
|
+ this._websocket.onopen = (function () {
|
|
|
|
+ Util.Debug('>> WebSock.onopen');
|
|
|
|
+ if (this._websocket.protocol) {
|
|
|
|
+ this._mode = this._websocket.protocol;
|
|
|
|
+ Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
|
} else {
|
|
} else {
|
|
- new_protocols.push(protocols[i]);
|
|
|
|
|
|
+ this._mode = 'base64';
|
|
|
|
+ Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol);
|
|
}
|
|
}
|
|
|
|
+ this._eventHandlers.open();
|
|
|
|
+ Util.Debug("<< WebSock.onopen");
|
|
|
|
+ }).bind(this);
|
|
|
|
+ this._websocket.onclose = (function (e) {
|
|
|
|
+ Util.Debug(">> WebSock.onclose");
|
|
|
|
+ this._eventHandlers.close(e);
|
|
|
|
+ Util.Debug("<< WebSock.onclose");
|
|
|
|
+ }).bind(this);
|
|
|
|
+ this._websocket.onerror = (function (e) {
|
|
|
|
+ Util.Debug(">> WebSock.onerror: " + e);
|
|
|
|
+ this._eventHandlers.error(e);
|
|
|
|
+ Util.Debug("<< WebSock.onerror: " + e);
|
|
|
|
+ }).bind(this);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ close: function () {
|
|
|
|
+ if (this._websocket) {
|
|
|
|
+ if ((this._websocket.readyState === WebSocket.OPEN) ||
|
|
|
|
+ (this._websocket.readyState === WebSocket.CONNECTING)) {
|
|
|
|
+ Util.Info("Closing WebSocket connection");
|
|
|
|
+ this._websocket.close();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._websocket.onmessage = function (e) { return; };
|
|
}
|
|
}
|
|
- if (new_protocols.length > 0) {
|
|
|
|
- protocols = new_protocols;
|
|
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // private methods
|
|
|
|
+ _encode_message: function () {
|
|
|
|
+ if (this._mode === 'binary') {
|
|
|
|
+ // Put in a binary arraybuffer
|
|
|
|
+ return (new Uint8Array(this._sQ)).buffer;
|
|
} else {
|
|
} else {
|
|
- throw("Only WebSocket binary sub-protocol was requested and not supported.");
|
|
|
|
|
|
+ // base64 encode
|
|
|
|
+ return Base64.encode(this._sQ);
|
|
}
|
|
}
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _decode_message: function (data) {
|
|
|
|
+ if (this._mode === 'binary') {
|
|
|
|
+ // push arraybuffer values onto the end
|
|
|
|
+ var u8 = new Uint8Array(data);
|
|
|
|
+ for (var i = 0; i < u8.length; i++) {
|
|
|
|
+ this._rQ.push(u8[i]);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // base64 decode and concat to end
|
|
|
|
+ this._rQ = this._rQ.concat(Base64.decode(data, 0));
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _recv_message: function (e) {
|
|
|
|
+ try {
|
|
|
|
+ this._decode_message(e.data);
|
|
|
|
+ if (this.rQlen() > 0) {
|
|
|
|
+ this._eventHandlers.message();
|
|
|
|
+ // Compact the receive queue
|
|
|
|
+ if (this._rQ.length > this._rQmax) {
|
|
|
|
+ this._rQ = this._rQ.slice(this._rQi);
|
|
|
|
+ this._rQi = 0;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ Util.Debug("Ignoring empty message");
|
|
|
|
+ }
|
|
|
|
+ } catch (exc) {
|
|
|
|
+ var exception_str = "";
|
|
|
|
+ if (exc.name) {
|
|
|
|
+ exception_str += "\n name: " + exc.name + "\n";
|
|
|
|
+ exception_str += " message: " + exc.message + "\n";
|
|
|
|
+ }
|
|
|
|
|
|
- return protocols;
|
|
|
|
-}
|
|
|
|
|
|
+ if (typeof exc.description !== 'undefined') {
|
|
|
|
+ exception_str += " description: " + exc.description + "\n";
|
|
|
|
+ }
|
|
|
|
|
|
-function open(uri, protocols) {
|
|
|
|
- var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
|
|
|
|
- protocols = init(protocols, ws_schema);
|
|
|
|
|
|
+ if (typeof exc.stack !== 'undefined') {
|
|
|
|
+ exception_str += exc.stack;
|
|
|
|
+ }
|
|
|
|
|
|
- if (test_mode) {
|
|
|
|
- websocket = {};
|
|
|
|
- } else {
|
|
|
|
- websocket = new WebSocket(uri, protocols);
|
|
|
|
- if (protocols.indexOf('binary') >= 0) {
|
|
|
|
- websocket.binaryType = 'arraybuffer';
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- websocket.onmessage = recv_message;
|
|
|
|
- websocket.onopen = function() {
|
|
|
|
- Util.Debug(">> WebSock.onopen");
|
|
|
|
- if (websocket.protocol) {
|
|
|
|
- mode = websocket.protocol;
|
|
|
|
- Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
|
|
|
- } else {
|
|
|
|
- mode = 'base64';
|
|
|
|
- Util.Error("Server select no sub-protocol!: " + websocket.protocol);
|
|
|
|
- }
|
|
|
|
- eventHandlers.open();
|
|
|
|
- Util.Debug("<< WebSock.onopen");
|
|
|
|
- };
|
|
|
|
- websocket.onclose = function(e) {
|
|
|
|
- Util.Debug(">> WebSock.onclose");
|
|
|
|
- eventHandlers.close(e);
|
|
|
|
- Util.Debug("<< WebSock.onclose");
|
|
|
|
- };
|
|
|
|
- websocket.onerror = function(e) {
|
|
|
|
- Util.Debug(">> WebSock.onerror: " + e);
|
|
|
|
- eventHandlers.error(e);
|
|
|
|
- Util.Debug("<< WebSock.onerror");
|
|
|
|
- };
|
|
|
|
-}
|
|
|
|
|
|
+ if (exception_str.length > 0) {
|
|
|
|
+ Util.Error("recv_message, caught exception: " + exception_str);
|
|
|
|
+ } else {
|
|
|
|
+ Util.Error("recv_message, caught exception: " + exc);
|
|
|
|
+ }
|
|
|
|
|
|
-function close() {
|
|
|
|
- if (websocket) {
|
|
|
|
- if ((websocket.readyState === WebSocket.OPEN) ||
|
|
|
|
- (websocket.readyState === WebSocket.CONNECTING)) {
|
|
|
|
- Util.Info("Closing WebSocket connection");
|
|
|
|
- websocket.close();
|
|
|
|
|
|
+ if (typeof exc.name !== 'undefined') {
|
|
|
|
+ this._eventHandlers.error(exc.name + ": " + exc.message);
|
|
|
|
+ } else {
|
|
|
|
+ this._eventHandlers.error(exc);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- websocket.onmessage = function (e) { return; };
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// Override internal functions for testing
|
|
|
|
-// Takes a send function, returns reference to recv function
|
|
|
|
-function testMode(override_send, data_mode) {
|
|
|
|
- test_mode = true;
|
|
|
|
- mode = data_mode;
|
|
|
|
- api.send = override_send;
|
|
|
|
- api.close = function () {};
|
|
|
|
- return recv_message;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function constructor() {
|
|
|
|
- // Configuration settings
|
|
|
|
- api.maxBufferedAmount = 200;
|
|
|
|
-
|
|
|
|
- // Direct access to send and receive queues
|
|
|
|
- api.get_sQ = get_sQ;
|
|
|
|
- api.get_rQ = get_rQ;
|
|
|
|
- api.get_rQi = get_rQi;
|
|
|
|
- api.set_rQi = set_rQi;
|
|
|
|
-
|
|
|
|
- // Routines to read from the receive queue
|
|
|
|
- api.rQlen = rQlen;
|
|
|
|
- api.rQpeek8 = rQpeek8;
|
|
|
|
- api.rQshift8 = rQshift8;
|
|
|
|
- api.rQunshift8 = rQunshift8;
|
|
|
|
- api.rQshift16 = rQshift16;
|
|
|
|
- api.rQshift32 = rQshift32;
|
|
|
|
- api.rQshiftStr = rQshiftStr;
|
|
|
|
- api.rQshiftBytes = rQshiftBytes;
|
|
|
|
- api.rQslice = rQslice;
|
|
|
|
- api.rQwait = rQwait;
|
|
|
|
-
|
|
|
|
- api.flush = flush;
|
|
|
|
- api.send = send;
|
|
|
|
- api.send_string = send_string;
|
|
|
|
-
|
|
|
|
- api.on = on;
|
|
|
|
- api.init = init;
|
|
|
|
- api.open = open;
|
|
|
|
- api.close = close;
|
|
|
|
- api.testMode = testMode;
|
|
|
|
-
|
|
|
|
- return api;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-return constructor();
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
|
|
+ };
|
|
|
|
+})();
|