socketioMEANseed/server/node_modules/socket.io-parser/binary.js
server/node_modules/socket.io-parser/binary.jsRaw
1/*global Blob,File*/
2
3/**
4 * Module requirements
5 */
6
7var isArray = require('isarray');
8var isBuf = require('./is-buffer');
9
10/**
11 * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
12 * Anything with blobs or files should be fed through removeBlobs before coming
13 * here.
14 *
15 * @param {Object} packet - socket.io event packet
16 * @return {Object} with deconstructed packet and list of buffers
17 * @api public
18 */
19
20exports.deconstructPacket = function(packet){
21 var buffers = [];
22 var packetData = packet.data;
23
24 function _deconstructPacket(data) {
25 if (!data) return data;
26
27 if (isBuf(data)) {
28 var placeholder = { _placeholder: true, num: buffers.length };
29 buffers.push(data);
30 return placeholder;
31 } else if (isArray(data)) {
32 var newData = new Array(data.length);
33 for (var i = 0; i < data.length; i++) {
34 newData[i] = _deconstructPacket(data[i]);
35 }
36 return newData;
37 } else if ('object' == typeof data && !(data instanceof Date)) {
38 var newData = {};
39 for (var key in data) {
40 newData[key] = _deconstructPacket(data[key]);
41 }
42 return newData;
43 }
44 return data;
45 }
46
47 var pack = packet;
48 pack.data = _deconstructPacket(packetData);
49 pack.attachments = buffers.length; // number of binary 'attachments'
50 return {packet: pack, buffers: buffers};
51};
52
53/**
54 * Reconstructs a binary packet from its placeholder packet and buffers
55 *
56 * @param {Object} packet - event packet with placeholders
57 * @param {Array} buffers - binary buffers to put in placeholder positions
58 * @return {Object} reconstructed packet
59 * @api public
60 */
61
62exports.reconstructPacket = function(packet, buffers) {
63 var curPlaceHolder = 0;
64
65 function _reconstructPacket(data) {
66 if (data && data._placeholder) {
67 var buf = buffers[data.num]; // appropriate buffer (should be natural order anyway)
68 return buf;
69 } else if (isArray(data)) {
70 for (var i = 0; i < data.length; i++) {
71 data[i] = _reconstructPacket(data[i]);
72 }
73 return data;
74 } else if (data && 'object' == typeof data) {
75 for (var key in data) {
76 data[key] = _reconstructPacket(data[key]);
77 }
78 return data;
79 }
80 return data;
81 }
82
83 packet.data = _reconstructPacket(packet.data);
84 packet.attachments = undefined; // no longer useful
85 return packet;
86};
87
88/**
89 * Asynchronously removes Blobs or Files from data via
90 * FileReader's readAsArrayBuffer method. Used before encoding
91 * data as msgpack. Calls callback with the blobless data.
92 *
93 * @param {Object} data
94 * @param {Function} callback
95 * @api private
96 */
97
98exports.removeBlobs = function(data, callback) {
99 function _removeBlobs(obj, curKey, containingObject) {
100 if (!obj) return obj;
101
102 // convert any blob
103 if ((global.Blob && obj instanceof Blob) ||
104 (global.File && obj instanceof File)) {
105 pendingBlobs++;
106
107 // async filereader
108 var fileReader = new FileReader();
109 fileReader.onload = function() { // this.result == arraybuffer
110 if (containingObject) {
111 containingObject[curKey] = this.result;
112 }
113 else {
114 bloblessData = this.result;
115 }
116
117 // if nothing pending its callback time
118 if(! --pendingBlobs) {
119 callback(bloblessData);
120 }
121 };
122
123 fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer
124 } else if (isArray(obj)) { // handle array
125 for (var i = 0; i < obj.length; i++) {
126 _removeBlobs(obj[i], i, obj);
127 }
128 } else if (obj && 'object' == typeof obj && !isBuf(obj)) { // and object
129 for (var key in obj) {
130 _removeBlobs(obj[key], key, obj);
131 }
132 }
133 }
134
135 var pendingBlobs = 0;
136 var bloblessData = data;
137 _removeBlobs(bloblessData);
138 if (!pendingBlobs) {
139 callback(bloblessData);
140 }
141};