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; } if (this._rfb_state !== 'normal' || this._view_only) { return false; }
Util.Info("Sending Ctrl-Alt-Del"); Util.Info("Sending Ctrl-Alt-Del");
var arr = []; RFB.messages.keyEvent(this._sock, XK_Control_L, 1);
arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 1)); RFB.messages.keyEvent(this._sock, XK_Alt_L, 1);
arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 1)); RFB.messages.keyEvent(this._sock, XK_Delete, 1);
arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 1)); RFB.messages.keyEvent(this._sock, XK_Delete, 0);
arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 0)); RFB.messages.keyEvent(this._sock, XK_Alt_L, 0);
arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 0)); RFB.messages.keyEvent(this._sock, XK_Control_L, 0);
arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 0));
this._sock.send(arr); this._sock.flush();
}, },
xvpOp: function (ver, op) { xvpOp: function (ver, op) {
@ -292,21 +292,22 @@ var RFB;
// followed by an up key. // followed by an up key.
sendKey: function (code, down) { sendKey: function (code, down) {
if (this._rfb_state !== "normal" || this._view_only) { return false; } if (this._rfb_state !== "normal" || this._view_only) { return false; }
var arr = [];
if (typeof down !== 'undefined') { if (typeof down !== 'undefined') {
Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code); 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 { } else {
Util.Info("Sending key code (down + up): " + code); Util.Info("Sending key code (down + up): " + code);
arr = arr.concat(RFB.messages.keyEvent(code, 1)); RFB.messages.keyEvent(this._sock, code, 1);
arr = arr.concat(RFB.messages.keyEvent(code, 0)); RFB.messages.keyEvent(this._sock, code, 0);
} }
this._sock.send(arr);
this._sock.flush();
}, },
clipboardPasteFrom: function (text) { clipboardPasteFrom: function (text) {
if (this._rfb_state !== 'normal') { return; } 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) { 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) { _handleKeyPress: function (keysym, down) {
if (this._view_only) { return; } // View only, skip keyboard, events 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) { _handleMouseButton: function (x, y, down, bmask) {
@ -605,10 +600,8 @@ var RFB;
if (this._view_only) { return; } // View only, skip mouse events if (this._view_only) { return; } // View only, skip mouse events
this._mouse_arr = this._mouse_arr.concat( if (this._rfb_state !== "normal") { return; }
RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask)); RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
this._sock.send(this._mouse_arr);
this._mouse_arr = [];
}, },
_handleMouseMove: function (x, y) { _handleMouseMove: function (x, y) {
@ -625,10 +618,8 @@ var RFB;
if (this._view_only) { return; } // View only, skip mouse events if (this._view_only) { return; } // View only, skip mouse events
this._mouse_arr = this._mouse_arr.concat( if (this._rfb_state !== "normal") { return; }
RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask)); RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
this._checkEvents();
}, },
// Message Handlers // Message Handlers
@ -895,7 +886,7 @@ var RFB;
/* Screen size */ /* Screen size */
this._fb_width = this._sock.rQshift16(); this._fb_width = this._sock.rQshift16();
this._fb_height = 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 */ /* PIXEL_FORMAT */
var bpp = this._sock.rQshift8(); var bpp = this._sock.rQshift8();
@ -991,18 +982,13 @@ var RFB;
this._fb_depth = 1; this._fb_depth = 1;
} }
var response = RFB.messages.pixelFormat(this._fb_Bpp, this._fb_depth, this._true_color); RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color);
response = response.concat( RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color);
RFB.messages.clientEncodings(this._encodings, this._local_cursor, this._true_color)); RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
response = response.concat(
RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(),
this._fb_width, this._fb_height));
this._timing.fbu_rt_start = (new Date()).getTime(); this._timing.fbu_rt_start = (new Date()).getTime();
this._timing.pixels = 0; this._timing.pixels = 0;
this._sock.send(response); this._sock.flush();
this._checkEvents();
if (this._encrypt) { if (this._encrypt) {
this._updateState('normal', 'Connected (encrypted) to: ' + this._fb_name); this._updateState('normal', 'Connected (encrypted) to: ' + this._fb_name);
@ -1104,8 +1090,8 @@ var RFB;
case 0: // FramebufferUpdate case 0: // FramebufferUpdate
var ret = this._framebufferUpdate(); var ret = this._framebufferUpdate();
if (ret) { if (ret) {
this._sock.send(RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(), RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
this._fb_width, this._fb_height)); this._sock.flush();
} }
return ret; return ret;
@ -1279,64 +1265,111 @@ var RFB;
// Class Methods // Class Methods
RFB.messages = { RFB.messages = {
keyEvent: function (keysym, down) { keyEvent: function (sock, keysym, down) {
var arr = [4]; var buff = sock._sQ;
arr.push8(down); var offset = sock._sQlen;
arr.push16(0);
arr.push32(keysym); buff[offset] = 4; // msg-type
return arr; 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) { pointerEvent: function (sock, x, y, mask) {
var arr = [5]; // msg-type var buff = sock._sQ;
arr.push8(mask); var offset = sock._sQlen;
arr.push16(x);
arr.push16(y); buff[offset] = 5; // msg-type
return arr;
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? // TODO(directxman12): make this unicode compatible?
clientCutText: function (text) { clientCutText: function (sock, text) {
var arr = [6]; // msg-type var buff = sock._sQ;
arr.push8(0); // padding var offset = sock._sQlen;
arr.push8(0); // padding
arr.push8(0); // padding buff[offset] = 6; // msg-type
arr.push32(text.length);
buff[offset + 1] = 0; // padding
buff[offset + 2] = 0; // padding
buff[offset + 3] = 0; // padding
var n = text.length; 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++) { 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) { pixelFormat: function (sock, bpp, depth, true_color) {
var arr = [0]; // msg-type var buff = sock._sQ;
arr.push8(0); // padding var offset = sock._sQlen;
arr.push8(0); // padding
arr.push8(0); // padding
arr.push8(bpp * 8); // bits-per-pixel buff[offset] = 0; // msg-type
arr.push8(depth * 8); // depth
arr.push8(0); // little-endian
arr.push8(true_color ? 1 : 0); // true-color
arr.push16(255); // red-max buff[offset + 1] = 0; // padding
arr.push16(255); // green-max buff[offset + 2] = 0; // padding
arr.push16(255); // blue-max buff[offset + 3] = 0; // padding
arr.push8(16); // red-shift
arr.push8(8); // green-shift
arr.push8(0); // blue-shift
arr.push8(0); // padding buff[offset + 4] = bpp * 8; // bits-per-pixel
arr.push8(0); // padding buff[offset + 5] = depth * 8; // depth
arr.push8(0); // padding buff[offset + 6] = 0; // little-endian
return arr; 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) { clientEncodings: function (sock, encodings, local_cursor, true_color) {
var i, encList = []; 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++) { for (i = 0; i < encodings.length; i++) {
if (encodings[i][0] === "Cursor" && !local_cursor) { if (encodings[i][0] === "Cursor" && !local_cursor) {
Util.Debug("Skipping Cursor pseudo-encoding"); Util.Debug("Skipping Cursor pseudo-encoding");
@ -1344,23 +1377,25 @@ var RFB;
// TODO: remove this when we have tight+non-true-color // TODO: remove this when we have tight+non-true-color
Util.Warn("Skipping tight as it is only supported with true color"); Util.Warn("Skipping tight as it is only supported with true color");
} else { } 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 buff[offset + 2] = cnt >> 8;
arr.push8(0); // padding buff[offset + 3] = cnt;
arr.push16(encList.length); // encoding count sock._sQlen += j - offset;
for (i = 0; i < encList.length; i++) {
arr.push32(encList[i]);
}
return arr;
}, },
fbUpdateRequests: function (cleanDirty, fb_width, fb_height) { fbUpdateRequests: function (sock, cleanDirty, fb_width, fb_height) {
var arr = []; var offsetIncrement = 0;
var cb = cleanDirty.cleanBox; var cb = cleanDirty.cleanBox;
var w, h; var w, h;
@ -1368,7 +1403,7 @@ var RFB;
w = typeof cb.w === "undefined" ? fb_width : cb.w; w = typeof cb.w === "undefined" ? fb_width : cb.w;
h = typeof cb.h === "undefined" ? fb_height : cb.h; h = typeof cb.h === "undefined" ? fb_height : cb.h;
// Request incremental for clean box // 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++) { for (var i = 0; i < cleanDirty.dirtyBoxes.length; i++) {
@ -1376,24 +1411,33 @@ var RFB;
// Force all (non-incremental) for dirty box // Force all (non-incremental) for dirty box
w = typeof db.w === "undefined" ? fb_width : db.w; w = typeof db.w === "undefined" ? fb_width : db.w;
h = typeof db.h === "undefined" ? fb_height : db.h; 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(x) === "undefined") { x = 0; }
if (typeof(y) === "undefined") { y = 0; } if (typeof(y) === "undefined") { y = 0; }
var arr = [3]; // msg-type buff[offset] = 3; // msg-type
arr.push8(incremental); buff[offset + 1] = incremental;
arr.push16(x);
arr.push16(y);
arr.push16(w);
arr.push16(h);
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._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB) this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8; this._rQmax = this._rQbufferSize / 8;
this._sQ = []; // Send queue
// called in init: this._rQ = new Uint8Array(this._rQbufferSize); // called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue 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._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
this.maxBufferedAmount = 200; this.maxBufferedAmount = 200;
@ -183,9 +187,9 @@ function Websock() {
} }
if (this._websocket.bufferedAmount < this.maxBufferedAmount) { if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
if (this._sQ.length > 0) { if (this._sQlen > 0) {
this._websocket.send(this._encode_message()); this._websocket.send(this._encode_message());
this._sQ = []; this._sQlen = 0;
} }
return true; return true;
@ -197,7 +201,8 @@ function Websock() {
}, },
send: function (arr) { send: function (arr) {
this._sQ = this._sQ.concat(arr); this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
return this.flush(); return this.flush();
}, },
@ -218,12 +223,12 @@ function Websock() {
_allocate_buffers: function () { _allocate_buffers: function () {
this._rQ = new Uint8Array(this._rQbufferSize); this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize);
}, },
init: function (protocols, ws_schema) { init: function (protocols, ws_schema) {
this._allocate_buffers(); this._allocate_buffers();
this._rQi = 0; this._rQi = 0;
this._sQ = [];
this._websocket = null; this._websocket = null;
// Check for full typed array support // Check for full typed array support
@ -327,7 +332,8 @@ function Websock() {
// private methods // private methods
_encode_message: function () { _encode_message: function () {
// Put in a binary arraybuffer // 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) { _decode_message: function (data) {

View File

@ -24,6 +24,12 @@ chai.use(function (_chai, utils) {
_chai.Assertion.addMethod('sent', function (target_data) { _chai.Assertion.addMethod('sent', function (target_data) {
var obj = this._obj; 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 data = obj._websocket._get_sent_data();
var same = true; var same = true;
for (var i = 0; i < obj.length; i++) { for (var i = 0; i < obj.length; i++) {
@ -38,8 +44,8 @@ chai.use(function (_chai, utils) {
this.assert(same, this.assert(same,
"expected #{this} to have sent the data #{exp}, but it actually sent #{act}", "expected #{this} to have sent the data #{exp}, but it actually sent #{act}",
"expected #{this} not to have sent the data #{act}", "expected #{this} not to have sent the data #{act}",
target_data, Array.prototype.slice.call(target_data),
data); Array.prototype.slice.call(data));
}); });
_chai.Assertion.addProperty('array', function () { _chai.Assertion.addProperty('array', function () {

View File

@ -51,14 +51,9 @@ var FakeWebSocket;
}, },
_get_sent_data: function () { _get_sent_data: function () {
var arr = []; var res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount);
for (var i = 0; i < this.bufferedAmount; i++) {
arr[i] = this._send_queue[i];
}
this.bufferedAmount = 0; this.bufferedAmount = 0;
return res;
return arr;
}, },
_open: function (data) { _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 // Use a single set of buffers instead of reallocating to
// speed up tests // speed up tests
var sock = new Websock(); var sock = new Websock();
var _sQ = new Uint8Array(sock._sQbufferSize);
var rQ = new Uint8Array(sock._rQbufferSize); var rQ = new Uint8Array(sock._rQbufferSize);
Websock.prototype._old_allocate_buffers = Websock.prototype._allocate_buffers; Websock.prototype._old_allocate_buffers = Websock.prototype._allocate_buffers;
Websock.prototype._allocate_buffers = function () { Websock.prototype._allocate_buffers = function () {
this._sQ = _sQ;
this._rQ = rQ; this._rQ = rQ;
}; };
@ -124,34 +126,34 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock(); client._sock = new Websock();
client._sock.open('ws://', 'binary'); client._sock.open('ws://', 'binary');
client._sock._websocket._open(); client._sock._websocket._open();
sinon.spy(client._sock, 'send'); sinon.spy(client._sock, 'flush');
client._rfb_state = "normal"; client._rfb_state = "normal";
client._view_only = false; client._view_only = false;
}); });
it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () { it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
var expected = []; var expected = {_sQ: new Uint8Array(48), _sQlen: 0};
expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 1)); RFB.messages.keyEvent(expected, 0xFFE3, 1);
expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 1)); RFB.messages.keyEvent(expected, 0xFFE9, 1);
expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 1)); RFB.messages.keyEvent(expected, 0xFFFF, 1);
expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 0)); RFB.messages.keyEvent(expected, 0xFFFF, 0);
expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 0)); RFB.messages.keyEvent(expected, 0xFFE9, 0);
expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 0)); RFB.messages.keyEvent(expected, 0xFFE3, 0);
client.sendCtrlAltDel(); 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 () { it('should not send the keys if we are not in a normal state', function () {
client._rfb_state = "broken"; client._rfb_state = "broken";
client.sendCtrlAltDel(); 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 () { it('should not send the keys if we are set as view_only', function () {
client._view_only = true; client._view_only = true;
client.sendCtrlAltDel(); 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 = new Websock();
client._sock.open('ws://', 'binary'); client._sock.open('ws://', 'binary');
client._sock._websocket._open(); client._sock._websocket._open();
sinon.spy(client._sock, 'send'); sinon.spy(client._sock, 'flush');
client._rfb_state = "normal"; client._rfb_state = "normal";
client._view_only = false; client._view_only = false;
}); });
it('should send a single key with the given code and state (down = true)', function () { 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); 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 () { it('should send both a down and up event if the state is not specified', function () {
var expected = RFB.messages.keyEvent(123, 1); var expected = {_sQ: new Uint8Array(16), _sQlen: 0};
expected = expected.concat(RFB.messages.keyEvent(123, 0)); RFB.messages.keyEvent(expected, 123, 1);
RFB.messages.keyEvent(expected, 123, 0);
client.sendKey(123); 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 () { it('should not send the key if we are not in a normal state', function () {
client._rfb_state = "broken"; client._rfb_state = "broken";
client.sendKey(123); 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 () { it('should not send the key if we are set as view_only', function () {
client._view_only = true; client._view_only = true;
client.sendKey(123); 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 = new Websock();
client._sock.open('ws://', 'binary'); client._sock.open('ws://', 'binary');
client._sock._websocket._open(); client._sock._websocket._open();
sinon.spy(client._sock, 'send'); sinon.spy(client._sock, 'flush');
client._rfb_state = "normal"; client._rfb_state = "normal";
client._view_only = false; client._view_only = false;
}); });
it('should send the given text in a paste event', function () { 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'); 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 () { it('should not send the text if we are not in a normal state', function () {
client._rfb_state = "broken"; client._rfb_state = "broken";
client.clipboardPasteFrom('abc'); 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 = new Websock();
client._sock.open('ws://', 'binary'); client._sock.open('ws://', 'binary');
client._sock._websocket._open(); client._sock._websocket._open();
sinon.spy(client._sock, 'send'); sinon.spy(client._sock, 'flush');
client._rfb_state = "normal"; client._rfb_state = "normal";
client._view_only = false; client._view_only = false;
client._supportsSetDesktopSize = true; client._supportsSetDesktopSize = true;
@ -240,19 +245,19 @@ describe('Remote Frame Buffer Protocol Client', function() {
expected.push32(0); // flags expected.push32(0); // flags
client.setDesktopSize(1, 2); 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 () { it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
client._supportsSetDesktopSize = false; client._supportsSetDesktopSize = false;
client.setDesktopSize(1,2); 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 () { it('should not send the request if we are not in a normal state', function () {
client._rfb_state = "broken"; client._rfb_state = "broken";
client.setDesktopSize(1,2); 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 = new Websock();
client._sock.open('ws://', 'binary'); client._sock.open('ws://', 'binary');
client._sock._websocket._open(); client._sock._websocket._open();
sinon.spy(client._sock, 'send'); sinon.spy(client._sock, 'flush');
client._rfb_state = "normal"; client._rfb_state = "normal";
client._view_only = false; client._view_only = false;
client._rfb_xvp_ver = 1; 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 () { it('should send the shutdown signal on #xvpShutdown', function () {
client.xvpShutdown(); 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 () { it('should send the reboot signal on #xvpReboot', function () {
client.xvpReboot(); 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 () { it('should send the reset signal on #xvpReset', function () {
client.xvpReset(); 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 () { it('should support sending arbitrary XVP operations via #xvpOp', function () {
client.xvpOp(1, 7); 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 () { it('should not send XVP operations with higher versions than we support', function () {
expect(client.xvpOp(2, 7)).to.be.false; 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); expect(client._rfb_version).to.equal(0);
var sent_data = client._sock._websocket._get_sent_data(); 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 () { 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); send_ver('000.000', client);
expect(client._rfb_version).to.equal(0); expect(client._rfb_version).to.equal(0);
var sent_data = client._sock._websocket._get_sent_data(); 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); expect(sent_data).to.have.length(250);
send_ver('003.008', client); send_ver('003.008', client);
@ -582,7 +587,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
expected[i] = expected_str.charCodeAt(i); 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 () { 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]; var auth_schemes = [2, 1, 2];
client._sock._websocket._receive_data(auth_schemes); client._sock._websocket._receive_data(auth_schemes);
expect(client._rfb_auth_scheme).to.equal(2); 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 () { 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)); client._sock._websocket._receive_data(new Uint8Array(challenge));
var des_pass = RFB.genDES('passwd', 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 () { 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 var expected = [22, 4, 6]; // auth selection, len user, len target
for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); } 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 () { it('should choose the notunnel tunnel type', function () {
send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client); 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 () { it('should continue to sub-auth negotiation after tunnel negotiation', function () {
send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client); send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client);
client._sock._websocket._get_sent_data(); // skip the tunnel choice here client._sock._websocket._get_sent_data(); // skip the tunnel choice here
send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client); 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'); 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 () { it('should accept the "no auth" auth type and transition to SecurityResult', function () {
client._rfb_tightvnc = true; client._rfb_tightvnc = true;
send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client); 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'); expect(client._rfb_state).to.equal('SecurityResult');
}); });
@ -857,7 +862,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._rfb_tightvnc = true; client._rfb_tightvnc = true;
client._negotiate_std_vnc_auth = sinon.spy(); client._negotiate_std_vnc_auth = sinon.spy();
send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client); 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._negotiate_std_vnc_auth).to.have.been.calledOnce;
expect(client._rfb_auth_scheme).to.equal(2); 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 () { it('should send 1 if we are in shared mode', function () {
client.set_shared(true); client.set_shared(true);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0])); 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 () { it('should send 0 if we are not in shared mode', function () {
client.set_shared(false); client.set_shared(false);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0])); 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 () { it('should reply with the pixel format, client encodings, and initial update request', function () {
client.set_true_color(true); client.set_true_color(true);
client.set_local_cursor(false); client.set_local_cursor(false);
var expected = RFB.messages.pixelFormat(4, 3, true); // we skip the cursor encoding
expected = expected.concat(RFB.messages.clientEncodings(client._encodings, false, true)); 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 }, var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 27, h: 32 } ] }; 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); send_server_init({ width: 27, height: 32 }, client);
expect(client._sock).to.have.sent(expected); expect(client._sock).to.have.sent(expected._sQ);
});
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;
}); });
it('should transition to the "normal" state', function () { 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 () { 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 }, var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] }; 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._framebufferUpdate = function () { return true; };
client._sock._websocket._receive_data(new Uint8Array([0])); 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 () { 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 () { 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 }, var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] }; 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 // just enough to set FBU.rects
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3])); 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 client._framebufferUpdate = function () { return true; }; // we magically have enough data
// 247 should *not* be used as the message type here // 247 should *not* be used as the message type here
client._sock._websocket._receive_data(new Uint8Array([247])); 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 () { 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; var client;
beforeEach(function () { beforeEach(function () {
client = make_rfb(); 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'; client._rfb_state = 'normal';
}); });
it('should not send button messages in view-only mode', function () { it('should not send button messages in view-only mode', function () {
client._view_only = true; client._view_only = true;
client._mouse._onMouseButton(0, 0, 1, 0x001); 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 () { it('should not send movement messages in view-only mode', function () {
client._view_only = true; client._view_only = true;
client._mouse._onMouseMove(0, 0); 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 () { it('should send a pointer event on mouse button presses', function () {
client._mouse._onMouseButton(10, 12, 1, 0x001); client._mouse._onMouseButton(10, 12, 1, 0x001);
expect(client._sock.send).to.have.been.calledOnce; var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001); RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
expect(client._sock.send).to.have.been.calledWith(pointer_msg); expect(client._sock).to.have.sent(pointer_msg._sQ);
}); });
it('should send a mask of 1 on mousedown', function () { it('should send a mask of 1 on mousedown', function () {
client._mouse._onMouseButton(10, 12, 1, 0x001); client._mouse._onMouseButton(10, 12, 1, 0x001);
expect(client._sock.send).to.have.been.calledOnce; var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001); RFB.messages.pointerEvent(pointer_msg, 0, 10, 12, 0x001);
expect(client._sock.send).to.have.been.calledWith(pointer_msg); expect(client._sock).to.have.sent(pointer_msg._sQ);
}); });
it('should send a mask of 0 on mouseup', function () { it('should send a mask of 0 on mouseup', function () {
client._mouse_buttonMask = 0x001; client._mouse_buttonMask = 0x001;
client._mouse._onMouseButton(10, 12, 0, 0x001); client._mouse._onMouseButton(10, 12, 0, 0x001);
expect(client._sock.send).to.have.been.calledOnce; var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x000); RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
expect(client._sock.send).to.have.been.calledWith(pointer_msg); expect(client._sock).to.have.sent(pointer_msg._sQ);
}); });
it('should send a pointer event on mouse movement', function () { it('should send a pointer event on mouse movement', function () {
client._mouse._onMouseMove(10, 12); client._mouse._onMouseMove(10, 12);
expect(client._sock.send).to.have.been.calledOnce; var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
var pointer_msg = RFB.messages.pointerEvent(10, 12, 0); RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
expect(client._sock.send).to.have.been.calledWith(pointer_msg); expect(client._sock).to.have.sent(pointer_msg._sQ);
}); });
it('should set the button mask so that future mouse movements use it', function () { it('should set the button mask so that future mouse movements use it', function () {
client._mouse._onMouseButton(10, 12, 1, 0x010); client._mouse._onMouseButton(10, 12, 1, 0x010);
client._sock.send = sinon.spy();
client._mouse._onMouseMove(13, 9); client._mouse._onMouseMove(13, 9);
expect(client._sock.send).to.have.been.calledOnce; var pointer_msg = {_sQ: new Uint8Array(12), _sQlen: 0};
var pointer_msg = RFB.messages.pointerEvent(13, 9, 0x010); RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x010);
expect(client._sock.send).to.have.been.calledWith(pointer_msg); 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 // 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._viewportDragging = true;
client._display.viewportChangePos = sinon.spy(); client._display.viewportChangePos = sinon.spy();
client._mouse._onMouseMove(13, 9); 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 () { it('should not send button messages when initiating viewport dragging', function () {
client._viewportDrag = true; client._viewportDrag = true;
client._mouse._onMouseButton(13, 9, 0x001); 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 () { 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; var client;
beforeEach(function () { beforeEach(function () {
client = make_rfb(); 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 () { it('should send a key message on a key press', function () {
client._keyboard._onKeyPress(1234, 1); client._keyboard._onKeyPress(1234, 1);
expect(client._sock.send).to.have.been.calledOnce; var key_msg = {_sQ: new Uint8Array(8), _sQlen: 0};
var key_msg = RFB.messages.keyEvent(1234, 1); RFB.messages.keyEvent(key_msg, 1234, 1);
expect(client._sock.send).to.have.been.calledWith(key_msg); expect(client._sock).to.have.sent(key_msg._sQ);
}); });
it('should not send messages in view-only mode', function () { it('should not send messages in view-only mode', function () {
client._view_only = true; client._view_only = true;
client._keyboard._onKeyPress(1234, 1); 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 () { it('should actually send on the websocket if the websocket does not have too much buffered', function () {
sock.maxBufferedAmount = 10; sock.maxBufferedAmount = 10;
sock._websocket.bufferedAmount = 8; sock._websocket.bufferedAmount = 8;
sock._sQ = [1, 2, 3]; sock._sQ = new Uint8Array([1, 2, 3]);
sock._sQlen = 3;
var encoded = sock._encode_message(); var encoded = sock._encode_message();
sock.flush(); sock.flush();
@ -189,7 +190,7 @@ describe('Websock', function() {
}); });
it('should not call send if we do not have anything queued up', function () { it('should not call send if we do not have anything queued up', function () {
sock._sQ = []; sock._sQlen = 0;
sock.maxBufferedAmount = 10; sock.maxBufferedAmount = 10;
sock._websocket.bufferedAmount = 8; sock._websocket.bufferedAmount = 8;
@ -215,7 +216,7 @@ describe('Websock', function() {
it('should add to the send queue', function () { it('should add to the send queue', function () {
sock.send([1, 2, 3]); sock.send([1, 2, 3]);
var sq = sock.get_sQ(); 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 () { it('should call flush', function () {
@ -425,15 +426,16 @@ describe('Websock', function() {
sock._websocket._open(); sock._websocket._open();
}); });
it('should convert the send queue into an ArrayBuffer', function () { it('should only send the send queue up to the send queue length', function () {
sock._sQ = [1, 2, 3]; sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
var res = sock._encode_message(); // An ArrayBuffer sock._sQlen = 3;
expect(new Uint8Array(res)).to.deep.equal(new Uint8Array(res)); 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 () { it('should properly pass the encoded data off to the actual WebSocket', function () {
sock.send([1, 2, 3]); 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]));
}); });
}); });
}); });