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}