You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
4.8 KiB
245 lines
4.8 KiB
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
var Transport = require('../transport'); |
|
var parseqs = require('parseqs'); |
|
var parser = require('engine.io-parser'); |
|
var inherit = require('component-inherit'); |
|
var yeast = require('yeast'); |
|
var debug = require('debug')('engine.io-client:polling'); |
|
|
|
/** |
|
* Module exports. |
|
*/ |
|
|
|
module.exports = Polling; |
|
|
|
/** |
|
* Is XHR2 supported? |
|
*/ |
|
|
|
var hasXHR2 = (function () { |
|
var XMLHttpRequest = require('xmlhttprequest-ssl'); |
|
var xhr = new XMLHttpRequest({ xdomain: false }); |
|
return null != xhr.responseType; |
|
})(); |
|
|
|
/** |
|
* Polling interface. |
|
* |
|
* @param {Object} opts |
|
* @api private |
|
*/ |
|
|
|
function Polling (opts) { |
|
var forceBase64 = (opts && opts.forceBase64); |
|
if (!hasXHR2 || forceBase64) { |
|
this.supportsBinary = false; |
|
} |
|
Transport.call(this, opts); |
|
} |
|
|
|
/** |
|
* Inherits from Transport. |
|
*/ |
|
|
|
inherit(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 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) { |
|
var self = this; |
|
debug('polling got data %s', data); |
|
var callback = function (packet, index, total) { |
|
// if its the first message we consider the transport open |
|
if ('opening' === self.readyState) { |
|
self.onOpen(); |
|
} |
|
|
|
// if its a close packet, we close the ongoing requests |
|
if ('close' === packet.type) { |
|
self.onClose(); |
|
return false; |
|
} |
|
|
|
// otherwise bypass onData and handle the message |
|
self.onPacket(packet); |
|
}; |
|
|
|
// decode payload |
|
parser.decodePayload(data, this.socket.binaryType, callback); |
|
|
|
// if an event did not trigger closing |
|
if ('closed' !== this.readyState) { |
|
// 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 () { |
|
var self = this; |
|
|
|
function close () { |
|
debug('writing close packet'); |
|
self.write([{ type: 'close' }]); |
|
} |
|
|
|
if ('open' === this.readyState) { |
|
debug('transport open - closing'); |
|
close(); |
|
} else { |
|
// in case we're trying to close while |
|
// handshaking is in progress (GH-164) |
|
debug('transport not open - deferring close'); |
|
this.once('open', 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; |
|
var callbackfn = function () { |
|
self.writable = true; |
|
self.emit('drain'); |
|
}; |
|
|
|
parser.encodePayload(packets, this.supportsBinary, function (data) { |
|
self.doWrite(data, callbackfn); |
|
}); |
|
}; |
|
|
|
/** |
|
* 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 |
|
if (false !== this.timestampRequests) { |
|
query[this.timestampParam] = yeast(); |
|
} |
|
|
|
if (!this.supportsBinary && !query.sid) { |
|
query.b64 = 1; |
|
} |
|
|
|
query = parseqs.encode(query); |
|
|
|
// avoid port if default for schema |
|
if (this.port && (('https' === schema && Number(this.port) !== 443) || |
|
('http' === schema && Number(this.port) !== 80))) { |
|
port = ':' + this.port; |
|
} |
|
|
|
// prepend ? to query |
|
if (query.length) { |
|
query = '?' + query; |
|
} |
|
|
|
var ipv6 = this.hostname.indexOf(':') !== -1; |
|
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; |
|
};
|
|
|