зеркало из https://github.com/Azure/ms-rest-js.git
193 строки
7.1 KiB
TypeScript
193 строки
7.1 KiB
TypeScript
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
import * as FormData from "form-data";
|
|
import * as xml2js from "isomorphic-xml2js";
|
|
import axios, { 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 "isomorphic-tough-cookie";
|
|
|
|
const axiosClient = axios.create();
|
|
|
|
if (isNode) {
|
|
// Workaround for https://github.com/axios/axios/issues/1158
|
|
axiosClient.interceptors.request.use(config => ({ ...config, method: config.method && config.method.toUpperCase() }));
|
|
}
|
|
|
|
/**
|
|
* A HttpClient implementation that uses axios to send HTTP requests.
|
|
*/
|
|
export class AxiosHttpClient implements HttpClient {
|
|
private readonly cookieJar = isNode ? new tough.CookieJar() : undefined;
|
|
|
|
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."));
|
|
}
|
|
|
|
if (!httpRequest.headers) {
|
|
httpRequest.headers = {};
|
|
}
|
|
|
|
if (httpRequest.formData) {
|
|
const formData: any = httpRequest.formData;
|
|
const requestForm = new FormData();
|
|
const appendFormValue = (key: string, value: any) => {
|
|
if (value && value.hasOwnProperty("value") && value.hasOwnProperty("options")) {
|
|
requestForm.append(key, value.value, value.options);
|
|
} else {
|
|
requestForm.append(key, value);
|
|
}
|
|
};
|
|
for (const formKey in formData) {
|
|
if (formData.hasOwnProperty(formKey)) {
|
|
const formValue = formData[formKey];
|
|
if (formValue instanceof Array) {
|
|
for (let j = 0; j < formValue.length; j++) {
|
|
appendFormValue(formKey, formValue[j]);
|
|
}
|
|
} else {
|
|
appendFormValue(formKey, formValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
httpRequest.body = requestForm;
|
|
httpRequest.formData = undefined;
|
|
if (httpRequest.headers && httpRequest.headers["Content-Type"] &&
|
|
httpRequest.headers["Content-Type"].indexOf("multipart/form-data") !== -1) {
|
|
if (typeof requestForm.getBoundary === "function") {
|
|
httpRequest.headers["Content-Type"] = `multipart/form-data; boundary=${requestForm.getBoundary()}`;
|
|
} else {
|
|
// browser will automatically apply a suitable content-type header
|
|
delete httpRequest.headers["Content-Type"];
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
});
|
|
});
|
|
|
|
httpRequest.headers["Cookie"] = cookieString;
|
|
}
|
|
|
|
const abortSignal = httpRequest.abortSignal;
|
|
if (abortSignal && abortSignal.aborted) {
|
|
throw new RestError("The request was aborted", "REQUEST_ABORTED_ERROR", undefined, httpRequest);
|
|
}
|
|
|
|
const cancelToken = abortSignal && new axios.CancelToken(canceler => {
|
|
abortSignal.addEventListener("abort", () => canceler());
|
|
});
|
|
|
|
let res: AxiosResponse;
|
|
try {
|
|
const config: AxiosRequestConfig = {
|
|
method: httpRequest.method,
|
|
url: httpRequest.url,
|
|
headers: httpRequest.headers,
|
|
// Workaround for https://github.com/axios/axios/issues/755
|
|
// tslint:disable-next-line:no-null-keyword
|
|
data: httpRequest.body === undefined ? null : httpRequest.body,
|
|
transformResponse: undefined,
|
|
validateStatus: () => true,
|
|
withCredentials: true,
|
|
// Workaround for https://github.com/axios/axios/issues/1362
|
|
maxContentLength: 1024 * 1024 * 1024 * 10,
|
|
responseType: httpRequest.rawResponse ? (isNode ? "stream" : "blob") : "text",
|
|
cancelToken
|
|
};
|
|
res = await axiosClient(config);
|
|
} catch (err) {
|
|
if (err instanceof axios.Cancel) {
|
|
throw new RestError(err.message, "REQUEST_ABORTED_ERROR", undefined, httpRequest);
|
|
} else {
|
|
const axiosErr = err as AxiosError;
|
|
throw new RestError(axiosErr.message, "REQUEST_SEND_ERROR", undefined, httpRequest);
|
|
}
|
|
}
|
|
|
|
const headers = new HttpHeaders(res.headers);
|
|
|
|
const operationResponse: HttpOperationResponse = {
|
|
request: httpRequest,
|
|
status: res.status,
|
|
headers,
|
|
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 = 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.data);
|
|
return Promise.reject(e);
|
|
}
|
|
|
|
try {
|
|
if (operationResponse.bodyAsText) {
|
|
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) {
|
|
xmlParser.parseString(operationResponse.bodyAsText!, function (err: any, result: any) {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
operationResponse.parsedBody = await parseString;
|
|
} else if (contentType === "application/json" || contentType === "text/json" || !contentType) {
|
|
operationResponse.parsedBody = JSON.parse(operationResponse.bodyAsText);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
const msg = `Error "${err}" occured while executing JSON.parse on the response body - ${operationResponse.bodyAsText}.`;
|
|
const errCode = err.code || "JSON_PARSE_ERROR";
|
|
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, operationResponse.bodyAsText);
|
|
return Promise.reject(e);
|
|
}
|
|
}
|
|
return Promise.resolve(operationResponse);
|
|
}
|
|
}
|
|
|
|
const XML2JS_PARSER_OPTS: xml2js.OptionsV2 = {
|
|
explicitArray: false,
|
|
explicitCharkey: false,
|
|
explicitRoot: false
|
|
}; |