Skip to content

Commit

Permalink
feat: pass agent options when using agent pool config (#149)
Browse files Browse the repository at this point in the history
* feat: pass agent options when using agent pool config, fix/enhance existing tests

* chore(agents): revert back to lazy loading of proxy libs
  • Loading branch information
zamnuts committed Apr 5, 2020
1 parent 2e12180 commit 38ece79
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 11 deletions.
21 changes: 15 additions & 6 deletions src/agents.ts
Expand Up @@ -16,19 +16,25 @@

import {Agent as HTTPAgent} from 'http';
import {Agent as HTTPSAgent} from 'https';
import {parse} from 'url';
import {Options} from './';

const pool = new Map<string, HTTPAgent>();
export const pool = new Map<string, HTTPAgent>();

export type HttpAnyAgent = HTTPAgent | HTTPSAgent;

/**
* Returns a custom request Agent if one is found, otherwise returns undefined
* which will result in the global http(s) Agent being used.
* @private
* @param {string} uri The request uri
* @param {object} reqOpts The request options
* @returns {Agent|undefined}
* @param {Options} reqOpts The request options
* @returns {HttpAnyAgent|undefined}
*/
export function getAgent(uri: string, reqOpts: Options): HTTPAgent | undefined {
export function getAgent(
uri: string,
reqOpts: Options
): HttpAnyAgent | undefined {
const isHttp = uri.startsWith('http://');
const proxy =
reqOpts.proxy ||
Expand All @@ -37,13 +43,16 @@ export function getAgent(uri: string, reqOpts: Options): HTTPAgent | undefined {
process.env.HTTPS_PROXY ||
process.env.https_proxy;

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

if (proxy) {
// tslint:disable-next-line variable-name
const Agent = isHttp
? require('http-proxy-agent')
: require('https-proxy-agent');

return new Agent(proxy) as HTTPAgent;
const proxyOpts = {...parse(proxy), ...poolOptions};
return new Agent(proxyOpts);
}

let key = isHttp ? 'http' : 'https';
Expand All @@ -54,7 +63,7 @@ export function getAgent(uri: string, reqOpts: Options): HTTPAgent | undefined {
if (!pool.has(key)) {
// tslint:disable-next-line variable-name
const Agent = isHttp ? HTTPAgent : HTTPSAgent;
pool.set(key, new Agent({keepAlive: true}));
pool.set(key, new Agent({...poolOptions, keepAlive: true}));
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Expand Up @@ -14,7 +14,8 @@
* limitations under the License.
*/

import {Agent} from 'https';
import {Agent, AgentOptions as HttpsAgentOptions} from 'https';
import {AgentOptions as HttpAgentOptions} from 'http';
import fetch, * as f from 'node-fetch';
import {PassThrough, Readable} from 'stream';
import * as uuid from 'uuid';
Expand All @@ -35,6 +36,7 @@ export interface CoreOptions {
proxy?: string;
multipart?: RequestPart[];
forever?: boolean;
pool?: HttpsAgentOptions | HttpAgentOptions;
}

export interface OptionsWithUri extends CoreOptions {
Expand Down
100 changes: 96 additions & 4 deletions test/agents.ts
Expand Up @@ -17,8 +17,9 @@
import * as assert from 'assert';
import {describe, it, afterEach} from 'mocha';
import * as http from 'http';
import * as https from 'https';
import * as sinon from 'sinon';
import {getAgent} from '../src/agents';
import {getAgent, pool} from '../src/agents';

// tslint:disable-next-line variable-name
const HttpProxyAgent = require('http-proxy-agent');
Expand All @@ -30,7 +31,10 @@ describe('agents', () => {
const httpsUri = 'https://example.com';
const sandbox = sinon.createSandbox();

afterEach(() => sandbox.restore());
afterEach(() => {
sandbox.restore();
pool.clear();
});

describe('getAgent', () => {
const defaultOptions = {uri: httpUri};
Expand All @@ -51,11 +55,22 @@ describe('agents', () => {
describe('http', () => {
const uri = httpUri;
const proxy = 'http://hello.there:8080';
const proxyExpected = {
hostname: 'hello.there',
port: 8080,
protocol: 'http:',
};

it('should respect the proxy option', () => {
const options = Object.assign({proxy}, defaultOptions);
const agent = getAgent(uri, options);
assert(agent instanceof HttpProxyAgent);

// tslint:disable-next-line:no-any
const {proxy: proxyActual}: any = agent!;
assert.strictEqual(proxyActual.protocol, proxyExpected.protocol);
assert.strictEqual(proxyActual.hostname, proxyExpected.hostname);
assert.strictEqual(proxyActual.port, proxyExpected.port);
});

envVars.forEach(envVar => {
Expand All @@ -71,11 +86,22 @@ describe('agents', () => {
describe('https', () => {
const uri = httpsUri;
const proxy = 'https://hello.there:8080';
const proxyExpected = {
hostname: 'hello.there',
port: 8080,
protocol: 'https:',
};

it('should respect the proxy option', () => {
const options = Object.assign({proxy}, defaultOptions);
const agent = getAgent(uri, options);
assert(agent instanceof HttpsProxyAgent);

// tslint:disable-next-line:no-any
const {proxy: proxyActual}: any = agent!;
assert.strictEqual(proxyActual.protocol, proxyExpected.protocol);
assert.strictEqual(proxyActual.hostname, proxyExpected.hostname);
assert.strictEqual(proxyActual.port, proxyExpected.port);
});

envVars.forEach(envVar => {
Expand Down Expand Up @@ -107,12 +133,12 @@ describe('agents', () => {
});

describe('https', () => {
const uri = httpUri;
const uri = httpsUri;
const options = Object.assign({forever: true}, defaultOptions);

it('should return an http Agent', () => {
const agent = getAgent(uri, options)!;
assert(agent instanceof http.Agent);
assert(agent instanceof https.Agent);
});

it('should cache the agent', () => {
Expand All @@ -122,5 +148,71 @@ describe('agents', () => {
});
});
});

describe('pool', () => {
describe('http', () => {
const uri = httpUri;

it('should pass AgentOptions from pool config when providing agent', () => {
const options = Object.assign(
{
forever: true,
pool: {
maxSockets: 1000,
},
},
defaultOptions
);
const agent = getAgent(uri, options);
assert.strictEqual(agent!.maxSockets, 1000);
});

it('should not set global AgentOptions from only pool config', () => {
const options = Object.assign(
{
pool: {
maxSockets: 1000,
},
},
defaultOptions
);
const agent = getAgent(uri, options);
assert.strictEqual(agent, undefined);
assert.notStrictEqual(http.globalAgent.maxSockets, 1000);
});
});

describe('https', () => {
const uri = httpsUri;

it('should pass AgentOptions from pool config when providing agent', () => {
const options = Object.assign(
{
forever: true,
pool: {
maxSockets: 1000,
},
},
defaultOptions
);
const agent = getAgent(uri, options);
assert.strictEqual(agent!.maxSockets, 1000);
});

it('should not set global AgentOptions from only pool config', () => {
const options = Object.assign(
{
pool: {
maxSockets: 1000,
},
},
defaultOptions
);
const agent = getAgent(uri, options);
assert.strictEqual(agent, undefined);
assert.notStrictEqual(https.globalAgent.maxSockets, 1000);
});
});
});
});
});
2 changes: 2 additions & 0 deletions test/index.ts
Expand Up @@ -20,6 +20,7 @@ import * as nock from 'nock';
import {Readable, PassThrough} from 'stream';
import * as sinon from 'sinon';
import {teenyRequest} from '../src';
import {pool} from '../src/agents';

// tslint:disable-next-line variable-name
const HttpProxyAgent = require('http-proxy-agent');
Expand All @@ -38,6 +39,7 @@ function mockJson() {
describe('teeny', () => {
const sandbox = sinon.createSandbox();
afterEach(() => {
pool.clear();
sandbox.restore();
nock.cleanAll();
});
Expand Down

0 comments on commit 38ece79

Please sign in to comment.