зеркало из https://github.com/Azure/ms-rest-js.git
Merge pull request #441 from jeremymeng/no-proxy
Port some proxy enhancements from core-http
This commit is contained in:
Коммит
caa149a25d
|
@ -2,6 +2,8 @@
|
|||
|
||||
## 2.3.0 - UNRELEASED
|
||||
- Moving @types dependencies into devdependencies
|
||||
- Add NO_PROXY and ALL_PROXY support.
|
||||
- Add username/password support in proxy url string.
|
||||
|
||||
## 2.2.3 - 2021-02-10
|
||||
- Dependent projects of @azure/ms-rest-js no longer need to have a dev dependency on @types/tunnel.
|
||||
|
|
|
@ -13,22 +13,108 @@ import { WebResourceLike } from "../webResource";
|
|||
import { Constants } from "../util/constants";
|
||||
import { URLBuilder } from "../url";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const noProxyList: string[] = loadNoProxy();
|
||||
const byPassedList: Map<string, boolean> = new Map();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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 host of a given `uri` is in the noProxyList.
|
||||
// If there's a match, any request sent to the same host won't have the proxy settings set.
|
||||
// This implementation is a port of https://github.com/Azure/azure-sdk-for-net/blob/8cca811371159e527159c7eb65602477898683e2/sdk/core/Azure.Core/src/Pipeline/Internal/HttpEnvironmentProxy.cs#L210
|
||||
function isBypassed(uri: string): boolean | undefined {
|
||||
if (noProxyList.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const host = URLBuilder.parse(uri).getHost()!;
|
||||
if (byPassedList.has(host)) {
|
||||
return byPassedList.get(host);
|
||||
}
|
||||
let isBypassedFlag = false;
|
||||
for (const pattern of noProxyList) {
|
||||
if (pattern[0] === ".") {
|
||||
// This should match either domain it self or any subdomain or host
|
||||
// .foo.com will match foo.com it self or *.foo.com
|
||||
if (host.endsWith(pattern)) {
|
||||
isBypassedFlag = true;
|
||||
} else {
|
||||
if (host.length === pattern.length - 1 && host === pattern.slice(1)) {
|
||||
isBypassedFlag = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (host === pattern) {
|
||||
isBypassedFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
byPassedList.set(host, isBypassedFlag);
|
||||
return isBypassedFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function loadNoProxy(): string[] {
|
||||
const noProxy = getEnvironmentValue(Constants.NO_PROXY);
|
||||
if (noProxy) {
|
||||
return noProxy
|
||||
.split(",")
|
||||
.map((item) => item.trim())
|
||||
.filter((item) => item.length);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function extractAuthFromUrl(
|
||||
url: string
|
||||
): { username?: string; password?: string; urlWithoutAuth: string } {
|
||||
const atIndex = url.indexOf("@");
|
||||
if (atIndex === -1) {
|
||||
return { urlWithoutAuth: url };
|
||||
}
|
||||
|
||||
const schemeIndex = url.indexOf("://");
|
||||
const authStart = schemeIndex !== -1 ? schemeIndex + 3 : 0;
|
||||
const auth = url.substring(authStart, atIndex);
|
||||
const colonIndex = auth.indexOf(":");
|
||||
const hasPassword = colonIndex !== -1;
|
||||
const username = hasPassword ? auth.substring(0, colonIndex) : auth;
|
||||
const password = hasPassword ? auth.substring(colonIndex + 1) : undefined;
|
||||
const urlWithoutAuth = url.substring(0, authStart) + url.substring(atIndex + 1);
|
||||
return {
|
||||
username,
|
||||
password,
|
||||
urlWithoutAuth,
|
||||
};
|
||||
}
|
||||
|
||||
export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | undefined {
|
||||
|
@ -39,14 +125,21 @@ export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | unde
|
|||
}
|
||||
}
|
||||
|
||||
const parsedUrl = URLBuilder.parse(proxyUrl);
|
||||
const { username, password, urlWithoutAuth } = extractAuthFromUrl(proxyUrl);
|
||||
const parsedUrl = URLBuilder.parse(urlWithoutAuth);
|
||||
const schema = parsedUrl.getScheme() ? parsedUrl.getScheme() + "://" : "";
|
||||
return {
|
||||
host: parsedUrl.getScheme() + "://" + parsedUrl.getHost(),
|
||||
host: schema + parsedUrl.getHost(),
|
||||
port: Number.parseInt(parsedUrl.getPort() || "80"),
|
||||
username,
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
export function proxyPolicy(proxySettings?: ProxySettings): RequestPolicyFactory {
|
||||
if (!proxySettings) {
|
||||
proxySettings = getDefaultProxySettings();
|
||||
}
|
||||
return {
|
||||
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
|
||||
return new ProxyPolicy(nextPolicy, options, proxySettings!);
|
||||
|
@ -67,7 +160,7 @@ export class ProxyPolicy extends BaseRequestPolicy {
|
|||
}
|
||||
|
||||
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
|
||||
if (!request.proxySettings) {
|
||||
if (!request.proxySettings && !isBypassed(request.url)) {
|
||||
request.proxySettings = this.proxySettings;
|
||||
}
|
||||
return this._nextPolicy.sendRequest(request);
|
||||
|
|
|
@ -41,6 +41,16 @@ export const Constants = {
|
|||
*/
|
||||
HTTPS_PROXY: "HTTPS_PROXY",
|
||||
|
||||
/**
|
||||
* Specifies NO Proxy.
|
||||
*/
|
||||
NO_PROXY: "NO_PROXY",
|
||||
|
||||
/**
|
||||
* Specifies ALL Proxy.
|
||||
*/
|
||||
ALL_PROXY: "ALL_PROXY",
|
||||
|
||||
HttpConstants: {
|
||||
/**
|
||||
* Http Verbs
|
||||
|
|
|
@ -7,7 +7,13 @@ import { ProxySettings } from "../../lib/serviceClient";
|
|||
import { RequestPolicyOptions } from "../../lib/policies/requestPolicy";
|
||||
import { WebResource, WebResourceLike } from "../../lib/webResource";
|
||||
import { HttpHeaders } from "../../lib/httpHeaders";
|
||||
import { proxyPolicy, ProxyPolicy, getDefaultProxySettings } from "../../lib/policies/proxyPolicy";
|
||||
import {
|
||||
proxyPolicy,
|
||||
ProxyPolicy,
|
||||
getDefaultProxySettings,
|
||||
noProxyList,
|
||||
loadNoProxy,
|
||||
} from "../../lib/policies/proxyPolicy";
|
||||
import { Constants } from "../../lib/msRest";
|
||||
import { nodeDescribe, browserDescribe } from "../msAssert";
|
||||
|
||||
|
@ -64,6 +70,54 @@ describe("ProxyPolicy", function () {
|
|||
|
||||
request.proxySettings!.should.be.deep.equal(requestSpecificProxySettings);
|
||||
});
|
||||
|
||||
it("should not assign proxy settings to the web request when noProxyList contain request url", async () => {
|
||||
const saved = process.env["NO_PROXY"];
|
||||
try {
|
||||
process.env[Constants.NO_PROXY] = ".foo.com, test.com";
|
||||
noProxyList.splice(0, noProxyList.length);
|
||||
noProxyList.push(...loadNoProxy());
|
||||
|
||||
const request = new WebResource();
|
||||
const 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://test.foo.com/path1";
|
||||
await policy.sendRequest(request);
|
||||
should().not.exist(request.proxySettings);
|
||||
|
||||
request.url = "http://test.foo.com/path2";
|
||||
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);
|
||||
} finally {
|
||||
process.env["NO_PROXY"] = saved;
|
||||
noProxyList.splice(0, noProxyList.length);
|
||||
noProxyList.push(...loadNoProxy());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
browserDescribe("for browser", () => {
|
||||
|
@ -99,6 +153,56 @@ describe("getDefaultProxySettings", () => {
|
|||
proxySettings.port.should.equal(port);
|
||||
});
|
||||
|
||||
[
|
||||
{
|
||||
proxyUrl: "prot://user:pass@proxy.microsoft.com",
|
||||
proxyUrlWithoutAuth: "prot://proxy.microsoft.com",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
},
|
||||
{
|
||||
proxyUrl: "prot://user@proxy.microsoft.com",
|
||||
proxyUrlWithoutAuth: "prot://proxy.microsoft.com",
|
||||
username: "user",
|
||||
password: undefined,
|
||||
},
|
||||
{
|
||||
proxyUrl: "prot://:pass@proxy.microsoft.com",
|
||||
proxyUrlWithoutAuth: "prot://proxy.microsoft.com",
|
||||
username: undefined,
|
||||
password: "pass",
|
||||
},
|
||||
{
|
||||
proxyUrl: "prot://proxy.microsoft.com",
|
||||
proxyUrlWithoutAuth: "prot://proxy.microsoft.com",
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
},
|
||||
{
|
||||
proxyUrl: "user:pass@proxy.microsoft.com",
|
||||
proxyUrlWithoutAuth: "proxy.microsoft.com",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
},
|
||||
{
|
||||
proxyUrl: "proxy.microsoft.com",
|
||||
proxyUrlWithoutAuth: "proxy.microsoft.com",
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
},
|
||||
].forEach((testCase) => {
|
||||
it(`should return settings with passed proxyUrl : ${testCase.proxyUrl}`, () => {
|
||||
const proxySettings: ProxySettings = getDefaultProxySettings(testCase.proxyUrl)!;
|
||||
proxySettings.host.should.equal(testCase.proxyUrlWithoutAuth);
|
||||
if (testCase.username) {
|
||||
proxySettings.username!.should.equal(testCase.username);
|
||||
}
|
||||
if (testCase.password) {
|
||||
proxySettings.password!.should.equal(testCase.password);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("with loadEnvironmentProxyValue", () => {
|
||||
beforeEach(() => {
|
||||
delete process.env[Constants.HTTP_PROXY];
|
||||
|
|
Загрузка…
Ссылка в новой задаче