diff --git a/lib/adapters/http.js b/lib/adapters/http.js index f6d7067240..3a57bb0281 100644 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -12,6 +12,7 @@ var zlib = require('zlib'); var pkg = require('./../../package.json'); var createError = require('../core/createError'); var enhanceError = require('../core/enhanceError'); +var getProxy = require('./http.proxy'); /*eslint consistent-return:0*/ module.exports = function httpAdapter(config) { @@ -81,26 +82,7 @@ module.exports = function httpAdapter(config) { auth: auth }; - var proxy = config.proxy; - if (!proxy && proxy !== false) { - var proxyEnv = protocol.slice(0, -1) + '_proxy'; - var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()]; - if (proxyUrl) { - var parsedProxyUrl = url.parse(proxyUrl); - proxy = { - host: parsedProxyUrl.hostname, - port: parsedProxyUrl.port - }; - - if (parsedProxyUrl.auth) { - var proxyUrlAuth = parsedProxyUrl.auth.split(':'); - proxy.auth = { - username: proxyUrlAuth[0], - password: proxyUrlAuth[1] - }; - } - } - } + var proxy = getProxy(config); if (proxy) { options.hostname = proxy.host; diff --git a/lib/adapters/http.proxy.js b/lib/adapters/http.proxy.js new file mode 100644 index 0000000000..4f8ccd22b1 --- /dev/null +++ b/lib/adapters/http.proxy.js @@ -0,0 +1,42 @@ +'use strict'; + +var url = require('url'); + +var getProxyForUrl = require('proxy-from-env').getProxyForUrl; + +/** + * + * @param {Object} config + * @param {string} config.url + * @param {Object=) config.proxy + * @param {string} config.proxy.host + * @param {number} config.proxy.port + * @return {object | undefined} proxy + * @return {string} proxy.host + * @return {number} proxy.number + * @return {object | undefined} proxy.auth + * @return {string} proxy.auth.username + * @return {string} proxy.auth.password + */ +module.exports = function getProxy(config) { + var proxy = config.proxy; + if (!proxy && proxy !== false) { + var envProxy = getProxyForUrl(config.url); + if (envProxy) { + var parsedProxyUrl = url.parse(envProxy); + proxy = { + host: parsedProxyUrl.hostname, + port: parsedProxyUrl.port + }; + + if (parsedProxyUrl.auth) { + var proxyUrlAuth = parsedProxyUrl.auth.split(':'); + proxy.auth = { + username: proxyUrlAuth[0], + password: proxyUrlAuth[1] + }; + } + } + } + return proxy || undefined; +}; diff --git a/package.json b/package.json index 24d4acb4d8..f5552a639f 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,8 @@ "typings": "./index.d.ts", "dependencies": { "follow-redirects": "^1.2.5", - "is-buffer": "^1.1.5" + "is-buffer": "^1.1.5", + "proxy-from-env": "^1.0.0" }, "bundlesize": [ { diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js index ae8238b433..e7cf2332de 100644 --- a/test/unit/adapters/http.js +++ b/test/unit/adapters/http.js @@ -3,14 +3,17 @@ var http = require('http'); var url = require('url'); var zlib = require('zlib'); var fs = require('fs'); +var adapter = require('../../../lib/adapters/http.js'); var server, proxy; module.exports = { tearDown: function (callback) { - server.close(); - server = null; + if (server) { + server.close(); + server = null; + } if (proxy) { - proxy.close() + proxy.close(); proxy = null; } @@ -18,6 +21,9 @@ module.exports = { delete process.env.http_proxy; } + delete process.env.no_proxy; + delete process.env.NO_PROXY; + callback(); }, @@ -355,6 +361,7 @@ module.exports = { }).listen(4000, function() { // set the env variable process.env.http_proxy = 'http://localhost:4000/'; + process.env.no_proxy = 'my.corp.com'; axios.get('http://localhost:4444/').then(function(res) { test.equal(res.data, '45671234', 'should use proxy set by process.env.http_proxy'); @@ -364,6 +371,45 @@ module.exports = { }); }, + testHTTPNoProxyEnv: function(test) { + server = http.createServer(function(req, res) { + res.setHeader('Content-Type', 'text/html; charset=UTF-8'); + res.end('4567'); + }).listen(4444, function() { + proxy = http.createServer(function(request, response) { + var parsed = url.parse(request.url); + var opts = { + host: parsed.hostname, + port: parsed.port, + path: parsed.path + }; + + http.get(opts, function(res) { + var body = ''; + res.on('data', function(data) { + body += data; + }); + res.on('end', function() { + response.setHeader('Content-Type', 'text/html; charset=UTF-8'); + response.end(body + '1234'); + }); + }); + }).listen(4000, function() { + // set the env variable + process.env.http_proxy = 'http://127.0.0.1:4000/'; + process.env.no_proxy = 'localhost'; + + axios.get('http://localhost:4444/').then(function(res) { + test.equal(res.data, '4567', 'should not use proxy set by process.env.http_proxy as of process.env.no_proxy'); + test.done(); + }, function() { + test.ok(false, 'error occured during request with no_proxy'); + test.done(); + }); + }); + }); + }, + testHTTPProxyAuth: function(test) { server = http.createServer(function(req, res) { res.end(); diff --git a/test/unit/adapters/proxy.js b/test/unit/adapters/proxy.js new file mode 100644 index 0000000000..878d96471a --- /dev/null +++ b/test/unit/adapters/proxy.js @@ -0,0 +1,203 @@ +'use strict'; + +var getProxy = require('../../../lib/adapters/http.proxy'); + +function config(def) { + var cfg = {}; + cfg.url = def && def.url || 'http://google.com'; + cfg.proxy = def && def.proxy; + return cfg; +} + + +module.exports = { + + setUp: function(done) { + delete process.env.http_proxy; + delete process.env.https_proxy; + delete process.env.no_proxy; + delete process.env.HTTP_PROXY; + delete process.env.HTTPS_PROXY; + delete process.env.NO_PROXY; + done(); + }, + + isEmptyIfNotConfigured: function(test) { + test.equal(getProxy(config()), undefined); + test.done(); + }, + + canReadProxyHostFromConfig: function(test) { + var cfg = config({proxy: {host: 'corpproxy.com'}}); + var proxy = getProxy(cfg); + test.equal(proxy.host, 'corpproxy.com'); + test.equal(proxy.port, undefined); + test.equal(proxy.auth, undefined); + test.done(); + }, + + canReadProxyAndPortHostFromConfig: function(test) { + var cfg = config({proxy: {host: 'corpproxy.com', port: 80}}); + var proxy = getProxy(cfg); + test.equal(proxy.host, 'corpproxy.com'); + test.equal(proxy.port, 80); + test.equal(proxy.auth, undefined); + test.done(); + }, + + + canReadProxyAndAuthHostFromConfig: function(test) { + var cfg = config({proxy: {host: 'corpproxy.com', auth: {password: 'secret', username: 'bob'}}}); + var proxy = getProxy(cfg); + test.equal(proxy.host, 'corpproxy.com'); + test.equal(proxy.port, undefined); + test.deepEqual(proxy.auth, {password: 'secret', username: 'bob'}); + test.done(); + }, + + canReadProxyHostFromEnv: function(test) { + process.env.http_proxy = 'http://corpproxy.com/'; + var proxy = getProxy(config()); + test.equal(proxy.host, 'corpproxy.com'); + test.equal(proxy.port, undefined); + test.equal(proxy.auth, undefined); + test.done(); + }, + + canReadProxyHostAndPortFromEnv: function(test) { + process.env.http_proxy = 'http://corpproxy.com:77/'; + var proxy = getProxy(config()); + test.equal(proxy.host, 'corpproxy.com'); + test.equal(proxy.port, 77); + test.equal(proxy.auth, undefined); + test.done(); + }, + + canReadProxyHostAndPortAndAuthFromEnv: function(test) { + process.env.http_proxy = 'http://bob:secret@corpproxy.com:77/'; + var proxy = getProxy(config()); + test.equal(proxy.host, 'corpproxy.com'); + test.equal(proxy.port, 77); + test.deepEqual(proxy.auth, {username: 'bob', password: 'secret'}); + test.done(); + }, + + proxyFromConfigOverridesProxyFromEnv: function(test) { + process.env.http_proxy = 'http://bob:secret@corpproxy.com:77/'; + var proxy = getProxy(config({proxy: {host: 'other-host.com'}})); + test.equal(proxy.host, 'other-host.com'); + test.equal(proxy.port, undefined); + test.equal(proxy.auth, undefined); + test.done(); + }, + + proxyUnsetFromConfigOverridesProxyFromEnv: function(test) { + process.env.http_proxy = 'http://bob:secret@corpproxy.com:77/'; + var proxy = getProxy(config({proxy: false})); + test.equal(proxy, undefined); + test.done(); + }, + + readProxyFromCorrectEnv: function(test) { + process.env.https_proxy = 'http://corpproxy.com/'; + test.equal(getProxy(config()), undefined); + process.env.http_proxy = 'http://other-proxy.com/'; + test.equal(getProxy(config()).host, 'other-proxy.com'); + test.equal(getProxy(config({url: 'https://google.com/'})).host, 'corpproxy.com'); + delete process.env.https_proxy; + test.equal(getProxy(config({url: 'https://google.com/'})), undefined); + test.done(); + }, + + canHandleCasingInProxyEnv: function(test) { + process.env.HTTPS_PROXY = 'http://corpproxy.com/'; + test.equal(getProxy(config()), undefined); + process.env.HTTP_PROXY = 'http://other-proxy.com/'; + test.equal(getProxy(config()).host, 'other-proxy.com'); + test.equal(getProxy(config({url: 'https://google.com/'})).host, 'corpproxy.com'); + delete process.env.HTTPS_PROXY; + test.equal(getProxy(config({url: 'https://google.com/'})), undefined); + test.done(); + }, + + canHandleNoProxy: function(test) { + process.env.HTTPS_PROXY = 'http://corpproxy.com/'; + process.env.NO_PROXY = 'google.com'; + test.equal(getProxy(config({url: 'https://google.com'})), undefined); + test.equal(getProxy(config({url: 'https://google.com/page.php'})), undefined); + test.done(); + }, + + canHandleNoNoProxy: function(test) { + process.env.HTTPS_PROXY = 'http://cc.com/'; + process.env.NO_PROXY = 'google.com'; + test.equal(getProxy(config({url: 'https://sbb.com'})).host, 'cc.com'); + test.equal(getProxy(config({url: 'https://www.google.com/page.php'})).host, 'cc.com'); + test.done(); + }, + + canHandleMultipleNoProxy: function(test) { + process.env.HTTPS_PROXY = 'http://corpproxy.com/'; + process.env.NO_PROXY = 'google.com, .amazon.de nzz.ch '; + test.equal(getProxy(config({url: 'https://google.com'})), undefined); + test.equal(getProxy(config({url: 'https://www.amazon.de/index.html'})), undefined); + test.equal(getProxy(config({url: 'https://nzz.ch/index.html'})), undefined); + test.done(); + }, + + canHandlePortsInNoProxy: function(test) { + process.env.HTTPS_PROXY = 'http://cc.com/'; + process.env.NO_PROXY = '*google.com:8080'; + test.equal(getProxy(config({url: 'https://www2.google.com:8080/index.html'})), undefined); + test.equal(getProxy(config({url: 'https://google.com/index.html'})).host, 'cc.com'); + test.equal(getProxy(config({url: 'https://nzz.ch/index.html'})).host, 'cc.com'); + test.done(); + }, + + canHandleStarInNoProxy: function(test) { + process.env.HTTPS_PROXY = 'http://cc.com/'; + process.env.NO_PROXY = '*'; + test.equal(getProxy(config({url: 'https://www2.google.com:8080/index.html'})), undefined); + test.equal(getProxy(config({url: 'https://google.com/index.html'})), undefined); + test.equal(getProxy(config({url: 'https://nzz.ch/index.html'})), undefined); + test.done(); + }, + + testIsANoProxyHost: function(test) { + process.env.HTTPS_PROXY = 'http://cc.com/'; + [ + {h: 'bliss.mit.edu', no_p: undefined, res: false}, + {h: 'bliss.mit.edu', no_p: 'localhost', res: false}, + {h: 'bliss.mit.edu', no_p: 'localhost, my.corp.com', res: false}, + {h: 'bliss.mit.edu', no_p: '.mit.edu', res: true}, + {h: 'bliss.mit.edu', no_p: '.mit.edu,localhost', res: true}, + {h: 'bliss.mit.edu', no_p: ' .mit.edu', res: true}, + {h: 'bliss.mit.edu', no_p: '.mit.edu ', res: true}, + {h: 'bliss.mit.edu', no_p: 'localhost, .mit.edu', res: true}, + {h: 'bliss.mit.edu', no_p: '.edu', res: true}, + {h: '127.0.0.1', no_p: '.0.0.1', res: true}, + {h: '127.0.0.1', no_p: 'localhost', res: false} + ].forEach(function(data) { + process.env.no_proxy = data.no_p; + var proxy = getProxy(config({url: 'https://' + data.h + '/index.html'})); + if (data.res) { + test.equal(proxy, undefined); + } else { + test.equal(proxy.host, 'cc.com'); + } + delete process.env.no_proxy; + + process.env.NO_PROXY = data.no_p; + var proxy2 = getProxy(config({url: 'https://' + data.h + '/index.html'})); + if (data.res) { + test.equal(proxy2, undefined); + } else { + test.equal(proxy2.host, 'cc.com'); + } + delete process.env.NO_PROXY; + }); + + test.done(); + } +}; +