diff --git a/include/rfb.js b/include/rfb.js index e2dd42f5..b8615af8 100644 --- a/include/rfb.js +++ b/include/rfb.js @@ -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; } }; diff --git a/include/websock.js b/include/websock.js index 71ae36ca..61d94672 100644 --- a/include/websock.js +++ b/include/websock.js @@ -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) { diff --git a/tests/assertions.js b/tests/assertions.js index 6c01c4c9..930e1460 100644 --- a/tests/assertions.js +++ b/tests/assertions.js @@ -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 () { diff --git a/tests/fake.websocket.js b/tests/fake.websocket.js index 749c0eaf..21012059 100644 --- a/tests/fake.websocket.js +++ b/tests/fake.websocket.js @@ -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) { diff --git a/tests/test.rfb.js b/tests/test.rfb.js index f22314da..961d9eb5 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -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; }); }); diff --git a/tests/test.websock.js b/tests/test.websock.js index a81f75da..14d57832 100644 --- a/tests/test.websock.js +++ b/tests/test.websock.js @@ -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])); }); }); });