Comparing version 0.2.1 to 0.3.0
@@ -1,2 +0,2 @@ | ||
export declare function send<T extends Array<any>>(...data: T): Buffer; | ||
export declare function receive<T extends Array<any>>(buf: Buffer, temp: Buffer[]): IterableIterator<T>; | ||
export declare function send<T extends any>(...data: T[]): Buffer; | ||
export declare function receive<T extends any>(buf: Buffer, temp: any[]): IterableIterator<T>; |
182
index.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const splitBuffer = require("buffer-split"); | ||
const toBuffer = require("to-buffer"); | ||
const bufsep = toBuffer("\r\n"); | ||
const sprintf = require("sprintf-js").sprintf; | ||
function send() { | ||
let data = Array.from(arguments); | ||
return Buffer.concat([toBuffer(JSON.stringify(data)), bufsep]); | ||
function send(...data) { | ||
let buf = Buffer.from([]); | ||
for (let payload of data) { | ||
let type = NaN; | ||
switch (typeof payload) { | ||
case "string": | ||
type = 1; | ||
payload = Buffer.from(payload); | ||
break; | ||
case "number": | ||
type = 2; | ||
payload = Buffer.from(payload.toString()); | ||
break; | ||
case "bigint": | ||
type = 3; | ||
payload = Buffer.from(payload.toString()); | ||
break; | ||
case "boolean": | ||
type = 4; | ||
payload = Buffer.from([Number(payload)]); | ||
break; | ||
case "object": | ||
if (null === payload) { | ||
type = 0; | ||
payload = Buffer.from([]); | ||
} else if (Buffer.isBuffer(payload)) { | ||
type = 6; | ||
} else { | ||
type = 5; | ||
payload = Buffer.from(JSON.stringify(payload)); | ||
} | ||
break; | ||
} | ||
let head = [type]; | ||
let len = payload.byteLength; | ||
if (len <= 255) { | ||
head.push(1, len); | ||
} else if (len <= 65535) { | ||
head.push(2); | ||
for (let i = 0, bin = sprintf("%016b", len); i < 16;) { | ||
head.push(parseInt(bin.slice(i, i += 8), 2)); | ||
} | ||
} else { | ||
head.push(3); | ||
for (let i = 0, bin = sprintf("%064b", len); i < 64;) { | ||
head.push(parseInt(bin.slice(i, i += 8), 2)); | ||
} | ||
} | ||
buf = Buffer.concat([buf, Buffer.from(head), payload]); | ||
} | ||
return buf; | ||
} | ||
/** | ||
* @param {Buffer} buf | ||
*/ | ||
function resolvePayloadInfo(buf) { | ||
let offset = 0; | ||
let length = 0; | ||
let type = buf.readUInt8(0); | ||
let lenType = buf.readUInt8(1); | ||
let bin = ""; | ||
switch (lenType) { | ||
case 1: | ||
offset = 3; | ||
length = buf.readUInt8(2); | ||
break; | ||
case 2: | ||
for (let i = 2; i < 4; i++) { | ||
bin += sprintf("%08b", buf.readUInt8(i)); | ||
} | ||
offset = 4; | ||
length = parseInt(bin, 2); | ||
break; | ||
case 3: | ||
for (let i = 2; i < 10; i++) { | ||
bin += sprintf("%08b", buf.readUInt8(i)); | ||
} | ||
offset = 10; | ||
length = parseInt(bin, 2); | ||
break; | ||
} | ||
return { type, offset, length }; | ||
} | ||
/** | ||
* @param {Buffer} buf | ||
* @param {[number, number, Buffer]} temp | ||
*/ | ||
function fillTemp(buf, temp) { | ||
let { type, offset, length } = resolvePayloadInfo(buf); | ||
if (offset !== 0) { | ||
temp[0] = type; | ||
temp[1] = length; | ||
temp[2] = buf.slice(offset); | ||
} | ||
} | ||
/** | ||
* @param {Buffer} buf | ||
* @param {[number, number, Buffer]} temp | ||
*/ | ||
function* receive(buf, temp) { | ||
temp[0] || (temp[0] = toBuffer([])); | ||
// put the buffer into the temp | ||
if (temp.length === 0) { | ||
fillTemp(buf, temp); | ||
} else if (temp.length === 3) { | ||
temp[2] = Buffer.concat([temp[2], buf]); | ||
} | ||
/** @type {Buffer[]} */ | ||
let packs = splitBuffer(Buffer.concat([temp[0], buf]), bufsep); | ||
// scan the temp and yield any parsed data | ||
while (temp.length === 3 && temp[2].byteLength >= temp[1]) { | ||
let [type, length, buf] = temp; | ||
let payload = buf.slice(0, length); | ||
temp[0] = toBuffer([]); | ||
buf = buf.slice(length); | ||
for (let pack of packs) { | ||
if (pack && pack.byteLength) { | ||
try { | ||
yield JSON.parse(pack.toString("utf8")); | ||
} catch (err) { | ||
(err.name === "SyntaxError") && (temp[0] = pack); | ||
} | ||
switch (type) { | ||
case 0: // null | ||
yield null; | ||
break; | ||
case 1: | ||
yield payload.toString("utf-8"); | ||
break; | ||
case 2: | ||
yield Number(payload.toString("utf-8")); | ||
break; | ||
case 3: | ||
yield BigInt(payload.toString("utf-8")); | ||
break; | ||
case 4: | ||
yield Boolean(payload.readUInt8(0)); | ||
break; | ||
case 5: | ||
yield JSON.parse(payload.toString("utf-8")); | ||
break; | ||
case 6: | ||
yield payload; | ||
break; | ||
} | ||
if (buf.byteLength > 0) { | ||
fillTemp(buf, temp); | ||
} else { | ||
temp.splice(0, 3); // clean temp | ||
} | ||
} | ||
@@ -30,0 +178,0 @@ } |
{ | ||
"name": "bsp", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Basic Socket Protocol", | ||
@@ -25,4 +25,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"buffer-split": "^1.0.0", | ||
"to-buffer": "^1.1.1" | ||
"sprintf-js": "^1.1.2" | ||
}, | ||
@@ -32,3 +31,6 @@ "devDependencies": { | ||
"mocha": "^5.2.0" | ||
}, | ||
"engines": { | ||
"node": ">=6.0" | ||
} | ||
} |
@@ -19,3 +19,3 @@ # Basic Socket Protocol | ||
socket.on("data", buf => { | ||
for (let [msg] of receive(buf, temp)) { | ||
for (let msg of receive(buf, temp)) { | ||
// the first message would be 'Hello, World!' | ||
@@ -38,3 +38,3 @@ } | ||
- `send(...data: any[]): Buffer` | ||
- `receive(buf: Buffer, temp: Buffer[]): IterableIterator<any[]>` | ||
- `receive(buf: Buffer, temp: any[]): IterableIterator<any>` | ||
@@ -44,2 +44,5 @@ ## Notice | ||
Due to performance and compatibility considerations, this module (since version | ||
0.2) uses *JSON* to transfer data instead. | ||
0.2) uses *JSON* to transfer data instead. | ||
API change in v0.3, passing multiple arguments into `send()` now acts exactly | ||
the same as calling `send()` multiple times. |
184
test.js
@@ -5,38 +5,176 @@ "use strict"; | ||
const assert = require("assert"); | ||
const fs = require("fs"); | ||
describe("Basic Socket Protocol", () => { | ||
it("should encode data to buffer and decode complete data as expected", () => { | ||
var data = ["a string", ["an", "array"]], | ||
buf = bsp.send.apply(void 0, data), | ||
temp = [], | ||
res = bsp.receive(buf, temp); | ||
it("should encode and decode string as expected", () => { | ||
let data = "Hello, World!"; | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let part of res) { | ||
assert.deepStrictEqual(part, data); | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.strictEqual(result, data); | ||
}); | ||
it("should encode data to buffer and decode incomplete data as expected", () => { | ||
var data = [["a string", ["an", "array"]], ["another string"]], | ||
buf = bsp.send.apply(void 0, data[0]), | ||
buf2 = bsp.send.apply(void 0, data[1]), | ||
buf3 = Buffer.concat([buf, buf2]), | ||
index = buf.byteLength + 2, | ||
temp = [], | ||
i = 0; | ||
it("should encode and decode number as expected", () => { | ||
let data = 12345; | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let part of bsp.receive(buf3.slice(0, index), temp)) { | ||
assert.deepStrictEqual(part, data[i]); | ||
i++; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.strictEqual(temp[0].byteLength, 2); | ||
assert.strictEqual(result, data); | ||
}); | ||
for (let part of bsp.receive(buf3.slice(index), temp)) { | ||
assert.deepStrictEqual(part, data[i]); | ||
i++; | ||
it("should encode and decode bigint as expected", () => { | ||
if (typeof BigInt === "function") { | ||
let data = BigInt("12345"); | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.strictEqual(result, data); | ||
} | ||
}); | ||
assert.strictEqual(temp[0].byteLength, 0); | ||
it("should encode and decode boolean as expected", () => { | ||
let data = true; | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.strictEqual(result, data); | ||
}); | ||
it("should encode and decode object as expected", () => { | ||
let data = { foo: "Hello", bar: "World" }; | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.deepStrictEqual(result, data); | ||
}); | ||
it("should encode and decode null as expected", () => { | ||
let data = null; | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.strictEqual(result, data); | ||
}); | ||
it("should encode and decode buffer as expected", () => { | ||
let filename = __dirname + "/buffer.html"; | ||
let data = fs.readFileSync(filename); | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert(Buffer.compare(result, data) === 0); | ||
}); | ||
it("should encode and decode string larger then 255 bytes as expected", () => { | ||
let data = [ | ||
"Prior to the introduction of TypedArray, ", | ||
"the JavaScript language had no mechanism for reading or ", | ||
"manipulating streams of binary data. The Buffer class was ", | ||
"introduced as part of the Node.js API to enable interaction ", | ||
"with octet streams in TCP streams, file system operations, ", | ||
"and other contexts." | ||
].join(""); | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.deepStrictEqual(result, data); | ||
}); | ||
it("should encode and decode string larger than 65535 bytes as expected", () => { | ||
let filename = __dirname + "/buffer.html"; | ||
let data = fs.readFileSync(filename, "utf8"); | ||
let buf = bsp.send(data); | ||
let temp = []; | ||
let res = bsp.receive(buf, temp); | ||
let result; | ||
for (let pack of res) { | ||
result = pack; | ||
} | ||
assert.strictEqual(result, data); | ||
}); | ||
it("should encode and decode multiple pieces of data as expected", () => { | ||
let filename = __dirname + "/buffer.html"; | ||
let data = ["Hello, World!", 12345, true, false, null]; | ||
let obj = { foo: "Hello, World", bar: ["an", "array"] }; | ||
let arr = ["Hello", "World"]; | ||
let file = fs.readFileSync(filename) | ||
let buf = bsp.send(...data); | ||
let objBuf = bsp.send(obj); | ||
let arrBuf = bsp.send(arr); | ||
let fileBuf = bsp.send(file); | ||
let temp = []; | ||
buf = Buffer.concat([buf, objBuf, arrBuf, fileBuf]); | ||
let buf1 = buf.slice(0, 255); | ||
let buf2 = buf.slice(255, 65535); | ||
let buf3 = buf.slice(65535); | ||
let result = []; | ||
for (let pack of bsp.receive(buf1, temp)) { | ||
result.push(pack); | ||
} | ||
for (let pack of bsp.receive(buf2, temp)) { | ||
result.push(pack); | ||
} | ||
for (let pack of bsp.receive(buf3, temp)) { | ||
result.push(pack); | ||
} | ||
assert.deepStrictEqual(result.slice(0, 7), [...data, obj, arr]); | ||
assert.ok(0 === Buffer.compare(result.slice(7)[0], file)); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
189361
1
8
289
45
1
1
+ Addedsprintf-js@^1.1.2
+ Addedsprintf-js@1.1.3(transitive)
- Removedbuffer-split@^1.0.0
- Removedto-buffer@^1.1.1
- Removedbuffer-indexof@0.0.2(transitive)
- Removedbuffer-split@1.0.0(transitive)
- Removedto-buffer@1.1.1(transitive)