Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit f36995c

Browse files
committed
Merge pull request #6339 from adobe/iwehrman/binary-node-commands
Receive binary Brackets-Node command responses
2 parents 43aed7b + e27971e commit f36995c

3 files changed

Lines changed: 222 additions & 9 deletions

File tree

src/utils/NodeConnection.js

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4,
2626
maxerr: 50, browser: true */
27-
/*global $, define, brackets, WebSocket */
27+
/*global $, define, brackets, WebSocket, ArrayBuffer, Uint32Array */
2828

2929
define(function (require, exports, module) {
3030
"use strict";
@@ -46,6 +46,9 @@ define(function (require, exports, module) {
4646
/** @define{number} Milliseconds to wait before retrying connecting */
4747
var RETRY_DELAY = 500; // 1/2 second
4848

49+
/** @define {number} Maximum value of the command ID counter */
50+
var MAX_COUNTER_VALUE = 4294967295; // 2^32 - 1
51+
4952
/**
5053
* @private
5154
* Helper function to auto-reject a deferred after a given amount of time.
@@ -74,6 +77,10 @@ define(function (require, exports, module) {
7477
port = nodePort;
7578
ws = new WebSocket("ws://localhost:" + port);
7679

80+
// Expect ArrayBuffer objects from Node when receiving binary
81+
// data instead of DOM Blobs, which are the default.
82+
ws.binaryType = "arraybuffer";
83+
7784
// If the server port isn't open, we get a close event
7885
// at some point in the future (and will not get an onopen
7986
// event)
@@ -174,6 +181,23 @@ define(function (require, exports, module) {
174181
*/
175182
NodeConnection.prototype._pendingCommandDeferreds = null;
176183

184+
/**
185+
* @private
186+
* @return {number} The next command ID to use. Always representable as an
187+
* unsigned 32-bit integer.
188+
*/
189+
NodeConnection.prototype._getNextCommandID = function () {
190+
var nextID;
191+
192+
if (this._commandCount > MAX_COUNTER_VALUE) {
193+
nextID = this._commandCount = 0;
194+
} else {
195+
nextID = this._commandCount++;
196+
}
197+
198+
return nextID;
199+
};
200+
177201
/**
178202
* @private
179203
* Helper function to do cleanup work when a connection fails
@@ -402,12 +426,36 @@ define(function (require, exports, module) {
402426
*/
403427
NodeConnection.prototype._receive = function (message) {
404428
var responseDeferred = null;
429+
var data = message.data;
405430
var m;
406-
try {
407-
m = JSON.parse(message.data);
408-
} catch (e) {
409-
console.error("[NodeConnection] received malformed message", message, e.message);
410-
return;
431+
432+
if (message.data instanceof ArrayBuffer) {
433+
// The first four bytes encode the command ID as an unsigned 32-bit integer
434+
if (data.byteLength < 4) {
435+
console.error("[NodeConnection] received malformed binary message");
436+
return;
437+
}
438+
439+
var header = data.slice(0, 4),
440+
body = data.slice(4),
441+
headerView = new Uint32Array(header),
442+
id = headerView[0];
443+
444+
// Unpack the binary message into a commandResponse
445+
m = {
446+
type: "commandResponse",
447+
message: {
448+
id: id,
449+
response: body
450+
}
451+
};
452+
} else {
453+
try {
454+
m = JSON.parse(data);
455+
} catch (e) {
456+
console.error("[NodeConnection] received malformed message", message, e.message);
457+
return;
458+
}
411459
}
412460

413461
switch (m.type) {
@@ -467,7 +515,7 @@ define(function (require, exports, module) {
467515
return function () {
468516
var deferred = $.Deferred();
469517
var parameters = Array.prototype.slice.call(arguments, 0);
470-
var id = self._commandCount++;
518+
var id = self._getNextCommandID();
471519
self._pendingCommandDeferreds[id] = deferred;
472520
self._send({id: id,
473521
domain: domainName,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a
5+
* copy of this software and associated documentation files (the "Software"),
6+
* to deal in the Software without restriction, including without limitation
7+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8+
* and/or sell copies of the Software, and to permit persons to whom the
9+
* Software is furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20+
* DEALINGS IN THE SOFTWARE.
21+
*
22+
*/
23+
24+
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, node: true */
25+
/*global */
26+
27+
(function () {
28+
"use strict";
29+
30+
/**
31+
* @private
32+
* @type {DomainManager}
33+
* The DomainManager passed in at init.
34+
*/
35+
var _domainManager = null;
36+
37+
38+
/**
39+
* @private
40+
* @type {Buffer}
41+
*/
42+
var _buffer = new Buffer(18);
43+
44+
// write some bytes into the buffer with varied alignments
45+
_buffer.writeUInt8(1, 0);
46+
_buffer.writeUInt32LE(Math.pow(2, 32) - 1, 1);
47+
_buffer.writeFloatBE(3.141592, 5);
48+
_buffer.writeDoubleLE(Number.MAX_VALUE, 9);
49+
_buffer.writeInt8(-128, 17);
50+
51+
/**
52+
* @private
53+
* @return {Buffer}
54+
*/
55+
function _getBufferSync() {
56+
return _buffer;
57+
}
58+
59+
/**
60+
* @private
61+
* @param {function(?string, Buffer=)} callback
62+
*/
63+
function _getBufferAsync(callback) {
64+
process.nextTick(function () {
65+
callback(null, _buffer);
66+
});
67+
}
68+
69+
/**
70+
* @param {DomainManager} DomainManager The DomainManager for the server
71+
*/
72+
function init(DomainManager) {
73+
_domainManager = DomainManager;
74+
if (!_domainManager.hasDomain("test")) {
75+
_domainManager.registerDomain("test", {major: 0, minor: 1});
76+
}
77+
_domainManager.registerCommand(
78+
"binaryTest",
79+
"getBufferSync",
80+
_getBufferSync,
81+
false,
82+
"Get a byte array synchronously",
83+
[],
84+
{name: "bytes", type: "Buffer"}
85+
);
86+
_domainManager.registerCommand(
87+
"binaryTest",
88+
"getBufferAsync",
89+
_getBufferAsync,
90+
true,
91+
"Get a byte array asynchronously",
92+
[],
93+
{name: "bytes", type: "Buffer"}
94+
);
95+
}
96+
97+
exports.init = init;
98+
99+
}());

test/spec/NodeConnection-test.js

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true,
2626
indent: 4, maxerr: 50 */
2727
/*global define, describe, it, xit, expect, beforeEach, afterEach, waits,
28-
waitsFor, runs, $, brackets, waitsForDone */
28+
waitsFor, runs, $, brackets, waitsForDone, ArrayBuffer, DataView */
2929

3030
define(function (require, exports, module) {
3131
"use strict";
@@ -399,5 +399,71 @@ define(function (require, exports, module) {
399399
});
400400

401401
});
402+
403+
it("should receive synchronous binary command responses", function () {
404+
var connection = createConnection();
405+
var commandDeferred = null;
406+
var result = null;
407+
runConnectAndWait(connection, false);
408+
runLoadDomainsAndWait(connection, ["BinaryTestCommands"], false);
409+
runs(function () {
410+
commandDeferred = connection.domains.binaryTest.getBufferSync();
411+
commandDeferred.done(function (response) {
412+
result = response;
413+
});
414+
});
415+
waitsFor(
416+
function () {
417+
return commandDeferred &&
418+
commandDeferred.state() === "resolved" &&
419+
result;
420+
},
421+
CONNECTION_TIMEOUT
422+
);
423+
runs(function () {
424+
var view = new DataView(result);
425+
426+
expect(result instanceof ArrayBuffer).toBe(true);
427+
expect(result.byteLength).toBe(18);
428+
expect(view.getUint8(0)).toBe(1);
429+
expect(view.getUint32(1)).toBe(4294967295);
430+
expect(view.getFloat32(5, false)).toBe(3.141592025756836);
431+
expect(view.getFloat64(9, true)).toBe(1.7976931348623157e+308);
432+
expect(view.getInt8(17)).toBe(-128);
433+
});
434+
});
435+
436+
it("should receive asynchronous binary command response", function () {
437+
var connection = createConnection();
438+
var commandDeferred = null;
439+
var result = null;
440+
runConnectAndWait(connection, false);
441+
runLoadDomainsAndWait(connection, ["BinaryTestCommands"], false);
442+
runs(function () {
443+
commandDeferred = connection.domains.binaryTest.getBufferAsync();
444+
commandDeferred.done(function (response) {
445+
result = response;
446+
});
447+
});
448+
waitsFor(
449+
function () {
450+
return commandDeferred &&
451+
commandDeferred.state() === "resolved" &&
452+
result;
453+
},
454+
CONNECTION_TIMEOUT
455+
);
456+
runs(function () {
457+
var view = new DataView(result);
458+
459+
expect(result instanceof ArrayBuffer).toBe(true);
460+
expect(result.byteLength).toBe(18);
461+
expect(view.getUint8(0)).toBe(1);
462+
expect(view.getUint32(1)).toBe(4294967295);
463+
expect(view.getFloat32(5, false)).toBe(3.141592025756836);
464+
expect(view.getFloat64(9, true)).toBe(1.7976931348623157e+308);
465+
expect(view.getInt8(17)).toBe(-128);
466+
});
467+
});
402468
});
403-
});
469+
});

0 commit comments

Comments
 (0)