Description
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.

211 lines
4.4 KiB

4 years ago
const Transport = require("../transport");
const parseqs = require("parseqs");
const parser = require("engine.io-parser");
const yeast = require("yeast");
const debug = require("debug")("engine.io-client:polling");
class Polling extends Transport {
/**
* Transport name.
*/
get name() {
return "polling";
}
/**
* Opens the socket (triggers polling). We write a PING message to determine
* when the transport is open.
*
* @api private
*/
doOpen() {
this.poll();
}
/**
* Pauses polling.
*
* @param {Function} callback upon buffers are flushed and transport is paused
* @api private
*/
pause(onPause) {
const self = this;
this.readyState = "pausing";
function pause() {
debug("paused");
self.readyState = "paused";
onPause();
}
if (this.polling || !this.writable) {
let 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
*/
poll() {
debug("polling");
this.polling = true;
this.doPoll();
this.emit("poll");
}
/**
* Overloads onData to detect payloads.
*
* @api private
*/
onData(data) {
const self = this;
debug("polling got data %s", data);
const callback = function(packet, index, total) {
// if its the first message we consider the transport open
if ("opening" === self.readyState && packet.type === "open") {
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).forEach(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
*/
doClose() {
const 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
*/
write(packets) {
this.writable = false;
parser.encodePayload(packets, data => {
this.doWrite(data, () => {
this.writable = true;
this.emit("drain");
});
});
}
/**
* Generates uri for connection.
*
* @api private
*/
uri() {
let query = this.query || {};
const schema = this.opts.secure ? "https" : "http";
let port = "";
// cache busting is forced
if (false !== this.opts.timestampRequests) {
query[this.opts.timestampParam] = yeast();
}
if (!this.supportsBinary && !query.sid) {
query.b64 = 1;
}
query = parseqs.encode(query);
// avoid port if default for schema
if (
this.opts.port &&
(("https" === schema && Number(this.opts.port) !== 443) ||
("http" === schema && Number(this.opts.port) !== 80))
) {
port = ":" + this.opts.port;
}
// prepend ? to query
if (query.length) {
query = "?" + query;
}
const ipv6 = this.opts.hostname.indexOf(":") !== -1;
return (
schema +
"://" +
(ipv6 ? "[" + this.opts.hostname + "]" : this.opts.hostname) +
port +
this.opts.path +
query
);
}
}
module.exports = Polling;