Add proxy support using tunnel package

This commit is contained in:
Kamil Pajdzik 2019-01-17 14:19:47 -08:00
Родитель 0632d022c1
Коммит 5f2b44c718
5 изменённых файлов: 137 добавлений и 10 удалений

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosProxyConfig, AxiosInstance } from "axios";
import { Transform, Readable } from "stream";
import FormData from "form-data";
import * as tough from "tough-cookie";
@ -10,15 +10,14 @@ import { HttpHeaders } from "./httpHeaders";
import { HttpOperationResponse } from "./httpOperationResponse";
import { RestError } from "./restError";
import { WebResource, HttpRequestBody } from "./webResource";
const axiosClient = axios.create();
// Workaround for https://github.com/axios/axios/issues/1158
axiosClient.interceptors.request.use(config => ({ ...config, method: config.method && config.method.toUpperCase() as any }));
import { ProxySettings } from "./serviceClient";
import * as tunnel from "tunnel";
/**
* A HttpClient implementation that uses axios to send HTTP requests.
*/
export class AxiosHttpClient implements HttpClient {
// public static readonly axiosClient = axios.create();
private readonly cookieJar = new tough.CookieJar();
public async sendRequest(httpRequest: WebResource): Promise<HttpOperationResponse> {
@ -96,8 +95,8 @@ export class AxiosHttpClient implements HttpClient {
// Workaround for https://github.com/axios/axios/issues/755
// tslint:disable-next-line:no-null-keyword
typeof httpRequestBody === "undefined" ? null :
typeof httpRequestBody === "function" ? httpRequestBody() :
httpRequestBody;
typeof httpRequestBody === "function" ? httpRequestBody() :
httpRequestBody;
const onUploadProgress = httpRequest.onUploadProgress;
if (onUploadProgress && axiosBody) {
@ -130,8 +129,24 @@ export class AxiosHttpClient implements HttpClient {
maxContentLength: 1024 * 1024 * 1024 * 10,
responseType: httpRequest.streamResponseBody ? "stream" : "text",
cancelToken,
timeout: httpRequest.timeout
timeout: httpRequest.timeout,
};
let axiosClient: AxiosInstance;
if (httpRequest.proxySettings) {
const agent = tunnel.httpsOverHttp({
proxy: {
host: httpRequest.proxySettings.host,
port: httpRequest.proxySettings.port,
headers: {}
}
});
axiosClient = axios.create({ httpAgent: agent, proxy: false });
} else {
axiosClient = axios.create();
}
res = await axiosClient(config);
} catch (err) {
if (err instanceof axios.Cancel) {
@ -198,6 +213,25 @@ export class AxiosHttpClient implements HttpClient {
}
}
export function convertToAxiosProxyConfig(proxySettings: ProxySettings | undefined): AxiosProxyConfig | undefined {
if (!proxySettings) {
return undefined;
}
const axiosAuthConfig = (proxySettings.username && proxySettings.password) ? {
username: proxySettings.username,
password: proxySettings.password
} : undefined;
const axiosProxyConfig: AxiosProxyConfig = {
host: proxySettings.host,
port: proxySettings.port,
auth: axiosAuthConfig
};
return axiosProxyConfig;
}
function isReadableStream(body: any): body is Readable {
return typeof body.pipe === "function";
}

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

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptions } from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { ProxySettings } from "../serviceClient";
import { WebResource } from "../webResource";
import { Constants } from "../util/constants";
import { URLBuilder } from "../url";
function loadEnvironmentProxyValue(): string | undefined {
if (!process) {
return undefined;
}
let proxyUrl = undefined;
if (process.env[Constants.HTTPS_PROXY]) {
proxyUrl = process.env[Constants.HTTPS_PROXY];
} else if (process.env[Constants.HTTPS_PROXY.toLowerCase()]) {
proxyUrl = process.env[Constants.HTTPS_PROXY.toLowerCase()];
} else if (process.env[Constants.HTTP_PROXY]) {
proxyUrl = process.env[Constants.HTTP_PROXY];
} else if (process.env[Constants.HTTP_PROXY.toLowerCase()]) {
proxyUrl = process.env[Constants.HTTP_PROXY.toLowerCase()];
}
return proxyUrl;
}
export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | undefined {
if (!proxyUrl) {
proxyUrl = loadEnvironmentProxyValue();
if (!proxyUrl) {
return undefined;
}
}
const parsedUrl = URLBuilder.parse(proxyUrl);
return {
host: parsedUrl.getScheme() + "//" + parsedUrl.getHost(),
port: Number.parseInt(parsedUrl.getPort() || "80")
};
}
export function proxyPolicy(proxySettings?: ProxySettings): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptions) => {
return new ProxyPolicy(nextPolicy, options, proxySettings!);
}
};
}
export class ProxyPolicy extends BaseRequestPolicy {
proxySettings: ProxySettings;
constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptions, proxySettings: ProxySettings) {
super(nextPolicy, options);
this.proxySettings = proxySettings;
}
public sendRequest(request: WebResource): Promise<HttpOperationResponse> {
if (!request.proxySettings) {
request.proxySettings = this.proxySettings;
}
return this._nextPolicy.sendRequest(request);
}
}

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

