Skip to content

Commit

Permalink
feat: move binary detection back to the parser
Browse files Browse the repository at this point in the history
The binary detection was moved from the parser to the client/server in
[1], in order to allow the user to skip the binary detection for huge
JSON payloads.

```js
socket.binary(false).emit(...);
```

The binary detection is needed in the default parser, because the
payload is encoded with JSON.stringify(), which does not support binary
content (ArrayBuffer, Blob, ...).

But other parsers (like [2] or [3]) do not need this check, so we'll
move the binary detection back here and remove the socket.binary()
method, as this use case is now covered by the ability to provide your
own parser.

Note: the hasBinary method was copied from [4].

[1]: f44256c
[2]: https://github.com/darrachequesne/socket.io-msgpack-parser
[3]: https://github.com/darrachequesne/socket.io-json-parser
[4]: https://github.com/darrachequesne/has-binary
  • Loading branch information
darrachequesne committed Oct 14, 2020
1 parent 7fc3c42 commit 285e7cd
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 27 deletions.
2 changes: 1 addition & 1 deletion lib/binary.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import isBinary from "./is-binary";
import { isBinary } from "./is-binary";

/**
* Replaces every Buffer | ArrayBuffer | Blob | File in packet with a numbered placeholder.
Expand Down
21 changes: 11 additions & 10 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Emitter from "component-emitter";
import Emitter = require("component-emitter");
import { deconstructPacket, reconstructPacket } from "./binary";
import isBinary from "./is-binary";
import { isBinary, hasBinary } from "./is-binary";

const debug = require("debug")("socket.io-parser");

Expand Down Expand Up @@ -44,15 +44,16 @@ export class Encoder {
public encode(obj: Packet) {
debug("encoding packet %j", obj);

if (
obj.type === PacketType.BINARY_EVENT ||
obj.type === PacketType.BINARY_ACK
) {
return this.encodeAsBinary(obj);
} else {
const encoding = this.encodeAsString(obj);
return [encoding];
if (obj.type === PacketType.EVENT || obj.type === PacketType.ACK) {
if (hasBinary(obj)) {
obj.type =
obj.type === PacketType.EVENT
? PacketType.BINARY_EVENT
: PacketType.BINARY_ACK;
return this.encodeAsBinary(obj);
}
}
return [this.encodeAsString(obj)];
}

/**
Expand Down
40 changes: 36 additions & 4 deletions lib/is-binary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const withNativeBuffer: boolean =
typeof Buffer === "function" && typeof Buffer.isBuffer === "function";
const withNativeArrayBuffer: boolean = typeof ArrayBuffer === "function";

const isView = (obj: any) => {
Expand All @@ -24,11 +22,45 @@ const withNativeFile =
* @private
*/

export default function isBinary(obj: any) {
export function isBinary(obj: any) {
return (
(withNativeBuffer && Buffer.isBuffer(obj)) ||
(withNativeArrayBuffer && (obj instanceof ArrayBuffer || isView(obj))) ||
(withNativeBlob && obj instanceof Blob) ||
(withNativeFile && obj instanceof File)
);
}

export function hasBinary(obj: any, toJSON?: boolean) {
if (!obj || typeof obj !== "object") {
return false;
}

if (Array.isArray(obj)) {
for (let i = 0, l = obj.length; i < l; i++) {
if (hasBinary(obj[i])) {
return true;
}
}
return false;
}

if (isBinary(obj)) {
return true;
}

if (
obj.toJSON &&
typeof obj.toJSON === "function" &&
arguments.length === 1
) {
return hasBinary(obj.toJSON(), true);
}

for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) {
return true;
}
}

return false;
}
10 changes: 5 additions & 5 deletions test/arraybuffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const encoder = new Encoder();
describe("parser", () => {
it("encodes an ArrayBuffer", (done) => {
const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", new ArrayBuffer(2)],
id: 0,
nsp: "/",
Expand All @@ -19,7 +19,7 @@ describe("parser", () => {
for (let i = 0; i < array.length; i++) array[i] = i;

const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", array],
id: 0,
nsp: "/",
Expand All @@ -29,7 +29,7 @@ describe("parser", () => {

it("encodes ArrayBuffers deep in JSON", (done) => {
const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: [
"a",
{
Expand All @@ -46,7 +46,7 @@ describe("parser", () => {

it("encodes deep binary JSON with null values", (done) => {
const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", { a: "b", c: 4, e: { g: null }, h: new ArrayBuffer(9) }],
nsp: "/",
id: 600,
Expand All @@ -56,7 +56,7 @@ describe("parser", () => {

it("cleans itself up on close", () => {
const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", new ArrayBuffer(2), new ArrayBuffer(3)],
id: 0,
nsp: "/",
Expand Down
6 changes: 3 additions & 3 deletions test/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe("parser", () => {
}

const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", data],
id: 0,
nsp: "/",
Expand All @@ -43,7 +43,7 @@ describe("parser", () => {
}

const packet = {
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", { a: "hi", b: { why: data }, c: "bye" }],
id: 999,
nsp: "/deep",
Expand All @@ -62,7 +62,7 @@ describe("parser", () => {
}

const packet = {
type: PacketType.BINARY_ACK,
type: PacketType.ACK,
data: [{ a: "hi ack", b: { why: data }, c: "bye ack" }],
id: 999,
nsp: "/deep",
Expand Down
4 changes: 2 additions & 2 deletions test/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("parser", () => {
it("encodes a Buffer", (done) => {
helpers.test_bin(
{
type: PacketType.BINARY_EVENT,
type: PacketType.EVENT,
data: ["a", Buffer.from("abc", "utf8")],
id: 23,
nsp: "/cool",
Expand All @@ -17,7 +17,7 @@ describe("parser", () => {
it("encodes a binary ack with Buffer", (done) => {
helpers.test_bin(
{
type: PacketType.BINARY_ACK,
type: PacketType.ACK,
data: ["a", Buffer.from("xxx", "utf8"), {}],
id: 127,
nsp: "/back",
Expand Down
3 changes: 1 addition & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"allowJs": false,
"target": "es2017",
"module": "commonjs",
"declaration": true,
"esModuleInterop": true
"declaration": true
},
"include": [
"./lib/**/*"
Expand Down

0 comments on commit 285e7cd

Please sign in to comment.