123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- import { AbortError, HttpError, TimeoutError } from "./Errors";
- import { HttpClient, HttpResponse } from "./HttpClient";
- import { LogLevel } from "./ILogger";
- import { Platform, getGlobalThis, isArrayBuffer } from "./Utils";
- export class FetchHttpClient extends HttpClient {
- constructor(logger) {
- super();
- this._logger = logger;
- if (typeof fetch === "undefined") {
- // In order to ignore the dynamic require in webpack builds we need to do this magic
- // @ts-ignore: TS doesn't know about these names
- const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
- // Cookies aren't automatically handled in Node so we need to add a CookieJar to preserve cookies across requests
- this._jar = new (requireFunc("tough-cookie")).CookieJar();
- this._fetchType = requireFunc("node-fetch");
- // node-fetch doesn't have a nice API for getting and setting cookies
- // fetch-cookie will wrap a fetch implementation with a default CookieJar or a provided one
- this._fetchType = requireFunc("fetch-cookie")(this._fetchType, this._jar);
- }
- else {
- this._fetchType = fetch.bind(getGlobalThis());
- }
- if (typeof AbortController === "undefined") {
- // In order to ignore the dynamic require in webpack builds we need to do this magic
- // @ts-ignore: TS doesn't know about these names
- const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
- // Node needs EventListener methods on AbortController which our custom polyfill doesn't provide
- this._abortControllerType = requireFunc("abort-controller");
- }
- else {
- this._abortControllerType = AbortController;
- }
- }
- /** @inheritDoc */
- async send(request) {
- // Check that abort was not signaled before calling send
- if (request.abortSignal && request.abortSignal.aborted) {
- throw new AbortError();
- }
- if (!request.method) {
- throw new Error("No method defined.");
- }
- if (!request.url) {
- throw new Error("No url defined.");
- }
- const abortController = new this._abortControllerType();
- let error;
- // Hook our abortSignal into the abort controller
- if (request.abortSignal) {
- request.abortSignal.onabort = () => {
- abortController.abort();
- error = new AbortError();
- };
- }
- // If a timeout has been passed in, setup a timeout to call abort
- // Type needs to be any to fit window.setTimeout and NodeJS.setTimeout
- let timeoutId = null;
- if (request.timeout) {
- const msTimeout = request.timeout;
- timeoutId = setTimeout(() => {
- abortController.abort();
- this._logger.log(LogLevel.Warning, `Timeout from HTTP request.`);
- error = new TimeoutError();
- }, msTimeout);
- }
- if (request.content === "") {
- request.content = undefined;
- }
- if (request.content) {
- // Explicitly setting the Content-Type header for React Native on Android platform.
- request.headers = request.headers || {};
- if (isArrayBuffer(request.content)) {
- request.headers["Content-Type"] = "application/octet-stream";
- }
- else {
- request.headers["Content-Type"] = "text/plain;charset=UTF-8";
- }
- }
- let response;
- try {
- response = await this._fetchType(request.url, {
- body: request.content,
- cache: "no-cache",
- credentials: request.withCredentials === true ? "include" : "same-origin",
- headers: {
- "X-Requested-With": "XMLHttpRequest",
- ...request.headers,
- },
- method: request.method,
- mode: "cors",
- redirect: "follow",
- signal: abortController.signal,
- });
- }
- catch (e) {
- if (error) {
- throw error;
- }
- this._logger.log(LogLevel.Warning, `Error from HTTP request. ${e}.`);
- throw e;
- }
- finally {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- if (request.abortSignal) {
- request.abortSignal.onabort = null;
- }
- }
- if (!response.ok) {
- const errorMessage = await deserializeContent(response, "text");
- throw new HttpError(errorMessage || response.statusText, response.status);
- }
- const content = deserializeContent(response, request.responseType);
- const payload = await content;
- return new HttpResponse(response.status, response.statusText, payload);
- }
- getCookieString(url) {
- let cookies = "";
- if (Platform.isNode && this._jar) {
- // @ts-ignore: unused variable
- this._jar.getCookies(url, (e, c) => cookies = c.join("; "));
- }
- return cookies;
- }
- }
- function deserializeContent(response, responseType) {
- let content;
- switch (responseType) {
- case "arraybuffer":
- content = response.arrayBuffer();
- break;
- case "text":
- content = response.text();
- break;
- case "blob":
- case "document":
- case "json":
- throw new Error(`${responseType} is not supported.`);
- default:
- content = response.text();
- break;
- }
- return content;
- }
- //# sourceMappingURL=FetchHttpClient.js.map
|