智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. import platform from '../platform/index.js';
  2. import utils from '../utils.js';
  3. import AxiosError from '../core/AxiosError.js';
  4. import composeSignals from '../helpers/composeSignals.js';
  5. import { trackStream } from '../helpers/trackStream.js';
  6. import AxiosHeaders from '../core/AxiosHeaders.js';
  7. import {
  8. progressEventReducer,
  9. progressEventDecorator,
  10. asyncDecorator,
  11. } from '../helpers/progressEventReducer.js';
  12. import resolveConfig from '../helpers/resolveConfig.js';
  13. import settle from '../core/settle.js';
  14. import estimateDataURLDecodedBytes from '../helpers/estimateDataURLDecodedBytes.js';
  15. import { VERSION } from '../env/data.js';
  16. import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js';
  17. const DEFAULT_CHUNK_SIZE = 64 * 1024;
  18. const { isFunction } = utils;
  19. /**
  20. * Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
  21. * This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
  22. *
  23. * @param {string} str The string to encode
  24. *
  25. * @returns {string} UTF-8 bytes as a Latin-1 string
  26. */
  27. const encodeUTF8 = (str) =>
  28. encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) =>
  29. String.fromCharCode(parseInt(hex, 16))
  30. );
  31. // Node's WHATWG URL parser returns `username` and `password` percent-encoded.
  32. // Decode before composing the `auth` option so credentials such as
  33. // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
  34. // original value for malformed input so a bad encoding never throws.
  35. const decodeURIComponentSafe = (value) => {
  36. if (!utils.isString(value)) {
  37. return value;
  38. }
  39. try {
  40. return decodeURIComponent(value);
  41. } catch (error) {
  42. return value;
  43. }
  44. };
  45. const test = (fn, ...args) => {
  46. try {
  47. return !!fn(...args);
  48. } catch (e) {
  49. return false;
  50. }
  51. };
  52. const maybeWithAuthCredentials = (url) => {
  53. const protocolIndex = url.indexOf('://');
  54. let urlToCheck = url;
  55. if (protocolIndex !== -1) {
  56. urlToCheck = urlToCheck.slice(protocolIndex + 3);
  57. }
  58. return urlToCheck.includes('@') || urlToCheck.includes(':');
  59. };
  60. const factory = (env) => {
  61. const globalObject =
  62. utils.global !== undefined && utils.global !== null
  63. ? utils.global
  64. : globalThis;
  65. const { ReadableStream, TextEncoder } = globalObject;
  66. env = utils.merge.call(
  67. {
  68. skipUndefined: true,
  69. },
  70. {
  71. Request: globalObject.Request,
  72. Response: globalObject.Response,
  73. },
  74. env
  75. );
  76. const { fetch: envFetch, Request, Response } = env;
  77. const isFetchSupported = envFetch ? isFunction(envFetch) : typeof fetch === 'function';
  78. const isRequestSupported = isFunction(Request);
  79. const isResponseSupported = isFunction(Response);
  80. if (!isFetchSupported) {
  81. return false;
  82. }
  83. const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream);
  84. const encodeText =
  85. isFetchSupported &&
  86. (typeof TextEncoder === 'function'
  87. ? (
  88. (encoder) => (str) =>
  89. encoder.encode(str)
  90. )(new TextEncoder())
  91. : async (str) => new Uint8Array(await new Request(str).arrayBuffer()));
  92. const supportsRequestStream =
  93. isRequestSupported &&
  94. isReadableStreamSupported &&
  95. test(() => {
  96. let duplexAccessed = false;
  97. const request = new Request(platform.origin, {
  98. body: new ReadableStream(),
  99. method: 'POST',
  100. get duplex() {
  101. duplexAccessed = true;
  102. return 'half';
  103. },
  104. });
  105. const hasContentType = request.headers.has('Content-Type');
  106. if (request.body != null) {
  107. request.body.cancel();
  108. }
  109. return duplexAccessed && !hasContentType;
  110. });
  111. const supportsResponseStream =
  112. isResponseSupported &&
  113. isReadableStreamSupported &&
  114. test(() => utils.isReadableStream(new Response('').body));
  115. const resolvers = {
  116. stream: supportsResponseStream && ((res) => res.body),
  117. };
  118. isFetchSupported &&
  119. (() => {
  120. ['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach((type) => {
  121. !resolvers[type] &&
  122. (resolvers[type] = (res, config) => {
  123. let method = res && res[type];
  124. if (method) {
  125. return method.call(res);
  126. }
  127. throw new AxiosError(
  128. `Response type '${type}' is not supported`,
  129. AxiosError.ERR_NOT_SUPPORT,
  130. config
  131. );
  132. });
  133. });
  134. })();
  135. const getBodyLength = async (body) => {
  136. if (body == null) {
  137. return 0;
  138. }
  139. if (utils.isBlob(body)) {
  140. return body.size;
  141. }
  142. if (utils.isSpecCompliantForm(body)) {
  143. const _request = new Request(platform.origin, {
  144. method: 'POST',
  145. body,
  146. });
  147. return (await _request.arrayBuffer()).byteLength;
  148. }
  149. if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
  150. return body.byteLength;
  151. }
  152. if (utils.isURLSearchParams(body)) {
  153. body = body + '';
  154. }
  155. if (utils.isString(body)) {
  156. return (await encodeText(body)).byteLength;
  157. }
  158. };
  159. const resolveBodyLength = async (headers, body) => {
  160. const length = utils.toFiniteNumber(headers.getContentLength());
  161. return length == null ? getBodyLength(body) : length;
  162. };
  163. return async (config) => {
  164. let {
  165. url,
  166. method,
  167. data,
  168. signal,
  169. cancelToken,
  170. timeout,
  171. onDownloadProgress,
  172. onUploadProgress,
  173. responseType,
  174. headers,
  175. withCredentials = 'same-origin',
  176. fetchOptions,
  177. maxContentLength,
  178. maxBodyLength,
  179. } = resolveConfig(config);
  180. const hasMaxContentLength = utils.isNumber(maxContentLength) && maxContentLength > -1;
  181. const hasMaxBodyLength = utils.isNumber(maxBodyLength) && maxBodyLength > -1;
  182. const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined);
  183. let _fetch = envFetch || fetch;
  184. responseType = responseType ? (responseType + '').toLowerCase() : 'text';
  185. let composedSignal = composeSignals(
  186. [signal, cancelToken && cancelToken.toAbortSignal()],
  187. timeout
  188. );
  189. let request = null;
  190. const unsubscribe =
  191. composedSignal &&
  192. composedSignal.unsubscribe &&
  193. (() => {
  194. composedSignal.unsubscribe();
  195. });
  196. let requestContentLength;
  197. // AxiosError we raise while the request body is being streamed. Captured
  198. // by identity so the catch block can surface it directly, regardless of
  199. // how the runtime wraps the resulting fetch rejection (undici exposes it
  200. // as `err.cause`; some browsers drop the original error entirely).
  201. let pendingBodyError = null;
  202. const maxBodyLengthError = () =>
  203. new AxiosError(
  204. 'Request body larger than maxBodyLength limit',
  205. AxiosError.ERR_BAD_REQUEST,
  206. config,
  207. request
  208. );
  209. try {
  210. // HTTP basic authentication
  211. let auth = undefined;
  212. const configAuth = own('auth');
  213. if (configAuth) {
  214. const username = utils.getSafeProp(configAuth, 'username') || '';
  215. const password = utils.getSafeProp(configAuth, 'password') || '';
  216. auth = {
  217. username,
  218. password
  219. };
  220. }
  221. if (maybeWithAuthCredentials(url)) {
  222. const parsedURL = new URL(url, platform.origin);
  223. if (!auth && (parsedURL.username || parsedURL.password)) {
  224. const urlUsername = decodeURIComponentSafe(parsedURL.username);
  225. const urlPassword = decodeURIComponentSafe(parsedURL.password);
  226. auth = {
  227. username: urlUsername,
  228. password: urlPassword
  229. };
  230. }
  231. if (parsedURL.username || parsedURL.password) {
  232. parsedURL.username = '';
  233. parsedURL.password = '';
  234. url = parsedURL.href;
  235. }
  236. }
  237. if (auth) {
  238. headers.delete('authorization');
  239. headers.set(
  240. 'Authorization',
  241. 'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || '')))
  242. );
  243. }
  244. // Enforce maxContentLength for data: URLs up-front so we never materialize
  245. // an oversized payload. The HTTP adapter applies the same check (see http.js
  246. // "if (protocol === 'data:')" branch).
  247. if (hasMaxContentLength && typeof url === 'string' && url.startsWith('data:')) {
  248. const estimated = estimateDataURLDecodedBytes(url);
  249. if (estimated > maxContentLength) {
  250. throw new AxiosError(
  251. 'maxContentLength size of ' + maxContentLength + ' exceeded',
  252. AxiosError.ERR_BAD_RESPONSE,
  253. config,
  254. request
  255. );
  256. }
  257. }
  258. // Enforce maxBodyLength against known-size bodies before dispatch using
  259. // the body's *actual* size — never a caller-declared Content-Length,
  260. // which could under-report to slip an oversized body past the check.
  261. // Unknown-size streams return undefined here and are counted per-chunk
  262. // below as fetch consumes them.
  263. if (hasMaxBodyLength && method !== 'get' && method !== 'head') {
  264. const outboundLength = await getBodyLength(data);
  265. if (typeof outboundLength === 'number' && isFinite(outboundLength)) {
  266. requestContentLength = outboundLength;
  267. if (outboundLength > maxBodyLength) {
  268. throw maxBodyLengthError();
  269. }
  270. }
  271. }
  272. // A streamed body under maxBodyLength must be counted as fetch consumes
  273. // it; its size is never trusted from a caller-declared Content-Length.
  274. const mustEnforceStreamBody =
  275. hasMaxBodyLength && (utils.isReadableStream(data) || utils.isStream(data));
  276. const trackRequestStream = (stream, onProgress, flush) =>
  277. trackStream(
  278. stream,
  279. DEFAULT_CHUNK_SIZE,
  280. (loadedBytes) => {
  281. if (hasMaxBodyLength && loadedBytes > maxBodyLength) {
  282. throw (pendingBodyError = maxBodyLengthError());
  283. }
  284. onProgress && onProgress(loadedBytes);
  285. },
  286. flush
  287. );
  288. if (
  289. supportsRequestStream &&
  290. method !== 'get' &&
  291. method !== 'head' &&
  292. (onUploadProgress || mustEnforceStreamBody)
  293. ) {
  294. requestContentLength =
  295. requestContentLength == null ? await resolveBodyLength(headers, data) : requestContentLength;
  296. // A declared length of 0 is only trusted to skip the wrap when we are
  297. // not enforcing a stream limit (which must not rely on that header).
  298. if (requestContentLength !== 0 || mustEnforceStreamBody) {
  299. let _request = new Request(url, {
  300. method: 'POST',
  301. body: data,
  302. duplex: 'half',
  303. });
  304. let contentTypeHeader;
  305. if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
  306. headers.setContentType(contentTypeHeader);
  307. }
  308. if (_request.body) {
  309. const [onProgress, flush] =
  310. (onUploadProgress &&
  311. progressEventDecorator(
  312. requestContentLength,
  313. progressEventReducer(asyncDecorator(onUploadProgress))
  314. )) ||
  315. [];
  316. data = trackRequestStream(_request.body, onProgress, flush);
  317. }
  318. }
  319. } else if (
  320. mustEnforceStreamBody &&
  321. !isRequestSupported &&
  322. isReadableStreamSupported &&
  323. method !== 'get' &&
  324. method !== 'head'
  325. ) {
  326. data = trackRequestStream(data);
  327. } else if (
  328. mustEnforceStreamBody &&
  329. isRequestSupported &&
  330. !supportsRequestStream &&
  331. method !== 'get' &&
  332. method !== 'head'
  333. ) {
  334. throw new AxiosError(
  335. 'Stream request bodies are not supported by the current fetch implementation',
  336. AxiosError.ERR_NOT_SUPPORT,
  337. config,
  338. request
  339. );
  340. }
  341. if (!utils.isString(withCredentials)) {
  342. withCredentials = withCredentials ? 'include' : 'omit';
  343. }
  344. // Cloudflare Workers throws when credentials are defined
  345. // see https://github.com/cloudflare/workerd/issues/902
  346. const isCredentialsSupported = isRequestSupported && 'credentials' in Request.prototype;
  347. // If data is FormData and Content-Type is multipart/form-data without boundary,
  348. // delete it so fetch can set it correctly with the boundary
  349. if (utils.isFormData(data)) {
  350. const contentType = headers.getContentType();
  351. if (
  352. contentType &&
  353. /^multipart\/form-data/i.test(contentType) &&
  354. !/boundary=/i.test(contentType)
  355. ) {
  356. headers.delete('content-type');
  357. }
  358. }
  359. // Set User-Agent header if not already set (fetch defaults to 'node' in Node.js)
  360. headers.set('User-Agent', 'axios/' + VERSION, false);
  361. const resolvedOptions = {
  362. ...fetchOptions,
  363. signal: composedSignal,
  364. method: method.toUpperCase(),
  365. headers: toByteStringHeaderObject(headers.normalize()),
  366. body: data,
  367. duplex: 'half',
  368. credentials: isCredentialsSupported ? withCredentials : undefined,
  369. };
  370. request = isRequestSupported && new Request(url, resolvedOptions);
  371. let response = await (isRequestSupported
  372. ? _fetch(request, fetchOptions)
  373. : _fetch(url, resolvedOptions));
  374. const responseHeaders = AxiosHeaders.from(response.headers);
  375. // Cheap pre-check: if the server honestly declares a content-length that
  376. // already exceeds the cap, reject before we start streaming.
  377. if (hasMaxContentLength) {
  378. const declaredLength = utils.toFiniteNumber(responseHeaders.getContentLength());
  379. if (declaredLength != null && declaredLength > maxContentLength) {
  380. throw new AxiosError(
  381. 'maxContentLength size of ' + maxContentLength + ' exceeded',
  382. AxiosError.ERR_BAD_RESPONSE,
  383. config,
  384. request
  385. );
  386. }
  387. }
  388. const isStreamResponse =
  389. supportsResponseStream && (responseType === 'stream' || responseType === 'response');
  390. if (
  391. supportsResponseStream &&
  392. response.body &&
  393. (onDownloadProgress || hasMaxContentLength || (isStreamResponse && unsubscribe))
  394. ) {
  395. const options = {};
  396. ['status', 'statusText', 'headers'].forEach((prop) => {
  397. options[prop] = response[prop];
  398. });
  399. const responseContentLength = utils.toFiniteNumber(responseHeaders.getContentLength());
  400. const [onProgress, flush] =
  401. (onDownloadProgress &&
  402. progressEventDecorator(
  403. responseContentLength,
  404. progressEventReducer(asyncDecorator(onDownloadProgress), true)
  405. )) ||
  406. [];
  407. let bytesRead = 0;
  408. const onChunkProgress = (loadedBytes) => {
  409. if (hasMaxContentLength) {
  410. bytesRead = loadedBytes;
  411. if (bytesRead > maxContentLength) {
  412. throw new AxiosError(
  413. 'maxContentLength size of ' + maxContentLength + ' exceeded',
  414. AxiosError.ERR_BAD_RESPONSE,
  415. config,
  416. request
  417. );
  418. }
  419. }
  420. onProgress && onProgress(loadedBytes);
  421. };
  422. response = new Response(
  423. trackStream(response.body, DEFAULT_CHUNK_SIZE, onChunkProgress, () => {
  424. flush && flush();
  425. unsubscribe && unsubscribe();
  426. }),
  427. options
  428. );
  429. }
  430. responseType = responseType || 'text';
  431. let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](
  432. response,
  433. config
  434. );
  435. // Fallback enforcement for environments without ReadableStream support
  436. // (legacy runtimes). Detect materialized size from typed output; skip
  437. // streams/Response passthrough since the user will read those themselves.
  438. if (hasMaxContentLength && !supportsResponseStream && !isStreamResponse) {
  439. let materializedSize;
  440. if (responseData != null) {
  441. if (typeof responseData.byteLength === 'number') {
  442. materializedSize = responseData.byteLength;
  443. } else if (typeof responseData.size === 'number') {
  444. materializedSize = responseData.size;
  445. } else if (typeof responseData === 'string') {
  446. materializedSize =
  447. typeof TextEncoder === 'function'
  448. ? new TextEncoder().encode(responseData).byteLength
  449. : responseData.length;
  450. }
  451. }
  452. if (typeof materializedSize === 'number' && materializedSize > maxContentLength) {
  453. throw new AxiosError(
  454. 'maxContentLength size of ' + maxContentLength + ' exceeded',
  455. AxiosError.ERR_BAD_RESPONSE,
  456. config,
  457. request
  458. );
  459. }
  460. }
  461. !isStreamResponse && unsubscribe && unsubscribe();
  462. return await new Promise((resolve, reject) => {
  463. settle(resolve, reject, {
  464. data: responseData,
  465. headers: AxiosHeaders.from(response.headers),
  466. status: response.status,
  467. statusText: response.statusText,
  468. config,
  469. request,
  470. });
  471. });
  472. } catch (err) {
  473. unsubscribe && unsubscribe();
  474. // Safari can surface fetch aborts as a DOMException-like object whose
  475. // branded getters throw. Prefer our composed signal reason before reading
  476. // the caught error, preserving timeout vs cancellation semantics.
  477. if (composedSignal && composedSignal.aborted && composedSignal.reason instanceof AxiosError) {
  478. const canceledError = composedSignal.reason;
  479. canceledError.config = config;
  480. request && (canceledError.request = request);
  481. err !== canceledError && (canceledError.cause = err);
  482. throw canceledError;
  483. }
  484. // Surface a maxBodyLength violation we raised while the request body was
  485. // being streamed. Matching by identity (rather than reading
  486. // `err.cause.isAxiosError`) keeps the error deterministic across runtimes
  487. // and avoids both prototype-pollution reads and mis-attributing a foreign
  488. // AxiosError that merely happened to land in `err.cause`.
  489. if (pendingBodyError) {
  490. request && !pendingBodyError.request && (pendingBodyError.request = request);
  491. throw pendingBodyError;
  492. }
  493. // Re-throw AxiosErrors we raised synchronously (data: URL / content-length
  494. // pre-checks, response size enforcement) without re-wrapping them.
  495. if (err instanceof AxiosError) {
  496. request && !err.request && (err.request = request);
  497. throw err;
  498. }
  499. if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
  500. throw Object.assign(
  501. new AxiosError(
  502. 'Network Error',
  503. AxiosError.ERR_NETWORK,
  504. config,
  505. request,
  506. err && err.response
  507. ),
  508. {
  509. cause: err.cause || err,
  510. }
  511. );
  512. }
  513. throw AxiosError.from(err, err && err.code, config, request, err && err.response);
  514. }
  515. };
  516. };
  517. const seedCache = new Map();
  518. export const getFetch = (config) => {
  519. let env = (config && config.env) || {};
  520. const { fetch, Request, Response } = env;
  521. const seeds = [Request, Response, fetch];
  522. let len = seeds.length,
  523. i = len,
  524. seed,
  525. target,
  526. map = seedCache;
  527. while (i--) {
  528. seed = seeds[i];
  529. target = map.get(seed);
  530. target === undefined && map.set(seed, (target = i ? new Map() : factory(env)));
  531. map = target;
  532. }
  533. return target;
  534. };
  535. const adapter = getFetch();
  536. export default adapter;