Skip to content
This repository was archived by the owner on May 5, 2023. It is now read-only.

Add support for env vars by default (#11) #16

Merged
merged 2 commits into from
Feb 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 83 additions & 33 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ var LRU = require('lru-cache');
var Agent = require('agent-base');
var inherits = require('util').inherits;
var debug = require('debug')('proxy-agent');
var getProxyForUrl = require('proxy-from-env').getProxyForUrl;

var http = require('http');
var https = require('https');
var PacProxyAgent = require('pac-proxy-agent');
var HttpProxyAgent = require('http-proxy-agent');
var HttpsProxyAgent = require('https-proxy-agent');
Expand Down Expand Up @@ -53,7 +56,17 @@ PacProxyAgent.protocols.forEach(function (protocol) {
exports.proxies['pac+' + protocol] = PacProxyAgent;
});

function httpOrHttpsProxy (opts, secureEndpoint) {
function httpOrHttps(opts, secureEndpoint) {
if (secureEndpoint) {
// HTTPS
return https.globalAgent;
} else {
// HTTP
return http.globalAgent;
}
}

function httpOrHttpsProxy(opts, secureEndpoint) {
if (secureEndpoint) {
// HTTPS
return new HttpsProxyAgent(opts);
Expand All @@ -63,25 +76,16 @@ function httpOrHttpsProxy (opts, secureEndpoint) {
}
}

/**
* Attempts to get an `http.Agent` instance based off of the given proxy URI
* information, and the `secure` flag.
*
* An LRU cache is used, to prevent unnecessary creation of proxy
* `http.Agent` instances.
*
* @param {String} uri proxy url
* @param {Boolean} secure true if this is for an HTTPS request, false for HTTP
* @return {http.Agent}
* @api public
*/
function mapOptsToProxy(opts) {
// NO_PROXY case
if (!opts) {
return {
uri: 'no proxy',
fn: httpOrHttps
};
}

function ProxyAgent (opts) {
if (!(this instanceof ProxyAgent)) return new ProxyAgent(opts);
if ('string' == typeof opts) opts = url.parse(opts);
if (!opts) throw new TypeError('an HTTP(S) proxy server `host` and `protocol` must be specified!');
debug('creating new ProxyAgent instance: %o', opts);
Agent.call(this, connect);

var proxies;
if (opts.proxies) {
Expand All @@ -108,41 +112,87 @@ function ProxyAgent (opts) {
throw new TypeError('unsupported proxy protocol: "' + protocol + '"');
}

this.proxy = opts;
// format the proxy info back into a URI, since an opts object
// could have been passed in originally. This generated URI is
// used as part of the "key" for the LRU cache
this.proxyUri = url.format({
protocol: protocol + ':',
slashes: true,
auth:opts.auth,
hostname: opts.hostname || opts.host,
port: opts.port
});
this.proxyFn = proxyFn;
// could have been passed in originally. This generated URI is used
// as part of the "key" for the LRU cache
return {
opts: opts,
uri: url.format({
protocol: protocol + ':',
slashes: true,
auth: opts.auth,
hostname: opts.hostname || opts.host,
port: opts.port
}),
fn: proxyFn,
}
}

/**
* Attempts to get an `http.Agent` instance based off of the given proxy URI
* information, and the `secure` flag.
*
* An LRU cache is used, to prevent unnecessary creation of proxy
* `http.Agent` instances.
*
* @param {String} uri proxy url
* @param {Boolean} secure true if this is for an HTTPS request, false for HTTP
* @return {http.Agent}
* @api public
*/

function ProxyAgent (opts) {
if (!(this instanceof ProxyAgent)) return new ProxyAgent(opts);
debug('creating new ProxyAgent instance: %o', opts);
Agent.call(this, connect);

if (opts) {
var proxy = mapOptsToProxy(opts);
this.proxy = proxy.opts;
this.proxyUri = proxy.uri;
this.proxyFn = proxy.fn;
}
}
inherits(ProxyAgent, Agent);

/**
*
*/

function connect (req, opts) {
function connect (req, opts, fn) {
var proxyOpts = this.proxy;
var proxyUri = this.proxyUri;
var proxyFn = this.proxyFn;

// if we did not instantiate with a proxy, set one per request
if (!proxyOpts) {
var urlOpts = getProxyForUrl(opts);
var proxy = mapOptsToProxy(urlOpts, opts);
proxyOpts = proxy.opts;
proxyUri = proxy.uri;
proxyFn = proxy.fn;
}

// create the "key" for the LRU cache
var key = this.proxyUri;
var key = proxyUri;
if (opts.secureEndpoint) key += ' secure';

// attempt to get a cached `http.Agent` instance first
var agent = exports.cache.get(key);
if (!agent) {
// get an `http.Agent` instance from protocol-specific agent function
agent = this.proxyFn(this.proxy, opts.secureEndpoint);
agent = proxyFn(proxyOpts, opts.secureEndpoint);
if (agent) {
exports.cache.set(key, agent);
}
} else {
debug('cache hit with key: %o', key);
}

return agent;
if (!proxyOpts) {
agent.addRequest(req, opts);
} else {
// XXX: agent.callback() is an agent-base-ism
agent.callback(req, opts, fn);
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"https-proxy-agent": "^1.0.0",
"lru-cache": "^2.6.5",
"pac-proxy-agent": "^2.0.0",
"proxy-from-env": "^1.0.0",
"socks-proxy-agent": "^3.0.0"
},
"devDependencies": {
Expand Down
58 changes: 50 additions & 8 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,6 @@ describe('ProxyAgent', function () {
});

describe('constructor', function () {
it('should throw a TypeError if no "proxy" argument is given', function () {
assert.throws(function () {
new ProxyAgent();
}, TypeError);
});
it('should throw a TypeError if no "protocol" is given', function () {
assert.throws(function () {
ProxyAgent({ host: 'foo.com', port: 3128 });
Expand Down Expand Up @@ -163,6 +158,56 @@ describe('ProxyAgent', function () {
});
});

describe('over "http" proxy from env', function () {
it('should work', function (done) {
httpServer.once('request', function (req, res) {
res.end(JSON.stringify(req.headers));
});

process.env.HTTP_PROXY = 'http://127.0.0.1:' + proxyPort;
var agent = new ProxyAgent();

var opts = url.parse('http://127.0.0.1:' + httpPort + '/test');
opts.agent = agent;

var req = http.get(opts, function (res) {
toBuffer(res, function (err, buf) {
if (err) return done(err);
var data = JSON.parse(buf.toString('utf8'));
assert.equal('127.0.0.1:' + httpPort, data.host);
assert('via' in data);
done();
});
});
req.once('error', done);
});
});

describe('with no proxy from env', function () {
it('should work', function (done) {
httpServer.once('request', function (req, res) {
res.end(JSON.stringify(req.headers));
});

process.env.NO_PROXY = '*';
var agent = new ProxyAgent();

var opts = url.parse('http://127.0.0.1:' + httpPort + '/test');
opts.agent = agent;

var req = http.get(opts, function (res) {
toBuffer(res, function (err, buf) {
if (err) return done(err);
var data = JSON.parse(buf.toString('utf8'));
assert.equal('127.0.0.1:' + httpPort, data.host);
assert(!('via' in data));
done();
});
});
req.once('error', done);
});
});

describe('over "https" proxy', function () {
it('should work', function (done) {
httpServer.once('request', function (req, res) {
Expand Down Expand Up @@ -213,10 +258,8 @@ describe('ProxyAgent', function () {
req.once('error', done);
});
});

});


describe('"https" module', function () {
describe('over "http" proxy', function () {
it('should work', function (done) {
Expand Down Expand Up @@ -303,5 +346,4 @@ describe('ProxyAgent', function () {
});
});
});

});