From 887ec56c83a1ed02188b1d253dda77e817237161 Mon Sep 17 00:00:00 2001 From: "Ziheng Zhou(MSFT)" Date: Sat, 26 Sep 2020 04:49:51 +0800 Subject: [PATCH] Update the proxyPolicy and constants (#7069) * update to latest version of azure-sdk-for-js * udpate core-http.api.md * update proxyPolicy.ts * update proxyPolicy.ts according to comments --- sdk/core/core-http/review/core-http.api.md | 2 + .../core-http/src/policies/proxyPolicy.ts | 58 ++++++++++--- sdk/core/core-http/src/util/constants.ts | 16 ++++ sdk/core/core-http/src/util/utils.ts | 9 +++ .../test/policies/proxyPolicyTests.node.ts | 81 ++++++++++++++----- 5 files changed, 137 insertions(+), 29 deletions(-) diff --git a/sdk/core/core-http/review/core-http.api.md b/sdk/core/core-http/review/core-http.api.md index 105b283bbf6e..c5163b408cc2 100644 --- a/sdk/core/core-http/review/core-http.api.md +++ b/sdk/core/core-http/review/core-http.api.md @@ -129,6 +129,8 @@ export const Constants: { HTTPS: string; HTTP_PROXY: string; HTTPS_PROXY: string; + NO_PROXY: string; + ALL_PROXY: string; HttpConstants: { HttpVerbs: { PUT: string; diff --git a/sdk/core/core-http/src/policies/proxyPolicy.ts b/sdk/core/core-http/src/policies/proxyPolicy.ts index 99be69c9737c..77144a3e2700 100644 --- a/sdk/core/core-http/src/policies/proxyPolicy.ts +++ b/sdk/core/core-http/src/policies/proxyPolicy.ts @@ -12,23 +12,61 @@ import { ProxySettings } from "../serviceClient"; import { WebResourceLike } from "../webResource"; import { Constants } from "../util/constants"; import { URLBuilder } from "../url"; +import { getEnvironmentValue } from "../util/utils"; + +let noProxyList: string[] = []; +let isNoProxyInitalized = false; +let byPassedList = new Map(); function loadEnvironmentProxyValue(): string | undefined { if (!process) { return undefined; } - if (process.env[Constants.HTTPS_PROXY]) { - return process.env[Constants.HTTPS_PROXY]; - } else if (process.env[Constants.HTTPS_PROXY.toLowerCase()]) { - return process.env[Constants.HTTPS_PROXY.toLowerCase()]; - } else if (process.env[Constants.HTTP_PROXY]) { - return process.env[Constants.HTTP_PROXY]; - } else if (process.env[Constants.HTTP_PROXY.toLowerCase()]) { - return process.env[Constants.HTTP_PROXY.toLowerCase()]; + const httpsProxy = getEnvironmentValue(Constants.HTTPS_PROXY); + const allProxy = getEnvironmentValue(Constants.ALL_PROXY); + const httpProxy = getEnvironmentValue(Constants.HTTP_PROXY); + + return httpsProxy || allProxy || httpProxy; +} + +// Check whether the given `uri` matches the noProxyList. If it matches, any request sent to that same `uri` won't set the proxy settings. +function isBypassed(uri: string) { + if (byPassedList.has(uri)) { + return byPassedList.get(uri); } + loadNoProxy(); + let isBypassed = false; + let host = URLBuilder.parse(uri).getHost()!; + for (const proxyString of noProxyList) { + if (proxyString[0] === ".") { + if (uri.endsWith(proxyString)) { + isBypassed = true; + } else { + if (host === proxyString.slice(1) && host.length === proxyString.length - 1) { + isBypassed = true; + } + } + } else { + if (host === proxyString) { + isBypassed = true; + } + } + } + byPassedList.set(uri, isBypassed); + return isBypassed; +} - return undefined; +function loadNoProxy() { + if (isNoProxyInitalized) { + return; + } + const noProxy = getEnvironmentValue(Constants.NO_PROXY); + if (noProxy) { + let list = noProxy.split(","); + noProxyList = list.map((item) => item.trim()).filter((item) => item.length); + } + isNoProxyInitalized = true; } export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | undefined { @@ -97,7 +135,7 @@ export class ProxyPolicy extends BaseRequestPolicy { } public sendRequest(request: WebResourceLike): Promise { - if (!request.proxySettings) { + if (!request.proxySettings && !isBypassed(request.url)) { request.proxySettings = this.proxySettings; } return this._nextPolicy.sendRequest(request); diff --git a/sdk/core/core-http/src/util/constants.ts b/sdk/core/core-http/src/util/constants.ts index 98d1432ae4fb..b02aafc7f1bb 100644 --- a/sdk/core/core-http/src/util/constants.ts +++ b/sdk/core/core-http/src/util/constants.ts @@ -41,6 +41,22 @@ export const Constants = { */ HTTPS_PROXY: "HTTPS_PROXY", + /** + * Specifies NO Proxy. + * + * @const + * @type {string} + */ + NO_PROXY: "NO_PROXY", + + /** + * Specifies ALL Proxy. + * + * @const + * @type {string} + */ + ALL_PROXY: "ALL_PROXY", + HttpConstants: { /** * Http Verbs diff --git a/sdk/core/core-http/src/util/utils.ts b/sdk/core/core-http/src/util/utils.ts index 10cdc1a92363..20f7e062f425 100644 --- a/sdk/core/core-http/src/util/utils.ts +++ b/sdk/core/core-http/src/util/utils.ts @@ -254,3 +254,12 @@ export function replaceAll( export function isPrimitiveType(value: any): boolean { return (typeof value !== "object" && typeof value !== "function") || value === null; } + +export function getEnvironmentValue(name: string): string | undefined { + if (process.env[name]) { + return process.env[name]; + } else if (process.env[name.toLowerCase()]) { + return process.env[name.toLowerCase()]; + } + return undefined; +} diff --git a/sdk/core/core-http/test/policies/proxyPolicyTests.node.ts b/sdk/core/core-http/test/policies/proxyPolicyTests.node.ts index f1549cdd1514..d50ac35822d7 100644 --- a/sdk/core/core-http/test/policies/proxyPolicyTests.node.ts +++ b/sdk/core/core-http/test/policies/proxyPolicyTests.node.ts @@ -28,6 +28,7 @@ describe("ProxyPolicy (node)", function() { }; const emptyPolicyOptions = new RequestPolicyOptions(); + process.env[Constants.NO_PROXY] = ".foo.com, test.com"; describe("for Node.js", function() { it("factory passes correct proxy settings", function(done) { @@ -63,6 +64,35 @@ describe("ProxyPolicy (node)", function() { request.proxySettings!.should.be.deep.equal(requestSpecificProxySettings); }); + + it("should not assign proxy settings to the web request when noProxyList contain request url", async () => { + let request = new WebResource(); + let policy = new ProxyPolicy(emptyRequestPolicy, emptyPolicyOptions, proxySettings); + request.url = "http://foo.com"; + await policy.sendRequest(request); + should().not.exist(request.proxySettings); + + request.url = "https://www.foo.com"; + await policy.sendRequest(request); + should().not.exist(request.proxySettings); + + request.url = "http://test.foo.com"; + await policy.sendRequest(request); + should().not.exist(request.proxySettings); + + request.url = "http://abcfoo.com"; + await policy.sendRequest(request); + request.proxySettings!.should.be.deep.equal(proxySettings); + + request.proxySettings = undefined; + request.url = "http://test.com"; + await policy.sendRequest(request); + should().not.exist(request.proxySettings); + + request.url = "http://www.test.com"; + await policy.sendRequest(request); + request.proxySettings!.should.be.deep.equal(proxySettings); + }); }); }); @@ -146,6 +176,10 @@ describe("getDefaultProxySettings", () => { delete process.env[Constants.HTTPS_PROXY]; delete process.env[Constants.HTTP_PROXY.toLowerCase()]; delete process.env[Constants.HTTPS_PROXY.toLowerCase()]; + delete process.env[Constants.ALL_PROXY]; + delete process.env[Constants.ALL_PROXY.toLowerCase()]; + delete process.env[Constants.NO_PROXY]; + delete process.env[Constants.NO_PROXY.toLowerCase()]; }); it("should return undefined when no proxy passed and environment variable is not set", () => { @@ -162,6 +196,24 @@ describe("getDefaultProxySettings", () => { proxySettings.port.should.equal(defaultPort); }); + describe("should load setting from ALL_PROXY(all_proxy) environmental variable when no proxy passed and one of HTTPS proxy and HTTP proxy is not set ", () => { + [ + { name: "lower case", func: (envVar: string) => envVar.toLowerCase() }, + { name: "upper case", func: (envVar: string) => envVar.toUpperCase() } + ].forEach((testCase) => { + it(`with ${testCase.name}`, () => { + const allProxy = "https://proxy.azure.com"; + const httpProxy = "http://proxy.microsoft.com"; + process.env[testCase.func(Constants.HTTP_PROXY)] = httpProxy; + process.env[testCase.func(Constants.ALL_PROXY)] = allProxy; + + const proxySettings: ProxySettings = getDefaultProxySettings()!; + proxySettings.host.should.equal(allProxy); + proxySettings.port.should.equal(defaultPort); + }); + }); + }); + describe("should prefer HTTPS proxy over HTTP proxy", () => { [ { name: "lower case", func: (envVar: string) => envVar.toLowerCase() }, @@ -178,28 +230,19 @@ describe("getDefaultProxySettings", () => { proxySettings.port.should.equal(defaultPort); }); }); - - it("should prefer HTTPS proxy over HTTP proxy", () => { - const httpProxy = "http://proxy.microsoft.com"; - const httpsProxy = "https://proxy.azure.com"; - process.env[Constants.HTTP_PROXY] = httpProxy; - process.env[Constants.HTTPS_PROXY] = httpsProxy; - - const proxySettings: ProxySettings = getDefaultProxySettings()!; - proxySettings.host.should.equal(httpsProxy); - proxySettings.port.should.equal(defaultPort); - }); }); - ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"].forEach((envVariableName) => { - it(`should should load setting from "${envVariableName}" environmental variable`, () => { - process.env[envVariableName] = proxyUrl; - const proxySettings: ProxySettings = getDefaultProxySettings()!; + ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "all_proxy"].forEach( + (envVariableName) => { + it(`should load setting from "${envVariableName}" environmental variable`, () => { + process.env[envVariableName] = proxyUrl; + const proxySettings: ProxySettings = getDefaultProxySettings()!; - proxySettings.host.should.equal(proxyUrl); - proxySettings.port.should.equal(defaultPort); - }); - }); + proxySettings.host.should.equal(proxyUrl); + proxySettings.port.should.equal(defaultPort); + }); + } + ); }); }); });