[Service] Integrate DeviceHome and SignalingServer
[platform/framework/web/wrtjs.git] / device_home / node_modules / socket.io-parser / index.js
1
2 /**
3  * Module dependencies.
4  */
5
6 var debug = require('debug')('socket.io-parser');
7 var Emitter = require('component-emitter');
8 var binary = require('./binary');
9 var isArray = require('isarray');
10 var isBuf = require('./is-buffer');
11
12 /**
13  * Protocol version.
14  *
15  * @api public
16  */
17
18 exports.protocol = 4;
19
20 /**
21  * Packet types.
22  *
23  * @api public
24  */
25
26 exports.types = [
27   'CONNECT',
28   'DISCONNECT',
29   'EVENT',
30   'ACK',
31   'ERROR',
32   'BINARY_EVENT',
33   'BINARY_ACK'
34 ];
35
36 /**
37  * Packet type `connect`.
38  *
39  * @api public
40  */
41
42 exports.CONNECT = 0;
43
44 /**
45  * Packet type `disconnect`.
46  *
47  * @api public
48  */
49
50 exports.DISCONNECT = 1;
51
52 /**
53  * Packet type `event`.
54  *
55  * @api public
56  */
57
58 exports.EVENT = 2;
59
60 /**
61  * Packet type `ack`.
62  *
63  * @api public
64  */
65
66 exports.ACK = 3;
67
68 /**
69  * Packet type `error`.
70  *
71  * @api public
72  */
73
74 exports.ERROR = 4;
75
76 /**
77  * Packet type 'binary event'
78  *
79  * @api public
80  */
81
82 exports.BINARY_EVENT = 5;
83
84 /**
85  * Packet type `binary ack`. For acks with binary arguments.
86  *
87  * @api public
88  */
89
90 exports.BINARY_ACK = 6;
91
92 /**
93  * Encoder constructor.
94  *
95  * @api public
96  */
97
98 exports.Encoder = Encoder;
99
100 /**
101  * Decoder constructor.
102  *
103  * @api public
104  */
105
106 exports.Decoder = Decoder;
107
108 /**
109  * A socket.io Encoder instance
110  *
111  * @api public
112  */
113
114 function Encoder() {}
115
116 var ERROR_PACKET = exports.ERROR + '"encode error"';
117
118 /**
119  * Encode a packet as a single string if non-binary, or as a
120  * buffer sequence, depending on packet type.
121  *
122  * @param {Object} obj - packet object
123  * @param {Function} callback - function to handle encodings (likely engine.write)
124  * @return Calls callback with Array of encodings
125  * @api public
126  */
127
128 Encoder.prototype.encode = function(obj, callback){
129   debug('encoding packet %j', obj);
130
131   if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) {
132     encodeAsBinary(obj, callback);
133   } else {
134     var encoding = encodeAsString(obj);
135     callback([encoding]);
136   }
137 };
138
139 /**
140  * Encode packet as string.
141  *
142  * @param {Object} packet
143  * @return {String} encoded
144  * @api private
145  */
146
147 function encodeAsString(obj) {
148
149   // first is type
150   var str = '' + obj.type;
151
152   // attachments if we have them
153   if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) {
154     str += obj.attachments + '-';
155   }
156
157   // if we have a namespace other than `/`
158   // we append it followed by a comma `,`
159   if (obj.nsp && '/' !== obj.nsp) {
160     str += obj.nsp + ',';
161   }
162
163   // immediately followed by the id
164   if (null != obj.id) {
165     str += obj.id;
166   }
167
168   // json data
169   if (null != obj.data) {
170     var payload = tryStringify(obj.data);
171     if (payload !== false) {
172       str += payload;
173     } else {
174       return ERROR_PACKET;
175     }
176   }
177
178   debug('encoded %j as %s', obj, str);
179   return str;
180 }
181
182 function tryStringify(str) {
183   try {
184     return JSON.stringify(str);
185   } catch(e){
186     return false;
187   }
188 }
189
190 /**
191  * Encode packet as 'buffer sequence' by removing blobs, and
192  * deconstructing packet into object with placeholders and
193  * a list of buffers.
194  *
195  * @param {Object} packet
196  * @return {Buffer} encoded
197  * @api private
198  */
199
200 function encodeAsBinary(obj, callback) {
201
202   function writeEncoding(bloblessData) {
203     var deconstruction = binary.deconstructPacket(bloblessData);
204     var pack = encodeAsString(deconstruction.packet);
205     var buffers = deconstruction.buffers;
206
207     buffers.unshift(pack); // add packet info to beginning of data list
208     callback(buffers); // write all the buffers
209   }
210
211   binary.removeBlobs(obj, writeEncoding);
212 }
213
214 /**
215  * A socket.io Decoder instance
216  *
217  * @return {Object} decoder
218  * @api public
219  */
220
221 function Decoder() {
222   this.reconstructor = null;
223 }
224
225 /**
226  * Mix in `Emitter` with Decoder.
227  */
228
229 Emitter(Decoder.prototype);
230
231 /**
232  * Decodes an encoded packet string into packet JSON.
233  *
234  * @param {String} obj - encoded packet
235  * @return {Object} packet
236  * @api public
237  */
238
239 Decoder.prototype.add = function(obj) {
240   var packet;
241   if (typeof obj === 'string') {
242     packet = decodeString(obj);
243     if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json
244       this.reconstructor = new BinaryReconstructor(packet);
245
246       // no attachments, labeled binary but no binary data to follow
247       if (this.reconstructor.reconPack.attachments === 0) {
248         this.emit('decoded', packet);
249       }
250     } else { // non-binary full packet
251       this.emit('decoded', packet);
252     }
253   } else if (isBuf(obj) || obj.base64) { // raw binary data
254     if (!this.reconstructor) {
255       throw new Error('got binary data when not reconstructing a packet');
256     } else {
257       packet = this.reconstructor.takeBinaryData(obj);
258       if (packet) { // received final buffer
259         this.reconstructor = null;
260         this.emit('decoded', packet);
261       }
262     }
263   } else {
264     throw new Error('Unknown type: ' + obj);
265   }
266 };
267
268 /**
269  * Decode a packet String (JSON data)
270  *
271  * @param {String} str
272  * @return {Object} packet
273  * @api private
274  */
275
276 function decodeString(str) {
277   var i = 0;
278   // look up type
279   var p = {
280     type: Number(str.charAt(0))
281   };
282
283   if (null == exports.types[p.type]) {
284     return error('unknown packet type ' + p.type);
285   }
286
287   // look up attachments if type binary
288   if (exports.BINARY_EVENT === p.type || exports.BINARY_ACK === p.type) {
289     var start = i + 1;
290     while (str.charAt(++i) !== '-' && i != str.length) {}
291     var buf = str.substring(start, i);
292     if (buf != Number(buf) || str.charAt(i) !== '-') {
293       throw new Error('Illegal attachments');
294     }
295     p.attachments = Number(buf);
296   }
297
298   // look up namespace (if any)
299   if ('/' === str.charAt(i + 1)) {
300     var start = i + 1;
301     while (++i) {
302       var c = str.charAt(i);
303       if (',' === c) break;
304       if (i === str.length) break;
305     }
306     p.nsp = str.substring(start, i);
307   } else {
308     p.nsp = '/';
309   }
310
311   // look up id
312   var next = str.charAt(i + 1);
313   if ('' !== next && Number(next) == next) {
314     var start = i + 1;
315     while (++i) {
316       var c = str.charAt(i);
317       if (null == c || Number(c) != c) {
318         --i;
319         break;
320       }
321       if (i === str.length) break;
322     }
323     p.id = Number(str.substring(start, i + 1));
324   }
325
326   // look up json data
327   if (str.charAt(++i)) {
328     var payload = tryParse(str.substr(i));
329     var isPayloadValid = payload !== false && (p.type === exports.ERROR || isArray(payload));
330     if (isPayloadValid) {
331       p.data = payload;
332     } else {
333       return error('invalid payload');
334     }
335   }
336
337   debug('decoded %s as %j', str, p);
338   return p;
339 }
340
341 function tryParse(str) {
342   try {
343     return JSON.parse(str);
344   } catch(e){
345     return false;
346   }
347 }
348
349 /**
350  * Deallocates a parser's resources
351  *
352  * @api public
353  */
354
355 Decoder.prototype.destroy = function() {
356   if (this.reconstructor) {
357     this.reconstructor.finishedReconstruction();
358   }
359 };
360
361 /**
362  * A manager of a binary event's 'buffer sequence'. Should
363  * be constructed whenever a packet of type BINARY_EVENT is
364  * decoded.
365  *
366  * @param {Object} packet
367  * @return {BinaryReconstructor} initialized reconstructor
368  * @api private
369  */
370
371 function BinaryReconstructor(packet) {
372   this.reconPack = packet;
373   this.buffers = [];
374 }
375
376 /**
377  * Method to be called when binary data received from connection
378  * after a BINARY_EVENT packet.
379  *
380  * @param {Buffer | ArrayBuffer} binData - the raw binary data received
381  * @return {null | Object} returns null if more binary data is expected or
382  *   a reconstructed packet object if all buffers have been received.
383  * @api private
384  */
385
386 BinaryReconstructor.prototype.takeBinaryData = function(binData) {
387   this.buffers.push(binData);
388   if (this.buffers.length === this.reconPack.attachments) { // done with buffer list
389     var packet = binary.reconstructPacket(this.reconPack, this.buffers);
390     this.finishedReconstruction();
391     return packet;
392   }
393   return null;
394 };
395
396 /**
397  * Cleans up binary packet reconstruction variables.
398  *
399  * @api private
400  */
401
402 BinaryReconstructor.prototype.finishedReconstruction = function() {
403   this.reconPack = null;
404   this.buffers = [];
405 };
406
407 function error(msg) {
408   return {
409     type: exports.ERROR,
410     data: 'parser error: ' + msg
411   };
412 }