arnaucube
arnaucube/raspberryGPIOhtmlserverPublic Self-hosted repository
raspberryGPIOhtmlserver/node_modules/socket.io-client/lib/manager.js
node_modules/socket.io-client/lib/manager.jsRaw
  1
  2/**
  3 * Module dependencies.
  4 */
  5
  6var url = require('./url');
  7var eio = require('engine.io-client');
  8var Socket = require('./socket');
  9var Emitter = require('component-emitter');
 10var parser = require('socket.io-parser');
 11var on = require('./on');
 12var bind = require('component-bind');
 13var object = require('object-component');
 14var debug = require('debug')('socket.io-client:manager');
 15var indexOf = require('indexof');
 16
 17/**
 18 * Module exports
 19 */
 20
 21module.exports = Manager;
 22
 23/**
 24 * `Manager` constructor.
 25 *
 26 * @param {String} engine instance or engine uri/opts
 27 * @param {Object} options
 28 * @api public
 29 */
 30
 31function Manager(uri, opts){
 32  if (!(this instanceof Manager)) return new Manager(uri, opts);
 33  if (uri && ('object' == typeof uri)) {
 34    opts = uri;
 35    uri = undefined;
 36  }
 37  opts = opts || {};
 38
 39  opts.path = opts.path || '/socket.io';
 40  this.nsps = {};
 41  this.subs = [];
 42  this.opts = opts;
 43  this.reconnection(opts.reconnection !== false);
 44  this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
 45  this.reconnectionDelay(opts.reconnectionDelay || 1000);
 46  this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
 47  this.timeout(null == opts.timeout ? 20000 : opts.timeout);
 48  this.readyState = 'closed';
 49  this.uri = uri;
 50  this.connected = [];
 51  this.attempts = 0;
 52  this.encoding = false;
 53  this.packetBuffer = [];
 54  this.encoder = new parser.Encoder();
 55  this.decoder = new parser.Decoder();
 56  this.autoConnect = opts.autoConnect !== false;
 57  if (this.autoConnect) this.open();
 58}
 59
 60/**
 61 * Propagate given event to sockets and emit on `this`
 62 *
 63 * @api private
 64 */
 65
 66Manager.prototype.emitAll = function() {
 67  this.emit.apply(this, arguments);
 68  for (var nsp in this.nsps) {
 69    this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
 70  }
 71};
 72
 73/**
 74 * Mix in `Emitter`.
 75 */
 76
 77Emitter(Manager.prototype);
 78
 79/**
 80 * Sets the `reconnection` config.
 81 *
 82 * @param {Boolean} true/false if it should automatically reconnect
 83 * @return {Manager} self or value
 84 * @api public
 85 */
 86
 87Manager.prototype.reconnection = function(v){
 88  if (!arguments.length) return this._reconnection;
 89  this._reconnection = !!v;
 90  return this;
 91};
 92
 93/**
 94 * Sets the reconnection attempts config.
 95 *
 96 * @param {Number} max reconnection attempts before giving up
 97 * @return {Manager} self or value
 98 * @api public
 99 */
100
101Manager.prototype.reconnectionAttempts = function(v){
102  if (!arguments.length) return this._reconnectionAttempts;
103  this._reconnectionAttempts = v;
104  return this;
105};
106
107/**
108 * Sets the delay between reconnections.
109 *
110 * @param {Number} delay
111 * @return {Manager} self or value
112 * @api public
113 */
114
115Manager.prototype.reconnectionDelay = function(v){
116  if (!arguments.length) return this._reconnectionDelay;
117  this._reconnectionDelay = v;
118  return this;
119};
120
121/**
122 * Sets the maximum delay between reconnections.
123 *
124 * @param {Number} delay
125 * @return {Manager} self or value
126 * @api public
127 */
128
129Manager.prototype.reconnectionDelayMax = function(v){
130  if (!arguments.length) return this._reconnectionDelayMax;
131  this._reconnectionDelayMax = v;
132  return this;
133};
134
135/**
136 * Sets the connection timeout. `false` to disable
137 *
138 * @return {Manager} self or value
139 * @api public
140 */
141
142Manager.prototype.timeout = function(v){
143  if (!arguments.length) return this._timeout;
144  this._timeout = v;
145  return this;
146};
147
148/**
149 * Starts trying to reconnect if reconnection is enabled and we have not
150 * started reconnecting yet
151 *
152 * @api private
153 */
154
155Manager.prototype.maybeReconnectOnOpen = function() {
156  // Only try to reconnect if it's the first time we're connecting
157  if (!this.openReconnect && !this.reconnecting && this._reconnection && this.attempts === 0) {
158    // keeps reconnection from firing twice for the same reconnection loop
159    this.openReconnect = true;
160    this.reconnect();
161  }
162};
163
164
165/**
166 * Sets the current transport `socket`.
167 *
168 * @param {Function} optional, callback
169 * @return {Manager} self
170 * @api public
171 */
172
173Manager.prototype.open =
174Manager.prototype.connect = function(fn){
175  debug('readyState %s', this.readyState);
176  if (~this.readyState.indexOf('open')) return this;
177
178  debug('opening %s', this.uri);
179  this.engine = eio(this.uri, this.opts);
180  var socket = this.engine;
181  var self = this;
182  this.readyState = 'opening';
183  this.skipReconnect = false;
184
185  // emit `open`
186  var openSub = on(socket, 'open', function() {
187    self.onopen();
188    fn && fn();
189  });
190
191  // emit `connect_error`
192  var errorSub = on(socket, 'error', function(data){
193    debug('connect_error');
194    self.cleanup();
195    self.readyState = 'closed';
196    self.emitAll('connect_error', data);
197    if (fn) {
198      var err = new Error('Connection error');
199      err.data = data;
200      fn(err);
201    }
202
203    self.maybeReconnectOnOpen();
204  });
205
206  // emit `connect_timeout`
207  if (false !== this._timeout) {
208    var timeout = this._timeout;
209    debug('connect attempt will timeout after %d', timeout);
210
211    // set timer
212    var timer = setTimeout(function(){
213      debug('connect attempt timed out after %d', timeout);
214      openSub.destroy();
215      socket.close();
216      socket.emit('error', 'timeout');
217      self.emitAll('connect_timeout', timeout);
218    }, timeout);
219
220    this.subs.push({
221      destroy: function(){
222        clearTimeout(timer);
223      }
224    });
225  }
226
227  this.subs.push(openSub);
228  this.subs.push(errorSub);
229
230  return this;
231};
232
233/**
234 * Called upon transport open.
235 *
236 * @api private
237 */
238
239Manager.prototype.onopen = function(){
240  debug('open');
241
242  // clear old subs
243  this.cleanup();
244
245  // mark as open
246  this.readyState = 'open';
247  this.emit('open');
248
249  // add new subs
250  var socket = this.engine;
251  this.subs.push(on(socket, 'data', bind(this, 'ondata')));
252  this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
253  this.subs.push(on(socket, 'error', bind(this, 'onerror')));
254  this.subs.push(on(socket, 'close', bind(this, 'onclose')));
255};
256
257/**
258 * Called with data.
259 *
260 * @api private
261 */
262
263Manager.prototype.ondata = function(data){
264  this.decoder.add(data);
265};
266
267/**
268 * Called when parser fully decodes a packet.
269 *
270 * @api private
271 */
272
273Manager.prototype.ondecoded = function(packet) {
274  this.emit('packet', packet);
275};
276
277/**
278 * Called upon socket error.
279 *
280 * @api private
281 */
282
283Manager.prototype.onerror = function(err){
284  debug('error', err);
285  this.emitAll('error', err);
286};
287
288/**
289 * Creates a new socket for the given `nsp`.
290 *
291 * @return {Socket}
292 * @api public
293 */
294
295Manager.prototype.socket = function(nsp){
296  var socket = this.nsps[nsp];
297  if (!socket) {
298    socket = new Socket(this, nsp);
299    this.nsps[nsp] = socket;
300    var self = this;
301    socket.on('connect', function(){
302      if (!~indexOf(self.connected, socket)) {
303        self.connected.push(socket);
304      }
305    });
306  }
307  return socket;
308};
309
310/**
311 * Called upon a socket close.
312 *
313 * @param {Socket} socket
314 */
315
316Manager.prototype.destroy = function(socket){
317  var index = indexOf(this.connected, socket);
318  if (~index) this.connected.splice(index, 1);
319  if (this.connected.length) return;
320
321  this.close();
322};
323
324/**
325 * Writes a packet.
326 *
327 * @param {Object} packet
328 * @api private
329 */
330
331Manager.prototype.packet = function(packet){
332  debug('writing packet %j', packet);
333  var self = this;
334
335  if (!self.encoding) {
336    // encode, then write to engine with result
337    self.encoding = true;
338    this.encoder.encode(packet, function(encodedPackets) {
339      for (var i = 0; i < encodedPackets.length; i++) {
340        self.engine.write(encodedPackets[i]);
341      }
342      self.encoding = false;
343      self.processPacketQueue();
344    });
345  } else { // add packet to the queue
346    self.packetBuffer.push(packet);
347  }
348};
349
350/**
351 * If packet buffer is non-empty, begins encoding the
352 * next packet in line.
353 *
354 * @api private
355 */
356
357Manager.prototype.processPacketQueue = function() {
358  if (this.packetBuffer.length > 0 && !this.encoding) {
359    var pack = this.packetBuffer.shift();
360    this.packet(pack);
361  }
362};
363
364/**
365 * Clean up transport subscriptions and packet buffer.
366 *
367 * @api private
368 */
369
370Manager.prototype.cleanup = function(){
371  var sub;
372  while (sub = this.subs.shift()) sub.destroy();
373
374  this.packetBuffer = [];
375  this.encoding = false;
376
377  this.decoder.destroy();
378};
379
380/**
381 * Close the current socket.
382 *
383 * @api private
384 */
385
386Manager.prototype.close =
387Manager.prototype.disconnect = function(){
388  this.skipReconnect = true;
389  this.readyState = 'closed';
390  this.engine && this.engine.close();
391};
392
393/**
394 * Called upon engine close.
395 *
396 * @api private
397 */
398
399Manager.prototype.onclose = function(reason){
400  debug('close');
401  this.cleanup();
402  this.readyState = 'closed';
403  this.emit('close', reason);
404  if (this._reconnection && !this.skipReconnect) {
405    this.reconnect();
406  }
407};
408
409/**
410 * Attempt a reconnection.
411 *
412 * @api private
413 */
414
415Manager.prototype.reconnect = function(){
416  if (this.reconnecting || this.skipReconnect) return this;
417
418  var self = this;
419  this.attempts++;
420
421  if (this.attempts > this._reconnectionAttempts) {
422    debug('reconnect failed');
423    this.emitAll('reconnect_failed');
424    this.reconnecting = false;
425  } else {
426    var delay = this.attempts * this.reconnectionDelay();
427    delay = Math.min(delay, this.reconnectionDelayMax());
428    debug('will wait %dms before reconnect attempt', delay);
429
430    this.reconnecting = true;
431    var timer = setTimeout(function(){
432      if (self.skipReconnect) return;
433
434      debug('attempting reconnect');
435      self.emitAll('reconnect_attempt', self.attempts);
436      self.emitAll('reconnecting', self.attempts);
437
438      // check again for the case socket closed in above events
439      if (self.skipReconnect) return;
440
441      self.open(function(err){
442        if (err) {
443          debug('reconnect attempt error');
444          self.reconnecting = false;
445          self.reconnect();
446          self.emitAll('reconnect_error', err.data);
447        } else {
448          debug('reconnect success');
449          self.onreconnect();
450        }
451      });
452    }, delay);
453
454    this.subs.push({
455      destroy: function(){
456        clearTimeout(timer);
457      }
458    });
459  }
460};
461
462/**
463 * Called upon successful reconnect.
464 *
465 * @api private
466 */
467
468Manager.prototype.onreconnect = function(){
469  var attempt = this.attempts;
470  this.attempts = 0;
471  this.reconnecting = false;
472  this.emitAll('reconnect', attempt);
473};