Skip to content

Commit 28d4f03

Browse files
refactor: do not convert Blobs
This was needed in a previous version of the parser, which used msgpack to encode the payload. Blobs (and Files) will now be included in the array of binary attachments without any additional transformation. Breaking change: the encode method is now synchronous See also 299849b
1 parent fe33ff7 commit 28d4f03

File tree

6 files changed

+77
-142
lines changed

6 files changed

+77
-142
lines changed

lib/binary.ts

Lines changed: 3 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
1-
import isBuf from "./is-buffer";
2-
3-
const toString = Object.prototype.toString;
4-
const withNativeBlob =
5-
typeof Blob === "function" ||
6-
(typeof Blob !== "undefined" &&
7-
toString.call(Blob) === "[object BlobConstructor]");
8-
const withNativeFile =
9-
typeof File === "function" ||
10-
(typeof File !== "undefined" &&
11-
toString.call(File) === "[object FileConstructor]");
1+
import isBinary from "./is-binary";
122

133
/**
14-
* Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
15-
* Anything with blobs or files should be fed through removeBlobs before coming
16-
* here.
4+
* Replaces every Buffer | ArrayBuffer | Blob | File in packet with a numbered placeholder.
175
*
186
* @param {Object} packet - socket.io event packet
197
* @return {Object} with deconstructed packet and list of buffers
@@ -32,7 +20,7 @@ export function deconstructPacket(packet) {
3220
function _deconstructPacket(data, buffers) {
3321
if (!data) return data;
3422

35-
if (isBuf(data)) {
23+
if (isBinary(data)) {
3624
const placeholder = { _placeholder: true, num: buffers.length };
3725
buffers.push(data);
3826
return placeholder;
@@ -88,64 +76,3 @@ function _reconstructPacket(data, buffers) {
8876

8977
return data;
9078
}
91-
92-
/**
93-
* Asynchronously removes Blobs or Files from data via
94-
* FileReader's readAsArrayBuffer method. Used before encoding
95-
* data as msgpack. Calls callback with the blobless data.
96-
*
97-
* @param {Object} data
98-
* @param {Function} callback
99-
* @api private
100-
*/
101-
102-
export function removeBlobs(data, callback) {
103-
function _removeBlobs(obj, curKey?, containingObject?) {
104-
if (!obj) return obj;
105-
106-
// convert any blob
107-
if (
108-
(withNativeBlob && obj instanceof Blob) ||
109-
(withNativeFile && obj instanceof File)
110-
) {
111-
pendingBlobs++;
112-
113-
// async filereader
114-
const fileReader = new FileReader();
115-
fileReader.onload = function () {
116-
// this.result == arraybuffer
117-
if (containingObject) {
118-
containingObject[curKey] = this.result;
119-
} else {
120-
bloblessData = this.result;
121-
}
122-
123-
// if nothing pending its callback time
124-
if (!--pendingBlobs) {
125-
callback(bloblessData);
126-
}
127-
};
128-
129-
fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer
130-
} else if (Array.isArray(obj)) {
131-
// handle array
132-
for (let i = 0; i < obj.length; i++) {
133-
_removeBlobs(obj[i], i, obj);
134-
}
135-
} else if (typeof obj === "object" && !isBuf(obj)) {
136-
// and object
137-
for (const key in obj) {
138-
if (obj.hasOwnProperty(key)) {
139-
_removeBlobs(obj[key], key, obj);
140-
}
141-
}
142-
}
143-
}
144-
145-
let pendingBlobs = 0;
146-
let bloblessData = data;
147-
_removeBlobs(bloblessData);
148-
if (!pendingBlobs) {
149-
callback(bloblessData);
150-
}
151-
}

lib/index.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as Emitter from "component-emitter";
22
import * as binary from "./binary";
3-
import isBuf from "./is-buffer";
3+
import isBinary from "./is-binary";
44
import debugModule from "debug";
55

