|
|
/** * Module dependencies. */
var Transport = require('../transport'); var parser = require('engine.io-parser'); var parseqs = require('parseqs'); var inherit = require('component-inherit'); var debug = require('debug')('engine.io-client:websocket');
/** * `ws` exposes a WebSocket-compatible interface in * Node, or the `WebSocket` or `MozWebSocket` globals * in the browser. */
var WebSocket = require('ws');
/** * Module exports. */
module.exports = WS;
/** * WebSocket transport constructor. * * @api {Object} connection options * @api public */
function WS(opts){ var forceBase64 = (opts && opts.forceBase64); if (forceBase64) { this.supportsBinary = false; } Transport.call(this, opts); }
/** * Inherits from Transport. */
inherit(WS, Transport);
/** * Transport name. * * @api public */
WS.prototype.name = 'websocket';
/* * WebSockets support binary */
WS.prototype.supportsBinary = true;
/** * Opens socket. * * @api private */
WS.prototype.doOpen = function(){ if (!this.check()) { // let probe timeout
return; }
var self = this; var uri = this.uri(); var protocols = void(0); var opts = { agent: this.agent };
this.ws = new WebSocket(uri, protocols, opts);
if (this.ws.binaryType === undefined) { this.supportsBinary = false; }
this.ws.binaryType = 'arraybuffer'; this.addEventListeners(); };
/** * Adds event listeners to the socket * * @api private */
WS.prototype.addEventListeners = function(){ var self = this;
this.ws.onopen = function(){ self.onOpen(); }; this.ws.onclose = function(){ self.onClose(); }; this.ws.onmessage = function(ev){ self.onData(ev.data); }; this.ws.onerror = function(e){ self.onError('websocket error', e); }; };
/** * Override `onData` to use a timer on iOS. * See: https://gist.github.com/mloughran/2052006
* * @api private */
if ('undefined' != typeof navigator && /iPad|iPhone|iPod/i.test(navigator.userAgent)) { WS.prototype.onData = function(data){ var self = this; setTimeout(function(){ Transport.prototype.onData.call(self, data); }, 0); }; }
/** * Writes data to socket. * * @param {Array} array of packets. * @api private */
WS.prototype.write = function(packets){ var self = this; this.writable = false; // encodePacket efficient as it uses WS framing
// no need for encodePayload
for (var i = 0, l = packets.length; i < l; i++) { parser.encodePacket(packets[i], this.supportsBinary, function(data) { //Sometimes the websocket has already been closed but the browser didn't
//have a chance of informing us about it yet, in that case send will
//throw an error
try { self.ws.send(data); } catch (e){ debug('websocket closed before onclose event'); } }); }
function ondrain() { self.writable = true; self.emit('drain'); } // fake drain
// defer to next tick to allow Socket to clear writeBuffer
setTimeout(ondrain, 0); };
/** * Called upon close * * @api private */
WS.prototype.onClose = function(){ Transport.prototype.onClose.call(this); };
/** * Closes socket. * * @api private */
WS.prototype.doClose = function(){ if (typeof this.ws !== 'undefined') { this.ws.close(); } };
/** * Generates uri for connection. * * @api private */
WS.prototype.uri = function(){ var query = this.query || {}; var schema = this.secure ? 'wss' : 'ws'; var port = '';
// avoid port if default for schema
if (this.port && (('wss' == schema && this.port != 443) || ('ws' == schema && this.port != 80))) { port = ':' + this.port; }
// append timestamp to URI
if (this.timestampRequests) { query[this.timestampParam] = +new Date; }
// communicate binary support capabilities
if (!this.supportsBinary) { query.b64 = 1; }
query = parseqs.encode(query);
// prepend ? to query
if (query.length) { query = '?' + query; }
return schema + '://' + this.hostname + port + this.path + query; };
/** * Feature detection for WebSocket. * * @return {Boolean} whether this transport is available. * @api public */
WS.prototype.check = function(){ return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); };
|