diff options
author | Solly Ross <sross@redhat.com> | 2015-12-03 21:30:47 -0500 |
---|---|---|
committer | Solly Ross <sross@redhat.com> | 2015-12-22 16:05:33 -0500 |
commit | 40037b6a29d3f372b16073f7b2d06850c32a1cd9 (patch) | |
tree | 0df59176b23697f2428b9127dafa60c56f283d18 | |
parent | c8f14d175bbfe00f69fde268bd10de2b80a089d9 (diff) | |
download | novnc-bug/dynamic-rq-resize.tar.gz |
On-Demand Dynamic Receive Queue Resizingbug/dynamic-rq-resize
This commit causes the receive queue to dynamically
resize to fit incoming messages.
Fixes #557
-rw-r--r-- | include/websock.js | 60 | ||||
-rw-r--r-- | tests/test.websock.js | 14 |
2 files changed, 57 insertions, 17 deletions
diff --git a/include/websock.js b/include/websock.js index 7c27255..f3336a0 100644 --- a/include/websock.js +++ b/include/websock.js @@ -66,12 +66,13 @@ function Websock() { (function () { "use strict"; - // this has performance issues in some versions Chromium, and // doesn't gain a tremendous amount of performance increase in Firefox // at the moment. It may be valuable to turn it on in the future. var ENABLE_COPYWITHIN = false; + var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB + var typedArrayToString = (function () { // This is only for PhantomJS, which doesn't like apply-ing // with Typed Arrays @@ -345,9 +346,49 @@ function Websock() { return new Uint8Array(this._sQ.buffer, 0, this._sQlen); }, + _expand_compact_rQ: function (min_fit) { + var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2; + if (resizeNeeded) { + if (!min_fit) { + // just double the size if we need to do compaction + this._rQbufferSize *= 2; + } else { + // otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8 + this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8; + } + } + + // we don't want to grow unboundedly + if (this._rQbufferSize > MAX_RQ_GROW_SIZE) { + this._rQbufferSize = MAX_RQ_GROW_SIZE; + if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) { + throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit"); + } + } + + if (resizeNeeded) { + var old_rQbuffer = this._rQ.buffer; + this._rQmax = this._rQbufferSize / 8; + this._rQ = new Uint8Array(this._rQbufferSize); + this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi)); + } else { + if (ENABLE_COPYWITHIN) { + this._rQ.copyWithin(0, this._rQi); + } else { + this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi)); + } + } + + this._rQlen = this._rQlen - this._rQi; + this._rQi = 0; + }, + _decode_message: function (data) { // push arraybuffer values onto the end var u8 = new Uint8Array(data); + if (u8.length > this._rQbufferSize - this._rQlen) { + this._expand_compact_rQ(u8.length); + } this._rQ.set(u8, this._rQlen); this._rQlen += u8.length; }, @@ -362,22 +403,7 @@ function Websock() { this._rQlen = 0; this._rQi = 0; } else if (this._rQlen > this._rQmax) { - if (this._rQlen - this._rQi > 0.5 * this._rQbufferSize) { - var old_rQbuffer = this._rQ.buffer; - this._rQbufferSize *= 2; - this._rQmax = this._rQbufferSize / 8; - this._rQ = new Uint8Array(this._rQbufferSize); - this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi)); - } else { - if (ENABLE_COPYWITHIN) { - this._rQ.copyWithin(0, this._rQi); - } else { - this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi)); - } - } - - this._rQlen = this._rQlen - this._rQi; - this._rQi = 0; + this._expand_compact_rQ(); } } else { Util.Debug("Ignoring empty message"); diff --git a/tests/test.websock.js b/tests/test.websock.js index 14d5783..953a526 100644 --- a/tests/test.websock.js +++ b/tests/test.websock.js @@ -404,6 +404,20 @@ describe('Websock', function() { expect(sock.get_rQi()).to.equal(0); }); + it('should automatically resize the receive queue if the incoming message is too large', function () { + sock._rQ = new Uint8Array(20); + sock._rQlen = 0; + sock.set_rQi(0); + sock._rQbufferSize = 20; + sock._rQmax = 2; + var msg = { data: new Uint8Array(30).buffer }; + sock._mode = 'binary'; + sock._recv_message(msg); + expect(sock._rQlen).to.equal(30); + expect(sock.get_rQi()).to.equal(0); + expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen + }); + it('should call the error event handler on an exception', function () { sock._eventHandlers.error = sinon.spy(); sock._eventHandlers.message = sinon.stub().throws(); |