/** * Module dependencies. */ var Transport = require('../transport') , util = require('../util') , parser = require('../parser') , debug = require('debug')('engine.io-client:polling'); /** * Module exports. */ module.exports = Polling; /** * Global reference. */ var global = 'undefined' != typeof window ? window : global; /** * Polling interface. * * @param {Object} opts * @api private */ function Polling(opts){ Transport.call(this, opts); } /** * Inherits from Transport. */ util.inherits(Polling, Transport); /** * Transport name. */ Polling.prototype.name = 'polling'; /** * Opens the socket (triggers polling). We write a PING message to determine * when the transport is open. * * @api private */ Polling.prototype.doOpen = function(){ this.poll(); }; /** * Pauses polling. * * @param {Function} callback upon buffers are flushed and transport is paused * @api private */ Polling.prototype.pause = function(onPause){ var pending = 0; var self = this; this.readyState = 'pausing'; function pause(){ debug('paused'); self.readyState = 'paused'; onPause(); } if (this.polling || !this.writable) { var total = 0; if (this.polling) { debug('we are currently polling - waiting to pause'); total++; this.once('pollComplete', function(){ debug('pre-pause polling complete'); --total || pause(); }); } if (!this.writable) { debug('we are currently writing - waiting to pause'); total++; this.once('drain', function(){ debug('pre-pause writing complete'); --total || pause(); }); } } else { pause(); } }; /** * Starts polling cycle. * * @api public */ Polling.prototype.poll = function(){ debug('polling'); this.polling = true; this.doPoll(); this.emit('poll'); }; /** * Overloads onData to detect payloads. * * @api private */ Polling.prototype.onData = function(data){ debug('polling got data %s', data); // decode payload var packets = parser.decodePayload(data); for (var i = 0, l = packets.length; i < l; i++) { // if its the first message we consider the trnasport open if ('opening' == this.readyState) { this.onOpen(); } // if its a close packet, we close the ongoing requests if ('close' == packets[i].type) { this.onClose(); return; } // otherwise bypass onData and handle the message this.onPacket(packets[i]); } // if we got data we're not polling this.polling = false; this.emit('pollComplete'); if ('open' == this.readyState) { this.poll(); } else { debug('ignoring poll - transport state "%s"', this.readyState); } }; /** * For polling, send a close packet. * * @api private */ Polling.prototype.doClose = function(){ debug('sending close packet'); this.send([{ type: 'close' }]); }; /** * Writes a packets payload. * * @param {Array} data packets * @param {Function} drain callback * @api private */ Polling.prototype.write = function(packets){ var self = this; this.writable = false; this.doWrite(parser.encodePayload(packets), function(){ self.writable = true; self.emit('drain'); }); }; /** * Generates uri for connection. * * @api private */ Polling.prototype.uri = function(){ var query = this.query || {}; var schema = this.secure ? 'https' : 'http'; var port = ''; // cache busting is forced for IE / android / iOS6 ಠ_ಠ if (global.ActiveXObject || util.ua.android || util.ua.ios6 || this.timestampRequests) { query[this.timestampParam] = +new Date; } query = util.qs(query); // avoid port if default for schema if (this.port && (('https' == schema && this.port != 443) || ('http' == schema && this.port != 80))) { port = ':' + this.port; } // prepend ? to query if (query.length) { query = '?' + query; } return schema + '://' + this.host + port + this.path + query; };