Skip to content

Commit

Permalink
feat: Add support for NO_PROXY (#272)
Browse files Browse the repository at this point in the history
* feat: Add support for `NO_PROXY`

* docs: Document `shouldUseProxyForURI`

* test: Use sandbox for the environment
  • Loading branch information
danielbankhead authored Mar 31, 2022
1 parent a34d562 commit b02b6e5
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ let defaultRequest = teenyRequest.defaults({timeout: 60000});
```

## Proxy environment variables
If environment variables `HTTP_PROXY` or `HTTPS_PROXY` are set, they are respected. `NO_PROXY` is currently not implemented.
If environment variables `HTTP_PROXY`, `HTTPS_PROXY`, or `NO_PROXY` are set, they are respected.

## Building with Webpack 4+
Since 4.0.0, Webpack uses `javascript/esm` for `.mjs` files which handles ESM more strictly compared to `javascript/auto`. If you get the error `Can't import the named export 'PassThrough' from non EcmaScript module`, please add the following to your Webpack config:
Expand Down
36 changes: 35 additions & 1 deletion src/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,37 @@ export const pool = new Map<string, HTTPAgent>();

export type HttpAnyAgent = HTTPAgent | HTTPSAgent;

/**
* Determines if a proxy should be considered based on the environment.
*
* @param uri The request uri
* @returns {boolean}
*/
function shouldUseProxyForURI(uri: string): boolean {
const noProxyEnv = process.env.NO_PROXY || process.env.no_proxy;
if (!noProxyEnv) {
return true;
}

const givenURI = new URL(uri);

for (const noProxyRaw of noProxyEnv.split(',')) {
const noProxy = noProxyRaw.trim();

if (noProxy === givenURI.origin || noProxy === givenURI.hostname) {
return false;
} else if (noProxy.startsWith('*.') || noProxy.startsWith('.')) {
const noProxyWildcard = noProxy.replace(/^\*\./, '.');

if (givenURI.hostname.endsWith(noProxyWildcard)) {
return false;
}
}
}

return true;
}

/**
* Returns a custom request Agent if one is found, otherwise returns undefined
* which will result in the global http(s) Agent being used.
Expand All @@ -47,7 +78,10 @@ export function getAgent(

const poolOptions = Object.assign({}, reqOpts.pool);

if (proxy) {
const manuallyProvidedProxy = !!reqOpts.proxy;
const shouldUseProxy = manuallyProvidedProxy || shouldUseProxyForURI(uri);

if (proxy && shouldUseProxy) {
// tslint:disable-next-line variable-name
const Agent = isHttp
? require('http-proxy-agent')
Expand Down
49 changes: 49 additions & 0 deletions test/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ describe('agents', () => {
'HTTPS_PROXY',
];

const noProxyEnvVars = ['no_proxy', 'NO_PROXY'];

describe('http', () => {
const uri = httpUri;
const proxy = 'http://hello.there:8080';
Expand Down Expand Up @@ -114,6 +116,53 @@ describe('agents', () => {
});
});
});

describe('no_proxy', () => {
const uri = httpsUri;
const proxy = 'https://hello.there:8080';

beforeEach(() => {
sandbox.stub(process, 'env').value({});
});

noProxyEnvVars.forEach(noProxEnvVar => {
it(`should respect the proxy option, even if is in ${noProxEnvVar} env var`, () => {
process.env[noProxEnvVar] = new URL(uri).hostname;

const options = Object.assign({proxy}, defaultOptions);
const agent = getAgent(uri, options);
assert(agent instanceof HttpsProxyAgent);
});
});

noProxyEnvVars.forEach(noProxEnvVar => {
envVars.forEach(envVar => {
const root = 'example.com';
const subDomain = 'abc.' + root;

const uri = new URL(`https://${subDomain}`);

const cases = [
{name: '`.` support', value: `.${root}`},
{name: '`*.` support', value: `*.${root}`},
{name: 'list support', value: `a, b,${subDomain},.c,*.d`},
{name: '`.` + list support', value: `a, b,.${root},.c,*.d`},
{name: '`*.` + list support', value: `a, b,*.${root},.c,*.d`},
];

for (const {name, value} of cases) {
it(`should respect the ${noProxEnvVar} env var > ${envVar}': ${name}`, () => {
process.env[envVar] = proxy;

process.env[noProxEnvVar] = value;
const agent = getAgent(uri.toString(), defaultOptions);
assert(!(agent instanceof HttpProxyAgent));
assert(!(agent instanceof HttpsProxyAgent));
});
}
});
});
});
});

describe('forever', () => {
Expand Down

0 comments on commit b02b6e5

Please sign in to comment.