test.js
1 /* eslint max-statements:0 */ 2 'use strict'; 3 4 var assert = require('assert'); 5 var parseUrl = require('url').parse; 6 7 var getProxyForUrl = require('./').getProxyForUrl; 8 9 // Runs the callback with process.env temporarily set to env. 10 function runWithEnv(env, callback) { 11 var originalEnv = process.env; 12 process.env = env; 13 try { 14 callback(); 15 } finally { 16 process.env = originalEnv; 17 } 18 } 19 20 // Defines a test case that checks whether getProxyForUrl(input) === expected. 21 function testProxyUrl(env, expected, input) { 22 assert(typeof env === 'object' && env !== null); 23 // Copy object to make sure that the in param does not get modified between 24 // the call of this function and the use of it below. 25 env = JSON.parse(JSON.stringify(env)); 26 27 var title = 'getProxyForUrl(' + JSON.stringify(input) + ')' + 28 ' === ' + JSON.stringify(expected); 29 30 // Save call stack for later use. 31 var stack = {}; 32 Error.captureStackTrace(stack, testProxyUrl); 33 // Only use the last stack frame because that shows where this function is 34 // called, and that is sufficient for our purpose. No need to flood the logs 35 // with an uninteresting stack trace. 36 stack = stack.stack.split('\n', 2)[1]; 37 38 it(title, function() { 39 var actual; 40 runWithEnv(env, function() { 41 actual = getProxyForUrl(input); 42 }); 43 if (expected === actual) { 44 return; // Good! 45 } 46 try { 47 assert.strictEqual(expected, actual); // Create a formatted error message. 48 // Should not happen because previously we determined expected !== actual. 49 throw new Error('assert.strictEqual passed. This is impossible!'); 50 } catch (e) { 51 // Use the original stack trace, so we can see a helpful line number. 52 e.stack = e.message + stack; 53 throw e; 54 } 55 }); 56 } 57 58 describe('getProxyForUrl', function() { 59 describe('No proxy variables', function() { 60 var env = {}; 61 testProxyUrl(env, '', 'http://example.com'); 62 testProxyUrl(env, '', 'https://example.com'); 63 testProxyUrl(env, '', 'ftp://example.com'); 64 }); 65 66 describe('Invalid URLs', function() { 67 var env = {}; 68 env.ALL_PROXY = 'http://unexpected.proxy'; 69 testProxyUrl(env, '', 'bogus'); 70 testProxyUrl(env, '', '//example.com'); 71 testProxyUrl(env, '', '://example.com'); 72 testProxyUrl(env, '', '://'); 73 testProxyUrl(env, '', '/path'); 74 testProxyUrl(env, '', ''); 75 testProxyUrl(env, '', 'http:'); 76 testProxyUrl(env, '', 'http:/'); 77 testProxyUrl(env, '', 'http://'); 78 testProxyUrl(env, '', 'prototype://'); 79 testProxyUrl(env, '', 'hasOwnProperty://'); 80 testProxyUrl(env, '', '__proto__://'); 81 testProxyUrl(env, '', undefined); 82 testProxyUrl(env, '', null); 83 testProxyUrl(env, '', {}); 84 testProxyUrl(env, '', {host: 'x', protocol: 1}); 85 testProxyUrl(env, '', {host: 1, protocol: 'x'}); 86 }); 87 88 describe('http_proxy and HTTP_PROXY', function() { 89 var env = {}; 90 env.HTTP_PROXY = 'http://http-proxy'; 91 92 testProxyUrl(env, '', 'https://example'); 93 testProxyUrl(env, 'http://http-proxy', 'http://example'); 94 testProxyUrl(env, 'http://http-proxy', parseUrl('http://example')); 95 96 // eslint-disable-next-line camelcase 97 env.http_proxy = 'http://priority'; 98 testProxyUrl(env, 'http://priority', 'http://example'); 99 }); 100 101 describe('http_proxy with non-sensical value', function() { 102 var env = {}; 103 // Crazy values should be passed as-is. It is the responsibility of the 104 // one who launches the application that the value makes sense. 105 // TODO: Should we be stricter and perform validation? 106 env.HTTP_PROXY = 'Crazy \n!() { ::// }'; 107 testProxyUrl(env, 'Crazy \n!() { ::// }', 'http://wow'); 108 109 // The implementation assumes that the HTTP_PROXY environment variable is 110 // somewhat reasonable, and if the scheme is missing, it is added. 111 // Garbage in, garbage out some would say... 112 env.HTTP_PROXY = 'crazy without colon slash slash'; 113 testProxyUrl(env, 'http://crazy without colon slash slash', 'http://wow'); 114 }); 115 116 describe('https_proxy and HTTPS_PROXY', function() { 117 var env = {}; 118 // Assert that there is no fall back to http_proxy 119 env.HTTP_PROXY = 'http://unexpected.proxy'; 120 testProxyUrl(env, '', 'https://example'); 121 122 env.HTTPS_PROXY = 'http://https-proxy'; 123 testProxyUrl(env, 'http://https-proxy', 'https://example'); 124 125 // eslint-disable-next-line camelcase 126 env.https_proxy = 'http://priority'; 127 testProxyUrl(env, 'http://priority', 'https://example'); 128 }); 129 130 describe('ftp_proxy', function() { 131 var env = {}; 132 // Something else than http_proxy / https, as a sanity check. 133 env.FTP_PROXY = 'http://ftp-proxy'; 134 135 testProxyUrl(env, 'http://ftp-proxy', 'ftp://example'); 136 testProxyUrl(env, '', 'ftps://example'); 137 }); 138 139 describe('all_proxy', function() { 140 var env = {}; 141 env.ALL_PROXY = 'http://catch-all'; 142 testProxyUrl(env, 'http://catch-all', 'https://example'); 143 144 // eslint-disable-next-line camelcase 145 env.all_proxy = 'http://priority'; 146 testProxyUrl(env, 'http://priority', 'https://example'); 147 }); 148 149 describe('all_proxy without scheme', function() { 150 var env = {}; 151 env.ALL_PROXY = 'noscheme'; 152 testProxyUrl(env, 'http://noscheme', 'http://example'); 153 testProxyUrl(env, 'https://noscheme', 'https://example'); 154 155 // The module does not impose restrictions on the scheme. 156 testProxyUrl(env, 'bogus-scheme://noscheme', 'bogus-scheme://example'); 157 158 // But the URL should still be valid. 159 testProxyUrl(env, '', 'bogus'); 160 }); 161 162 describe('no_proxy empty', function() { 163 var env = {}; 164 env.HTTPS_PROXY = 'http://proxy'; 165 166 // NO_PROXY set but empty. 167 env.NO_PROXY = ''; 168 testProxyUrl(env, 'http://proxy', 'https://example'); 169 170 // No entries in NO_PROXY (comma). 171 env.NO_PROXY = ','; 172 testProxyUrl(env, 'http://proxy', 'https://example'); 173 174 // No entries in NO_PROXY (whitespace). 175 env.NO_PROXY = ' '; 176 testProxyUrl(env, 'http://proxy', 'https://example'); 177 178 // No entries in NO_PROXY (multiple whitespace / commas). 179 env.NO_PROXY = ',\t,,,\n, ,\r'; 180 testProxyUrl(env, 'http://proxy', 'https://example'); 181 }); 182 183 describe('no_proxy=example (single host)', function() { 184 var env = {}; 185 env.HTTP_PROXY = 'http://proxy'; 186 187 env.NO_PROXY = 'example'; 188 testProxyUrl(env, '', 'http://example'); 189 testProxyUrl(env, '', 'http://example:80'); 190 testProxyUrl(env, '', 'http://example:0'); 191 testProxyUrl(env, '', 'http://example:1337'); 192 testProxyUrl(env, 'http://proxy', 'http://sub.example'); 193 testProxyUrl(env, 'http://proxy', 'http://prefexample'); 194 testProxyUrl(env, 'http://proxy', 'http://example.no'); 195 testProxyUrl(env, 'http://proxy', 'http://a.b.example'); 196 testProxyUrl(env, 'http://proxy', 'http://host/example'); 197 }); 198 199 describe('no_proxy=sub.example (subdomain)', function() { 200 var env = {}; 201 env.HTTP_PROXY = 'http://proxy'; 202 203 env.NO_PROXY = 'sub.example'; 204 testProxyUrl(env, 'http://proxy', 'http://example'); 205 testProxyUrl(env, 'http://proxy', 'http://example:80'); 206 testProxyUrl(env, 'http://proxy', 'http://example:0'); 207 testProxyUrl(env, 'http://proxy', 'http://example:1337'); 208 testProxyUrl(env, '', 'http://sub.example'); 209 testProxyUrl(env, 'http://proxy', 'http://no.sub.example'); 210 testProxyUrl(env, 'http://proxy', 'http://sub-example'); 211 testProxyUrl(env, 'http://proxy', 'http://example.sub'); 212 }); 213 214 describe('no_proxy=example:80 (host + port)', function() { 215 var env = {}; 216 env.HTTP_PROXY = 'http://proxy'; 217 218 env.NO_PROXY = 'example:80'; 219 testProxyUrl(env, '', 'http://example'); 220 testProxyUrl(env, '', 'http://example:80'); 221 testProxyUrl(env, '', 'http://example:0'); 222 testProxyUrl(env, 'http://proxy', 'http://example:1337'); 223 testProxyUrl(env, 'http://proxy', 'http://sub.example'); 224 testProxyUrl(env, 'http://proxy', 'http://prefexample'); 225 testProxyUrl(env, 'http://proxy', 'http://example.no'); 226 testProxyUrl(env, 'http://proxy', 'http://a.b.example'); 227 }); 228 229 describe('no_proxy=.example (host suffix)', function() { 230 var env = {}; 231 env.HTTP_PROXY = 'http://proxy'; 232 233 env.NO_PROXY = '.example'; 234 testProxyUrl(env, 'http://proxy', 'http://example'); 235 testProxyUrl(env, 'http://proxy', 'http://example:80'); 236 testProxyUrl(env, 'http://proxy', 'http://example:1337'); 237 testProxyUrl(env, '', 'http://sub.example'); 238 testProxyUrl(env, '', 'http://sub.example:80'); 239 testProxyUrl(env, '', 'http://sub.example:1337'); 240 testProxyUrl(env, 'http://proxy', 'http://prefexample'); 241 testProxyUrl(env, 'http://proxy', 'http://example.no'); 242 testProxyUrl(env, '', 'http://a.b.example'); 243 }); 244 245 describe('no_proxy=*', function() { 246 var env = {}; 247 env.HTTP_PROXY = 'http://proxy'; 248 env.NO_PROXY = '*'; 249 testProxyUrl(env, '', 'http://example.com'); 250 }); 251 252 describe('no_proxy=*.example (host suffix with *.)', function() { 253 var env = {}; 254 env.HTTP_PROXY = 'http://proxy'; 255 256 env.NO_PROXY = '*.example'; 257 testProxyUrl(env, 'http://proxy', 'http://example'); 258 testProxyUrl(env, 'http://proxy', 'http://example:80'); 259 testProxyUrl(env, 'http://proxy', 'http://example:1337'); 260 testProxyUrl(env, '', 'http://sub.example'); 261 testProxyUrl(env, '', 'http://sub.example:80'); 262 testProxyUrl(env, '', 'http://sub.example:1337'); 263 testProxyUrl(env, 'http://proxy', 'http://prefexample'); 264 testProxyUrl(env, 'http://proxy', 'http://example.no'); 265 testProxyUrl(env, '', 'http://a.b.example'); 266 }); 267 268 describe('no_proxy=*example (substring suffix)', function() { 269 var env = {}; 270 env.HTTP_PROXY = 'http://proxy'; 271 272 env.NO_PROXY = '*example'; 273 testProxyUrl(env, '', 'http://example'); 274 testProxyUrl(env, '', 'http://example:80'); 275 testProxyUrl(env, '', 'http://example:1337'); 276 testProxyUrl(env, '', 'http://sub.example'); 277 testProxyUrl(env, '', 'http://sub.example:80'); 278 testProxyUrl(env, '', 'http://sub.example:1337'); 279 testProxyUrl(env, '', 'http://prefexample'); 280 testProxyUrl(env, '', 'http://a.b.example'); 281 testProxyUrl(env, 'http://proxy', 'http://example.no'); 282 testProxyUrl(env, 'http://proxy', 'http://host/example'); 283 }); 284 285 describe('no_proxy=.*example (arbitrary wildcards are NOT supported)', 286 function() { 287 var env = {}; 288 env.HTTP_PROXY = 'http://proxy'; 289 290 env.NO_PROXY = '.*example'; 291 testProxyUrl(env, 'http://proxy', 'http://example'); 292 testProxyUrl(env, 'http://proxy', 'http://sub.example'); 293 testProxyUrl(env, 'http://proxy', 'http://sub.example'); 294 testProxyUrl(env, 'http://proxy', 'http://prefexample'); 295 testProxyUrl(env, 'http://proxy', 'http://x.prefexample'); 296 testProxyUrl(env, 'http://proxy', 'http://a.b.example'); 297 }); 298 299 describe('no_proxy=[::1],[::2]:80,10.0.0.1,10.0.0.2:80 (IP addresses)', 300 function() { 301 var env = {}; 302 env.HTTP_PROXY = 'http://proxy'; 303 304 env.NO_PROXY = '[::1],[::2]:80,10.0.0.1,10.0.0.2:80'; 305 testProxyUrl(env, '', 'http://[::1]/'); 306 testProxyUrl(env, '', 'http://[::1]:80/'); 307 testProxyUrl(env, '', 'http://[::1]:1337/'); 308 309 testProxyUrl(env, '', 'http://[::2]/'); 310 testProxyUrl(env, '', 'http://[::2]:80/'); 311 testProxyUrl(env, 'http://proxy', 'http://[::2]:1337/'); 312 313 testProxyUrl(env, '', 'http://10.0.0.1/'); 314 testProxyUrl(env, '', 'http://10.0.0.1:80/'); 315 testProxyUrl(env, '', 'http://10.0.0.1:1337/'); 316 317 testProxyUrl(env, '', 'http://10.0.0.2/'); 318 testProxyUrl(env, '', 'http://10.0.0.2:80/'); 319 testProxyUrl(env, 'http://proxy', 'http://10.0.0.2:1337/'); 320 }); 321 322 describe('no_proxy=127.0.0.1/32 (CIDR is NOT supported)', function() { 323 var env = {}; 324 env.HTTP_PROXY = 'http://proxy'; 325 326 env.NO_PROXY = '127.0.0.1/32'; 327 testProxyUrl(env, 'http://proxy', 'http://127.0.0.1'); 328 testProxyUrl(env, 'http://proxy', 'http://127.0.0.1/32'); 329 }); 330 331 describe('no_proxy=127.0.0.1 does NOT match localhost', function() { 332 var env = {}; 333 env.HTTP_PROXY = 'http://proxy'; 334 335 env.NO_PROXY = '127.0.0.1'; 336 testProxyUrl(env, '', 'http://127.0.0.1'); 337 // We're not performing DNS queries, so this shouldn't match. 338 testProxyUrl(env, 'http://proxy', 'http://localhost'); 339 }); 340 341 describe('no_proxy with protocols that have a default port', function() { 342 var env = {}; 343 env.WS_PROXY = 'http://ws'; 344 env.WSS_PROXY = 'http://wss'; 345 env.HTTP_PROXY = 'http://http'; 346 env.HTTPS_PROXY = 'http://https'; 347 env.GOPHER_PROXY = 'http://gopher'; 348 env.FTP_PROXY = 'http://ftp'; 349 env.ALL_PROXY = 'http://all'; 350 351 env.NO_PROXY = 'xxx:21,xxx:70,xxx:80,xxx:443'; 352 353 testProxyUrl(env, '', 'http://xxx'); 354 testProxyUrl(env, '', 'http://xxx:80'); 355 testProxyUrl(env, 'http://http', 'http://xxx:1337'); 356 357 testProxyUrl(env, '', 'ws://xxx'); 358 testProxyUrl(env, '', 'ws://xxx:80'); 359 testProxyUrl(env, 'http://ws', 'ws://xxx:1337'); 360 361 testProxyUrl(env, '', 'https://xxx'); 362 testProxyUrl(env, '', 'https://xxx:443'); 363 testProxyUrl(env, 'http://https', 'https://xxx:1337'); 364 365 testProxyUrl(env, '', 'wss://xxx'); 366 testProxyUrl(env, '', 'wss://xxx:443'); 367 testProxyUrl(env, 'http://wss', 'wss://xxx:1337'); 368 369 testProxyUrl(env, '', 'gopher://xxx'); 370 testProxyUrl(env, '', 'gopher://xxx:70'); 371 testProxyUrl(env, 'http://gopher', 'gopher://xxx:1337'); 372 373 testProxyUrl(env, '', 'ftp://xxx'); 374 testProxyUrl(env, '', 'ftp://xxx:21'); 375 testProxyUrl(env, 'http://ftp', 'ftp://xxx:1337'); 376 }); 377 378 describe('no_proxy should not be case-sensitive', function() { 379 var env = {}; 380 env.HTTP_PROXY = 'http://proxy'; 381 env.NO_PROXY = 'XXX,YYY,ZzZ'; 382 383 testProxyUrl(env, '', 'http://xxx'); 384 testProxyUrl(env, '', 'http://XXX'); 385 testProxyUrl(env, '', 'http://yyy'); 386 testProxyUrl(env, '', 'http://YYY'); 387 testProxyUrl(env, '', 'http://ZzZ'); 388 testProxyUrl(env, '', 'http://zZz'); 389 }); 390 391 describe('NPM proxy configuration', function() { 392 describe('npm_config_http_proxy should work', function() { 393 var env = {}; 394 // eslint-disable-next-line camelcase 395 env.npm_config_http_proxy = 'http://http-proxy'; 396 397 testProxyUrl(env, '', 'https://example'); 398 testProxyUrl(env, 'http://http-proxy', 'http://example'); 399 400 // eslint-disable-next-line camelcase 401 env.npm_config_http_proxy = 'http://priority'; 402 testProxyUrl(env, 'http://priority', 'http://example'); 403 }); 404 // eslint-disable-next-line max-len 405 describe('npm_config_http_proxy should take precedence over HTTP_PROXY and npm_config_proxy', function() { 406 var env = {}; 407 // eslint-disable-next-line camelcase 408 env.npm_config_http_proxy = 'http://http-proxy'; 409 // eslint-disable-next-line camelcase 410 env.npm_config_proxy = 'http://unexpected-proxy'; 411 env.HTTP_PROXY = 'http://unexpected-proxy'; 412 413 testProxyUrl(env, 'http://http-proxy', 'http://example'); 414 }); 415 describe('npm_config_https_proxy should work', function() { 416 var env = {}; 417 // eslint-disable-next-line camelcase 418 env.npm_config_http_proxy = 'http://unexpected.proxy'; 419 testProxyUrl(env, '', 'https://example'); 420 421 // eslint-disable-next-line camelcase 422 env.npm_config_https_proxy = 'http://https-proxy'; 423 testProxyUrl(env, 'http://https-proxy', 'https://example'); 424 425 // eslint-disable-next-line camelcase 426 env.npm_config_https_proxy = 'http://priority'; 427 testProxyUrl(env, 'http://priority', 'https://example'); 428 }); 429 // eslint-disable-next-line max-len 430 describe('npm_config_https_proxy should take precedence over HTTPS_PROXY and npm_config_proxy', function() { 431 var env = {}; 432 // eslint-disable-next-line camelcase 433 env.npm_config_https_proxy = 'http://https-proxy'; 434 // eslint-disable-next-line camelcase 435 env.npm_config_proxy = 'http://unexpected-proxy'; 436 env.HTTPS_PROXY = 'http://unexpected-proxy'; 437 438 testProxyUrl(env, 'http://https-proxy', 'https://example'); 439 }); 440 describe('npm_config_proxy should work', function() { 441 var env = {}; 442 // eslint-disable-next-line camelcase 443 env.npm_config_proxy = 'http://http-proxy'; 444 testProxyUrl(env, 'http://http-proxy', 'http://example'); 445 testProxyUrl(env, 'http://http-proxy', 'https://example'); 446 447 // eslint-disable-next-line camelcase 448 env.npm_config_proxy = 'http://priority'; 449 testProxyUrl(env, 'http://priority', 'http://example'); 450 testProxyUrl(env, 'http://priority', 'https://example'); 451 }); 452 // eslint-disable-next-line max-len 453 describe('HTTP_PROXY and HTTPS_PROXY should take precedence over npm_config_proxy', function() { 454 var env = {}; 455 env.HTTP_PROXY = 'http://http-proxy'; 456 env.HTTPS_PROXY = 'http://https-proxy'; 457 // eslint-disable-next-line camelcase 458 env.npm_config_proxy = 'http://unexpected-proxy'; 459 testProxyUrl(env, 'http://http-proxy', 'http://example'); 460 testProxyUrl(env, 'http://https-proxy', 'https://example'); 461 }); 462 describe('npm_config_no_proxy should work', function() { 463 var env = {}; 464 env.HTTP_PROXY = 'http://proxy'; 465 // eslint-disable-next-line camelcase 466 env.npm_config_no_proxy = 'example'; 467 468 testProxyUrl(env, '', 'http://example'); 469 testProxyUrl(env, 'http://proxy', 'http://otherwebsite'); 470 }); 471 // eslint-disable-next-line max-len 472 describe('npm_config_no_proxy should take precedence over NO_PROXY', function() { 473 var env = {}; 474 env.HTTP_PROXY = 'http://proxy'; 475 env.NO_PROXY = 'otherwebsite'; 476 // eslint-disable-next-line camelcase 477 env.npm_config_no_proxy = 'example'; 478 479 testProxyUrl(env, '', 'http://example'); 480 testProxyUrl(env, 'http://proxy', 'http://otherwebsite'); 481 }); 482 }); 483 });