Use Typed Arrays for the send queue

This commit converts the send queue to use typed arrays, and converts
message creation functions in 'rfb.js' to create messages directly into
the socket's send queue.  This commit also removes the separate mouse array,
which is no longer needed.
This commit is contained in:
Solly Ross 2015-05-28 15:28:30 -04:00
parent 27880486c7
commit 48deea531c
6 changed files with 277 additions and 216 deletions

View File

@ -259,14 +259,14 @@ var RFB;
if (this._rfb_state !== 'normal' || this._view_only) { return false; }
Util.Info("Sending Ctrl-Alt-Del");
var arr = [];
arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 1));
arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 1));
arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 1));
arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 0));
arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 0));
arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 0));
this._sock.send(arr);
RFB.messages.keyEvent(this._sock, XK_Control_L, 1);
RFB.messages.keyEvent(this._sock, XK_Alt_L, 1);
RFB.messages.keyEvent(this._sock, XK_Delete, 1);
RFB.messages.keyEvent(this._sock, XK_Delete, 0);
RFB.messages.keyEvent(this._sock, XK_Alt_L, 0);
RFB.messages.keyEvent(this._sock, XK_Control_L, 0);
this._sock.flush();
},
xvpOp: function (ver, op) {
@ -292,21 +292,22 @@ var RFB;
// followed by an up key.
sendKey: function (code, down) {
if (this._rfb_state !== "normal" || this._view_only) { return false; }
var arr = [];
if (typeof down !== 'undefined') {
Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
arr = arr.concat(RFB.messages.keyEvent(code, down ? 1 : 0));
RFB.messages.keyEvent(this._sock, code, down ? 1 : 0);
} else {
Util.Info("Sending key code (down + up): " + code);
arr = arr.concat(RFB.messages.keyEvent(code, 1));
arr = arr.concat(RFB.messages.keyEvent(code, 0));
RFB.messages.keyEvent(this._sock, code, 1);
RFB.messages.keyEvent(this._sock, code, 0);
}
this._sock.send(arr);
this._sock.flush();
},
clipboardPasteFrom: function (text) {
if (this._rfb_state !== 'normal') { return; }
this._sock.send(RFB.messages.clientCutText(text));
RFB.messages.clientCutText(this._sock, text);
this._sock.flush();
},
setDesktopSize: function (width, height) {
@ -572,16 +573,10 @@ var RFB;
}
},
_checkEvents: function () {
if (this._rfb_state === 'normal' && !this._viewportDragging && this._mouse_arr.length > 0) {
this._sock.send(this._mouse_arr);
this._mouse_arr = [];
}
},
_handleKeyPress: function (keysym, down) {
if (this._view_only) { return; } // View only, skip keyboard, events
this._sock.send(RFB.messages.keyEvent(keysym, down));
RFB.messages.keyEvent(this._sock, keysym, down);
this._sock.flush();
},
_handleMouseButton: function (x, y, down, bmask) {
@ -605,10 +600,8 @@ var RFB;
if (this._view_only) { return; } // View only, skip mouse events
this._mouse_arr = this._mouse_arr.concat(
RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask));
this._sock.send(this._mouse_arr);
this._mouse_arr = [];
if (this._rfb_state !== "normal") { return; }
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
},
_handleMouseMove: function (x, y) {
@ -625,10 +618,8 @@ var RFB;
if (this._view_only) { return; } // View only, skip mouse events
this._mouse_arr = this._mouse_arr.concat(
RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask));
this._checkEvents();
if (this._rfb_state !== "normal") { return; }
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
},
// Message Handlers
@ -895,7 +886,7 @@ var RFB;
/* Screen size */
this._fb_width = this._sock.rQshift16();
this._fb_height = this._sock.rQshift16();
this._dest_buff = new Uint8Array(this._fb_width * this._fb_height * 4);
this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4);
/* PIXEL_FORMAT */
var bpp = this._sock.rQshift8();
@ -991,18 +982,13 @@ var RFB;
this._fb_depth = 1;
}
var response = RFB.messages.pixelFormat(this._fb_Bpp, this._fb_depth, this._true_color);
response = response.concat(
RFB.messages.clientEncodings(this._encodings, this._local_cursor, this._true_color));
response = response.concat(
RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(),
this._fb_width, this._fb_height));
RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color);
RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color);
RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
this._timing.fbu_rt_start = (new Date()).getTime();
this._timing.pixels = 0;
this._sock.send(response);
this._checkEvents();
this._sock.flush();
if (this._encrypt) {
this._updateState('normal', 'Connected (encrypted) to: ' + this._fb_name);
@ -1104,8 +1090,8 @@ var RFB;
case 0: // FramebufferUpdate
var ret = this._framebufferUpdate();
if (ret) {
this._sock.send(RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(),
this._fb_width, this._fb_height));
RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
this._sock.flush();
}
return ret;
@ -1279,64 +1265,111 @@ var RFB;
// Class Methods
RFB.messages = {
keyEvent: function (keysym, down) {
var arr = [4];
arr.push8(down);
arr.push16(0);
arr.push32(keysym);
return arr;
keyEvent: function (sock, keysym, down) {
var buff = sock._sQ;
var offset = sock._sQlen;
buff[offset] = 4; // msg-type
buff[offset + 1] = down;
buff[offset + 2] = 0;
buff[offset + 3] = 0;
buff[offset + 4] = (keysym >> 24);
buff[offset + 5] = (keysym >> 16);
buff[offset + 6] = (keysym >> 8);
buff[offset + 7] = keysym;
sock._sQlen += 8;
},
pointerEvent: function (x, y, mask) {
var arr = [5]; // msg-type
arr.push8(mask);
arr.push16(x);
arr.push16(y);
return arr;
pointerEvent: function (sock, x, y, mask) {
var buff = sock._sQ;
var offset = sock._sQlen;
buff[offset] = 5; // msg-type
buff[offset + 1] = mask;
buff[offset + 2] = x >> 8;
buff[offset + 3] = x;
buff[offset + 4] = y >> 8;
buff[offset + 5] = y;
sock._sQlen += 6;
},
// TODO(directxman12): make this unicode compatible?
clientCutText: function (text) {
var arr = [6]; // msg-type
arr.push8(0); // padding
arr.push8(0); // padding
arr.push8(0); // padding
arr.push32(text.length);
clientCutText: function (sock, text) {
var buff = sock._sQ;
var offset = sock._sQlen;
buff[offset] = 6; // msg-type
buff[offset + 1] = 0; // padding
buff[offset + 2] = 0; // padding
buff[offset + 3] = 0; // padding
var n = text.length;
buff[offset + 4] = n >> 24;
buff[offset + 5] = n >> 16;
buff[offset + 6] = n >> 8;
buff[offset + 7] = n;
for (var i = 0; i < n; i++) {
arr.push(text.charCodeAt(i));
buff[offset + 8 + i] = text.charCodeAt(i);
}
return arr;
sock._sQlen += 8 + n;
},
pixelFormat: function (bpp, depth, true_color) {
var arr = [0]; // msg-type
arr.push8(0); // padding
arr.push8(0); // padding
arr.push8(0); // padding
pixelFormat: function (sock, bpp, depth, true_color) {
var buff = sock._sQ;
var offset = sock._sQlen;
arr.push8(bpp * 8); // bits-per-pixel
arr.push8(depth * 8); // depth
arr.push8(0); // little-endian
arr.push8(true_color ? 1 : 0); // true-color
buff[offset] = 0; // msg-type
arr.push16(255); // red-max
arr.push16(255); // green-max
arr.push16(255); // blue-max
arr.push8(16); // red-shift
arr.push8(8); // green-shift
arr.push8(0); // blue-shift
buff[offset + 1] = 0; // padding
buff[offset + 2] = 0; // padding
buff[offset + 3] = 0; // padding
arr.push8(0); // padding
arr.push8(0); // padding
arr.push8(0); // padding
return arr;
buff[offset + 4] = bpp * 8; // bits-per-pixel
buff[offset + 5] = depth * 8; // depth
buff[offset + 6] = 0; // little-endian
buff[offset + 7] = true_color ? 1 : 0; // true-color
buff[offset + 8] = 0; // red-max
buff[offset + 9] = 255; // red-max
buff[offset + 10] = 0; // green-max
buff[offset + 11] = 255; // green-max
buff[offset + 12] = 0; // blue-max
buff[offset + 13] = 255; // blue-max
buff[offset + 14] = 16; // red-shift
buff[offset + 15] = 8; // green-shift
buff[offset + 16] = 0; // blue-shift
buff[offset + 17] = 0; // padding
buff[offset + 18] = 0; // padding
buff[offset + 19] = 0; // padding
sock._sQlen += 20;
},
clientEncodings: function (encodings, local_cursor, true_color) {
var i, encList = [];
clientEncodings: function (sock, encodings, local_cursor, true_color) {
var buff = sock._sQ;
var offset = sock._sQlen;
buff[offset] = 2; // msg-type
buff[offset + 1] = 0; // padding
// offset + 2 and offset + 3 are encoding count
var i, j = offset + 4, cnt = 0;
for (i = 0; i < encodings.length; i++) {
if (encodings[i][0] === "Cursor" && !local_cursor) {
Util.Debug("Skipping Cursor pseudo-encoding");
@ -1344,23 +1377,25 @@ var RFB;
// TODO: remove this when we have tight+non-true-color
Util.Warn("Skipping tight as it is only supported with true color");
} else {
encList.push(encodings[i][1]);
var enc = encodings[i][1];
buff[j] = enc >> 24;
buff[j + 1] = enc >> 16;
buff[j + 2] = enc >> 8;
buff[j + 3] = enc;
j += 4;
cnt++;
}
}
var arr = [2]; // msg-type
arr.push8(0); // padding
buff[offset + 2] = cnt >> 8;
buff[offset + 3] = cnt;
arr.push16(encList.length); // encoding count
for (i = 0; i < encList.length; i++) {
arr.push32(encList[i]);
}
return arr;
sock._sQlen += j - offset;
},
fbUpdateRequests: function (cleanDirty, fb_width, fb_height) {
var arr = [];
fbUpdateRequests: function (sock, cleanDirty, fb_width, fb_height) {
var offsetIncrement = 0;
var cb = cleanDirty.cleanBox;
var w, h;
@ -1368,7 +1403,7 @@ var RFB;
w = typeof cb.w === "undefined" ? fb_width : cb.w;
h = typeof cb.h === "undefined" ? fb_height : cb.h;
// Request incremental for clean box
arr = arr.concat(RFB.messages.fbUpdateRequest(1, cb.x, cb.y, w, h));
RFB.messages.fbUpdateRequest(sock, 1, cb.x, cb.y, w, h);
}
for (var i = 0; i < cleanDirty.dirtyBoxes.length; i++) {
@ -1376,24 +1411,33 @@ var RFB;
// Force all (non-incremental) for dirty box
w = typeof db.w === "undefined" ? fb_width : db.w;
h = typeof db.h === "undefined" ? fb_height : db.h;
arr = arr.concat(RFB.messages.fbUpdateRequest(0, db.x, db.y, w, h));
RFB.messages.fbUpdateRequest(sock, 0, db.x, db.y, w, h);
}
return arr;
},
fbUpdateRequest: function (incremental, x, y, w, h) {
fbUpdateRequest: function (sock, incremental, x, y, w, h) {
var buff = sock._sQ;
var offset = sock._sQlen;
if (typeof(x) === "undefined") { x = 0; }
if (typeof(y) === "undefined") { y = 0; }
var arr = [3]; // msg-type
arr.push8(incremental);
arr.push16(x);
arr.push16(y);
arr.push16(w);
arr.push16(h);
buff[offset] = 3; // msg-type
buff[offset + 1] = incremental;
return arr;
buff[offset + 2] = (x >> 8) & 0xFF;
buff[offset + 3] = x & 0xFF;
buff[offset + 4] = (y >> 8) & 0xFF;
buff[offset + 5] = y & 0xFF;
buff[offset + 6] = (w >> 8) & 0xFF;
buff[offset + 7] = w & 0xFF;
buff[offset + 8] = (h >> 8) & 0xFF;
buff[offset + 9] = h & 0xFF;
sock._sQlen += 10;
}
};

View File

@ -45,10 +45,14 @@ function Websock() {
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8;
this._sQ = []; // Send queue
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
this.maxBufferedAmount = 200;
@ -183,9 +187,9 @@ function Websock() {
}
if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
if (this._sQ.length > 0) {
if (this._sQlen > 0) {
this._websocket.send(this._encode_message());
this._sQ = [];
this._sQlen = 0;
}
return true;
@ -197,8 +201,9 @@ function Websock() {
},
send: function (arr) {
this._sQ = this._sQ.concat(arr);
return this.flush();
this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
return this.flush();
},
send_string: function (str) {
@ -218,12 +223,12 @@ function Websock() {
_allocate_buffers: function () {
this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize);
},
init: function (protocols, ws_schema) {
this._allocate_buffers();
this._rQi = 0;
this._sQ = [];
this._websocket = null;
// Check for full typed array support
@ -327,7 +332,8 @@ function Websock() {
// private methods
_encode_message: function () {
// Put in a binary arraybuffer
return (new Uint8Array(this._sQ)).buffer;
// according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
_decode_message: function (data) {

View File

@ -24,6 +24,12 @@ chai.use(function (_chai, utils) {
_chai.Assertion.addMethod('sent', function (target_data) {
var obj = this._obj;
obj.inspect = function () {
var res = { _websocket: obj._websocket, rQi: obj._rQi, _rQ: new Uint8Array(obj._rQ.buffer, 0, obj._rQlen),
_sQ: new Uint8Array(obj._sQ.buffer, 0, obj._sQlen) };
res.prototype = obj;
return res;
};
var data = obj._websocket._get_sent_data();
var same = true;
for (var i = 0; i < obj.length; i++) {
@ -38,8 +44,8 @@ chai.use(function (_chai, utils) {
this.assert(same,
"expected #{this} to have sent the data #{exp}, but it actually sent #{act}",
"expected #{this} not to have sent the data #{act}",
target_data,
data);
Array.prototype.slice.call(target_data),
Array.prototype.slice.call(data));
});
_chai.Assertion.addProperty('array', function () {

View File

@ -51,14 +51,9 @@ var FakeWebSocket;
},
_get_sent_data: function () {
var arr = [];
for (var i = 0; i < this.bufferedAmount; i++) {
arr[i] = this._send_queue[i];
}
var res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount);
this.bufferedAmount = 0;
return arr;
return res;
},
_open: function (data) {

View File

@ -23,10 +23,12 @@ describe('Remote Frame Buffer Protocol Client', function() {
// Use a single set of buffers instead of reallocating to
// speed up tests
var sock = new Websock();
var _sQ = new Uint8Array(sock._sQbufferSize);
var rQ = new Uint8Array(sock._rQbufferSize);
Websock.prototype._old_allocate_buffers = Websock.prototype._allocate_buffers;
Websock.prototype._allocate_buffers = function () {
this._sQ = _sQ;
this._rQ = rQ;
};
@ -124,34 +126,34 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'send');
sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
});
it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
var expected = [];
expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 1));
expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 1));
expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 1));
expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 0));
expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 0));
expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 0));
var expected = {_sQ: new Uint8Array(48), _sQlen: 0};
RFB.messages.keyEvent(expected, 0xFFE3, 1);
RFB.messages.keyEvent(expected, 0xFFE9, 1);
RFB.messages.keyEvent(expected, 0xFFFF, 1);
RFB.messages.keyEvent(expected, 0xFFFF, 0);
RFB.messages.keyEvent(expected, 0xFFE9, 0);
RFB.messages.keyEvent(expected, 0xFFE3, 0);
client.sendCtrlAltDel();
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(expected._sQ);
});
it('should not send the keys if we are not in a normal state', function () {
client._rfb_state = "broken";
client.sendCtrlAltDel();
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should not send the keys if we are set as view_only', function () {
client._view_only = true;
client.sendCtrlAltDel();
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
});
@ -160,34 +162,36 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'send');
sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
});
it('should send a single key with the given code and state (down = true)', function () {
var expected = RFB.messages.keyEvent(123, 1);
var expected = {_sQ: new Uint8Array(8), _sQlen: 0};
RFB.messages.keyEvent(expected, 123, 1);
client.sendKey(123, true);
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(expected._sQ);
});
it('should send both a down and up event if the state is not specified', function () {
var expected = RFB.messages.keyEvent(123, 1);
expected = expected.concat(RFB.messages.keyEvent(123, 0));
var expected = {_sQ: new Uint8Array(16), _sQlen: 0};
RFB.messages.keyEvent(expected, 123, 1);
RFB.messages.keyEvent(expected, 123, 0);
client.sendKey(123);
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(expected._sQ);
});
it('should not send the key if we are not in a normal state', function () {
client._rfb_state = "broken";
client.sendKey(123);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should not send the key if we are set as view_only', function () {
client._view_only = true;
client.sendKey(123);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
});
@ -196,21 +200,22 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'send');
sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
});
it('should send the given text in a paste event', function () {
var expected = RFB.messages.clientCutText('abc');
var expected = {_sQ: new Uint8Array(11), _sQlen: 0};
RFB.messages.clientCutText(expected, 'abc');
client.clipboardPasteFrom('abc');
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(expected._sQ);
});
it('should not send the text if we are not in a normal state', function () {
client._rfb_state = "broken";
client.clipboardPasteFrom('abc');
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
});
@ -219,7 +224,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'send');
sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
client._supportsSetDesktopSize = true;
@ -240,19 +245,19 @@ describe('Remote Frame Buffer Protocol Client', function() {
expected.push32(0); // flags
client.setDesktopSize(1, 2);
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(new Uint8Array(expected));
});
it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
client._supportsSetDesktopSize = false;
client.setDesktopSize(1,2);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should not send the request if we are not in a normal state', function () {
client._rfb_state = "broken";
client.setDesktopSize(1,2);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
});
@ -261,7 +266,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'send');
sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
client._rfb_xvp_ver = 1;
@ -269,27 +274,27 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should send the shutdown signal on #xvpShutdown', function () {
client.xvpShutdown();
expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x02]);
expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
});
it('should send the reboot signal on #xvpReboot', function () {
client.xvpReboot();
expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x03]);
expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
});
it('should send the reset signal on #xvpReset', function () {
client.xvpReset();
expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x04]);
expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
});
it('should support sending arbitrary XVP operations via #xvpOp', function () {
client.xvpOp(1, 7);
expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x07]);
expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
});
it('should not send XVP operations with higher versions than we support', function () {
expect(client.xvpOp(2, 7)).to.be.false;
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
});
});
@ -502,7 +507,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
expect(client._rfb_version).to.equal(0);
var sent_data = client._sock._websocket._get_sent_data();
expect(sent_data.slice(0, 5)).to.deep.equal([1, 2, 3, 4, 5]);
expect(new Uint8Array(sent_data.buffer, 0, 5)).to.array.equal(new Uint8Array([1, 2, 3, 4, 5]));
});
it('should interpret version 003.003 as version 3.3', function () {
@ -559,7 +564,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
send_ver('000.000', client);
expect(client._rfb_version).to.equal(0);
var sent_data = client._sock._websocket._get_sent_data();
expect(sent_data.slice(0, 5)).to.deep.equal([1, 2, 3, 4, 5]);
expect(new Uint8Array(sent_data.buffer, 0, 5)).to.array.equal(new Uint8Array([1, 2, 3, 4, 5]));
expect(sent_data).to.have.length(250);
send_ver('003.008', client);
@ -582,7 +587,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
expected[i] = expected_str.charCodeAt(i);
}
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(new Uint8Array(expected));
});
it('should transition to the Security state on successful negotiation', function () {
@ -615,7 +620,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
var auth_schemes = [2, 1, 2];
client._sock._websocket._receive_data(auth_schemes);
expect(client._rfb_auth_scheme).to.equal(2);
expect(client._sock).to.have.sent([2]);
expect(client._sock).to.have.sent(new Uint8Array([2]));
});
it('should fail if there are no supported schemes for versions >= 3.7', function () {
@ -721,7 +726,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock._websocket._receive_data(new Uint8Array(challenge));
var des_pass = RFB.genDES('passwd', challenge);
expect(client._sock).to.have.sent(des_pass);
expect(client._sock).to.have.sent(new Uint8Array(des_pass));
});
it('should transition to SecurityResult immediately after sending the password', function () {
@ -778,7 +783,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
var expected = [22, 4, 6]; // auth selection, len user, len target
for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }
expect(client._sock).to.have.sent(expected);
expect(client._sock).to.have.sent(new Uint8Array(expected));
});
});
@ -826,14 +831,14 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should choose the notunnel tunnel type', function () {
send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client);
expect(client._sock).to.have.sent([0, 0, 0, 0]);
expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
});
it('should continue to sub-auth negotiation after tunnel negotiation', function () {
send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client);
client._sock._websocket._get_sent_data(); // skip the tunnel choice here
send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
expect(client._sock).to.have.sent([0, 0, 0, 1]);
expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
expect(client._rfb_state).to.equal('SecurityResult');
});
@ -849,7 +854,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should accept the "no auth" auth type and transition to SecurityResult', function () {
client._rfb_tightvnc = true;
send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
expect(client._sock).to.have.sent([0, 0, 0, 1]);
expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
expect(client._rfb_state).to.equal('SecurityResult');
});
@ -857,7 +862,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._rfb_tightvnc = true;
client._negotiate_std_vnc_auth = sinon.spy();
send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client);
expect(client._sock).to.have.sent([0, 0, 0, 2]);
expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2]));
expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
expect(client._rfb_auth_scheme).to.equal(2);
});
@ -921,13 +926,13 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should send 1 if we are in shared mode', function () {
client.set_shared(true);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
expect(client._sock).to.have.sent([1]);
expect(client._sock).to.have.sent(new Uint8Array([1]));
});
it('should send 0 if we are not in shared mode', function () {
client.set_shared(false);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
expect(client._sock).to.have.sent([0]);
expect(client._sock).to.have.sent(new Uint8Array([0]));
});
});
@ -1064,21 +1069,16 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should reply with the pixel format, client encodings, and initial update request', function () {
client.set_true_color(true);
client.set_local_cursor(false);
var expected = RFB.messages.pixelFormat(4, 3, true);
expected = expected.concat(RFB.messages.clientEncodings(client._encodings, false, true));
// we skip the cursor encoding
var expected = {_sQ: new Uint8Array(34 + 4 * (client._encodings.length - 1)), _sQlen: 0};
RFB.messages.pixelFormat(expected, 4, 3, true);
RFB.messages.clientEncodings(expected, client._encodings, false, true);
var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 27, h: 32 } ] };
expected = expected.concat(RFB.messages.fbUpdateRequests(expected_cdr, 27, 32));
RFB.messages.fbUpdateRequests(expected, expected_cdr, 27, 32);
send_server_init({ width: 27, height: 32 }, client);
expect(client._sock).to.have.sent(expected);
});
it('should check for sending mouse events', function () {
// be lazy with our checking so we don't have to check through the whole sent buffer
sinon.spy(client, '_checkEvents');
send_server_init({}, client);
expect(client._checkEvents).to.have.been.calledOnce;
expect(client._sock).to.have.sent(expected._sQ);
});
it('should transition to the "normal" state', function () {
@ -1161,14 +1161,15 @@ describe('Remote Frame Buffer Protocol Client', function() {
}
it('should send an update request if there is sufficient data', function () {
var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0};
var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] };
var expected_msg = RFB.messages.fbUpdateRequests(expected_cdr, 240, 20);
RFB.messages.fbUpdateRequests(expected_msg, expected_cdr, 240, 20);
client._framebufferUpdate = function () { return true; };
client._sock._websocket._receive_data(new Uint8Array([0]));
expect(client._sock).to.have.sent(expected_msg);
expect(client._sock).to.have.sent(expected_msg._sQ);
});
it('should not send an update request if we need more data', function () {
@ -1177,9 +1178,10 @@ describe('Remote Frame Buffer Protocol Client', function() {
});
it('should resume receiving an update if we previously did not have enough data', function () {
var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0};
var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] };
var expected_msg = RFB.messages.fbUpdateRequests(expected_cdr, 240, 20);
RFB.messages.fbUpdateRequests(expected_msg, expected_cdr, 240, 20);
// just enough to set FBU.rects
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3]));
@ -1188,7 +1190,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._framebufferUpdate = function () { return true; }; // we magically have enough data
// 247 should *not* be used as the message type here
client._sock._websocket._receive_data(new Uint8Array([247]));
expect(client._sock).to.have.sent(expected_msg);
expect(client._sock).to.have.sent(expected_msg._sQ);
});
it('should parse out information from a header before any actual data comes in', function () {
@ -1710,58 +1712,61 @@ describe('Remote Frame Buffer Protocol Client', function() {
var client;
beforeEach(function () {
client = make_rfb();
client._sock.send = sinon.spy();
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'flush');
client._rfb_state = 'normal';
});
it('should not send button messages in view-only mode', function () {
client._view_only = true;
client._mouse._onMouseButton(0, 0, 1, 0x001);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should not send movement messages in view-only mode', function () {
client._view_only = true;
client._mouse._onMouseMove(0, 0);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should send a pointer event on mouse button presses', function () {
client._mouse._onMouseButton(10, 12, 1, 0x001);
expect(client._sock.send).to.have.been.calledOnce;
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001);
expect(client._sock.send).to.have.been.calledWith(pointer_msg);
var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should send a mask of 1 on mousedown', function () {
client._mouse._onMouseButton(10, 12, 1, 0x001);
expect(client._sock.send).to.have.been.calledOnce;
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001);
expect(client._sock.send).to.have.been.calledWith(pointer_msg);
var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
RFB.messages.pointerEvent(pointer_msg, 0, 10, 12, 0x001);
expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should send a mask of 0 on mouseup', function () {
client._mouse_buttonMask = 0x001;
client._mouse._onMouseButton(10, 12, 0, 0x001);
expect(client._sock.send).to.have.been.calledOnce;
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x000);
expect(client._sock.send).to.have.been.calledWith(pointer_msg);
var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should send a pointer event on mouse movement', function () {
client._mouse._onMouseMove(10, 12);
expect(client._sock.send).to.have.been.calledOnce;
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0);
expect(client._sock.send).to.have.been.calledWith(pointer_msg);
var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should set the button mask so that future mouse movements use it', function () {
client._mouse._onMouseButton(10, 12, 1, 0x010);
client._sock.send = sinon.spy();
client._mouse._onMouseMove(13, 9);
expect(client._sock.send).to.have.been.calledOnce;
var pointer_msg = RFB.messages.pointerEvent(13, 9, 0x010);
expect(client._sock.send).to.have.been.calledWith(pointer_msg);
var pointer_msg = {_sQ: new Uint8Array(12), _sQlen: 0};
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x010);
RFB.messages.pointerEvent(pointer_msg, 13, 9, 0x010);
expect(client._sock).to.have.sent(pointer_msg._sQ);
});
// NB(directxman12): we don't need to test not sending messages in
@ -1772,13 +1777,13 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._viewportDragging = true;
client._display.viewportChangePos = sinon.spy();
client._mouse._onMouseMove(13, 9);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should not send button messages when initiating viewport dragging', function () {
client._viewportDrag = true;
client._mouse._onMouseButton(13, 9, 0x001);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
it('should be initiate viewport dragging on a button down event, if enabled', function () {
@ -1814,20 +1819,23 @@ describe('Remote Frame Buffer Protocol Client', function() {
var client;
beforeEach(function () {
client = make_rfb();
client._sock.send = sinon.spy();
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
sinon.spy(client._sock, 'flush');
});
it('should send a key message on a key press', function () {
client._keyboard._onKeyPress(1234, 1);
expect(client._sock.send).to.have.been.calledOnce;
var key_msg = RFB.messages.keyEvent(1234, 1);
expect(client._sock.send).to.have.been.calledWith(key_msg);
var key_msg = {_sQ: new Uint8Array(8), _sQlen: 0};
RFB.messages.keyEvent(key_msg, 1234, 1);
expect(client._sock).to.have.sent(key_msg._sQ);
});
it('should not send messages in view-only mode', function () {
client._view_only = true;
client._keyboard._onKeyPress(1234, 1);
expect(client._sock.send).to.not.have.been.called;
expect(client._sock.flush).to.not.have.been.called;
});
});

View File

@ -173,7 +173,8 @@ describe('Websock', function() {
it('should actually send on the websocket if the websocket does not have too much buffered', function () {
sock.maxBufferedAmount = 10;
sock._websocket.bufferedAmount = 8;
sock._sQ = [1, 2, 3];
sock._sQ = new Uint8Array([1, 2, 3]);
sock._sQlen = 3;
var encoded = sock._encode_message();
sock.flush();
@ -189,7 +190,7 @@ describe('Websock', function() {
});
it('should not call send if we do not have anything queued up', function () {
sock._sQ = [];
sock._sQlen = 0;
sock.maxBufferedAmount = 10;
sock._websocket.bufferedAmount = 8;
@ -215,7 +216,7 @@ describe('Websock', function() {
it('should add to the send queue', function () {
sock.send([1, 2, 3]);
var sq = sock.get_sQ();
expect(sock.get_sQ().slice(sq.length - 3)).to.deep.equal([1, 2, 3]);
expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
});
it('should call flush', function () {
@ -425,15 +426,16 @@ describe('Websock', function() {
sock._websocket._open();
});
it('should convert the send queue into an ArrayBuffer', function () {
sock._sQ = [1, 2, 3];
var res = sock._encode_message(); // An ArrayBuffer
expect(new Uint8Array(res)).to.deep.equal(new Uint8Array(res));
it('should only send the send queue up to the send queue length', function () {
sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
sock._sQlen = 3;
var res = sock._encode_message();
expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
});
it('should properly pass the encoded data off to the actual WebSocket', function () {
sock.send([1, 2, 3]);
expect(sock._websocket._get_sent_data()).to.deep.equal([1, 2, 3]);
expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
});
});
});