uniapp,h5

polling-xhr.js 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import { Polling } from "./polling.js";
  2. import { Emitter } from "@socket.io/component-emitter";
  3. import { installTimerFunctions, pick } from "../util.js";
  4. import { globalThisShim as globalThis } from "../globals.node.js";
  5. import { hasCORS } from "../contrib/has-cors.js";
  6. function empty() { }
  7. export class BaseXHR extends Polling {
  8. /**
  9. * XHR Polling constructor.
  10. *
  11. * @param {Object} opts
  12. * @package
  13. */
  14. constructor(opts) {
  15. super(opts);
  16. if (typeof location !== "undefined") {
  17. const isSSL = "https:" === location.protocol;
  18. let port = location.port;
  19. // some user agents have empty `location.port`
  20. if (!port) {
  21. port = isSSL ? "443" : "80";
  22. }
  23. this.xd =
  24. (typeof location !== "undefined" &&
  25. opts.hostname !== location.hostname) ||
  26. port !== opts.port;
  27. }
  28. }
  29. /**
  30. * Sends data.
  31. *
  32. * @param {String} data to send.
  33. * @param {Function} called upon flush.
  34. * @private
  35. */
  36. doWrite(data, fn) {
  37. const req = this.request({
  38. method: "POST",
  39. data: data,
  40. });
  41. req.on("success", fn);
  42. req.on("error", (xhrStatus, context) => {
  43. this.onError("xhr post error", xhrStatus, context);
  44. });
  45. }
  46. /**
  47. * Starts a poll cycle.
  48. *
  49. * @private
  50. */
  51. doPoll() {
  52. const req = this.request();
  53. req.on("data", this.onData.bind(this));
  54. req.on("error", (xhrStatus, context) => {
  55. this.onError("xhr poll error", xhrStatus, context);
  56. });
  57. this.pollXhr = req;
  58. }
  59. }
  60. export class Request extends Emitter {
  61. /**
  62. * Request constructor
  63. *
  64. * @param {Object} options
  65. * @package
  66. */
  67. constructor(createRequest, uri, opts) {
  68. super();
  69. this.createRequest = createRequest;
  70. installTimerFunctions(this, opts);
  71. this._opts = opts;
  72. this._method = opts.method || "GET";
  73. this._uri = uri;
  74. this._data = undefined !== opts.data ? opts.data : null;
  75. this._create();
  76. }
  77. /**
  78. * Creates the XHR object and sends the request.
  79. *
  80. * @private
  81. */
  82. _create() {
  83. var _a;
  84. const opts = pick(this._opts, "agent", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "autoUnref");
  85. opts.xdomain = !!this._opts.xd;
  86. const xhr = (this._xhr = this.createRequest(opts));
  87. try {
  88. xhr.open(this._method, this._uri, true);
  89. try {
  90. if (this._opts.extraHeaders) {
  91. // @ts-ignore
  92. xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);
  93. for (let i in this._opts.extraHeaders) {
  94. if (this._opts.extraHeaders.hasOwnProperty(i)) {
  95. xhr.setRequestHeader(i, this._opts.extraHeaders[i]);
  96. }
  97. }
  98. }
  99. }
  100. catch (e) { }
  101. if ("POST" === this._method) {
  102. try {
  103. xhr.setRequestHeader("Content-type", "text/plain;charset=UTF-8");
  104. }
  105. catch (e) { }
  106. }
  107. try {
  108. xhr.setRequestHeader("Accept", "*/*");
  109. }
  110. catch (e) { }
  111. (_a = this._opts.cookieJar) === null || _a === void 0 ? void 0 : _a.addCookies(xhr);
  112. // ie6 check
  113. if ("withCredentials" in xhr) {
  114. xhr.withCredentials = this._opts.withCredentials;
  115. }
  116. if (this._opts.requestTimeout) {
  117. xhr.timeout = this._opts.requestTimeout;
  118. }
  119. xhr.onreadystatechange = () => {
  120. var _a;
  121. if (xhr.readyState === 3) {
  122. (_a = this._opts.cookieJar) === null || _a === void 0 ? void 0 : _a.parseCookies(
  123. // @ts-ignore
  124. xhr.getResponseHeader("set-cookie"));
  125. }
  126. if (4 !== xhr.readyState)
  127. return;
  128. if (200 === xhr.status || 1223 === xhr.status) {
  129. this._onLoad();
  130. }
  131. else {
  132. // make sure the `error` event handler that's user-set
  133. // does not throw in the same tick and gets caught here
  134. this.setTimeoutFn(() => {
  135. this._onError(typeof xhr.status === "number" ? xhr.status : 0);
  136. }, 0);
  137. }
  138. };
  139. xhr.send(this._data);
  140. }
  141. catch (e) {
  142. // Need to defer since .create() is called directly from the constructor
  143. // and thus the 'error' event can only be only bound *after* this exception
  144. // occurs. Therefore, also, we cannot throw here at all.
  145. this.setTimeoutFn(() => {
  146. this._onError(e);
  147. }, 0);
  148. return;
  149. }
  150. if (typeof document !== "undefined") {
  151. this._index = Request.requestsCount++;
  152. Request.requests[this._index] = this;
  153. }
  154. }
  155. /**
  156. * Called upon error.
  157. *
  158. * @private
  159. */
  160. _onError(err) {
  161. this.emitReserved("error", err, this._xhr);
  162. this._cleanup(true);
  163. }
  164. /**
  165. * Cleans up house.
  166. *
  167. * @private
  168. */
  169. _cleanup(fromError) {
  170. if ("undefined" === typeof this._xhr || null === this._xhr) {
  171. return;
  172. }
  173. this._xhr.onreadystatechange = empty;
  174. if (fromError) {
  175. try {
  176. this._xhr.abort();
  177. }
  178. catch (e) { }
  179. }
  180. if (typeof document !== "undefined") {
  181. delete Request.requests[this._index];
  182. }
  183. this._xhr = null;
  184. }
  185. /**
  186. * Called upon load.
  187. *
  188. * @private
  189. */
  190. _onLoad() {
  191. const data = this._xhr.responseText;
  192. if (data !== null) {
  193. this.emitReserved("data", data);
  194. this.emitReserved("success");
  195. this._cleanup();
  196. }
  197. }
  198. /**
  199. * Aborts the request.
  200. *
  201. * @package
  202. */
  203. abort() {
  204. this._cleanup();
  205. }
  206. }
  207. Request.requestsCount = 0;
  208. Request.requests = {};
  209. /**
  210. * Aborts pending requests when unloading the window. This is needed to prevent
  211. * memory leaks (e.g. when using IE) and to ensure that no spurious error is
  212. * emitted.
  213. */
  214. if (typeof document !== "undefined") {
  215. // @ts-ignore
  216. if (typeof attachEvent === "function") {
  217. // @ts-ignore
  218. attachEvent("onunload", unloadHandler);
  219. }
  220. else if (typeof addEventListener === "function") {
  221. const terminationEvent = "onpagehide" in globalThis ? "pagehide" : "unload";
  222. addEventListener(terminationEvent, unloadHandler, false);
  223. }
  224. }
  225. function unloadHandler() {
  226. for (let i in Request.requests) {
  227. if (Request.requests.hasOwnProperty(i)) {
  228. Request.requests[i].abort();
  229. }
  230. }
  231. }
  232. const hasXHR2 = (function () {
  233. const xhr = newRequest({
  234. xdomain: false,
  235. });
  236. return xhr && xhr.responseType !== null;
  237. })();
  238. /**
  239. * HTTP long-polling based on the built-in `XMLHttpRequest` object.
  240. *
  241. * Usage: browser
  242. *
  243. * @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
  244. */
  245. export class XHR extends BaseXHR {
  246. constructor(opts) {
  247. super(opts);
  248. const forceBase64 = opts && opts.forceBase64;
  249. this.supportsBinary = hasXHR2 && !forceBase64;
  250. }
  251. request(opts = {}) {
  252. Object.assign(opts, { xd: this.xd }, this.opts);
  253. return new Request(newRequest, this.uri(), opts);
  254. }
  255. }
  256. function newRequest(opts) {
  257. const xdomain = opts.xdomain;
  258. // XMLHttpRequest can be disabled on IE
  259. try {
  260. if ("undefined" !== typeof XMLHttpRequest && (!xdomain || hasCORS)) {
  261. return new XMLHttpRequest();
  262. }
  263. }
  264. catch (e) { }
  265. if (!xdomain) {
  266. try {
  267. return new globalThis[["Active"].concat("Object").join("X")]("Microsoft.XMLHTTP");
  268. }
  269. catch (e) { }
  270. }
  271. }