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

Commit

Permalink
Add support for env vars by default (#11) (#16)
Browse files Browse the repository at this point in the history
* Add support for env vars by default (#11)

* Use global agents for no proxy
  • Loading branch information
jackjocross authored and TooTallNate committed Feb 14, 2018
1 parent c007ea1 commit 787ede0
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 41 deletions.
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 () {
});
});
});

});

0 comments on commit 787ede0

Please sign in to comment.