123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- "use strict";
- var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
- };
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.Request = exports.Polling = void 0;
- const transport_js_1 = require("../transport.js");
- const debug_1 = __importDefault(require("debug")); // debug()
- const yeast_js_1 = require("../contrib/yeast.js");
- const engine_io_parser_1 = require("engine.io-parser");
- const xmlhttprequest_js_1 = require("./xmlhttprequest.js");
- const component_emitter_1 = require("@socket.io/component-emitter");
- const util_js_1 = require("../util.js");
- const globalThis_js_1 = require("../globalThis.js");
- const debug = (0, debug_1.default)("engine.io-client:polling"); // debug()
- function empty() { }
- const hasXHR2 = (function () {
- const xhr = new xmlhttprequest_js_1.XHR({
- xdomain: false,
- });
- return null != xhr.responseType;
- })();
- class Polling extends transport_js_1.Transport {
- /**
- * XHR Polling constructor.
- *
- * @param {Object} opts
- * @package
- */
- constructor(opts) {
- super(opts);
- this.polling = false;
- if (typeof location !== "undefined") {
- const isSSL = "https:" === location.protocol;
- let port = location.port;
- // some user agents have empty `location.port`
- if (!port) {
- port = isSSL ? "443" : "80";
- }
- this.xd =
- (typeof location !== "undefined" &&
- opts.hostname !== location.hostname) ||
- port !== opts.port;
- }
- /**
- * XHR supports binary
- */
- const forceBase64 = opts && opts.forceBase64;
- this.supportsBinary = hasXHR2 && !forceBase64;
- if (this.opts.withCredentials) {
- this.cookieJar = (0, xmlhttprequest_js_1.createCookieJar)();
- }
- }
- get name() {
- return "polling";
- }
- /**
- * Opens the socket (triggers polling). We write a PING message to determine
- * when the transport is open.
- *
- * @protected
- */
- doOpen() {
- this.poll();
- }
- /**
- * Pauses polling.
- *
- * @param {Function} onPause - callback upon buffers are flushed and transport is paused
- * @package
- */
- pause(onPause) {
- this.readyState = "pausing";
- const pause = () => {
- debug("paused");
- this.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.
- *
- * @private
- */
- poll() {
- debug("polling");
- this.polling = true;
- this.doPoll();
- this.emitReserved("poll");
- }
- /**
- * Overloads onData to detect payloads.
- *
- * @protected
- */
- onData(data) {
- debug("polling got data %s", data);
- const callback = (packet) => {
- // if its the first message we consider the transport open
- if ("opening" === this.readyState && packet.type === "open") {
- this.onOpen();
- }
- // if its a close packet, we close the ongoing requests
- if ("close" === packet.type) {
- this.onClose({ description: "transport closed by the server" });
- return false;
- }
- // otherwise bypass onData and handle the message
- this.onPacket(packet);
- };
- // decode payload
- (0, engine_io_parser_1.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.emitReserved("pollComplete");
- if ("open" === this.readyState) {
- this.poll();
- }
- else {
- debug('ignoring poll - transport state "%s"', this.readyState);
- }
- }
- }
- /**
- * For polling, send a close packet.
- *
- * @protected
- */
- doClose() {
- const close = () => {
- debug("writing close packet");
- this.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} packets - data packets
- * @protected
- */
- write(packets) {
- this.writable = false;
- (0, engine_io_parser_1.encodePayload)(packets, (data) => {
- this.doWrite(data, () => {
- this.writable = true;
- this.emitReserved("drain");
- });
- });
- }
- /**
- * Generates uri for connection.
- *
- * @private
- */
- uri() {
- const schema = this.opts.secure ? "https" : "http";
- const query = this.query || {};
- // cache busting is forced
- if (false !== this.opts.timestampRequests) {
- query[this.opts.timestampParam] = (0, yeast_js_1.yeast)();
- }
- if (!this.supportsBinary && !query.sid) {
- query.b64 = 1;
- }
- return this.createUri(schema, query);
- }
- /**
- * Creates a request.
- *
- * @param {String} method
- * @private
- */
- request(opts = {}) {
- Object.assign(opts, { xd: this.xd, cookieJar: this.cookieJar }, this.opts);
- return new Request(this.uri(), opts);
- }
- /**
- * Sends data.
- *
- * @param {String} data to send.
- * @param {Function} called upon flush.
- * @private
- */
- doWrite(data, fn) {
- const req = this.request({
- method: "POST",
- data: data,
- });
- req.on("success", fn);
- req.on("error", (xhrStatus, context) => {
- this.onError("xhr post error", xhrStatus, context);
- });
- }
- /**
- * Starts a poll cycle.
- *
- * @private
- */
- doPoll() {
- debug("xhr poll");
- const req = this.request();
- req.on("data", this.onData.bind(this));
- req.on("error", (xhrStatus, context) => {
- this.onError("xhr poll error", xhrStatus, context);
- });
- this.pollXhr = req;
- }
- }
- exports.Polling = Polling;
- class Request extends component_emitter_1.Emitter {
- /**
- * Request constructor
- *
- * @param {Object} options
- * @package
- */
- constructor(uri, opts) {
- super();
- (0, util_js_1.installTimerFunctions)(this, opts);
- this.opts = opts;
- this.method = opts.method || "GET";
- this.uri = uri;
- this.data = undefined !== opts.data ? opts.data : null;
- this.create();
- }
- /**
- * Creates the XHR object and sends the request.
- *
- * @private
- */
- create() {
- var _a;
- const opts = (0, util_js_1.pick)(this.opts, "agent", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "autoUnref");
- opts.xdomain = !!this.opts.xd;
- const xhr = (this.xhr = new xmlhttprequest_js_1.XHR(opts));
- try {
- debug("xhr open %s: %s", this.method, this.uri);
- xhr.open(this.method, this.uri, true);
- try {
- if (this.opts.extraHeaders) {
- xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);
- for (let i in this.opts.extraHeaders) {
- if (this.opts.extraHeaders.hasOwnProperty(i)) {
- xhr.setRequestHeader(i, this.opts.extraHeaders[i]);
- }
- }
- }
- }
- catch (e) { }
- if ("POST" === this.method) {
- try {
- xhr.setRequestHeader("Content-type", "text/plain;charset=UTF-8");
- }
- catch (e) { }
- }
- try {
- xhr.setRequestHeader("Accept", "*/*");
- }
- catch (e) { }
- (_a = this.opts.cookieJar) === null || _a === void 0 ? void 0 : _a.addCookies(xhr);
- // ie6 check
- if ("withCredentials" in xhr) {
- xhr.withCredentials = this.opts.withCredentials;
- }
- if (this.opts.requestTimeout) {
- xhr.timeout = this.opts.requestTimeout;
- }
- xhr.onreadystatechange = () => {
- var _a;
- if (xhr.readyState === 3) {
- (_a = this.opts.cookieJar) === null || _a === void 0 ? void 0 : _a.parseCookies(xhr);
- }
- if (4 !== xhr.readyState)
- return;
- if (200 === xhr.status || 1223 === xhr.status) {
- this.onLoad();
- }
- else {
- // make sure the `error` event handler that's user-set
- // does not throw in the same tick and gets caught here
- this.setTimeoutFn(() => {
- this.onError(typeof xhr.status === "number" ? xhr.status : 0);
- }, 0);
- }
- };
- debug("xhr data %s", this.data);
- xhr.send(this.data);
- }
- catch (e) {
- // Need to defer since .create() is called directly from the constructor
- // and thus the 'error' event can only be only bound *after* this exception
- // occurs. Therefore, also, we cannot throw here at all.
- this.setTimeoutFn(() => {
- this.onError(e);
- }, 0);
- return;
- }
- if (typeof document !== "undefined") {
- this.index = Request.requestsCount++;
- Request.requests[this.index] = this;
- }
- }
- /**
- * Called upon error.
- *
- * @private
- */
- onError(err) {
- this.emitReserved("error", err, this.xhr);
- this.cleanup(true);
- }
- /**
- * Cleans up house.
- *
- * @private
- */
- cleanup(fromError) {
- if ("undefined" === typeof this.xhr || null === this.xhr) {
- return;
- }
- this.xhr.onreadystatechange = empty;
- if (fromError) {
- try {
- this.xhr.abort();
- }
- catch (e) { }
- }
- if (typeof document !== "undefined") {
- delete Request.requests[this.index];
- }
- this.xhr = null;
- }
- /**
- * Called upon load.
- *
- * @private
- */
- onLoad() {
- const data = this.xhr.responseText;
- if (data !== null) {
- this.emitReserved("data", data);
- this.emitReserved("success");
- this.cleanup();
- }
- }
- /**
- * Aborts the request.
- *
- * @package
- */
- abort() {
- this.cleanup();
- }
- }
- exports.Request = Request;
- Request.requestsCount = 0;
- Request.requests = {};
- /**
- * Aborts pending requests when unloading the window. This is needed to prevent
- * memory leaks (e.g. when using IE) and to ensure that no spurious error is
- * emitted.
- */
- if (typeof document !== "undefined") {
- // @ts-ignore
- if (typeof attachEvent === "function") {
- // @ts-ignore
- attachEvent("onunload", unloadHandler);
- }
- else if (typeof addEventListener === "function") {
- const terminationEvent = "onpagehide" in globalThis_js_1.globalThisShim ? "pagehide" : "unload";
- addEventListener(terminationEvent, unloadHandler, false);
- }
- }
- function unloadHandler() {
- for (let i in Request.requests) {
- if (Request.requests.hasOwnProperty(i)) {
- Request.requests[i].abort();
- }
- }
- }
|