socket.io-client-v2 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,94 @@
1
+
2
+ /**
3
+ * Module dependencies.
4
+ */
5
+
6
+ var url = require('./url');
7
+ var parser = require('socket.io-parser');
8
+ var Manager = require('./manager');
9
+ var debug = require('debug')('socket.io-client');
10
+
11
+ /**
12
+ * Module exports.
13
+ */
14
+
15
+ module.exports = exports = lookup;
16
+
17
+ /**
18
+ * Managers cache.
19
+ */
20
+
21
+ var cache = exports.managers = {};
22
+
23
+ /**
24
+ * Looks up an existing `Manager` for multiplexing.
25
+ * If the user summons:
26
+ *
27
+ * `io('http://localhost/a');`
28
+ * `io('http://localhost/b');`
29
+ *
30
+ * We reuse the existing instance based on same scheme/port/host,
31
+ * and we initialize sockets for each namespace.
32
+ *
33
+ * @api public
34
+ */
35
+
36
+ function lookup (uri, opts) {
37
+ if (typeof uri === 'object') {
38
+ opts = uri;
39
+ uri = undefined;
40
+ }
41
+
42
+ opts = opts || {};
43
+
44
+ var parsed = url(uri);
45
+ var source = parsed.source;
46
+ var id = parsed.id;
47
+ var path = parsed.path;
48
+ var sameNamespace = cache[id] && path in cache[id].nsps;
49
+ var newConnection = opts.forceNew || opts['force new connection'] ||
50
+ false === opts.multiplex || sameNamespace;
51
+
52
+ var io;
53
+
54
+ if (newConnection) {
55
+ debug('ignoring socket cache for %s', source);
56
+ io = Manager(source, opts);
57
+ } else {
58
+ if (!cache[id]) {
59
+ debug('new io instance for %s', source);
60
+ cache[id] = Manager(source, opts);
61
+ }
62
+ io = cache[id];
63
+ }
64
+ if (parsed.query && !opts.query) {
65
+ opts.query = parsed.query;
66
+ }
67
+ return io.socket(parsed.path, opts);
68
+ }
69
+
70
+ /**
71
+ * Protocol version.
72
+ *
73
+ * @api public
74
+ */
75
+
76
+ exports.protocol = parser.protocol;
77
+
78
+ /**
79
+ * `connect`.
80
+ *
81
+ * @param {String} uri
82
+ * @api public
83
+ */
84
+
85
+ exports.connect = lookup;
86
+
87
+ /**
88
+ * Expose constructors for standalone build.
89
+ *
90
+ * @api public
91
+ */
92
+
93
+ exports.Manager = require('./manager');
94
+ exports.Socket = require('./socket');
package/lib/manager.js ADDED
@@ -0,0 +1,577 @@
1
+
2
+ /**
3
+ * Module dependencies.
4
+ */
5
+
6
+ var eio = require('engine.io-client');
7
+ var Socket = require('./socket');
8
+ var Emitter = require('component-emitter');
9
+ var parser = require('socket.io-parser');
10
+ var on = require('./on');
11
+ var bind = require('component-bind');
12
+ var debug = require('debug')('socket.io-client:manager');
13
+ var indexOf = require('indexof');
14
+ var Backoff = require('backo2');
15
+
16
+ /**
17
+ * IE6+ hasOwnProperty
18
+ */
19
+
20
+ var has = Object.prototype.hasOwnProperty;
21
+
22
+ /**
23
+ * Module exports
24
+ */
25
+
26
+ module.exports = Manager;
27
+
28
+ /**
29
+ * `Manager` constructor.
30
+ *
31
+ * @param {String} engine instance or engine uri/opts
32
+ * @param {Object} options
33
+ * @api public
34
+ */
35
+
36
+ function Manager (uri, opts) {
37
+ if (!(this instanceof Manager)) return new Manager(uri, opts);
38
+ if (uri && ('object' === typeof uri)) {
39
+ opts = uri;
40
+ uri = undefined;
41
+ }
42
+ opts = opts || {};
43
+
44
+ opts.path = opts.path || '/socket.io';
45
+ this.nsps = {};
46
+ this.subs = [];
47
+ this.opts = opts;
48
+ this.reconnection(opts.reconnection !== false);
49
+ this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
50
+ this.reconnectionDelay(opts.reconnectionDelay || 1000);
51
+ this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
52
+ this.randomizationFactor(opts.randomizationFactor || 0.5);
53
+ this.backoff = new Backoff({
54
+ min: this.reconnectionDelay(),
55
+ max: this.reconnectionDelayMax(),
56
+ jitter: this.randomizationFactor()
57
+ });
58
+ this.timeout(null == opts.timeout ? 20000 : opts.timeout);
59
+ this.readyState = 'closed';
60
+ this.uri = uri;
61
+ this.connecting = [];
62
+ this.lastPing = null;
63
+ this.encoding = false;
64
+ this.packetBuffer = [];
65
+ var _parser = opts.parser || parser;
66
+ this.encoder = new _parser.Encoder();
67
+ this.decoder = new _parser.Decoder();
68
+ this.autoConnect = opts.autoConnect !== false;
69
+ if (this.autoConnect) this.open();
70
+ }
71
+
72
+ /**
73
+ * Propagate given event to sockets and emit on `this`
74
+ *
75
+ * @api private
76
+ */
77
+
78
+ Manager.prototype.emitAll = function () {
79
+ this.emit.apply(this, arguments);
80
+ for (var nsp in this.nsps) {
81
+ if (has.call(this.nsps, nsp)) {
82
+ this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
83
+ }
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Update `socket.id` of all sockets
89
+ *
90
+ * @api private
91
+ */
92
+
93
+ Manager.prototype.updateSocketIds = function () {
94
+ for (var nsp in this.nsps) {
95
+ if (has.call(this.nsps, nsp)) {
96
+ this.nsps[nsp].id = this.generateId(nsp);
97
+ }
98
+ }
99
+ };
100
+
101
+ /**
102
+ * generate `socket.id` for the given `nsp`
103
+ *
104
+ * @param {String} nsp
105
+ * @return {String}
106
+ * @api private
107
+ */
108
+
109
+ Manager.prototype.generateId = function (nsp) {
110
+ return (nsp === '/' ? '' : (nsp + '#')) + this.engine.id;
111
+ };
112
+
113
+ /**
114
+ * Mix in `Emitter`.
115
+ */
116
+
117
+ Emitter(Manager.prototype);
118
+
119
+ /**
120
+ * Sets the `reconnection` config.
121
+ *
122
+ * @param {Boolean} true/false if it should automatically reconnect
123
+ * @return {Manager} self or value
124
+ * @api public
125
+ */
126
+
127
+ Manager.prototype.reconnection = function (v) {
128
+ if (!arguments.length) return this._reconnection;
129
+ this._reconnection = !!v;
130
+ return this;
131
+ };
132
+
133
+ /**
134
+ * Sets the reconnection attempts config.
135
+ *
136
+ * @param {Number} max reconnection attempts before giving up
137
+ * @return {Manager} self or value
138
+ * @api public
139
+ */
140
+
141
+ Manager.prototype.reconnectionAttempts = function (v) {
142
+ if (!arguments.length) return this._reconnectionAttempts;
143
+ this._reconnectionAttempts = v;
144
+ return this;
145
+ };
146
+
147
+ /**
148
+ * Sets the delay between reconnections.
149
+ *
150
+ * @param {Number} delay
151
+ * @return {Manager} self or value
152
+ * @api public
153
+ */
154
+
155
+ Manager.prototype.reconnectionDelay = function (v) {
156
+ if (!arguments.length) return this._reconnectionDelay;
157
+ this._reconnectionDelay = v;
158
+ this.backoff && this.backoff.setMin(v);
159
+ return this;
160
+ };
161
+
162
+ Manager.prototype.randomizationFactor = function (v) {
163
+ if (!arguments.length) return this._randomizationFactor;
164
+ this._randomizationFactor = v;
165
+ this.backoff && this.backoff.setJitter(v);
166
+ return this;
167
+ };
168
+
169
+ /**
170
+ * Sets the maximum delay between reconnections.
171
+ *
172
+ * @param {Number} delay
173
+ * @return {Manager} self or value
174
+ * @api public
175
+ */
176
+
177
+ Manager.prototype.reconnectionDelayMax = function (v) {
178
+ if (!arguments.length) return this._reconnectionDelayMax;
179
+ this._reconnectionDelayMax = v;
180
+ this.backoff && this.backoff.setMax(v);
181
+ return this;
182
+ };
183
+
184
+ /**
185
+ * Sets the connection timeout. `false` to disable
186
+ *
187
+ * @return {Manager} self or value
188
+ * @api public
189
+ */
190
+
191
+ Manager.prototype.timeout = function (v) {
192
+ if (!arguments.length) return this._timeout;
193
+ this._timeout = v;
194
+ return this;
195
+ };
196
+
197
+ /**
198
+ * Starts trying to reconnect if reconnection is enabled and we have not
199
+ * started reconnecting yet
200
+ *
201
+ * @api private
202
+ */
203
+
204
+ Manager.prototype.maybeReconnectOnOpen = function () {
205
+ // Only try to reconnect if it's the first time we're connecting
206
+ if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) {
207
+ // keeps reconnection from firing twice for the same reconnection loop
208
+ this.reconnect();
209
+ }
210
+ };
211
+
212
+ /**
213
+ * Sets the current transport `socket`.
214
+ *
215
+ * @param {Function} optional, callback
216
+ * @return {Manager} self
217
+ * @api public
218
+ */
219
+
220
+ Manager.prototype.open =
221
+ Manager.prototype.connect = function (fn, opts) {
222
+ debug('readyState %s', this.readyState);
223
+ if (~this.readyState.indexOf('open')) return this;
224
+
225
+ debug('opening %s', this.uri);
226
+ this.engine = eio(this.uri, this.opts);
227
+ var socket = this.engine;
228
+ var self = this;
229
+ this.readyState = 'opening';
230
+ this.skipReconnect = false;
231
+
232
+ // emit `open`
233
+ var openSub = on(socket, 'open', function () {
234
+ self.onopen();
235
+ fn && fn();
236
+ });
237
+
238
+ // emit `connect_error`
239
+ var errorSub = on(socket, 'error', function (data) {
240
+ debug('connect_error');
241
+ self.cleanup();
242
+ self.readyState = 'closed';
243
+ self.emitAll('connect_error', data);
244
+ if (fn) {
245
+ var err = new Error('Connection error');
246
+ err.data = data;
247
+ fn(err);
248
+ } else {
249
+ // Only do this if there is no fn to handle the error
250
+ self.maybeReconnectOnOpen();
251
+ }
252
+ });
253
+
254
+ // emit `connect_timeout`
255
+ if (false !== this._timeout) {
256
+ var timeout = this._timeout;
257
+ debug('connect attempt will timeout after %d', timeout);
258
+
259
+ if (timeout === 0) {
260
+ openSub.destroy(); // prevents a race condition with the 'open' event
261
+ }
262
+
263
+ // set timer
264
+ var timer = setTimeout(function () {
265
+ debug('connect attempt timed out after %d', timeout);
266
+ openSub.destroy();
267
+ socket.close();
268
+ socket.emit('error', 'timeout');
269
+ self.emitAll('connect_timeout', timeout);
270
+ }, timeout);
271
+
272
+ this.subs.push({
273
+ destroy: function () {
274
+ clearTimeout(timer);
275
+ }
276
+ });
277
+ }
278
+
279
+ this.subs.push(openSub);
280
+ this.subs.push(errorSub);
281
+
282
+ return this;
283
+ };
284
+
285
+ /**
286
+ * Called upon transport open.
287
+ *
288
+ * @api private
289
+ */
290
+
291
+ Manager.prototype.onopen = function () {
292
+ debug('open');
293
+
294
+ // clear old subs
295
+ this.cleanup();
296
+
297
+ // mark as open
298
+ this.readyState = 'open';
299
+ this.emit('open');
300
+
301
+ // add new subs
302
+ var socket = this.engine;
303
+ this.subs.push(on(socket, 'data', bind(this, 'ondata')));
304
+ this.subs.push(on(socket, 'ping', bind(this, 'onping')));
305
+ this.subs.push(on(socket, 'pong', bind(this, 'onpong')));
306
+ this.subs.push(on(socket, 'error', bind(this, 'onerror')));
307
+ this.subs.push(on(socket, 'close', bind(this, 'onclose')));
308
+ this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
309
+ };
310
+
311
+ /**
312
+ * Called upon a ping.
313
+ *
314
+ * @api private
315
+ */
316
+
317
+ Manager.prototype.onping = function () {
318
+ this.lastPing = new Date();
319
+ this.emitAll('ping');
320
+ };
321
+
322
+ /**
323
+ * Called upon a packet.
324
+ *
325
+ * @api private
326
+ */
327
+
328
+ Manager.prototype.onpong = function () {
329
+ this.emitAll('pong', new Date() - this.lastPing);
330
+ };
331
+
332
+ /**
333
+ * Called with data.
334
+ *
335
+ * @api private
336
+ */
337
+
338
+ Manager.prototype.ondata = function (data) {
339
+ this.decoder.add(data);
340
+ };
341
+
342
+ /**
343
+ * Called when parser fully decodes a packet.
344
+ *
345
+ * @api private
346
+ */
347
+
348
+ Manager.prototype.ondecoded = function (packet) {
349
+ this.emit('packet', packet);
350
+ };
351
+
352
+ /**
353
+ * Called upon socket error.
354
+ *
355
+ * @api private
356
+ */
357
+
358
+ Manager.prototype.onerror = function (err) {
359
+ debug('error', err);
360
+ this.emitAll('error', err);
361
+ };
362
+
363
+ /**
364
+ * Creates a new socket for the given `nsp`.
365
+ *
366
+ * @return {Socket}
367
+ * @api public
368
+ */
369
+
370
+ Manager.prototype.socket = function (nsp, opts) {
371
+ var socket = this.nsps[nsp];
372
+ if (!socket) {
373
+ socket = new Socket(this, nsp, opts);
374
+ this.nsps[nsp] = socket;
375
+ var self = this;
376
+ socket.on('connecting', onConnecting);
377
+ socket.on('connect', function () {
378
+ socket.id = self.generateId(nsp);
379
+ });
380
+
381
+ if (this.autoConnect) {
382
+ // manually call here since connecting event is fired before listening
383
+ onConnecting();
384
+ }
385
+ }
386
+
387
+ function onConnecting () {
388
+ if (!~indexOf(self.connecting, socket)) {
389
+ self.connecting.push(socket);
390
+ }
391
+ }
392
+
393
+ return socket;
394
+ };
395
+
396
+ /**
397
+ * Called upon a socket close.
398
+ *
399
+ * @param {Socket} socket
400
+ */
401
+
402
+ Manager.prototype.destroy = function (socket) {
403
+ var index = indexOf(this.connecting, socket);
404
+ if (~index) this.connecting.splice(index, 1);
405
+ if (this.connecting.length) return;
406
+
407
+ this.close();
408
+ };
409
+
410
+ /**
411
+ * Writes a packet.
412
+ *
413
+ * @param {Object} packet
414
+ * @api private
415
+ */
416
+
417
+ Manager.prototype.packet = function (packet) {
418
+ debug('writing packet %j', packet);
419
+ var self = this;
420
+ if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query;
421
+
422
+ if (!self.encoding) {
423
+ // encode, then write to engine with result
424
+ self.encoding = true;
425
+ this.encoder.encode(packet, function (encodedPackets) {
426
+ for (var i = 0; i < encodedPackets.length; i++) {
427
+ self.engine.write(encodedPackets[i], packet.options);
428
+ }
429
+ self.encoding = false;
430
+ self.processPacketQueue();
431
+ });
432
+ } else { // add packet to the queue
433
+ self.packetBuffer.push(packet);
434
+ }
435
+ };
436
+
437
+ /**
438
+ * If packet buffer is non-empty, begins encoding the
439
+ * next packet in line.
440
+ *
441
+ * @api private
442
+ */
443
+
444
+ Manager.prototype.processPacketQueue = function () {
445
+ if (this.packetBuffer.length > 0 && !this.encoding) {
446
+ var pack = this.packetBuffer.shift();
447
+ this.packet(pack);
448
+ }
449
+ };
450
+
451
+ /**
452
+ * Clean up transport subscriptions and packet buffer.
453
+ *
454
+ * @api private
455
+ */
456
+
457
+ Manager.prototype.cleanup = function () {
458
+ debug('cleanup');
459
+
460
+ var subsLength = this.subs.length;
461
+ for (var i = 0; i < subsLength; i++) {
462
+ var sub = this.subs.shift();
463
+ sub.destroy();
464
+ }
465
+
466
+ this.packetBuffer = [];
467
+ this.encoding = false;
468
+ this.lastPing = null;
469
+
470
+ this.decoder.destroy();
471
+ };
472
+
473
+ /**
474
+ * Close the current socket.
475
+ *
476
+ * @api private
477
+ */
478
+
479
+ Manager.prototype.close =
480
+ Manager.prototype.disconnect = function () {
481
+ debug('disconnect');
482
+ this.skipReconnect = true;
483
+ this.reconnecting = false;
484
+ if ('opening' === this.readyState) {
485
+ // `onclose` will not fire because
486
+ // an open event never happened
487
+ this.cleanup();
488
+ }
489
+ this.backoff.reset();
490
+ this.readyState = 'closed';
491
+ if (this.engine) this.engine.close();
492
+ };
493
+
494
+ /**
495
+ * Called upon engine close.
496
+ *
497
+ * @api private
498
+ */
499
+
500
+ Manager.prototype.onclose = function (reason) {
501
+ debug('onclose');
502
+
503
+ this.cleanup();
504
+ this.backoff.reset();
505
+ this.readyState = 'closed';
506
+ this.emit('close', reason);
507
+
508
+ if (this._reconnection && !this.skipReconnect) {
509
+ this.reconnect();
510
+ }
511
+ };
512
+
513
+ /**
514
+ * Attempt a reconnection.
515
+ *
516
+ * @api private
517
+ */
518
+
519
+ Manager.prototype.reconnect = function () {
520
+ if (this.reconnecting || this.skipReconnect) return this;
521
+
522
+ var self = this;
523
+
524
+ if (this.backoff.attempts >= this._reconnectionAttempts) {
525
+ debug('reconnect failed');
526
+ this.backoff.reset();
527
+ this.emitAll('reconnect_failed');
528
+ this.reconnecting = false;
529
+ } else {
530
+ var delay = this.backoff.duration();
531
+ debug('will wait %dms before reconnect attempt', delay);
532
+
533
+ this.reconnecting = true;
534
+ var timer = setTimeout(function () {
535
+ if (self.skipReconnect) return;
536
+
537
+ debug('attempting reconnect');
538
+ self.emitAll('reconnect_attempt', self.backoff.attempts);
539
+ self.emitAll('reconnecting', self.backoff.attempts);
540
+
541
+ // check again for the case socket closed in above events
542
+ if (self.skipReconnect) return;
543
+
544
+ self.open(function (err) {
545
+ if (err) {
546
+ debug('reconnect attempt error');
547
+ self.reconnecting = false;
548
+ self.reconnect();
549
+ self.emitAll('reconnect_error', err.data);
550
+ } else {
551
+ debug('reconnect success');
552
+ self.onreconnect();
553
+ }
554
+ });
555
+ }, delay);
556
+
557
+ this.subs.push({
558
+ destroy: function () {
559
+ clearTimeout(timer);
560
+ }
561
+ });
562
+ }
563
+ };
564
+
565
+ /**
566
+ * Called upon successful reconnect.
567
+ *
568
+ * @api private
569
+ */
570
+
571
+ Manager.prototype.onreconnect = function () {
572
+ var attempt = this.backoff.attempts;
573
+ this.reconnecting = false;
574
+ this.backoff.reset();
575
+ this.updateSocketIds();
576
+ this.emitAll('reconnect', attempt);
577
+ };
package/lib/on.js ADDED
@@ -0,0 +1,24 @@
1
+
2
+ /**
3
+ * Module exports.
4
+ */
5
+
6
+ module.exports = on;
7
+
8
+ /**
9
+ * Helper for subscriptions.
10
+ *
11
+ * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter`
12
+ * @param {String} event name
13
+ * @param {Function} callback
14
+ * @api public
15
+ */
16
+
17
+ function on (obj, ev, fn) {
18
+ obj.on(ev, fn);
19
+ return {
20
+ destroy: function () {
21
+ obj.removeListener(ev, fn);
22
+ }
23
+ };
24
+ }