twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

polling.js (4947B)


      1 /**
      2  * Module dependencies.
      3  */
      4 
      5 var Transport = require('../transport');
      6 var parseqs = require('parseqs');
      7 var parser = require('engine.io-parser');
      8 var inherit = require('component-inherit');
      9 var yeast = require('yeast');
     10 var debug = require('debug')('engine.io-client:polling');
     11 
     12 /**
     13  * Module exports.
     14  */
     15 
     16 module.exports = Polling;
     17 
     18 /**
     19  * Is XHR2 supported?
     20  */
     21 
     22 var hasXHR2 = (function () {
     23   var XMLHttpRequest = require('xmlhttprequest-ssl');
     24   var xhr = new XMLHttpRequest({ xdomain: false });
     25   return null != xhr.responseType;
     26 })();
     27 
     28 /**
     29  * Polling interface.
     30  *
     31  * @param {Object} opts
     32  * @api private
     33  */
     34 
     35 function Polling (opts) {
     36   var forceBase64 = (opts && opts.forceBase64);
     37   if (!hasXHR2 || forceBase64) {
     38     this.supportsBinary = false;
     39   }
     40   Transport.call(this, opts);
     41 }
     42 
     43 /**
     44  * Inherits from Transport.
     45  */
     46 
     47 inherit(Polling, Transport);
     48 
     49 /**
     50  * Transport name.
     51  */
     52 
     53 Polling.prototype.name = 'polling';
     54 
     55 /**
     56  * Opens the socket (triggers polling). We write a PING message to determine
     57  * when the transport is open.
     58  *
     59  * @api private
     60  */
     61 
     62 Polling.prototype.doOpen = function () {
     63   this.poll();
     64 };
     65 
     66 /**
     67  * Pauses polling.
     68  *
     69  * @param {Function} callback upon buffers are flushed and transport is paused
     70  * @api private
     71  */
     72 
     73 Polling.prototype.pause = function (onPause) {
     74   var self = this;
     75 
     76   this.readyState = 'pausing';
     77 
     78   function pause () {
     79     debug('paused');
     80     self.readyState = 'paused';
     81     onPause();
     82   }
     83 
     84   if (this.polling || !this.writable) {
     85     var total = 0;
     86 
     87     if (this.polling) {
     88       debug('we are currently polling - waiting to pause');
     89       total++;
     90       this.once('pollComplete', function () {
     91         debug('pre-pause polling complete');
     92         --total || pause();
     93       });
     94     }
     95 
     96     if (!this.writable) {
     97       debug('we are currently writing - waiting to pause');
     98       total++;
     99       this.once('drain', function () {
    100         debug('pre-pause writing complete');
    101         --total || pause();
    102       });
    103     }
    104   } else {
    105     pause();
    106   }
    107 };
    108 
    109 /**
    110  * Starts polling cycle.
    111  *
    112  * @api public
    113  */
    114 
    115 Polling.prototype.poll = function () {
    116   debug('polling');
    117   this.polling = true;
    118   this.doPoll();
    119   this.emit('poll');
    120 };
    121 
    122 /**
    123  * Overloads onData to detect payloads.
    124  *
    125  * @api private
    126  */
    127 
    128 Polling.prototype.onData = function (data) {
    129   var self = this;
    130   debug('polling got data %s', data);
    131   var callback = function (packet, index, total) {
    132     // if its the first message we consider the transport open
    133     if ('opening' === self.readyState) {
    134       self.onOpen();
    135     }
    136 
    137     // if its a close packet, we close the ongoing requests
    138     if ('close' === packet.type) {
    139       self.onClose();
    140       return false;
    141     }
    142 
    143     // otherwise bypass onData and handle the message
    144     self.onPacket(packet);
    145   };
    146 
    147   // decode payload
    148   parser.decodePayload(data, this.socket.binaryType, callback);
    149 
    150   // if an event did not trigger closing
    151   if ('closed' !== this.readyState) {
    152     // if we got data we're not polling
    153     this.polling = false;
    154     this.emit('pollComplete');
    155 
    156     if ('open' === this.readyState) {
    157       this.poll();
    158     } else {
    159       debug('ignoring poll - transport state "%s"', this.readyState);
    160     }
    161   }
    162 };
    163 
    164 /**
    165  * For polling, send a close packet.
    166  *
    167  * @api private
    168  */
    169 
    170 Polling.prototype.doClose = function () {
    171   var self = this;
    172 
    173   function close () {
    174     debug('writing close packet');
    175     self.write([{ type: 'close' }]);
    176   }
    177 
    178   if ('open' === this.readyState) {
    179     debug('transport open - closing');
    180     close();
    181   } else {
    182     // in case we're trying to close while
    183     // handshaking is in progress (GH-164)
    184     debug('transport not open - deferring close');
    185     this.once('open', close);
    186   }
    187 };
    188 
    189 /**
    190  * Writes a packets payload.
    191  *
    192  * @param {Array} data packets
    193  * @param {Function} drain callback
    194  * @api private
    195  */
    196 
    197 Polling.prototype.write = function (packets) {
    198   var self = this;
    199   this.writable = false;
    200   var callbackfn = function () {
    201     self.writable = true;
    202     self.emit('drain');
    203   };
    204 
    205   parser.encodePayload(packets, this.supportsBinary, function (data) {
    206     self.doWrite(data, callbackfn);
    207   });
    208 };
    209 
    210 /**
    211  * Generates uri for connection.
    212  *
    213  * @api private
    214  */
    215 
    216 Polling.prototype.uri = function () {
    217   var query = this.query || {};
    218   var schema = this.secure ? 'https' : 'http';
    219   var port = '';
    220 
    221   // cache busting is forced
    222   if (false !== this.timestampRequests) {
    223     query[this.timestampParam] = yeast();
    224   }
    225 
    226   if (!this.supportsBinary && !query.sid) {
    227     query.b64 = 1;
    228   }
    229 
    230   query = parseqs.encode(query);
    231 
    232   // avoid port if default for schema
    233   if (this.port && (('https' === schema && Number(this.port) !== 443) ||
    234      ('http' === schema && Number(this.port) !== 80))) {
    235     port = ':' + this.port;
    236   }
    237 
    238   // prepend ? to query
    239   if (query.length) {
    240     query = '?' + query;
    241   }
    242 
    243   var ipv6 = this.hostname.indexOf(':') !== -1;
    244   return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
    245 };