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.
285 lines
8.7 KiB
285 lines
8.7 KiB
"use strict"; |
|
var __importDefault = (this && this.__importDefault) || function (mod) { |
|
return (mod && mod.__esModule) ? mod : { "default": mod }; |
|
}; |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.XHR = exports.Request = exports.BaseXHR = void 0; |
|
const polling_js_1 = require("./polling.js"); |
|
const component_emitter_1 = require("@socket.io/component-emitter"); |
|
const util_js_1 = require("../util.js"); |
|
const globals_node_js_1 = require("../globals.node.js"); |
|
const has_cors_js_1 = require("../contrib/has-cors.js"); |
|
const debug_1 = __importDefault(require("debug")); // debug() |
|
const debug = (0, debug_1.default)("engine.io-client:polling"); // debug() |
|
function empty() { } |
|
class BaseXHR extends polling_js_1.Polling { |
|
/** |
|
* XHR Polling constructor. |
|
* |
|
* @param {Object} opts |
|
* @package |
|
*/ |
|
constructor(opts) { |
|
super(opts); |
|
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; |
|
} |
|
} |
|
/** |
|
* 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.BaseXHR = BaseXHR; |
|
class Request extends component_emitter_1.Emitter { |
|
/** |
|
* Request constructor |
|
* |
|
* @param {Object} options |
|
* @package |
|
*/ |
|
constructor(createRequest, uri, opts) { |
|
super(); |
|
this.createRequest = createRequest; |
|
(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 = this.createRequest(opts)); |
|
try { |
|
debug("xhr open %s: %s", this._method, this._uri); |
|
xhr.open(this._method, this._uri, true); |
|
try { |
|
if (this._opts.extraHeaders) { |
|
// @ts-ignore |
|
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( |
|
// @ts-ignore |
|
xhr.getResponseHeader("set-cookie")); |
|
} |
|
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 globals_node_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(); |
|
} |
|
} |
|
} |
|
const hasXHR2 = (function () { |
|
const xhr = newRequest({ |
|
xdomain: false, |
|
}); |
|
return xhr && xhr.responseType !== null; |
|
})(); |
|
/** |
|
* HTTP long-polling based on the built-in `XMLHttpRequest` object. |
|
* |
|
* Usage: browser |
|
* |
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest |
|
*/ |
|
class XHR extends BaseXHR { |
|
constructor(opts) { |
|
super(opts); |
|
const forceBase64 = opts && opts.forceBase64; |
|
this.supportsBinary = hasXHR2 && !forceBase64; |
|
} |
|
request(opts = {}) { |
|
Object.assign(opts, { xd: this.xd }, this.opts); |
|
return new Request(newRequest, this.uri(), opts); |
|
} |
|
} |
|
exports.XHR = XHR; |
|
function newRequest(opts) { |
|
const xdomain = opts.xdomain; |
|
// XMLHttpRequest can be disabled on IE |
|
try { |
|
if ("undefined" !== typeof XMLHttpRequest && (!xdomain || has_cors_js_1.hasCORS)) { |
|
return new XMLHttpRequest(); |
|
} |
|
} |
|
catch (e) { } |
|
if (!xdomain) { |
|
try { |
|
return new globals_node_js_1.globalThisShim[["Active"].concat("Object").join("X")]("Microsoft.XMLHTTP"); |
|
} |
|
catch (e) { } |
|
} |
|
}
|
|
|