├── .gitignore ├── .travis.yml ├── CODEOWNERS ├── LICENSE ├── README.md ├── index.js ├── lib └── agents.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | *node_modules* 2 | .DS_Store 3 | npm-debug* 4 | .*.sw[mnop] 5 | components/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.11" 5 | 6 | matrix: 7 | fast_finish: true 8 | allow_failures: 9 | - node_js: "0.11" 10 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. 2 | #ECCN:Open Source 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, GoInstant Inc., a salesforce.com company 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of salesforce.com, nor GoInstant, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo has been archived 2 | 3 | This project no longer has an active maintainer, and so it has been archived. You may be interested 4 | in [global-tunnel-ng](https://github.com/np-maintain/global-tunnel), a hard fork of this codebase. 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | 'use strict'; 3 | /** 4 | * @fileOverview 5 | * Global proxy settings. 6 | */ 7 | var globalTunnel = exports; 8 | exports.constructor = function globalTunnel(){}; 9 | 10 | var http = require('http'); 11 | var https = require('https'); 12 | var urlParse = require('url').parse; 13 | 14 | var _ = require('lodash'); 15 | var tunnel = require('tunnel'); 16 | 17 | var agents = require('./lib/agents'); 18 | exports.agents = agents; 19 | 20 | // save the original globalAgents for restoration later. 21 | var ORIGINALS = { 22 | http: _.pick(http, 'globalAgent', 'request'), 23 | https: _.pick(https, 'globalAgent', 'request') 24 | }; 25 | function resetGlobals() { 26 | _.assign(http, ORIGINALS.http); 27 | _.assign(https, ORIGINALS.https); 28 | } 29 | 30 | /** 31 | * Parses the de facto `http_proxy` environment. 32 | */ 33 | function tryParse(url) { 34 | if (!url) { 35 | return null; 36 | } 37 | 38 | var conf = {}; 39 | var parsed = urlParse(url); 40 | conf.protocol = parsed.protocol; 41 | conf.host = parsed.hostname; 42 | conf.port = parseInt(parsed.port,10); 43 | return conf; 44 | } 45 | 46 | globalTunnel.isProxying = false; 47 | 48 | /** 49 | * Overrides the node http/https `globalAgent`s to use the configured proxy. 50 | * 51 | * If the config is empty, the `http_proxy` environment variable is checked. If 52 | * that's not present, no proxying will be enabled. 53 | * 54 | * @param {object} conf 55 | * @param {string} conf.host 56 | * @param {int} conf.port 57 | * @param {int} [conf.sockets] maximum number of sockets to pool (falsy uses 58 | * node's default). 59 | */ 60 | globalTunnel.initialize = function(conf) { 61 | if (globalTunnel.isProxying) { 62 | return; 63 | } 64 | 65 | conf = conf || {}; 66 | if (typeof conf === 'string') { 67 | conf = tryParse(conf); 68 | } 69 | 70 | if (_.isEmpty(conf)) { 71 | conf = tryParse(process.env['http_proxy']); 72 | if (!conf) { 73 | globalTunnel.isProxying = false; 74 | return; 75 | } 76 | } 77 | conf = _.clone(conf); 78 | 79 | if (!conf.host) { 80 | throw new Error('upstream proxy host is required'); 81 | } 82 | if (!conf.port) { 83 | throw new Error('upstream proxy port is required'); 84 | } 85 | 86 | if (conf.protocol === undefined) { 87 | conf.protocol = 'http:'; // default to proxy speaking http 88 | } 89 | if (!/:$/.test(conf.protocol)) { 90 | conf.protocol = conf.protocol + ':'; 91 | } 92 | 93 | if (!conf.connect) { 94 | conf.connect = 'https'; // just HTTPS by default 95 | } 96 | switch(conf.connect) { 97 | case 'both': 98 | conf.connectHttp = true; 99 | conf.connectHttps = true; 100 | break; 101 | case 'neither': 102 | conf.connectHttp = false; 103 | conf.connectHttps = false; 104 | break; 105 | case 'https': 106 | conf.connectHttp = false; 107 | conf.connectHttps = true; 108 | break; 109 | default: 110 | throw new Error('valid connect options are "neither", "https", or "both"'); 111 | } 112 | 113 | if (conf.httpsOptions) { 114 | conf.outerHttpsOpts = conf.innerHttpsOpts = conf.httpsOptions; 115 | } 116 | 117 | try { 118 | http.globalAgent = globalTunnel._makeAgent(conf, 'http', conf.connectHttp); 119 | https.globalAgent = globalTunnel._makeAgent(conf, 'https', conf.connectHttps); 120 | 121 | http.request = globalTunnel._defaultedAgentRequest.bind(http, 'http'); 122 | https.request = globalTunnel._defaultedAgentRequest.bind(https, 'https'); 123 | 124 | globalTunnel.isProxying = true; 125 | } catch (e) { 126 | resetGlobals(); 127 | throw e; 128 | } 129 | }; 130 | 131 | /** 132 | * Construct an agent based on: 133 | * - is the connection to the proxy secure? 134 | * - is the connection to the origin secure? 135 | * - the address of the proxy 136 | */ 137 | globalTunnel._makeAgent = function(conf, innerProtocol, useCONNECT) { 138 | var outerProtocol = conf.protocol; 139 | innerProtocol = innerProtocol + ':'; 140 | 141 | var opts = { 142 | proxy: _.pick(conf, 'host','port','protocol','localAddress'), 143 | maxSockets: conf.sockets 144 | }; 145 | opts.proxy.innerProtocol = innerProtocol; 146 | 147 | if (useCONNECT) { 148 | if (conf.proxyHttpsOptions) { 149 | _.assign(opts.proxy, conf.proxyHttpsOptions); 150 | } 151 | if (conf.originHttpsOptions) { 152 | _.assign(opts, conf.originHttpsOptions); 153 | } 154 | 155 | if (outerProtocol === 'https:') { 156 | if (innerProtocol === 'https:') { 157 | return tunnel.httpsOverHttps(opts); 158 | } else { 159 | return tunnel.httpOverHttps(opts); 160 | } 161 | } else { 162 | if (innerProtocol === 'https:') { 163 | return tunnel.httpsOverHttp(opts); 164 | } else { 165 | return tunnel.httpOverHttp(opts); 166 | } 167 | } 168 | 169 | } else { 170 | if (conf.originHttpsOptions) { 171 | throw new Error('originHttpsOptions must be combined with a tunnel:true option'); 172 | } 173 | if (conf.proxyHttpsOptions) { 174 | // NB: not opts. 175 | _.assign(opts, conf.proxyHttpsOptions); 176 | } 177 | 178 | if (outerProtocol === 'https:') { 179 | return new agents.OuterHttpsAgent(opts); 180 | } else { 181 | return new agents.OuterHttpAgent(opts); 182 | } 183 | } 184 | }; 185 | 186 | /** 187 | * Override for http.request and https.request, makes sure to default the agent 188 | * to the global agent. Due to how node implements it in lib/http.js, the 189 | * globalAgent we define won't get used (node uses a module-scoped variable, 190 | * not the exports field). 191 | * @param {string} protocol bound during initialization 192 | * @param {string|object} options http/https request url or options 193 | * @param {function} [cb] 194 | * @private 195 | */ 196 | globalTunnel._defaultedAgentRequest = function(protocol, options, callback) { 197 | var httpOrHttps = this; 198 | 199 | if (typeof options === 'string') { 200 | options = urlParse(options); 201 | } else { 202 | options = _.clone(options); 203 | } 204 | 205 | // A literal `false` means "no agent at all", other falsey values should use 206 | // our global agent. 207 | if (!options.agent && options.agent !== false) { 208 | options.agent = httpOrHttps.globalAgent; 209 | } 210 | 211 | return ORIGINALS[protocol].request.call(httpOrHttps, options, callback); 212 | }; 213 | 214 | /** 215 | * Restores global http/https agents. 216 | */ 217 | globalTunnel.end = function() { 218 | resetGlobals(); 219 | globalTunnel.isProxying = false; 220 | }; 221 | -------------------------------------------------------------------------------- /lib/agents.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | 'use strict'; 3 | 4 | var util = require('util'); 5 | var http = require('http'); 6 | var HttpAgent = http.Agent; 7 | var https = require('https'); 8 | var HttpsAgent = https.Agent; 9 | 10 | var _ = require('lodash'); 11 | 12 | /** 13 | * Proxy some traffic over HTTP. 14 | */ 15 | function OuterHttpAgent(opts) { 16 | HttpAgent.call(this, opts); 17 | mixinProxying(this, opts.proxy); 18 | } 19 | util.inherits(OuterHttpAgent, HttpAgent); 20 | exports.OuterHttpAgent = OuterHttpAgent; 21 | 22 | /** 23 | * Proxy some traffic over HTTPS. 24 | */ 25 | function OuterHttpsAgent(opts) { 26 | HttpsAgent.call(this, opts); 27 | mixinProxying(this, opts.proxy); 28 | } 29 | util.inherits(OuterHttpsAgent, HttpsAgent); 30 | exports.OuterHttpsAgent = OuterHttpsAgent; 31 | 32 | /** 33 | * Override createConnection and addRequest methods on the supplied agent. 34 | * http.Agent and https.Agent will set up createConnection in the constructor. 35 | */ 36 | function mixinProxying(agent, proxyOpts) { 37 | agent.proxy = proxyOpts; 38 | 39 | var orig = _.pick(agent, 'createConnection', 'addRequest'); 40 | 41 | // Make the tcp or tls connection go to the proxy, ignoring the 42 | // destination host:port arguments. 43 | agent.createConnection = function(port, host, options) { 44 | return orig.createConnection.call(this, 45 | this.proxy.port, this.proxy.host, options); 46 | }; 47 | 48 | // tell the proxy where we really want to go by fully-qualifying the path 49 | // part. Force a localAddress if one was configured 50 | agent.addRequest = function(req, host, port, localAddress) { 51 | req.path = this.proxy.innerProtocol + '//' + host + ':' + port + req.path; 52 | if (this.proxy.localAddress) { 53 | localAddress = this.proxy.localAddress; 54 | } 55 | return orig.addRequest.call(this, req, host, port, localAddress); 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "global-tunnel", 3 | "version": "1.2.0", 4 | "description": "Global HTTP & HTTPS tunneling", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/salesforce/global-tunnel.git" 9 | }, 10 | "engines": { 11 | "node": "0.10.x" 12 | }, 13 | "dependencies": { 14 | "lodash": "1.3.1", 15 | "tunnel": "0.0.2" 16 | }, 17 | "devDependencies": { 18 | "mocha": "1.16.2", 19 | "goinstant-assert": "1.1.1", 20 | "request": "2.30.0", 21 | "sinon": "1.7.3" 22 | }, 23 | "scripts": { 24 | "test": "mocha test" 25 | }, 26 | "keywords": [ 27 | "http", 28 | "https", 29 | "tunnel", 30 | "global" 31 | ], 32 | "author": "GoInstant Inc., a salesforce.com company", 33 | "license": "BSD-3-Clause" 34 | } 35 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | var assert = require('goinstant-assert'); 4 | var sinon = require('sinon'); 5 | 6 | // deliberate: node and 3rd party modules before global-tunnel 7 | var EventEmitter = require('events').EventEmitter; 8 | var net = require('net'); 9 | var tls = require('tls'); 10 | var http = require('http'); 11 | var globalHttpAgent = http.globalAgent; 12 | var https = require('https'); 13 | var globalHttpsAgent = https.globalAgent; 14 | var request = require('request'); 15 | 16 | // deliberate: load after all 3rd party modules 17 | var globalTunnel = require('../index'); 18 | 19 | function newFakeAgent() { 20 | var fakeAgent = { 21 | addRequest: sinon.stub() 22 | }; 23 | return fakeAgent; 24 | } 25 | 26 | var origEnv; 27 | function saveEnv() { 28 | origEnv = process.env['http_proxy']; 29 | delete process.env['http_proxy']; 30 | } 31 | function restoreEnv() { 32 | if (origEnv !== undefined) { 33 | process.env['http_proxy'] = origEnv; 34 | } 35 | } 36 | 37 | describe('global-proxy', function() { 38 | // save and restore http_proxy environment variable (yes, it's lower-case by 39 | // convention). 40 | before(saveEnv); 41 | after(restoreEnv); 42 | 43 | 44 | // sinon setup & teardown 45 | var sandbox; 46 | 47 | before(function() { 48 | sandbox = sinon.sandbox.create(); 49 | 50 | sandbox.stub(globalHttpAgent, 'addRequest'); 51 | sandbox.stub(globalHttpsAgent, 'addRequest'); 52 | 53 | assert.equal(http.Agent.prototype.addRequest, 54 | https.Agent.prototype.addRequest); 55 | sandbox.spy(http.Agent.prototype, 'addRequest'); 56 | 57 | sandbox.stub(net, 'createConnection', function() { 58 | return new EventEmitter(); 59 | }); 60 | sandbox.stub(tls, 'connect', function() { 61 | return new EventEmitter(); 62 | }); 63 | }); 64 | 65 | afterEach(function() { 66 | // would love to sandbox.reset(), but alas: no such thing 67 | globalHttpAgent.addRequest.reset(); 68 | globalHttpsAgent.addRequest.reset(); 69 | http.Agent.prototype.addRequest.reset(); 70 | net.createConnection.reset(); 71 | tls.connect.reset(); 72 | }); 73 | 74 | after(function() { 75 | sandbox.restore(); 76 | }); 77 | 78 | 79 | describe('invalid configs', function() { 80 | it('requires a host', function() { 81 | var conf = { host: null, port: 1234 }; 82 | assert.exception(function() { 83 | globalTunnel.initialize(conf); 84 | }, 'upstream proxy host is required'); 85 | globalTunnel.end(); 86 | }); 87 | 88 | it('requires a port', function() { 89 | var conf = { host: '10.2.3.4', port: 0 }; 90 | assert.exception(function() { 91 | globalTunnel.initialize(conf); 92 | }, 'upstream proxy port is required'); 93 | globalTunnel.end(); 94 | }); 95 | 96 | it('clamps tunnel types', function() { 97 | var conf = { host: '10.2.3.4', port: 1234, connect: 'INVALID' }; 98 | assert.exception(function() { 99 | globalTunnel.initialize(conf); 100 | }, 'valid connect options are "neither", "https", or "both"'); 101 | globalTunnel.end(); 102 | }); 103 | }); 104 | 105 | function proxyEnabledTests(testParams) { 106 | 107 | function connected(innerProto) { 108 | var innerSecure = (innerProto === 'https:'); 109 | 110 | var called; 111 | if (testParams.secure) { 112 | called = tls.connect; 113 | sinon.assert.notCalled(net.createConnection); 114 | } else { 115 | called = net.createConnection; 116 | sinon.assert.notCalled(tls.connect); 117 | } 118 | 119 | sinon.assert.calledOnce(called); 120 | if (typeof called.getCall(0).args[0] === 'object') { 121 | sinon.assert.calledWith(called, sinon.match.has('port', testParams.port)); 122 | sinon.assert.calledWith(called, sinon.match.has('host', '10.2.3.4')); 123 | } else { 124 | sinon.assert.calledWith(called, 125 | testParams.port, '10.2.3.4'); 126 | } 127 | 128 | var isCONNECT = testParams.connect === 'both' || 129 | (innerSecure && testParams.connect === 'https'); 130 | if (isCONNECT) { 131 | var expectConnect = 'example.dev:' + (innerSecure ? 443 : 80); 132 | var whichAgent = innerSecure ? https.globalAgent : http.globalAgent; 133 | 134 | sinon.assert.calledOnce(whichAgent.request); 135 | sinon.assert.calledWith(whichAgent.request, 136 | sinon.match.has('method','CONNECT')); 137 | sinon.assert.calledWith(whichAgent.request, 138 | sinon.match.has('path',expectConnect)); 139 | } else { 140 | sinon.assert.calledOnce(http.Agent.prototype.addRequest); 141 | var req = http.Agent.prototype.addRequest.getCall(0).args[0]; 142 | 143 | var method = req.method; 144 | assert.equal(method, 'GET'); 145 | 146 | var path = req.path; 147 | if (innerSecure) { 148 | assert.match(path, new RegExp('^https://example\\.dev:443/')); 149 | } else { 150 | assert.match(path, new RegExp('^http://example\\.dev:80/')); 151 | } 152 | } 153 | } 154 | 155 | var localSandbox; 156 | beforeEach(function() { 157 | localSandbox = sinon.sandbox.create(); 158 | if (testParams.connect === 'both') { 159 | localSandbox.spy(http.globalAgent, 'request'); 160 | } 161 | if (testParams.connect !== 'neither') { 162 | localSandbox.spy(https.globalAgent, 'request'); 163 | } 164 | }); 165 | afterEach(function() { 166 | localSandbox.restore(); 167 | }); 168 | 169 | it('(got proxying set up)', function() { 170 | assert.isTrue(globalTunnel.isProxying); 171 | }); 172 | 173 | describe('with the request library', function() { 174 | it('will proxy http requests', function(done) { 175 | assert.isTrue(globalTunnel.isProxying); 176 | var dummyCb = sinon.stub(); 177 | request.get('http://example.dev/', dummyCb); 178 | setImmediate(function() { 179 | connected('http:'); 180 | sinon.assert.notCalled(globalHttpAgent.addRequest); 181 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 182 | done(); 183 | }); 184 | }); 185 | 186 | it('will proxy https requests', function(done) { 187 | assert.isTrue(globalTunnel.isProxying); 188 | var dummyCb = sinon.stub(); 189 | request.get('https://example.dev/', dummyCb); 190 | setImmediate(function() { 191 | connected('https:'); 192 | sinon.assert.notCalled(globalHttpAgent.addRequest); 193 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 194 | done(); 195 | }); 196 | }); 197 | }); 198 | 199 | describe('using raw request interface', function() { 200 | it('will proxy http requests', function() { 201 | var req = http.request({ 202 | method: 'GET', 203 | path: '/raw-http', 204 | host: 'example.dev' 205 | }, function() {}); 206 | req.end(); 207 | 208 | connected('http:'); 209 | sinon.assert.notCalled(globalHttpAgent.addRequest); 210 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 211 | }); 212 | 213 | it('will proxy https requests', function() { 214 | var req = https.request({ 215 | method: 'GET', 216 | path: '/raw-https', 217 | host: 'example.dev' 218 | }, function() {}); 219 | req.end(); 220 | 221 | connected('https:'); 222 | sinon.assert.notCalled(globalHttpAgent.addRequest); 223 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 224 | }); 225 | 226 | it('request respects explicit agent param', function() { 227 | var agent = newFakeAgent(); 228 | var req = http.request({ 229 | method: 'GET', 230 | path: '/raw-http-w-agent', 231 | host: 'example.dev', 232 | agent: agent 233 | }, function() {}); 234 | req.end(); 235 | 236 | sinon.assert.notCalled(globalHttpAgent.addRequest); 237 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 238 | sinon.assert.notCalled(net.createConnection); 239 | sinon.assert.notCalled(tls.connect); 240 | sinon.assert.calledOnce(agent.addRequest); 241 | }); 242 | 243 | describe('request with `false` agent', function() { 244 | before(function() { 245 | sinon.stub(http.ClientRequest.prototype, 'onSocket'); 246 | }); 247 | after(function() { 248 | http.ClientRequest.prototype.onSocket.restore(); 249 | }); 250 | 251 | it('uses no agent', function() { 252 | var createConnection = sinon.stub(); 253 | var req = http.request({ 254 | method: 'GET', 255 | path: '/no-agent', 256 | host: 'example.dev', 257 | agent: false, 258 | createConnection: createConnection 259 | }, function() {}); 260 | req.end(); 261 | 262 | sinon.assert.notCalled(globalHttpAgent.addRequest); 263 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 264 | sinon.assert.calledOnce(createConnection); 265 | }); 266 | }); 267 | }); 268 | } 269 | 270 | function enabledBlock(conf, testParams) { 271 | before(function() { 272 | globalTunnel.initialize(conf); 273 | }); 274 | after(function() { 275 | globalTunnel.end(); 276 | }); 277 | 278 | proxyEnabledTests(testParams); 279 | } 280 | 281 | describe('with http proxy in intercept mode', function() { 282 | var conf = { 283 | connect: 'neither', 284 | protocol: 'http:', 285 | host: '10.2.3.4', 286 | port: 3333 287 | }; 288 | enabledBlock(conf, { secure: false, connect: 'neither', port: 3333 }); 289 | }); 290 | 291 | describe('with https proxy in intercept mode', function() { 292 | var conf = { 293 | connect: 'neither', 294 | protocol: 'https:', 295 | host: '10.2.3.4', 296 | port: 3334 297 | }; 298 | enabledBlock(conf, { secure: true, connect: 'neither', port: 3334 }); 299 | }); 300 | 301 | describe('with http proxy in CONNECT mode', function() { 302 | var conf = { 303 | connect: 'both', 304 | protocol: 'http:', 305 | host: '10.2.3.4', 306 | port: 3335 307 | }; 308 | enabledBlock(conf, { secure: false, connect: 'both', port: 3335 }); 309 | }); 310 | 311 | describe('with https proxy in CONNECT mode', function() { 312 | var conf = { 313 | connect: 'both', 314 | protocol: 'https:', 315 | host: '10.2.3.4', 316 | port: 3336 317 | }; 318 | enabledBlock(conf, { secure: true, connect: 'both', port: 3336 }); 319 | }); 320 | 321 | describe('with http proxy in mixed mode', function() { 322 | var conf = { 323 | protocol: 'http:', 324 | host: '10.2.3.4', 325 | port: 3337 326 | }; 327 | enabledBlock(conf, { secure: false, connect: 'https', port: 3337 }); 328 | }); 329 | 330 | describe('with https proxy in mixed mode', function() { 331 | var conf = { 332 | protocol: 'https:', 333 | host: '10.2.3.4', 334 | port: 3338 335 | }; 336 | enabledBlock(conf, { secure: true, connect: 'https', port: 3338 }); 337 | }); 338 | 339 | 340 | describe('using env var', function() { 341 | after(function() { 342 | delete process.env['http_proxy']; 343 | }); 344 | 345 | describe('for http', function() { 346 | before(function() { 347 | process.env['http_proxy'] = 'http://10.2.3.4:1234'; 348 | }); 349 | enabledBlock({}, { secure: false, connect: 'https', port: 1234 }); 350 | }); 351 | 352 | describe('for https', function() { 353 | before(function() { 354 | process.env['http_proxy'] = 'https://10.2.3.4:1235'; 355 | }); 356 | enabledBlock({}, { secure: true, connect: 'https', port: 1235 }); 357 | }); 358 | }); 359 | 360 | 361 | // deliberately after the block above 362 | describe('with proxy disabled', function() { 363 | it('claims to be disabled', function() { 364 | assert.isFalse(globalTunnel.isProxying); 365 | }); 366 | 367 | it('will NOT proxy http requests', function(done) { 368 | var dummyCb = sinon.stub(); 369 | request.get('http://example.dev/', dummyCb); 370 | setImmediate(function() { 371 | sinon.assert.calledOnce(globalHttpAgent.addRequest); 372 | sinon.assert.notCalled(globalHttpsAgent.addRequest); 373 | done(); 374 | }); 375 | }); 376 | 377 | it('will NOT proxy https requests', function(done) { 378 | var dummyCb = sinon.stub(); 379 | request.get('https://example.dev/', dummyCb); 380 | setImmediate(function() { 381 | sinon.assert.notCalled(globalHttpAgent.addRequest); 382 | sinon.assert.calledOnce(globalHttpsAgent.addRequest); 383 | done(); 384 | }); 385 | }); 386 | }); 387 | }); 388 | --------------------------------------------------------------------------------