diff --git a/__tests__/proxy.test.ts b/__tests__/proxy.test.ts index b766f0c..437d05d 100644 --- a/__tests__/proxy.test.ts +++ b/__tests__/proxy.test.ts @@ -39,72 +39,112 @@ describe('proxy', () => { }) }) - it('does not return proxyUrl if variables not set', () => { + it('getProxyUrl does not return proxyUrl if variables not set', () => { let proxyUrl = pm.getProxyUrl(url.parse('https://github.com')); expect(proxyUrl).toBeUndefined(); }) - it('returns proxyUrl if https_proxy set for https url', () => { + it('getProxyUrl returns proxyUrl if https_proxy set for https url', () => { process.env["https_proxy"] = "https://myproxysvr"; let proxyUrl = pm.getProxyUrl(url.parse('https://github.com')); expect(proxyUrl).toBeDefined(); }) - it('does not return proxyUrl if http_proxy set for https url', () => { + it('getProxyUrl does not return proxyUrl if http_proxy set for https url', () => { process.env["http_proxy"] = "https://myproxysvr"; let proxyUrl = pm.getProxyUrl(url.parse('https://github.com')); expect(proxyUrl).toBeUndefined(); }) - it('returns proxyUrl if http_proxy set for http url', () => { + it('getProxyUrl returns proxyUrl if http_proxy set for http url', () => { process.env["http_proxy"] = "http://myproxysvr"; let proxyUrl = pm.getProxyUrl(url.parse('http://github.com')); expect(proxyUrl).toBeDefined(); }) - it('does not return proxyUrl if only host as no_proxy list', () => { - process.env["https_proxy"] = "https://myproxysvr"; - process.env["no_proxy"] = "myserver" - let proxyUrl = pm.getProxyUrl(url.parse('https://myserver')); - expect(proxyUrl).toBeUndefined(); - }) - - it('does not return proxyUrl if host in no_proxy list', () => { + it('getProxyUrl does not return proxyUrl if https_proxy set and in no_proxy list', () => { process.env["https_proxy"] = "https://myproxysvr"; process.env["no_proxy"] = "otherserver,myserver,anotherserver:8080" let proxyUrl = pm.getProxyUrl(url.parse('https://myserver')); expect(proxyUrl).toBeUndefined(); }) - - it('does not return proxyUrl if host in no_proxy list with spaces', () => { + + it('getProxyUrl returns proxyUrl if https_proxy set and not in no_proxy list', () => { process.env["https_proxy"] = "https://myproxysvr"; - process.env["no_proxy"] = "otherserver, myserver ,anotherserver:8080" - let proxyUrl = pm.getProxyUrl(url.parse('https://myserver')); - expect(proxyUrl).toBeUndefined(); - }) - - it('does not return proxyUrl if host in no_proxy list with ports', () => { - process.env["https_proxy"] = "https://myproxysvr"; - process.env["no_proxy"] = "otherserver, myserver:8080 ,anotherserver" - let proxyUrl = pm.getProxyUrl(url.parse('https://myserver:8080')); - expect(proxyUrl).toBeUndefined(); - }) - - it('returns proxyUrl if https_proxy set and not in no_proxy list', () => { - process.env["https_proxy"] = "https://myproxysvr"; - process.env["no_proxy"] = "otherserver, myserver ,anotherserver:8080" - let proxyUrl = pm.getProxyUrl(url.parse('https://github.com')); - expect(proxyUrl).toBeDefined(); - }) - - it('returns proxyUrl if https_proxy set empty no_proxy set', () => { - process.env["https_proxy"] = "https://myproxysvr"; - process.env["no_proxy"] = "" + process.env["no_proxy"] = "otherserver,myserver,anotherserver:8080" let proxyUrl = pm.getProxyUrl(url.parse('https://github.com')); expect(proxyUrl).toBeDefined(); }) - it('does basic http get request through proxy', async () => { + it('getProxyUrl does not return proxyUrl if http_proxy set and in no_proxy list', () => { + process.env["http_proxy"] = "http://myproxysvr"; + process.env["no_proxy"] = "otherserver,myserver,anotherserver:8080" + let proxyUrl = pm.getProxyUrl(url.parse('http://myserver')); + expect(proxyUrl).toBeUndefined(); + }) + + it('getProxyUrl returns proxyUrl if http_proxy set and not in no_proxy list', () => { + process.env["http_proxy"] = "http://myproxysvr"; + process.env["no_proxy"] = "otherserver,myserver,anotherserver:8080" + let proxyUrl = pm.getProxyUrl(url.parse('http://github.com')); + expect(proxyUrl).toBeDefined(); + }) + + it('checkBypass returns true if host as no_proxy list', () => { + process.env["no_proxy"] = "myserver" + let bypass = pm.checkBypass(url.parse('https://myserver')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns true if host in no_proxy list', () => { + process.env["no_proxy"] = "otherserver,myserver,anotherserver:8080" + let bypass = pm.checkBypass(url.parse('https://myserver')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns true if host in no_proxy list with spaces', () => { + process.env["no_proxy"] = "otherserver, myserver ,anotherserver:8080" + let bypass = pm.checkBypass(url.parse('https://myserver')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns true if host in no_proxy list with port', () => { + process.env["no_proxy"] = "otherserver, myserver:8080 ,anotherserver" + let bypass = pm.checkBypass(url.parse('https://myserver:8080')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns true if host with port in no_proxy list without port', () => { + process.env["no_proxy"] = "otherserver, myserver ,anotherserver" + let bypass = pm.checkBypass(url.parse('https://myserver:8080')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns true if host in no_proxy list with default https port', () => { + process.env["no_proxy"] = "otherserver, myserver:443 ,anotherserver" + let bypass = pm.checkBypass(url.parse('https://myserver')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns true if host in no_proxy list with default http port', () => { + process.env["no_proxy"] = "otherserver, myserver:80 ,anotherserver" + let bypass = pm.checkBypass(url.parse('http://myserver')); + expect(bypass).toBeTruthy(); + }) + + it('checkBypass returns false if host not in no_proxy list', () => { + process.env["no_proxy"] = "otherserver, myserver ,anotherserver:8080" + let bypass = pm.checkBypass(url.parse('https://github.com')); + expect(bypass).toBeFalsy(); + }) + + it('checkBypass returns false if empty no_proxy', () => { + process.env["no_proxy"] = "" + let bypass = pm.checkBypass(url.parse('https://github.com')); + expect(bypass).toBeFalsy(); + }) + + it('HttpClient does basic http get request through proxy', async () => { process.env['http_proxy'] = _proxyUrl const httpClient = new httpm.HttpClient(); let res: httpm.HttpClientResponse = await httpClient.get('http://httpbin.org/get'); @@ -115,7 +155,7 @@ describe('proxy', () => { expect(_proxyConnects).toEqual(['httpbin.org:80']) }) - it('does basic http get request when bypass proxy', async () => { + it('HttoClient does basic http get request when bypass proxy', async () => { process.env['http_proxy'] = _proxyUrl process.env['no_proxy'] = 'httpbin.org' const httpClient = new httpm.HttpClient(); @@ -127,7 +167,7 @@ describe('proxy', () => { expect(_proxyConnects).toHaveLength(0) }) - it('does basic https get request through proxy', async () => { + it('HttpClient does basic https get request through proxy', async () => { process.env['https_proxy'] = _proxyUrl const httpClient = new httpm.HttpClient(); let res: httpm.HttpClientResponse = await httpClient.get('https://httpbin.org/get'); @@ -138,7 +178,7 @@ describe('proxy', () => { expect(_proxyConnects).toEqual(['httpbin.org:443']) }) - it('does basic https get request when bypass proxy', async () => { + it('HttpClient does basic https get request when bypass proxy', async () => { process.env['https_proxy'] = _proxyUrl process.env['no_proxy'] = 'httpbin.org' const httpClient = new httpm.HttpClient(); diff --git a/index.ts b/index.ts index f39839d..2f7b475 100644 --- a/index.ts +++ b/index.ts @@ -35,13 +35,21 @@ export enum HttpCodes { GatewayTimeout = 504, } +/** + * Returns the proxy URL, depending upon the supplied url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ +export function getProxyUrl(serverUrl: string): string { + let proxyUrl = pm.getProxyUrl(url.parse(serverUrl)) + return proxyUrl ? proxyUrl.href : '' +} + const HttpRedirectCodes: number[] = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect]; const HttpResponseRetryCodes: number[] = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout]; const RetryableHttpVerbs: string[] = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; const ExponentialBackoffCeiling = 10; const ExponentialBackoffTimeSlice = 5; - export class HttpClientResponse implements ifm.IHttpClientResponse { constructor(message: http.IncomingMessage) { this.message = message; diff --git a/proxy.ts b/proxy.ts index c251575..76c59b0 100644 --- a/proxy.ts +++ b/proxy.ts @@ -1,30 +1,13 @@ import * as url from 'url'; -export function getProxyUrl(reqUrl: url.Url): url.Url { +export function getProxyUrl(reqUrl: url.Url): url.Url | undefined { let usingSsl = reqUrl.protocol === 'https:'; - let noProxy: string = process.env["no_proxy"] || - process.env["NO_PROXY"]; - - let bypass: boolean; - if (noProxy && typeof noProxy === 'string') { - let bypassList = noProxy.split(','); - for (let i=0; i < bypassList.length; i++) { - let item = bypassList[i]; - if (item && - typeof item === "string" && - reqUrl.host.toLocaleLowerCase() == item.trim().toLocaleLowerCase()) { - bypass = true; - break; - } - } - } - let proxyUrl: url.Url; - if (bypass) { + if (checkBypass(reqUrl)) { return proxyUrl; } - + let proxyVar: string; if (usingSsl) { proxyVar = process.env["https_proxy"] || @@ -40,4 +23,43 @@ export function getProxyUrl(reqUrl: url.Url): url.Url { } return proxyUrl; +} + + +export function checkBypass(reqUrl: url.Url): boolean { + if (!reqUrl.hostname) { + return false + } + + let noProxy = process.env["no_proxy"] || process.env["NO_PROXY"] || ''; + if (!noProxy) { + return false + } + + // Determine the request port + let reqPort: number + if (reqUrl.port) { + reqPort = Number(reqUrl.port) + } + else if (reqUrl.protocol === 'http:') { + reqPort = 80 + } + else if (reqUrl.protocol === 'https:') { + reqPort = 443 + } + + // Format the request hostname and hostname with port + let upperReqHosts = [reqUrl.hostname.toUpperCase()] + if (typeof reqPort === 'number') { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`) + } + + // Compare request host against noproxy + for (let upperNoProxyItem of noProxy.split(',').map(x => x.trim().toUpperCase()).filter(x => x)) { + if (upperReqHosts.some(x => x === upperNoProxyItem)) { + return true + } + } + + return false } \ No newline at end of file