This commit is contained in:
Rikki Gibson 2018-05-17 15:46:06 -07:00
Родитель cd33133617
Коммит 11896a2ce9
4 изменённых файлов: 68 добавлений и 43 удалений

Просмотреть файл

@ -3,17 +3,28 @@
import * as FormData from "form-data";
import * as xml2js from "isomorphic-xml2js";
import axiosFactory, { AxiosResponse, AxiosError, AxiosRequestConfig } from "axios";
import { HttpClient } from "./httpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResource } from "./webResource";
import { RestError } from "./restError";
import { HttpHeaders } from "./httpHeaders";
import { isNode } from "./util/utils";
import * as tough from "tough-cookie";
const axios = axiosFactory.create();
if (isNode) {
// Workaround for https://github.com/axios/axios/issues/1158
axios.interceptors.request.use(config => ({ ...config, method: config.method && config.method.toUpperCase() }));
}
/**
* A HttpClient implementation that uses fetch to send HTTP requests.
*/
export class FetchHttpClient implements HttpClient {
private readonly cookieJar = isNode ? new tough.CookieJar() : null;
public async sendRequest(httpRequest: WebResource): Promise<HttpOperationResponse> {
if (!httpRequest) {
return Promise.reject(new Error("options (WebResource) cannot be null or undefined and must be of type object."));
@ -59,43 +70,76 @@ export class FetchHttpClient implements HttpClient {
}
}
// allow cross-origin cookies in browser
(httpRequest as any).credentials = "include";
if (this.cookieJar) {
const cookieString = await new Promise<string>((resolve, reject) => {
this.cookieJar!.getCookieString(httpRequest.url, (err, cookie) => {
if (err) {
reject(err);
} else {
resolve(cookie);
}
});
});
let res: Response;
try {
res = await myFetch(httpRequest.url, httpRequest);
} catch (err) {
return Promise.reject(err);
httpRequest.headers["Cookie"] = cookieString;
}
let res: AxiosResponse;
try {
const config: AxiosRequestConfig = {
method: httpRequest.method,
url: httpRequest.url,
headers: httpRequest.headers,
data: httpRequest.body,
transformResponse: undefined,
validateStatus: () => true,
withCredentials: true,
responseType: httpRequest.rawResponse ? (isNode ? "stream" : "blob") : "text"
};
res = await axios(config);
} catch (err) {
const axiosErr = err as AxiosError;
throw new RestError(axiosErr.message, "REQUEST_SEND_ERROR", undefined, httpRequest);
}
const headers = new HttpHeaders();
res.headers.forEach((value: string, name: string) => {
headers.set(name, value);
});
const headers = new HttpHeaders(res.headers);
const operationResponse: HttpOperationResponse = {
request: httpRequest,
status: res.status,
headers,
readableStreamBody: isNode ? res.body as any : undefined,
blobBody: isNode ? undefined : () => res.blob()
readableStreamBody: httpRequest.rawResponse && isNode ? res.data as any : undefined,
blobBody: !httpRequest.rawResponse || isNode ? undefined : () => res.data
};
if (this.cookieJar) {
const setCookieHeader = operationResponse.headers.get("Set-Cookie");
if (setCookieHeader != undefined) {
await new Promise((resolve, reject) => {
this.cookieJar!.setCookie(setCookieHeader, httpRequest.url, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
}
if (!httpRequest.rawResponse) {
try {
operationResponse.bodyAsText = await res.text();
operationResponse.bodyAsText = res.data;
} catch (err) {
const msg = `Error "${err}" occured while converting the raw response body into string.`;
const errCode = err.code || "RAWTEXT_CONVERSION_ERROR";
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, res.body);
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, res.data);
return Promise.reject(e);
}
try {
if (operationResponse.bodyAsText) {
const contentType = res.headers.get("Content-Type")!;
const contentType = operationResponse.headers.get("Content-Type");
if (contentType === "application/xml" || contentType === "text/xml") {
const xmlParser = new xml2js.Parser(XML2JS_PARSER_OPTS);
const parseString = new Promise(function (resolve: (result: any) => void, reject: (err: any) => void) {
@ -124,26 +168,6 @@ export class FetchHttpClient implements HttpClient {
}
}
/**
* Provides the fetch() method based on the environment.
* @returns {fetch} fetch - The fetch() method available in the environment to make requests
*/
export function getFetch(): Function {
// using window.Fetch in Edge gives a TypeMismatchError
// (https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8546263/).
// Hence we will be using the fetch-ponyfill for Edge.
if (typeof window !== "undefined" && window.fetch && window.navigator &&
window.navigator.userAgent && window.navigator.userAgent.indexOf("Edge/") === -1) {
return window.fetch.bind(window);
}
return require("fetch-ponyfill")({ useCookie: true }).fetch;
}
/**
* A constant that provides the fetch() method based on the environment.
*/
export const myFetch = getFetch();
const XML2JS_PARSER_OPTS: xml2js.OptionsV2 = {
explicitArray: false,
explicitCharkey: false,

Просмотреть файл

@ -29,25 +29,26 @@
"types": "./typings/lib/msRest.d.ts",
"license": "MIT",
"dependencies": {
"@types/axios": "^0.14.0",
"@types/express": "^4.11.1",
"@types/form-data": "^2.2.1",
"@types/is-stream": "^1.1.0",
"@types/node": "^9.4.6",
"@types/node-fetch": "^1.6.7",
"@types/uuid": "^3.4.3",
"fetch-cookie": "^0.7.0",
"fetch-ponyfill": "amarzavery/fetch-ponyfill#master",
"axios": "^0.18.0",
"form-data": "^2.3.2",
"is-buffer": "^2.0.0",
"is-stream": "^1.1.0",
"isomorphic-xml2js": "^0.0.3",
"tough-cookie": "^2.3.4",
"url-parse": "^1.2.0",
"uuid": "^3.2.1",
"isomorphic-xml2js": "^0.0.3"
"uuid": "^3.2.1"
},
"devDependencies": {
"@types/glob": "^5.0.35",
"@types/mocha": "^5.2.0",
"@types/should": "^8.1.30",
"@types/tough-cookie": "^2.3.3",
"@types/url-parse": "^1.1.0",
"@types/webpack": "^4.1.3",
"@types/webpack-dev-middleware": "^2.0.1",

Просмотреть файл

@ -25,7 +25,7 @@ const config: webpack.Configuration = {
},
node: {
fs: false,
net: false,
net: "empty", // TODO: create wrapper package for tough-cookie and change this back to "false"
path: false,
dns: false,
tls: false,

Просмотреть файл

@ -31,7 +31,7 @@ const config = {
},
node: {
fs: false,
net: false,
net: "empty",
path: false,
dns: false,
tls: false,