@ -26,6 +26,17 @@ import { stringifyXML } from "./util/xml";
import { RequestOptionsBase, RequestPrepareOptions, WebResource } from "./webResource";
import { OperationResponse } from "./operationResponse";
import { ServiceCallback } from "./util/utils";
import { proxyPolicy, getDefaultProxySettings } from "./policies/proxyPolicy";
/**
* HTTP proxy settings (Node.js only)
*/
export interface ProxySettings {
host: string;
port: number;
username?: string;
password?: string;
}
/**
* Options to be provided while creating the client.
@ -74,6 +85,10 @@ export interface ServiceClientOptions {
* The string to be set to the telemetry header while sending the request.
*/
userAgent?: string;
/**
* Proxy settings which will be used for every HTTP request (Node.js only).
*/
proxySettings?: ProxySettings;
}
/**
@ -369,6 +384,10 @@ function createDefaultRequestPolicyFactories(credentials: ServiceClientCredentia
factories.push(deserializationPolicy(options.deserializationContentTypes));
if (options.proxySettings || (options.proxySettings = getDefaultProxySettings())) {
factories.push(proxyPolicy(options.proxySettings));
}
return factories;
}

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

@ -7,6 +7,7 @@ import { Mapper, Serializer } from "./serializer";
import { generateUuid } from "./util/utils";
import { HttpOperationResponse } from "./httpOperationResponse";
import { OperationResponse } from "./operationResponse";
import { ProxySettings } from "./serviceClient";
export type HttpMethods = "GET" | "PUT" | "POST" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS" | "TRACE";
export type HttpRequestBody = Blob | string | ArrayBuffer | ArrayBufferView | (() => NodeJS.ReadableStream);
@ -64,6 +65,7 @@ export class WebResource {
operationSpec?: OperationSpec;
withCredentials: boolean;
timeout: number;
proxySettings?: ProxySettings;
abortSignal?: AbortSignalLike;
@ -84,7 +86,8 @@ export class WebResource {
abortSignal?: AbortSignalLike,
timeout?: number,
onUploadProgress?: (progress: TransferProgressEvent) => void,
onDownloadProgress?: (progress: TransferProgressEvent) => void) {
onDownloadProgress?: (progress: TransferProgressEvent) => void,
proxySettings?: ProxySettings) {
this.streamResponseBody = streamResponseBody;
this.url = url || "";
@ -98,6 +101,7 @@ export class WebResource {
this.timeout = timeout || 0;
this.onUploadProgress = onUploadProgress;
this.onDownloadProgress = onDownloadProgress;
this.proxySettings = proxySettings;
}
/**

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

@ -5,7 +5,7 @@
"email": "azsdkteam@microsoft.com",
"url": "https://github.com/Azure/ms-rest-js"
},
"version": "1.0.0",
"version": "1.1.0",
"description": "Isomorphic client Runtime for Typescript/node.js/browser javascript client libraries generated using AutoRest",
"tags": [
"isomorphic",
@ -52,6 +52,7 @@
"form-data": "^2.3.2",
"tough-cookie": "^2.4.3",
"tslib": "^1.9.2",
"tunnel": "0.0.6",
"uuid": "^3.2.1",
"xml2js": "^0.4.19"
},
@ -63,6 +64,7 @@
"@types/node": "^10.11.4",
"@types/should": "^8.3.0",
"@types/tough-cookie": "^2.3.3",
"@types/tunnel": "0.0.0",
"@types/uuid": "^3.4.4",
"@types/webpack": "^4.4.13",
"@types/webpack-dev-middleware": "^2.0.2",