index.js
1 'use strict'; 2 3 var parseUrl = require('url').parse; 4 5 var DEFAULT_PORTS = { 6 ftp: 21, 7 gopher: 70, 8 http: 80, 9 https: 443, 10 ws: 80, 11 wss: 443, 12 }; 13 14 var stringEndsWith = String.prototype.endsWith || function(s) { 15 return s.length <= this.length && 16 this.indexOf(s, this.length - s.length) !== -1; 17 }; 18 19 /** 20 * @param {string|object} url - The URL, or the result from url.parse. 21 * @return {string} The URL of the proxy that should handle the request to the 22 * given URL. If no proxy is set, this will be an empty string. 23 */ 24 function getProxyForUrl(url) { 25 var parsedUrl = typeof url === 'string' ? parseUrl(url) : url || {}; 26 var proto = parsedUrl.protocol; 27 var hostname = parsedUrl.host; 28 var port = parsedUrl.port; 29 if (typeof hostname !== 'string' || !hostname || typeof proto !== 'string') { 30 return ''; // Don't proxy URLs without a valid scheme or host. 31 } 32 33 proto = proto.split(':', 1)[0]; 34 // Stripping ports in this way instead of using parsedUrl.hostname to make 35 // sure that the brackets around IPv6 addresses are kept. 36 hostname = hostname.replace(/:\d*$/, ''); 37 port = parseInt(port) || DEFAULT_PORTS[proto] || 0; 38 if (!shouldProxy(hostname, port)) { 39 return ''; // Don't proxy URLs that match NO_PROXY. 40 } 41 42 var proxy = 43 getEnv('npm_config_' + proto + '_proxy') || 44 getEnv(proto + '_proxy') || 45 getEnv('npm_config_proxy') || 46 getEnv('all_proxy'); 47 if (proxy && proxy.indexOf('://') === -1) { 48 // Missing scheme in proxy, default to the requested URL's scheme. 49 proxy = proto + '://' + proxy; 50 } 51 return proxy; 52 } 53 54 /** 55 * Determines whether a given URL should be proxied. 56 * 57 * @param {string} hostname - The host name of the URL. 58 * @param {number} port - The effective port of the URL. 59 * @returns {boolean} Whether the given URL should be proxied. 60 * @private 61 */ 62 function shouldProxy(hostname, port) { 63 var NO_PROXY = 64 (getEnv('npm_config_no_proxy') || getEnv('no_proxy')).toLowerCase(); 65 if (!NO_PROXY) { 66 return true; // Always proxy if NO_PROXY is not set. 67 } 68 if (NO_PROXY === '*') { 69 return false; // Never proxy if wildcard is set. 70 } 71 72 return NO_PROXY.split(/[,\s]/).every(function(proxy) { 73 if (!proxy) { 74 return true; // Skip zero-length hosts. 75 } 76 var parsedProxy = proxy.match(/^(.+):(\d+)$/); 77 var parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy; 78 var parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0; 79 if (parsedProxyPort && parsedProxyPort !== port) { 80 return true; // Skip if ports don't match. 81 } 82 83 if (!/^[.*]/.test(parsedProxyHostname)) { 84 // No wildcards, so stop proxying if there is an exact match. 85 return hostname !== parsedProxyHostname; 86 } 87 88 if (parsedProxyHostname.charAt(0) === '*') { 89 // Remove leading wildcard. 90 parsedProxyHostname = parsedProxyHostname.slice(1); 91 } 92 // Stop proxying if the hostname ends with the no_proxy host. 93 return !stringEndsWith.call(hostname, parsedProxyHostname); 94 }); 95 } 96 97 /** 98 * Get the value for an environment variable. 99 * 100 * @param {string} key - The name of the environment variable. 101 * @return {string} The value of the environment variable. 102 * @private 103 */ 104 function getEnv(key) { 105 return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ''; 106 } 107 108 exports.getProxyForUrl = getProxyForUrl;