66
const debug = debugModule("socket.io-parser");
@@ -41,20 +41,18 @@ export class Encoder {
4141
* buffer sequence, depending on packet type.
4242
*
4343
* @param {Object} obj - packet object
44-
* @param {Function} callback - function to handle encodings (likely engine.write)
45-
* @return Calls callback with Array of encodings
4644
*/
47-
public encode(obj: Packet, callback: Function) {
45+
public encode(obj: Packet) {
4846
debug("encoding packet %j", obj);
4947

5048
if (
5149
obj.type === PacketType.BINARY_EVENT ||
5250
obj.type === PacketType.BINARY_ACK
5351
) {
54-
this.encodeAsBinary(obj, callback);
52+
return this.encodeAsBinary(obj);
5553
} else {
5654
const encoding = this.encodeAsString(obj);
57-
callback([encoding]);
55+
return [encoding];
5856
}
5957
}
6058

@@ -100,15 +98,13 @@ export class Encoder {
10098
* a list of buffers.
10199
*/
102100

103-
private encodeAsBinary(obj: Packet, callback: Function) {
104-
binary.removeBlobs(obj, (bloblessData) => {
105-
const deconstruction = binary.deconstructPacket(bloblessData);
106-
const pack = this.encodeAsString(deconstruction.packet);
107-
const buffers = deconstruction.buffers;
101+
private encodeAsBinary(obj: Packet) {
102+
const deconstruction = binary.deconstructPacket(obj);
103+
const pack = this.encodeAsString(deconstruction.packet);
104+
const buffers = deconstruction.buffers;
108105

109-
buffers.unshift(pack); // add packet info to beginning of data list
110-
callback(buffers); // write all the buffers
111-
});
106+
buffers.unshift(pack); // add packet info to beginning of data list
107+
return buffers; // write all the buffers
112108
}
113109
}
114110

@@ -149,7 +145,7 @@ export class Decoder extends Emitter {
149145
// non-binary full packet
150146
super.emit("decoded", packet);
151147
}
152-
} else if (isBuf(obj) || obj.base64) {
148+
} else if (isBinary(obj) || obj.base64) {
153149
// raw binary data
154150
if (!this.reconstructor) {
155151
throw new Error("got binary data when not reconstructing a packet");

lib/is-binary.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const withNativeBuffer: boolean =
2+
typeof Buffer === "function" && typeof Buffer.isBuffer === "function";
3+
const withNativeArrayBuffer: boolean = typeof ArrayBuffer === "function";
4+
5+
const isView = (obj: any) => {
6+
return typeof ArrayBuffer.isView === "function"
7+
? ArrayBuffer.isView(obj)
8+
: obj.buffer instanceof ArrayBuffer;
9+
};
10+
11+
const toString = Object.prototype.toString;
12+
const withNativeBlob =
13+
typeof Blob === "function" ||
14+
(typeof Blob !== "undefined" &&
15+
toString.call(Blob) === "[object BlobConstructor]");
16+
const withNativeFile =
17+
typeof File === "function" ||
18+
(typeof File !== "undefined" &&
19+
toString.call(File) === "[object FileConstructor]");
20+
21+
/**
22+
* Returns true if obj is a Buffer, an ArrayBuffer, a Blob or a File.
23+
*
24+
* @private
25+
*/
26+
27+
export default function isBinary(obj: any) {
28+
return (
29+
(withNativeBuffer && Buffer.isBuffer(obj)) ||
30+
(withNativeArrayBuffer && (obj instanceof ArrayBuffer || isView(obj))) ||
31+
(withNativeBlob && obj instanceof Blob) ||
32+
(withNativeFile && obj instanceof File)
33+
);
34+
}

lib/is-buffer.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

test/arraybuffer.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,16 @@ describe('parser', () => {
5555
nsp: '/'
5656
};
5757

58-
encoder.encode(packet, encodedPackets => {
59-
var decoder = new Decoder();
60-
decoder.on('decoded', packet => {
61-
throw new Error("received a packet when not all binary data was sent.");
62-
});
58+
const encodedPackets = encoder.encode(packet);
6359

64-
decoder.add(encodedPackets[0]); // add metadata
65-
decoder.add(encodedPackets[1]); // add first attachment
66-
decoder.destroy(); // destroy before all data added
67-
expect(decoder.reconstructor.buffers.length).to.be(0); // expect that buffer is clean
60+
var decoder = new Decoder();
61+
decoder.on('decoded', packet => {
62+
throw new Error("received a packet when not all binary data was sent.");
6863
});
64+
65+
decoder.add(encodedPackets[0]); // add metadata
66+
decoder.add(encodedPackets[1]); // add first attachment
67+
decoder.destroy(); // destroy before all data added
68+
expect(decoder.reconstructor.buffers.length).to.be(0); // expect that buffer is clean
6969
});
7070
});

test/helpers.js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,33 @@ const encoder = new parser.Encoder();
44

55
// tests encoding and decoding a single packet
66
module.exports.test = (obj, done) => {
7-
encoder.encode(obj, encodedPackets => {
8-
const decoder = new parser.Decoder();
9-
decoder.on('decoded', packet => {
10-
expect(packet).to.eql(obj);
11-
done();
12-
});
13-
14-
decoder.add(encodedPackets[0]);
7+
const encodedPackets = encoder.encode(obj);
8+
9+
const decoder = new parser.Decoder();
10+
decoder.on('decoded', packet => {
11+
expect(packet).to.eql(obj);
12+
done();
1513
});
14+
15+
decoder.add(encodedPackets[0]);
1616
}
1717

1818
// tests encoding of binary packets
1919
module.exports.test_bin = (obj, done) => {
2020
const originalData = obj.data;
21-
encoder.encode(obj, encodedPackets => {
22-
const decoder = new parser.Decoder();
23-
decoder.on('decoded', packet => {
24-
obj.data = originalData;
25-
obj.attachments = undefined;
26-
expect(obj).to.eql(packet);
27-
done();
28-
});
29-
30-
for (let i = 0; i < encodedPackets.length; i++) {
31-
decoder.add(encodedPackets[i]);
32-
}
21+
const encodedPackets = encoder.encode(obj);
22+
23+
const decoder = new parser.Decoder();
24+
decoder.on('decoded', packet => {
25+
obj.data = originalData;
26+
obj.attachments = undefined;
27+
expect(obj).to.eql(packet);
28+
done();
3329
});
30+
31+
for (let i = 0; i < encodedPackets.length; i++) {
32+
decoder.add(encodedPackets[i]);
33+
}
3434
}
3535

3636
// array buffer's slice is native code that is not transported across

0 commit comments

Comments
 (0)