Use WebSocket binary data (no base64 enc/dec) if available.
If typed arrays (arraybuffers) are available and the WebSocket implementation supports them, then send and receive direct binary frames and skip base64 encode/decode. Otherwise we just fallback to the current method of sending base64 encoded strings (with a couple of extra checks for mode in the send/receive path). The check for binaryType support in WebSocket is a collosal hack right now due to the fact that the 'binaryType' property doesn't exist on the WebSocket prototype. So we have to create a connection to a localhost port in order to test. A potentionally big performance boost could probably be achieved by re-using a larger typed array for storing the data instead of creating a typed array every time we receive a message.
This commit is contained in:
parent
f55362ff07
commit
376872d993
|
|
@ -61,6 +61,8 @@ function Websock() {
|
||||||
|
|
||||||
var api = {}, // Public API
|
var api = {}, // Public API
|
||||||
websocket = null, // WebSocket object
|
websocket = null, // WebSocket object
|
||||||
|
protocols, // Protocols to request in priority order
|
||||||
|
mode = 'base64',
|
||||||
rQ = [], // Receive queue
|
rQ = [], // Receive queue
|
||||||
rQi = 0, // Receive queue index
|
rQi = 0, // Receive queue index
|
||||||
rQmax = 10000, // Max receive queue size before compacting
|
rQmax = 10000, // Max receive queue size before compacting
|
||||||
|
|
@ -92,7 +94,7 @@ function get_rQi() {
|
||||||
}
|
}
|
||||||
function set_rQi(val) {
|
function set_rQi(val) {
|
||||||
rQi = val;
|
rQi = val;
|
||||||
};
|
}
|
||||||
|
|
||||||
function rQlen() {
|
function rQlen() {
|
||||||
return rQ.length - rQi;
|
return rQ.length - rQi;
|
||||||
|
|
@ -123,26 +125,46 @@ function rQshift32() {
|
||||||
(rQ[rQi++] << 8) +
|
(rQ[rQi++] << 8) +
|
||||||
(rQ[rQi++] );
|
(rQ[rQi++] );
|
||||||
}
|
}
|
||||||
|
function rQslice(start, end) {
|
||||||
|
if (mode === 'binary') {
|
||||||
|
if (end) {
|
||||||
|
return rQ.subarray(rQi + start, rQi + end);
|
||||||
|
} else {
|
||||||
|
return rQ.subarray(rQi + start);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (end) {
|
||||||
|
return rQ.slice(rQi + start, rQi + end);
|
||||||
|
} else {
|
||||||
|
return rQ.slice(rQi + start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function rQshiftStr(len) {
|
function rQshiftStr(len) {
|
||||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
var arr = rQ.slice(rQi, rQi + len);
|
var arr = rQslice(0, len);
|
||||||
rQi += len;
|
rQi += len;
|
||||||
return arr.map(function (num) {
|
return String.fromCharCode.apply(null, arr);
|
||||||
return String.fromCharCode(num); } ).join('');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
function rQshiftBytes(len) {
|
function rQshiftBytes(len) {
|
||||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
rQi += len;
|
var a = rQslice(0, len), b = [];
|
||||||
return rQ.slice(rQi-len, rQi);
|
if (mode === 'binary') {
|
||||||
}
|
// Convert to plain array
|
||||||
|
b.push.apply(b, a);
|
||||||
function rQslice(start, end) {
|
|
||||||
if (end) {
|
|
||||||
return rQ.slice(rQi + start, rQi + end);
|
|
||||||
} else {
|
} else {
|
||||||
return rQ.slice(rQi + start);
|
// Already plain array, just return the original
|
||||||
|
b = a
|
||||||
}
|
}
|
||||||
|
rQi += len;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
function rQshiftArray(len) {
|
||||||
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
|
var a = rQslice(0, len);
|
||||||
|
rQi += len;
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||||
|
|
@ -170,13 +192,26 @@ function rQwait(msg, num, goback) {
|
||||||
|
|
||||||
function encode_message() {
|
function encode_message() {
|
||||||
/* base64 encode */
|
/* base64 encode */
|
||||||
return Base64.encode(sQ);
|
if (mode === 'binary') {
|
||||||
|
return (new Uint8Array(sQ)).buffer;
|
||||||
|
} else {
|
||||||
|
return Base64.encode(sQ);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decode_message(data) {
|
function decode_message(data) {
|
||||||
//Util.Debug(">> decode_message: " + data);
|
//Util.Debug(">> decode_message: " + data);
|
||||||
/* base64 decode */
|
if (mode === 'binary') {
|
||||||
rQ = rQ.concat(Base64.decode(data, 0));
|
// Create new arraybuffer and dump old and new data into it
|
||||||
|
// TODO: this could be far more efficient and re-use the array
|
||||||
|
var new_rQ = new Uint8Array(rQ.length + data.byteLength);
|
||||||
|
new_rQ.set(rQ);
|
||||||
|
new_rQ.set(new Uint8Array(data), rQ.length);
|
||||||
|
rQ = new_rQ;
|
||||||
|
} else {
|
||||||
|
/* base64 decode and concat to the end */
|
||||||
|
rQ = rQ.concat(Base64.decode(data, 0));
|
||||||
|
}
|
||||||
//Util.Debug(">> decode_message, rQ: " + rQ);
|
//Util.Debug(">> decode_message, rQ: " + rQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,7 +265,7 @@ function recv_message(e) {
|
||||||
// Compact the receive queue
|
// Compact the receive queue
|
||||||
if (rQ.length > rQmax) {
|
if (rQ.length > rQmax) {
|
||||||
//Util.Debug("Compacting receive queue");
|
//Util.Debug("Compacting receive queue");
|
||||||
rQ = rQ.slice(rQi);
|
rQ = rQslice(rQi);
|
||||||
rQi = 0;
|
rQi = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -263,7 +298,32 @@ function init() {
|
||||||
rQ = [];
|
rQ = [];
|
||||||
rQi = 0;
|
rQi = 0;
|
||||||
sQ = [];
|
sQ = [];
|
||||||
websocket = null;
|
websocket = null,
|
||||||
|
protocols = "base64";
|
||||||
|
|
||||||
|
var bt = false,
|
||||||
|
wsbt = false;
|
||||||
|
|
||||||
|
if (('Uint8Array' in window) &&
|
||||||
|
('set' in Uint8Array.prototype)) {
|
||||||
|
bt = true;
|
||||||
|
}
|
||||||
|
// TODO: this sucks, the property should exist on the prototype
|
||||||
|
// but it does not.
|
||||||
|
try {
|
||||||
|
if (bt && ('binaryType' in (new WebSocket("ws://localhost:17523")))) {
|
||||||
|
wsbt = true;
|
||||||
|
}
|
||||||
|
} catch (exc) {
|
||||||
|
// Just ignore failed test localhost connections
|
||||||
|
}
|
||||||
|
if (bt && wsbt) {
|
||||||
|
Util.Info("Detected binaryType support in WebSockets");
|
||||||
|
protocols = ['binary', 'base64'];
|
||||||
|
} else {
|
||||||
|
Util.Info("No binaryType support in WebSockets, using base64 encoding");
|
||||||
|
protocols = 'base64';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(uri) {
|
function open(uri) {
|
||||||
|
|
@ -272,16 +332,21 @@ function open(uri) {
|
||||||
if (test_mode) {
|
if (test_mode) {
|
||||||
websocket = {};
|
websocket = {};
|
||||||
} else {
|
} else {
|
||||||
websocket = new WebSocket(uri, 'base64');
|
websocket = new WebSocket(uri, protocols);
|
||||||
// TODO: future native binary support
|
|
||||||
//websocket = new WebSocket(uri, ['binary', 'base64']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket.onmessage = recv_message;
|
websocket.onmessage = recv_message;
|
||||||
websocket.onopen = function() {
|
websocket.onopen = function() {
|
||||||
Util.Debug(">> WebSock.onopen");
|
Util.Debug(">> WebSock.onopen");
|
||||||
if (websocket.protocol) {
|
if (websocket.protocol) {
|
||||||
|
mode = websocket.protocol;
|
||||||
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
||||||
|
} else {
|
||||||
|
mode = 'base64';
|
||||||
|
Util.Error("Server select no sub-protocol!: " + websocket.protocol);
|
||||||
|
}
|
||||||
|
if (mode === 'binary') {
|
||||||
|
websocket.binaryType = 'arraybuffer';
|
||||||
}
|
}
|
||||||
eventHandlers.open();
|
eventHandlers.open();
|
||||||
Util.Debug("<< WebSock.onopen");
|
Util.Debug("<< WebSock.onopen");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue