arnaucube
arnaucube/comunicationLeapPublic Self-hosted repository
comunicationLeap/node_modules/ws/lib/WebSocket.js
node_modules/ws/lib/WebSocket.jsRaw
  1/*!
  2 * ws: a node.js websocket client
  3 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
  4 * MIT Licensed
  5 */
  6
  7var util = require('util')
  8  , events = require('events')
  9  , http = require('http')
 10  , https = require('https')
 11  , crypto = require('crypto')
 12  , url = require('url')
 13  , fs = require('fs')
 14  , Options = require('options')
 15  , Sender = require('./Sender')
 16  , Receiver = require('./Receiver')
 17  , SenderHixie = require('./Sender.hixie')
 18  , ReceiverHixie = require('./Receiver.hixie');
 19
 20/**
 21 * Constants
 22 */
 23
 24// Default protocol version
 25
 26var protocolVersion = 13;
 27
 28// Close timeout
 29
 30var closeTimeout = 30000; // Allow 5 seconds to terminate the connection cleanly
 31
 32/**
 33 * Node version 0.4 and 0.6 compatibility
 34 */
 35
 36var isNodeV4 = /^v0\.4/.test(process.version);
 37
 38/**
 39 * WebSocket implementation
 40 */
 41
 42function WebSocket(address, protocols, options) {
 43
 44  if (protocols && !Array.isArray(protocols) && 'object' == typeof protocols) {
 45    // accept the "options" Object as the 2nd argument
 46    options = protocols;
 47    protocols = null;
 48  }
 49  if ('string' == typeof protocols) {
 50    protocols = [ protocols ];
 51  }
 52  if (!Array.isArray(protocols)) {
 53    protocols = [];
 54  }
 55  // TODO: actually handle the `Sub-Protocols` part of the WebSocket client
 56
 57  this._socket = null;
 58  this.bytesReceived = 0;
 59  this.readyState = null;
 60  this.supports = {};
 61
 62  if (Array.isArray(address)) {
 63    initAsServerClient.apply(this, address.concat(options));
 64  } else {
 65    initAsClient.apply(this, [address, protocols, options]);
 66  }
 67}
 68
 69/**
 70 * Inherits from EventEmitter.
 71 */
 72
 73util.inherits(WebSocket, events.EventEmitter);
 74
 75/**
 76 * Ready States
 77 */
 78
 79["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function (state, index) {
 80    WebSocket.prototype[state] = WebSocket[state] = index;
 81});
 82
 83/**
 84 * Gracefully closes the connection, after sending a description message to the server
 85 *
 86 * @param {Object} data to be sent to the server
 87 * @api public
 88 */
 89
 90WebSocket.prototype.close = function(code, data) {
 91  if (this.readyState == WebSocket.CLOSING || this.readyState == WebSocket.CLOSED) return;
 92  if (this.readyState == WebSocket.CONNECTING) {
 93    this.readyState = WebSocket.CLOSED;
 94    return;
 95  }
 96  try {
 97    this.readyState = WebSocket.CLOSING;
 98    this._closeCode = code;
 99    this._closeMessage = data;
100    var mask = !this._isServer;
101    this._sender.close(code, data, mask);
102  }
103  catch (e) {
104    this.emit('error', e);
105  }
106  finally {
107    this.terminate();
108  }
109}
110
111/**
112 * Pause the client stream
113 *
114 * @api public
115 */
116
117WebSocket.prototype.pause = function() {
118  if (this.readyState != WebSocket.OPEN) throw new Error('not opened');
119  return this._socket.pause();
120}
121
122/**
123 * Sends a ping
124 *
125 * @param {Object} data to be sent to the server
126 * @param {Object} Members - mask: boolean, binary: boolean
127 * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
128 * @api public
129 */
130
131WebSocket.prototype.ping = function(data, options, dontFailWhenClosed) {
132  if (this.readyState != WebSocket.OPEN) {
133    if (dontFailWhenClosed === true) return;
134    throw new Error('not opened');
135  }
136  options = options || {};
137  if (typeof options.mask == 'undefined') options.mask = !this._isServer;
138  this._sender.ping(data, options);
139}
140
141/**
142 * Sends a pong
143 *
144 * @param {Object} data to be sent to the server
145 * @param {Object} Members - mask: boolean, binary: boolean
146 * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
147 * @api public
148 */
149
150WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) {
151  if (this.readyState != WebSocket.OPEN) {
152    if (dontFailWhenClosed === true) return;
153    throw new Error('not opened');
154  }
155  options = options || {};
156  if (typeof options.mask == 'undefined') options.mask = !this._isServer;
157  this._sender.pong(data, options);
158}
159
160/**
161 * Resume the client stream
162 *
163 * @api public
164 */
165
166WebSocket.prototype.resume = function() {
167  if (this.readyState != WebSocket.OPEN) throw new Error('not opened');
168  return this._socket.resume();
169}
170
171/**
172 * Sends a piece of data
173 *
174 * @param {Object} data to be sent to the server
175 * @param {Object} Members - mask: boolean, binary: boolean
176 * @param {function} Optional callback which is executed after the send completes
177 * @api public
178 */
179
180WebSocket.prototype.send = function(data, options, cb) {
181  if (typeof options == 'function') {
182    cb = options;
183    options = {};
184  }
185  if (this.readyState != WebSocket.OPEN) {
186    if (typeof cb == 'function') cb(new Error('not opened'));
187    else throw new Error('not opened');
188    return;
189  }
190  if (!data) data = '';
191  if (this._queue) {
192    var self = this;
193    this._queue.push(function() { self.send(data, options, cb); });
194    return;
195  }
196  options = options || {};
197  options.fin = true;
198  if (typeof options.binary == 'undefined') {
199    options.binary = (data instanceof ArrayBuffer || data instanceof Buffer ||
200      data instanceof Uint8Array ||
201      data instanceof Uint16Array ||
202      data instanceof Uint32Array ||
203      data instanceof Int8Array ||
204      data instanceof Int16Array ||
205      data instanceof Int32Array ||
206      data instanceof Float32Array ||
207      data instanceof Float64Array);
208  }
209  if (typeof options.mask == 'undefined') options.mask = !this._isServer;
210  if (data instanceof fs.ReadStream) {
211    startQueue(this);
212    var self = this;
213    sendStream(this, data, options, function(error) {
214      process.nextTick(function() { executeQueueSends(self); });
215      if (typeof cb == 'function') cb(error);
216    });
217  }
218  else this._sender.send(data, options, cb);
219}
220
221/**
222 * Streams data through calls to a user supplied function
223 *
224 * @param {Object} Members - mask: boolean, binary: boolean
225 * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'.
226 * @api public
227 */
228
229WebSocket.prototype.stream = function(options, cb) {
230  if (typeof options == 'function') {
231    cb = options;
232    options = {};
233  }
234  var self = this;
235  if (typeof cb != 'function') throw new Error('callback must be provided');
236  if (this.readyState != WebSocket.OPEN) {
237    if (typeof cb == 'function') cb(new Error('not opened'));
238    else throw new Error('not opened');
239    return;
240  }
241  if (this._queue) {
242    this._queue.push(function() { self.stream(options, cb); });
243    return;
244  }
245  options = options || {};
246  if (typeof options.mask == 'undefined') options.mask = !this._isServer;
247  startQueue(this);
248  var send = function(data, final) {
249    try {
250      if (self.readyState != WebSocket.OPEN) throw new Error('not opened');
251      options.fin = final === true;
252      self._sender.send(data, options);
253      if (!final) process.nextTick(cb.bind(null, null, send));
254      else executeQueueSends(self);
255    }
256    catch (e) {
257      if (typeof cb == 'function') cb(e);
258      else {
259        delete self._queue;
260        self.emit('error', e);
261      }
262    }
263  }
264  process.nextTick(cb.bind(null, null, send));
265}
266
267/**
268 * Immediately shuts down the connection
269 *
270 * @api public
271 */
272
273WebSocket.prototype.terminate = function() {
274  if (this.readyState == WebSocket.CLOSED) return;
275  if (this._socket) {
276    try {
277      // End the connection
278      this._socket.end();
279    }
280    catch (e) {
281      // Socket error during end() call, so just destroy it right now
282      cleanupWebsocketResources.call(this, true);
283      return;
284    }
285
286    // Add a timeout to ensure that the connection is completely
287    // cleaned up within 30 seconds, even if the clean close procedure
288    // fails for whatever reason
289    this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout);
290  }
291  else if (this.readyState == WebSocket.CONNECTING) {
292    cleanupWebsocketResources.call(this, true);
293  }
294};
295
296/**
297 * Expose bufferedAmount
298 *
299 * @api public
300 */
301
302Object.defineProperty(WebSocket.prototype, 'bufferedAmount', {
303  get: function get() {
304    var amount = 0;
305    if (this._socket) {
306      amount = this._socket.bufferSize || 0;
307    }
308    return amount;
309  }
310});
311
312/**
313 * Emulates the W3C Browser based WebSocket interface using function members.
314 *
315 * @see http://dev.w3.org/html5/websockets/#the-websocket-interface
316 * @api public
317 */
318
319['open', 'error', 'close', 'message'].forEach(function(method) {
320  Object.defineProperty(WebSocket.prototype, 'on' + method, {
321    /**
322     * Returns the current listener
323     *
324     * @returns {Mixed} the set function or undefined
325     * @api public
326     */
327
328    get: function get() {
329      var listener = this.listeners(method)[0];
330      return listener ? (listener._listener ? listener._listener : listener) : undefined;
331    },
332
333    /**
334     * Start listening for events
335     *
336     * @param {Function} listener the listener
337     * @returns {Mixed} the set function or undefined
338     * @api public
339     */
340
341    set: function set(listener) {
342      this.removeAllListeners(method);
343      this.addEventListener(method, listener);
344    }
345  });
346});
347
348/**
349 * Emulates the W3C Browser based WebSocket interface using addEventListener.
350 *
351 * @see https://developer.mozilla.org/en/DOM/element.addEventListener
352 * @see http://dev.w3.org/html5/websockets/#the-websocket-interface
353 * @api public
354 */
355WebSocket.prototype.addEventListener = function(method, listener) {
356  var target = this;
357  if (typeof listener === 'function') {
358    if (method === 'message') {
359      function onMessage (data, flags) {
360        listener.call(this, new MessageEvent(data, flags.binary ? 'Binary' : 'Text', target));
361      }
362      // store a reference so we can return the original function from the addEventListener hook
363      onMessage._listener = listener;
364      this.on(method, onMessage);
365    } else if (method === 'close') {
366      function onClose (code, message) {
367        listener.call(this, new CloseEvent(code, message, target));
368      }
369      // store a reference so we can return the original function from the addEventListener hook
370      onClose._listener = listener;
371      this.on(method, onClose);
372    } else if (method === 'error') {
373      function onError (event) {
374        event.target = target;
375        listener.call(this, event);
376      }
377      // store a reference so we can return the original function from the addEventListener hook
378      onError._listener = listener;
379      this.on(method, onError);
380    } else if (method === 'open') {
381      function onOpen () {
382        listener.call(this, new OpenEvent(target));
383      }
384      // store a reference so we can return the original function from the addEventListener hook
385      onOpen._listener = listener;
386      this.on(method, onOpen);
387    } else {
388      this.on(method, listener);
389    }
390  }
391}
392
393module.exports = WebSocket;
394
395/**
396 * W3C MessageEvent
397 *
398 * @see http://www.w3.org/TR/html5/comms.html
399 * @api private
400 */
401
402function MessageEvent(dataArg, typeArg, target) {
403  this.data = dataArg;
404  this.type = typeArg;
405  this.target = target;
406}
407
408/**
409 * W3C CloseEvent
410 *
411 * @see http://www.w3.org/TR/html5/comms.html
412 * @api private
413 */
414
415function CloseEvent(code, reason, target) {
416  this.wasClean = (typeof code == 'undefined' || code == 1000);
417  this.code = code;
418  this.reason = reason;
419  this.target = target;
420}
421
422/**
423 * W3C OpenEvent
424 *
425 * @see http://www.w3.org/TR/html5/comms.html
426 * @api private
427 */
428
429function OpenEvent(target) {
430  this.target = target;
431}
432
433/**
434 * Entirely private apis,
435 * which may or may not be bound to a sepcific WebSocket instance.
436 */
437
438function initAsServerClient(req, socket, upgradeHead, options) {
439  options = new Options({
440    protocolVersion: protocolVersion,
441    protocol: null
442  }).merge(options);
443
444  // expose state properties
445  this.protocol = options.value.protocol;
446  this.protocolVersion = options.value.protocolVersion;
447  this.supports.binary = (this.protocolVersion != 'hixie-76');
448  this.upgradeReq = req;
449  this.readyState = WebSocket.CONNECTING;
450  this._isServer = true;
451
452  // establish connection
453  if (options.value.protocolVersion == 'hixie-76') establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead);
454  else establishConnection.call(this, Receiver, Sender, socket, upgradeHead);
455}
456
457function initAsClient(address, protocols, options) {
458  options = new Options({
459    origin: null,
460    protocolVersion: protocolVersion,
461    host: null,
462    headers: null,
463    protocol: null,
464    agent: null,
465
466    // ssl-related options
467    pfx: null,
468    key: null,
469    passphrase: null,
470    cert: null,
471    ca: null,
472    ciphers: null,
473    rejectUnauthorized: null
474  }).merge(options);
475  if (options.value.protocolVersion != 8 && options.value.protocolVersion != 13) {
476    throw new Error('unsupported protocol version');
477  }
478
479  // verify url and establish http class
480  var serverUrl = url.parse(address);
481  var isUnixSocket = serverUrl.protocol === 'ws+unix:';
482  if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url');
483  var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
484  var httpObj = isSecure ? https : http;
485  var port = serverUrl.port || (isSecure ? 443 : 80);
486  var auth = serverUrl.auth;
487
488  // expose state properties
489  this._isServer = false;
490  this.url = address;
491  this.protocolVersion = options.value.protocolVersion;
492  this.supports.binary = (this.protocolVersion != 'hixie-76');
493
494  // begin handshake
495  var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64');
496  var shasum = crypto.createHash('sha1');
497  shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
498  var expectedServerKey = shasum.digest('base64');
499
500  var agent = options.value.agent;
501  // node<=v0.4.x compatibility
502  if (!agent && isNodeV4) {
503    isNodeV4 = true;
504    agent = new httpObj.Agent({
505      host: serverUrl.hostname,
506      port: port
507    });
508  }
509
510  var headerHost = serverUrl.hostname;
511  // Append port number to Host and Origin header, only if specified in the url and non-default
512  if(serverUrl.port) {
513    if((isSecure && (port != 443)) || (!isSecure && (port != 80))){
514      headerHost = headerHost + ':' + port;
515    }
516  }
517
518  var requestOptions = {
519    port: port,
520    host: serverUrl.hostname,
521    headers: {
522      'Connection': 'Upgrade',
523      'Upgrade': 'websocket',
524      'Host': headerHost,
525      'Origin': headerHost,
526      'Sec-WebSocket-Version': options.value.protocolVersion,
527      'Sec-WebSocket-Key': key
528    }
529  };
530
531  // If we have basic auth.
532  if (auth) {
533    requestOptions.headers['Authorization'] = 'Basic ' + new Buffer(auth).toString('base64');
534  }
535
536  if (options.value.protocol) {
537    requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
538  }
539
540  if (options.value.host) {
541    requestOptions.headers['Host'] = options.value.host;
542  }
543
544  if (options.value.headers) {
545    for (var header in options.value.headers) {
546       if (options.value.headers.hasOwnProperty(header)) {
547        requestOptions.headers[header] = options.value.headers[header];
548       }
549    }
550  }
551
552  if (options.isDefinedAndNonNull('pfx')
553   || options.isDefinedAndNonNull('key')
554   || options.isDefinedAndNonNull('passphrase')
555   || options.isDefinedAndNonNull('cert')
556   || options.isDefinedAndNonNull('ca')
557   || options.isDefinedAndNonNull('ciphers')
558   || options.isDefinedAndNonNull('rejectUnauthorized')) {
559
560    if (isNodeV4) {
561      throw new Error('Client side certificates are not supported on Node 0.4.x');
562    }
563
564    if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx;
565    if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key;
566    if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase;
567    if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert;
568    if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca;
569    if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers;
570    if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized;
571
572    if (!agent) {
573        // global agent ignores client side certificates
574        agent = new httpObj.Agent(requestOptions);
575    }
576  }
577
578  if (isNodeV4) {
579    requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || '');
580  }
581  else requestOptions.path = serverUrl.path || '/';
582
583  if (agent) {
584    requestOptions.agent = agent;
585  }
586
587  if (isUnixSocket) {
588    requestOptions.socketPath = serverUrl.pathname;
589  }
590  if (options.value.origin) {
591    if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
592    else requestOptions.headers['Origin'] = options.value.origin;
593  }
594
595  var self = this;
596  var req = httpObj.request(requestOptions);
597
598  (isNodeV4 ? agent : req).on('error', function(error) {
599    self.emit('error', error);
600    cleanupWebsocketResources.call(this, error);
601  });
602  (isNodeV4 ? agent : req).once('response', function(res) {
603    var error = new Error('unexpected server response (' + res.statusCode + ')');
604    self.emit('error', error);
605    cleanupWebsocketResources.call(this, error);
606  });
607  (isNodeV4 ? agent : req).once('upgrade', function(res, socket, upgradeHead) {
608    if (self.readyState == WebSocket.CLOSED) {
609      // client closed before server accepted connection
610      self.emit('close');
611      removeAllListeners(self);
612      socket.end();
613      return;
614    }
615    var serverKey = res.headers['sec-websocket-accept'];
616    if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) {
617      self.emit('error', 'invalid server key');
618      removeAllListeners(self);
619      socket.end();
620      return;
621    }
622
623    var serverProt = res.headers['sec-websocket-protocol'];
624    var protList = (options.value.protocol || "").split(/, */);
625    var protError = null;
626    if (!options.value.protocol && serverProt) {
627        protError = 'server sent a subprotocol even though none requested';
628    } else if (options.value.protocol && !serverProt) {
629        protError = 'server sent no subprotocol even though requested';
630    } else if (serverProt && protList.indexOf(serverProt) === -1) {
631        protError = 'server responded with an invalid protocol';
632    }
633    if (protError) {
634        self.emit('error', protError);
635        removeAllListeners(self);
636        socket.end();
637        return;
638    } else if (serverProt) {
639        self.protocol = serverProt;
640    }
641
642    establishConnection.call(self, Receiver, Sender, socket, upgradeHead);
643
644    // perform cleanup on http resources
645    removeAllListeners(isNodeV4 ? agent : req);
646    req = null;
647    agent = null;
648  });
649
650  req.end();
651  this.readyState = WebSocket.CONNECTING;
652}
653
654function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) {
655  this._socket = socket;
656  socket.setTimeout(0);
657  socket.setNoDelay(true);
658  var self = this;
659  this._receiver = new ReceiverClass();
660
661  // socket cleanup handlers
662  socket.on('end', cleanupWebsocketResources.bind(this));
663  socket.on('close', cleanupWebsocketResources.bind(this));
664  socket.on('error', cleanupWebsocketResources.bind(this));
665
666  // ensure that the upgradeHead is added to the receiver
667  function firstHandler(data) {
668    if (self.readyState != WebSocket.OPEN) return;
669    if (upgradeHead && upgradeHead.length > 0) {
670      self.bytesReceived += upgradeHead.length;
671      var head = upgradeHead;
672      upgradeHead = null;
673      self._receiver.add(head);
674    }
675    dataHandler = realHandler;
676    if (data) {
677      self.bytesReceived += data.length;
678      self._receiver.add(data);
679    }
680  }
681  // subsequent packets are pushed straight to the receiver
682  function realHandler(data) {
683    if (data) self.bytesReceived += data.length;
684    self._receiver.add(data);
685  }
686  var dataHandler = firstHandler;
687  // if data was passed along with the http upgrade,
688  // this will schedule a push of that on to the receiver.
689  // this has to be done on next tick, since the caller
690  // hasn't had a chance to set event handlers on this client
691  // object yet.
692  process.nextTick(firstHandler);
693
694  // receiver event handlers
695  self._receiver.ontext = function (data, flags) {
696    flags = flags || {};
697    self.emit('message', data, flags);
698  };
699  self._receiver.onbinary = function (data, flags) {
700    flags = flags || {};
701    flags.binary = true;
702    self.emit('message', data, flags);
703  };
704  self._receiver.onping = function(data, flags) {
705    flags = flags || {};
706    self.pong(data, {mask: !self._isServer, binary: flags.binary === true}, true);
707    self.emit('ping', data, flags);
708  };
709  self._receiver.onpong = function(data, flags) {
710    self.emit('pong', data, flags);
711  };
712  self._receiver.onclose = function(code, data, flags) {
713    flags = flags || {};
714    self.close(code, data);
715  };
716  self._receiver.onerror = function(reason, errorCode) {
717    // close the connection when the receiver reports a HyBi error code
718    self.close(typeof errorCode != 'undefined' ? errorCode : 1002, '');
719    self.emit('error', reason, errorCode);
720  };
721
722  // finalize the client
723  this._sender = new SenderClass(socket);
724  this._sender.on('error', function(error) {
725    self.close(1002, '');
726    self.emit('error', error);
727  });
728  this.readyState = WebSocket.OPEN;
729  this.emit('open');
730
731  socket.on('data', dataHandler);
732}
733
734function startQueue(instance) {
735  instance._queue = instance._queue || [];
736}
737
738function executeQueueSends(instance) {
739  var queue = instance._queue;
740  if (typeof queue == 'undefined') return;
741  delete instance._queue;
742  for (var i = 0, l = queue.length; i < l; ++i) {
743    queue[i]();
744  }
745}
746
747function sendStream(instance, stream, options, cb) {
748  stream.on('data', function(data) {
749    if (instance.readyState != WebSocket.OPEN) {
750      if (typeof cb == 'function') cb(new Error('not opened'));
751      else {
752        delete instance._queue;
753        instance.emit('error', new Error('not opened'));
754      }
755      return;
756    }
757    options.fin = false;
758    instance._sender.send(data, options);
759  });
760  stream.on('end', function() {
761    if (instance.readyState != WebSocket.OPEN) {
762      if (typeof cb == 'function') cb(new Error('not opened'));
763      else {
764        delete instance._queue;
765        instance.emit('error', new Error('not opened'));
766      }
767      return;
768    }
769    options.fin = true;
770    instance._sender.send(null, options);
771    if (typeof cb == 'function') cb(null);
772  });
773}
774
775function cleanupWebsocketResources(error) {
776  if (this.readyState == WebSocket.CLOSED) return;
777  var emitClose = this.readyState != WebSocket.CONNECTING;
778  this.readyState = WebSocket.CLOSED;
779
780  clearTimeout(this._closeTimer);
781  this._closeTimer = null;
782  if (emitClose) this.emit('close', this._closeCode || 1000, this._closeMessage || '');
783
784  if (this._socket) {
785    removeAllListeners(this._socket);
786    // catch all socket error after removing all standard handlers
787    var socket = this._socket;
788    this._socket.on('error', function() {
789      try { socket.destroy(); } catch (e) {}
790    });
791    try {
792      if (!error) this._socket.end();
793      else this._socket.destroy();
794    }
795    catch (e) { /* Ignore termination errors */ }
796    this._socket = null;
797  }
798  if (this._sender) {
799    removeAllListeners(this._sender);
800    this._sender = null;
801  }
802  if (this._receiver) {
803    this._receiver.cleanup();
804    this._receiver = null;
805  }
806  removeAllListeners(this);
807  this.on('error', function() {}); // catch all errors after this
808  delete this._queue;
809}
810
811function removeAllListeners(instance) {
812  if (isNodeV4) {
813    // node v4 doesn't *actually* remove all listeners globally,
814    // so we do that instead
815    instance._events = {};
816  }
817  else instance.removeAllListeners();
818}