mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	first pass at common proxy determining logic
This commit is contained in:
		| @@ -16,6 +16,7 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     const { getProxyForUrl } = require('./lib/proxyHelper'); | ||||
|     var mqtt = require("mqtt"); | ||||
|     var isUtf8 = require('is-utf8'); | ||||
|     var HttpsProxyAgent = require('https-proxy-agent'); | ||||
| @@ -592,17 +593,8 @@ module.exports = function(RED) { | ||||
|                     // Only for ws or wss, check if proxy env var for additional configuration | ||||
|                     if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) { | ||||
|                         // check if proxy is set in env | ||||
|                         let prox, noprox, noproxy; | ||||
|                         if (process.env.http_proxy) { prox = process.env.http_proxy; } | ||||
|                         if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } | ||||
|                         if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } | ||||
|                         if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } | ||||
|                         if (noprox) { | ||||
|                             for (var i = 0; i < noprox.length; i += 1) { | ||||
|                                 if (node.brokerurl.indexOf(noprox[i].trim()) !== -1) { noproxy = true; } | ||||
|                             } | ||||
|                         } | ||||
|                         if (prox && !noproxy) { | ||||
|                         const prox = getProxyForUrl(node.brokerurl, RED.settings.proxyOptions); | ||||
|                         if (prox) { | ||||
|                             var parsedUrl = url.parse(node.brokerurl); | ||||
|                             var proxyOpts = url.parse(prox); | ||||
|                             // true for wss | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     const { getProxyForUrl, parseUrl } = require('./lib/proxyHelper'); | ||||
|     const got = require("got"); | ||||
|     const {CookieJar} = require("tough-cookie"); | ||||
|     const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent'); | ||||
| @@ -86,19 +87,18 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|         if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; } | ||||
|         else if (n.paytoqs === "body") { paytobody = true; } | ||||
|  | ||||
|  | ||||
|         var prox, noprox; | ||||
|         if (process.env.http_proxy) { prox = process.env.http_proxy; } | ||||
|         if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } | ||||
|         if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } | ||||
|         if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } | ||||
|  | ||||
|         var proxyConfig = null; | ||||
|         if (n.proxy) { | ||||
|             proxyConfig = RED.nodes.getNode(n.proxy); | ||||
|             prox = proxyConfig.url; | ||||
|             noprox = proxyConfig.noproxy; | ||||
|         let proxyConfig = n.proxy ? RED.nodes.getNode(n.proxy) || {} : null | ||||
|         const getProxy = (url) => { | ||||
|             const proxyOptions = Object.assign({}, RED.settings.proxyOptions); | ||||
|             if (n.proxy && proxyConfig) { | ||||
|                 proxyOptions.env = { | ||||
|                     no_proxy: (proxyConfig.noproxy || []).join(','), | ||||
|                     http_proxy: (proxyConfig.url) | ||||
|                 } | ||||
|             } | ||||
|             return getProxyForUrl(url, proxyOptions) | ||||
|         } | ||||
|         let prox = getProxy(nodeUrl || '') | ||||
|  | ||||
|         let timingLog = false; | ||||
|         if (RED.settings.hasOwnProperty("httpRequestTimingLog")) { | ||||
| @@ -171,7 +171,11 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|                     url = "http://"+url; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // before any parameters are appended to the `url`, lets check see if the proxy needs a refresh | ||||
|             let proxyUrl = prox; // The proxyUrl determined for `nodeUrl` | ||||
|             if(url !== nodeUrl) { | ||||
|                 proxyUrl = getProxy(url) | ||||
|             } | ||||
|             // The Request module used in Node-RED 1.x was tolerant of query strings that | ||||
|             // were partially encoded. For example - "?a=hello%20there&b=20%" | ||||
|             // The GOT module doesn't like that. | ||||
| @@ -507,27 +511,25 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|                 opts.headers[clSet] = opts.headers['content-length']; | ||||
|                 delete opts.headers['content-length']; | ||||
|             } | ||||
|  | ||||
|             var noproxy; | ||||
|             if (noprox) { | ||||
|                 for (var i = 0; i < noprox.length; i += 1) { | ||||
|                     if (url.indexOf(noprox[i]) !== -1) { noproxy=true; } | ||||
|                 } | ||||
|             if (!opts.headers.hasOwnProperty('user-agent')) { | ||||
|                 opts.headers['user-agent'] = 'Mozilla/5.0 (Node-RED)'; | ||||
|             } | ||||
|             if (prox && !noproxy) { | ||||
|                 var match = prox.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i); | ||||
|             if (proxyUrl) { | ||||
|                 var match = proxyUrl.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i); | ||||
|                 if (match) { | ||||
|                     let proxyAgent; | ||||
|                     let proxyURL = new URL(prox); | ||||
|                     const proxyURL = parseUrl(proxyUrl) | ||||
|                     //set username/password to null to stop empty creds header | ||||
|                     /** @type {HttpProxyAgentOptions} */ | ||||
|                     let proxyOptions = { | ||||
|                         proxy: { | ||||
|                             protocol: proxyURL.protocol, | ||||
|                             hostname: proxyURL.hostname, | ||||
|                             port: proxyURL.port, | ||||
|                             username: null, | ||||
|                             password: null | ||||
|                         }, | ||||
|                         proxy: proxyUrl, | ||||
|                         // proxy: { | ||||
|                         //     protocol: proxyURL.protocol, | ||||
|                         //     hostname: proxyURL.hostname, | ||||
|                         //     port: proxyURL.port, | ||||
|                         //     username: null, | ||||
|                         //     password: null | ||||
|                         // }, | ||||
|                         scheduling: 'lifo', | ||||
|                         maxFreeSockets: 256, | ||||
|                         maxSockets: 256, | ||||
|                         keepAlive: true | ||||
| @@ -536,21 +538,23 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|                         let proxyUsername = proxyConfig.credentials.username || ''; | ||||
|                         let proxyPassword = proxyConfig.credentials.password || ''; | ||||
|                         if (proxyUsername || proxyPassword) { | ||||
|                             proxyOptions.proxy = proxyURL | ||||
|                             proxyOptions.proxy.username = proxyUsername; | ||||
|                             proxyOptions.proxy.password = proxyPassword; | ||||
|                         } | ||||
|                     } else if (proxyURL.username || proxyURL.password){ | ||||
|                         proxyOptions.proxy.username = proxyURL.username; | ||||
|                         proxyOptions.proxy.password = proxyURL.password; | ||||
|                         proxyOptions.proxy = proxyURL | ||||
|                         // proxyOptions.proxy.username = proxyURL.username; | ||||
|                         // proxyOptions.proxy.password = proxyURL.password; | ||||
|                     } | ||||
|                     //need both incase of http -> https redirect | ||||
|                     opts.agent = { | ||||
|                         http: new HttpProxyAgent(proxyOptions), | ||||
|                         https: new HttpsProxyAgent(proxyOptions) | ||||
|                         https: new HttpProxyAgent(proxyOptions) | ||||
|                     }; | ||||
|  | ||||
|                 } else { | ||||
|                     node.warn("Bad proxy url: "+ prox); | ||||
|                     node.warn("Bad proxy url: "+ proxyUrl); | ||||
|                 } | ||||
|             } | ||||
|             if (tlsNode) { | ||||
|   | ||||
| @@ -68,21 +68,9 @@ module.exports = function(RED) { | ||||
|  | ||||
|         function startconn() {    // Connect to remote endpoint | ||||
|             node.tout = null; | ||||
|             var prox, noprox; | ||||
|             if (process.env.http_proxy) { prox = process.env.http_proxy; } | ||||
|             if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } | ||||
|             if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } | ||||
|             if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } | ||||
|  | ||||
|             var noproxy = false; | ||||
|             if (noprox) { | ||||
|                 for (var i in noprox) { | ||||
|                     if (node.path.indexOf(noprox[i].trim()) !== -1) { noproxy=true; } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var agent = undefined; | ||||
|             if (prox && !noproxy) { | ||||
|             const prox = getProxyForUrl(node.brokerurl, RED.settings.proxyOptions); | ||||
|             let agent = undefined; | ||||
|             if (prox) { | ||||
|                 agent = new HttpsProxyAgent(prox); | ||||
|             } | ||||
|  | ||||
|   | ||||
							
								
								
									
										215
									
								
								packages/node_modules/@node-red/nodes/core/network/lib/proxyHelper.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								packages/node_modules/@node-red/nodes/core/network/lib/proxyHelper.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| /* | ||||
| The MIT License | ||||
|  | ||||
| Copyright (C) 2016-2018 Rob Wu <rob@robwu.nl> | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||
| this software and associated documentation files (the "Software"), to deal in | ||||
| the Software without restriction, including without limitation the rights to | ||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||||
| of the Software, and to permit persons to whom the Software is furnished to do | ||||
| so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
| */ | ||||
|  | ||||
| /*  | ||||
| This proxy helper is heavily based on the proxy helper from Rob Wu as detailed above. | ||||
| It has been modified to work with the Node-RED runtime environment. | ||||
| The license for the original code is reproduced above. | ||||
| */ | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Parse a URL into its components. | ||||
|  * @param {String} url The URL to parse | ||||
|  * @returns {URL} | ||||
|  */ | ||||
| const parseUrl = (url) => { | ||||
|     let parsedUrl = { | ||||
|         protocol: null, | ||||
|         host: null, | ||||
|         port: null, | ||||
|         hostname: null, | ||||
|         query: null, | ||||
|         href: null | ||||
|     } | ||||
|     try { | ||||
|         if (!url) { return parsedUrl } | ||||
|         parsedUrl = new URL(url) | ||||
|     } catch (error) { | ||||
|         // dont throw error | ||||
|     } | ||||
|     return parsedUrl | ||||
| } | ||||
|  | ||||
| const DEFAULT_PORTS = { | ||||
|     ftp: 21, | ||||
|     gopher: 70, | ||||
|     http: 80, | ||||
|     https: 443, | ||||
|     ws: 80, | ||||
|     wss: 443, | ||||
|     mqtt: 1880, | ||||
|     mqtts: 8883 | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @typedef {Object} ProxyOptions | ||||
|  * @property {string} mode - Legacy mode is for non-strict previous proxy determination logic (node-red < v3.1) (default 'legacy') | ||||
|  * @property {boolean} favourUpperCase - Favour UPPER_CASE *_PROXY env vars (default false) | ||||
|  * @property {boolean} lowerCaseOnly - Prevent UPPER_CASE *_PROXY env vars being used. (default false) | ||||
|  * @property {boolean} excludeNpm - Prevent npm_config_*_proxy env vars being used. (default false) | ||||
|  * @property {object} env - The environment object to use (default process.env) | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Get the proxy URL for a given URL. | ||||
|  * @param {string|URL} url - The URL, or the result from url.parse. | ||||
|  * @param {ProxyOptions} [options] - The options object (optional) | ||||
|  * @return {string} The URL of the proxy that should handle the request to the | ||||
|  *  given URL. If no proxy is set, this will be an empty string. | ||||
|  */ | ||||
| function getProxyForUrl(url, options) { | ||||
|     url = url || '' | ||||
|     const defaultOptions = { | ||||
|         mode: 'legacy', // TODO: change to 'strict' in V4.x | ||||
|         lowerCaseOnly: false, | ||||
|         favourUpperCase: false, | ||||
|         excludeNpm: false, | ||||
|     } | ||||
|     options = Object.assign({}, defaultOptions, options) | ||||
|  | ||||
|     //TODO: V4.x default: if mode is not set, default to strict mode | ||||
|     // if (options.mode === 'legacy') { | ||||
|     if (options.mode !== 'strict') { | ||||
|         return legacyGetProxyForUrl(url, options.env || process.env) | ||||
|     } | ||||
|     | ||||
|     const parsedUrl = typeof url === 'string' ? parseUrl(url) : url || {} | ||||
|     let proto = parsedUrl.protocol | ||||
|     let hostname = parsedUrl.host | ||||
|     let port = parsedUrl.port | ||||
|     if (typeof hostname !== 'string' || !hostname || typeof proto !== 'string') { | ||||
|         return ''  // Don't proxy URLs without a valid scheme or host. | ||||
|     } | ||||
|  | ||||
|     proto = proto.split(':', 1)[0] | ||||
|     // Stripping ports in this way instead of using parsedUrl.hostname to make | ||||
|     // sure that the brackets around IPv6 addresses are kept. | ||||
|     hostname = hostname.replace(/:\d*$/, '') | ||||
|     port = parseInt(port) || DEFAULT_PORTS[proto] || 0 | ||||
|     if (!shouldProxy(hostname, port, options)) { | ||||
|         return ''  // Don't proxy URLs that match NO_PROXY. | ||||
|     } | ||||
|  | ||||
|     let proxy = | ||||
|         getEnv('npm_config_' + proto + '_proxy', options) || | ||||
|         getEnv(proto + '_proxy', options) || | ||||
|         getEnv('npm_config_proxy', options) || | ||||
|         getEnv('all_proxy', options) | ||||
|     if (proxy && proxy.indexOf('://') === -1) { | ||||
|         // Missing scheme in proxy, default to the requested URL's scheme. | ||||
|         proxy = proto + '://' + proxy | ||||
|     } | ||||
|     return proxy | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get the proxy URL for a given URL. | ||||
|  * For node-red < v3.1 or compatibility mode | ||||
|  * @param {string} url The URL to check for proxying | ||||
|  * @param {object} [env] The environment object to use (default process.env) | ||||
|  * @returns  | ||||
|  */ | ||||
| function legacyGetProxyForUrl(url, env) { | ||||
|     env = env || process.env | ||||
|     let prox, noprox; | ||||
|     if (env.http_proxy) { prox = env.http_proxy; } | ||||
|     if (env.HTTP_PROXY) { prox = env.HTTP_PROXY; } | ||||
|     if (env.no_proxy) { noprox = env.no_proxy.split(","); } | ||||
|     if (env.NO_PROXY) { noprox = env.NO_PROXY.split(","); } | ||||
|  | ||||
|     let noproxy = false; | ||||
|     if (noprox) { | ||||
|         for (let i in noprox) { | ||||
|             if (url.indexOf(noprox[i].trim()) !== -1) { noproxy=true; } | ||||
|         } | ||||
|     } | ||||
|     if (prox && !noproxy) { | ||||
|         return prox | ||||
|     } | ||||
|     return "" | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether a given URL should be proxied. | ||||
|  * | ||||
|  * @param {string} hostname - The host name of the URL. | ||||
|  * @param {number} port - The effective port of the URL. | ||||
|  * @returns {boolean} Whether the given URL should be proxied. | ||||
|  * @private | ||||
|  */ | ||||
| function shouldProxy(hostname, port, options) { | ||||
|     const NO_PROXY = | ||||
|         (getEnv('npm_config_no_proxy', options) || getEnv('no_proxy', options)).toLowerCase() | ||||
|     if (!NO_PROXY) { | ||||
|         return true  // Always proxy if NO_PROXY is not set. | ||||
|     } | ||||
|     if (NO_PROXY === '*') { | ||||
|         return false  // Never proxy if wildcard is set. | ||||
|     } | ||||
|  | ||||
|     return NO_PROXY.split(/[,\s]/).every(function (proxy) { | ||||
|         if (!proxy) { | ||||
|             return true  // Skip zero-length hosts. | ||||
|         } | ||||
|         const parsedProxy = proxy.match(/^(.+):(\d+)$/) | ||||
|         let parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy | ||||
|         const parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0 | ||||
|         if (parsedProxyPort && parsedProxyPort !== port) { | ||||
|             return true  // Skip if ports don't match. | ||||
|         } | ||||
|  | ||||
|         if (!/^[.*]/.test(parsedProxyHostname)) { | ||||
|             // No wildcards, so stop proxying if there is an exact match. | ||||
|             return hostname !== parsedProxyHostname | ||||
|         } | ||||
|  | ||||
|         if (parsedProxyHostname.charAt(0) === '*') { | ||||
|             // Remove leading wildcard. | ||||
|             parsedProxyHostname = parsedProxyHostname.slice(1) | ||||
|         } | ||||
|         // Stop proxying if the hostname ends with the no_proxy host. | ||||
|         return !hostname.endsWith(parsedProxyHostname) | ||||
|     }) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get the value for an environment constiable. | ||||
|  * | ||||
|  * @param {string} key - The name of the environment constiable. | ||||
|  * @param {ProxyOptions} options - The name of the environment constiable. | ||||
|  * @return {string} The value of the environment constiable. | ||||
|  * @private | ||||
|  */ | ||||
| function getEnv(key, options) { | ||||
|     const env = (options && options.env) || process.env | ||||
|     if (options && options.excludeNpm === true) { | ||||
|         if (key.startsWith('npm_config_')) { | ||||
|             return '' | ||||
|         } | ||||
|     } | ||||
|     if (options && options.lowerCaseOnly === true) { | ||||
|         return env[key.toLowerCase()] || '' | ||||
|     } else if (options && options.favourUpperCase === true) { | ||||
|         return env[key.toUpperCase()] ||  env[key.toLowerCase()] || '' | ||||
|     } | ||||
|     return env[key.toLowerCase()] || env[key.toUpperCase()] || '' | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getProxyForUrl, | ||||
|     parseUrl | ||||
| } | ||||
							
								
								
									
										648
									
								
								test/nodes/core/network/lib/proxyHelper_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										648
									
								
								test/nodes/core/network/lib/proxyHelper_spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,648 @@ | ||||
| const should = require("should"); | ||||
|  | ||||
| /***** | ||||
|  | ||||
| Issues with the current *_proxy implementation in Node-RED: | ||||
|     * no_proxy should not be case-sensitive | ||||
|         * i.e. if no_proxy contains "example.com", then "example.com" and "EXAMPLE.COM" should both be excluded | ||||
|     * no_proxy with protocols that have a default port are not considered | ||||
|         * i.e. if no_proxy contains "example.com:443", then "https://example.com" should be excluded | ||||
|         * i.e. if no_proxy contains "example.com:80", then "http://example.com" should be excluded | ||||
|         * i.e. if no_proxy contains "example.com:1880", then "mqtt://example.com" should be excluded | ||||
|     * Does not consider NPM proxy configuration at all | ||||
|         * i.e. if npm_config_proxy is set, then it should be used | ||||
|         * i.e. if npm_config_https_proxy is set, then it should be used | ||||
|         * i.e. if npm_config_http_proxy is set, then it should be used | ||||
|         * i.e. if npm_config_no_proxy is set, then it should be used | ||||
|     * Doesn't consider https_proxy or HTTPS_PROXY | ||||
|         * i.e. if https_proxy is set, then it should be used | ||||
|         * i.e. if HTTPS_PROXY is set, then it should be used | ||||
|         * i.e. incorrectly uses HTTP_PROXY 'http://http-proxy' when the url is 'https://example' | ||||
|     * Incorrectly prioritises HTTP_PROXY over http_proxy. HTTP_PROXY is not always supported or recommended | ||||
|         * i.e. if HTTP_PROXY and http_proxy are both set, then http_proxy should be used | ||||
|         * Use lowercase form. HTTP_PROXY is not always supported or recommended | ||||
|     * doesn't consider all_proxy or ALL_PROXY | ||||
|         * i.e. if all_proxy is set, then it should be used | ||||
|         * i.e. if ALL_PROXY is set, then it should be used | ||||
|         *  | ||||
|         *  | ||||
| This implementation is based on the following sources: | ||||
|     * https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/ (GitLab) | ||||
|     * https://www.npmjs.com/package/proxy-from-env (MIT License) | ||||
|  | ||||
| This implementation proposal follows the following rules: | ||||
|     * Support the following PROTOCOL_proxys | ||||
|         * i.e. http_proxy, https_proxy, mqtt_proxy, ws_proxy, wss_proxy, mqtt_proxy, mqtts_proxy | ||||
|     * Support all_proxy | ||||
|         * i.e. if all_proxy is set, then all URLs will be proxied | ||||
|     * Use comma-separated hostname[:port] values for no_proxy. | ||||
|         * no_proxy should contain a comma-separated list of domain extensions proxy should not be used for | ||||
|         * Each value may include optional whitespace. | ||||
|         * port is optional and is inferred if the protocol has a default port (supports http, https, mqtt, ws, wss, mqtt, mqtts) | ||||
|         * Use * to match all hosts | ||||
|     * Support .example (host suffix) | ||||
|     * Support sub.example (host sub domain) | ||||
|     * Upper case forms of *_PROXY are supported but not recommended | ||||
|     * Lower case forms of *_proxy will take precedence over upper case forms | ||||
|     * Does not perform DNS lookups or use regular expressions | ||||
|     * Does not perform validation on the *_proxy urls | ||||
|     * Does not support CIDR block matching | ||||
|     * Support IPv6 matching | ||||
| ******/ | ||||
|  | ||||
| /* eslint max-statements:0 */ | ||||
| 'use strict'; | ||||
|  | ||||
| const assert = require('assert'); | ||||
|  | ||||
| const { getProxyForUrl } = require("nr-test-utils").require('@node-red/nodes/core/network/lib/proxyHelper') | ||||
|  | ||||
| /** | ||||
|  * Defines a test case that checks whether getProxyForUrl(input) === expected. | ||||
|  * @param {object} env - The environment variables to use for the test | ||||
|  * @param {*} expected - The expected result | ||||
|  * @param {*} input - The input to test | ||||
|  * @param {import('../../../../../packages/node_modules/@node-red/nodes/core/network/lib/proxyHelper').ProxyOptions} options - The options to use for getProxyForUrl | ||||
|  */ | ||||
| function testProxyUrl(env, expected, input, options) { | ||||
|     assert(typeof env === 'object' && env !== null); | ||||
|     // Copy object to make sure that the in param does not get modified between | ||||
|     // the call of this function and the use of it below. | ||||
|     env = JSON.parse(JSON.stringify(env)); | ||||
|  | ||||
|     var title = 'getProxyForUrl(' + JSON.stringify(input) + ')' + | ||||
|         ' === ' + JSON.stringify(expected); | ||||
|  | ||||
|     // Save call stack for later use. | ||||
|     var stack = {}; | ||||
|     Error.captureStackTrace(stack, testProxyUrl); | ||||
|     // Only use the last stack frame because that shows where this function is | ||||
|     // called, and that is sufficient for our purpose. No need to flood the logs | ||||
|     // with an uninteresting stack trace. | ||||
|     stack = stack.stack.split('\n', 2)[1]; | ||||
|  | ||||
|     it(title, function () { | ||||
|         var actual; | ||||
|         // runWithEnv(env, function () { | ||||
|         //     actual = getProxyForUrl(input, options); | ||||
|         // }); | ||||
|         options = options || {}; | ||||
|         options.env = options.env || env || process.env; | ||||
|         options.mode = options.mode || 'strict'; | ||||
|         actual = getProxyForUrl(input, options); | ||||
|         if (expected === actual) { | ||||
|             return;  // Good! | ||||
|         } | ||||
|         try { | ||||
|             assert.strictEqual(expected, actual); // Create a formatted error message. | ||||
|             // Should not happen because previously we determined expected !== actual. | ||||
|             throw new Error('assert.strictEqual passed. This is impossible!'); | ||||
|         } catch (e) { | ||||
|             // Use the original stack trace, so we can see a helpful line number. | ||||
|             e.stack = e.message + stack; | ||||
|             throw e; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| describe('Proxy Helper', function () { | ||||
|     describe('No proxy variables', function () { | ||||
|         const env = {}; | ||||
|         testProxyUrl(env, '', 'http://example.com'); | ||||
|         testProxyUrl(env, '', 'https://example.com'); | ||||
|         testProxyUrl(env, '', 'ftp://example.com'); | ||||
|     }); | ||||
|  | ||||
|     describe('Invalid URLs', function () { | ||||
|         const env = {}; | ||||
|         env.ALL_PROXY = 'http://unexpected.proxy'; | ||||
|         testProxyUrl(env, '', 'bogus'); | ||||
|         testProxyUrl(env, '', '//example.com'); | ||||
|         testProxyUrl(env, '', '://example.com'); | ||||
|         testProxyUrl(env, '', '://'); | ||||
|         testProxyUrl(env, '', '/path'); | ||||
|         testProxyUrl(env, '', ''); | ||||
|         testProxyUrl(env, '', 'ws:'); | ||||
|         testProxyUrl(env, '', 'wss:'); | ||||
|         testProxyUrl(env, '', 'mqtt:'); | ||||
|         testProxyUrl(env, '', 'mqtts:'); | ||||
|         testProxyUrl(env, '', 'http:'); | ||||
|         testProxyUrl(env, '', 'http:/'); | ||||
|         testProxyUrl(env, '', 'http://'); | ||||
|         testProxyUrl(env, '', 'prototype://'); | ||||
|         testProxyUrl(env, '', 'hasOwnProperty://'); | ||||
|         testProxyUrl(env, '', '__proto__://'); | ||||
|         testProxyUrl(env, '', undefined); | ||||
|         testProxyUrl(env, '', null); | ||||
|         testProxyUrl(env, '', {}); | ||||
|         testProxyUrl(env, '', { host: 'x', protocol: 1 }); | ||||
|         testProxyUrl(env, '', { host: 1, protocol: 'x' }); | ||||
|     }); | ||||
|     describe('Proxy options', function () { | ||||
|         describe('allowUpperCase:false should prevent *_PROXY being returned', function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://upper-case-proxy'; | ||||
|             env.HTTPS_PROXY = 'https://upper-case-proxy'; | ||||
|             testProxyUrl(env, '', 'http://example', { lowerCaseOnly: true }); | ||||
|             testProxyUrl(env, '', 'https://example', { lowerCaseOnly: true }); | ||||
|             testProxyUrl(env, 'http://upper-case-proxy', 'http://example'); //returns lower case due to precedence | ||||
|             testProxyUrl(env, 'https://upper-case-proxy', 'https://example'); //returns lower case due to precedence | ||||
|         }); | ||||
|  | ||||
|         describe('favourLowerCase:false should cause *_PROXY to being used before *_proxy', function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://upper-case-proxy'; | ||||
|             env.http_proxy = 'http://lower-case-proxy'; | ||||
|             testProxyUrl(env, 'http://upper-case-proxy', 'http://example', { favourUpperCase: true }); | ||||
|             testProxyUrl(env, 'http://lower-case-proxy', 'http://example'); // lowercase takes precedence by default | ||||
|         }); | ||||
|  | ||||
|         describe('includeNpm:false should not return npm_config_*_proxy env vars', function () { | ||||
|             const env = {}; | ||||
|             env.npm_config_http_proxy = 'http://npm-proxy'; | ||||
|             testProxyUrl(env, '', 'http://example', { excludeNpm: true }); | ||||
|             testProxyUrl(env, 'http://npm-proxy', 'http://example'); // lowercase takes precedence by default | ||||
|         }); | ||||
|  | ||||
|         describe('legacyMode:true should process urls proxy in node-red < v3.1 compatibility mode', function () { | ||||
|             const env = {}; | ||||
|             // legacy mode does not consider npm_config_*_proxy | ||||
|             env.npm_config_http_proxy = 'http://npm-proxy'; | ||||
|             testProxyUrl(env, '', 'http://example/1', { mode: 'legacy' }); | ||||
|             testProxyUrl(env, 'http://npm-proxy', 'http://example/1'); | ||||
|  | ||||
|             // legacy mode does not consider all_proxy | ||||
|             env.all_proxy = 'http://all-proxy'; | ||||
|             testProxyUrl(env, '', 'http://example/2', { mode: 'legacy' }); | ||||
|             testProxyUrl(env, 'http://npm-proxy', 'http://example/2'); | ||||
|  | ||||
|             // legacy mode does not consider *_proxy | ||||
|             env.npm_config_http_proxy = null; | ||||
|             env.http_proxy = 'http://http-proxy'; | ||||
|             env.no_proxy = 'example'; | ||||
|             testProxyUrl(env, '', 'http://example/3a', { mode: 'legacy' }); | ||||
|             testProxyUrl(env, '', 'http://example/3b'); | ||||
|  | ||||
|             // legacy mode does not consider protocol_proxy for https urls and uses http_proxy | ||||
|             env.https_proxy = 'https://https-proxy'; | ||||
|             env.no_proxy = ''; | ||||
|             testProxyUrl(env, 'http://http-proxy', 'https://example/4', { mode: 'legacy' }); | ||||
|             testProxyUrl(env, 'https://https-proxy', 'https://example/4'); | ||||
|  | ||||
|             // legacy mode favours UPPER_CASE over lower_case | ||||
|             env.HTTP_PROXY = 'http://http-proxy-upper'; | ||||
|             env.no_proxy = ''; | ||||
|             testProxyUrl(env, 'http://http-proxy-upper', 'http://example/5', { mode: 'legacy' }); | ||||
|             testProxyUrl(env, 'http://http-proxy', 'http://example/5'); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe('http_proxy and HTTP_PROXY', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://http-proxy'; | ||||
|  | ||||
|         testProxyUrl(env, '', 'https://example'); | ||||
|         testProxyUrl(env, 'http://http-proxy', 'http://example'); | ||||
|         testProxyUrl(env, 'http://http-proxy', new URL('http://example')); | ||||
|  | ||||
|         // eslint-disable-next-line camelcase | ||||
|         env.http_proxy = 'http://priority'; | ||||
|         testProxyUrl(env, 'http://priority', 'http://example'); | ||||
|     }); | ||||
|  | ||||
|     describe('http_proxy with non-sensical value', function () { | ||||
|         const env = {}; | ||||
|         // Crazy values should be passed as-is. It is the responsibility of the | ||||
|         // one who launches the application that the value makes sense. | ||||
|         // TODO: Should we be stricter and perform validation? | ||||
|         env.HTTP_PROXY = 'Crazy \n!() { ::// }'; | ||||
|         testProxyUrl(env, 'Crazy \n!() { ::// }', 'http://wow'); | ||||
|  | ||||
|         // The implementation assumes that the HTTP_PROXY environment variable is | ||||
|         // somewhat reasonable, and if the scheme is missing, it is added. | ||||
|         // Garbage in, garbage out some would say... | ||||
|         env.HTTP_PROXY = 'crazy without colon slash slash'; | ||||
|         testProxyUrl(env, 'http://crazy without colon slash slash', 'http://wow'); | ||||
|     }); | ||||
|  | ||||
|     describe('https_proxy and HTTPS_PROXY', function () { | ||||
|         const env = {}; | ||||
|         // Assert that there is no fall back to http_proxy | ||||
|         env.HTTP_PROXY = 'http://unexpected.proxy'; | ||||
|         testProxyUrl(env, '', 'https://example'); | ||||
|  | ||||
|         env.HTTPS_PROXY = 'http://https-proxy'; | ||||
|         testProxyUrl(env, 'http://https-proxy', 'https://example'); | ||||
|  | ||||
|         // eslint-disable-next-line camelcase | ||||
|         env.https_proxy = 'http://priority'; | ||||
|         testProxyUrl(env, 'http://priority', 'https://example'); | ||||
|     }); | ||||
|  | ||||
|     describe('ftp_proxy', function () { | ||||
|         const env = {}; | ||||
|         // Something else than http_proxy / https, as a sanity check. | ||||
|         env.FTP_PROXY = 'http://ftp-proxy'; | ||||
|  | ||||
|         testProxyUrl(env, 'http://ftp-proxy', 'ftp://example'); | ||||
|         testProxyUrl(env, '', 'ftps://example'); | ||||
|     }); | ||||
|  | ||||
|     describe('ws_proxy', function () { | ||||
|         const env = {}; | ||||
|         // Something else than http_proxy / https, as a sanity check. | ||||
|         env.ws_proxy = 'ws://ws-proxy'; | ||||
|  | ||||
|         testProxyUrl(env, 'ws://ws-proxy', 'ws://example1'); | ||||
|         testProxyUrl(env, '', 'wss://example2'); | ||||
|     }); | ||||
|  | ||||
|     describe('mqtt_proxy', function () { | ||||
|         const env = {}; | ||||
|         // Something else than http_proxy / https, as a sanity check. | ||||
|         env.mqtt_proxy = 'mqtt://mqtt-proxy'; | ||||
|         env.no_proxy = 'mqtt://mqtt-proxy'; | ||||
|  | ||||
|         testProxyUrl(env, 'mqtt://mqtt-proxy', 'mqtt://example1'); | ||||
|         testProxyUrl(env, '', 'mqtts://example2'); | ||||
|     }); | ||||
|  | ||||
|     describe('all_proxy', function () { | ||||
|         const env = {}; | ||||
|         env.ALL_PROXY = 'http://catch-all'; | ||||
|         testProxyUrl(env, 'http://catch-all', 'https://example'); | ||||
|  | ||||
|         // eslint-disable-next-line camelcase | ||||
|         env.all_proxy = 'http://priority'; | ||||
|         testProxyUrl(env, 'http://priority', 'https://example'); | ||||
|     }); | ||||
|  | ||||
|     describe('all_proxy without scheme', function () { | ||||
|         const env = {}; | ||||
|         env.ALL_PROXY = 'noscheme'; | ||||
|         testProxyUrl(env, 'http://noscheme', 'http://example'); | ||||
|         testProxyUrl(env, 'https://noscheme', 'https://example'); | ||||
|  | ||||
|         // The module does not impose restrictions on the scheme. | ||||
|         testProxyUrl(env, 'bogus-scheme://noscheme', 'bogus-scheme://example'); | ||||
|  | ||||
|         // But the URL should still be valid. | ||||
|         testProxyUrl(env, '', 'bogus'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy empty', function () { | ||||
|         const env = {}; | ||||
|         env.HTTPS_PROXY = 'http://proxy'; | ||||
|  | ||||
|         // NO_PROXY set but empty. | ||||
|         env.NO_PROXY = ''; | ||||
|         testProxyUrl(env, 'http://proxy', 'https://example1'); | ||||
|  | ||||
|         // No entries in NO_PROXY (comma). | ||||
|         env.NO_PROXY = ','; | ||||
|         testProxyUrl(env, 'http://proxy', 'https://example2'); | ||||
|  | ||||
|         // No entries in NO_PROXY (whitespace). | ||||
|         env.NO_PROXY = ' '; | ||||
|         testProxyUrl(env, 'http://proxy', 'https://example3'); | ||||
|  | ||||
|         // No entries in NO_PROXY (multiple whitespace / commas). | ||||
|         env.NO_PROXY = ',\t,,,\n,  ,\r'; | ||||
|         testProxyUrl(env, 'http://proxy', 'https://example4'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=example (single host)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = 'example'; | ||||
|         testProxyUrl(env, '', 'http://example'); | ||||
|         testProxyUrl(env, '', 'http://example:80'); | ||||
|         testProxyUrl(env, '', 'http://example:0'); | ||||
|         testProxyUrl(env, '', 'http://example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://sub.example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://prefexample'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example.no'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://a.b.example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://host/example'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=sub.example (subdomain)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = 'sub.example'; | ||||
|         testProxyUrl(env, '', 'http://sub.example'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:80'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:80'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://bus.example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://bus.example:80'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://bus.example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://prefexample'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://a.b.example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example.no'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://host/example'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=example:80 (host + port)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = 'example:80'; | ||||
|         testProxyUrl(env, '', 'http://example'); | ||||
|         testProxyUrl(env, '', 'http://example:80'); | ||||
|         testProxyUrl(env, '', 'http://example:0'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://sub.example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://prefexample'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example.no'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://a.b.example'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=.example (host suffix)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = '.example'; | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:80'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:1337'); | ||||
|         testProxyUrl(env, '', 'http://sub.example'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:80'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://prefexample'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example.no'); | ||||
|         testProxyUrl(env, '', 'http://a.b.example'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=*', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|         env.NO_PROXY = '*'; | ||||
|         testProxyUrl(env, '', 'http://example.com'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=*.example (host suffix with *.)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = '*.example'; | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:80'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example:1337'); | ||||
|         testProxyUrl(env, '', 'http://sub.example'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:80'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:1337'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://prefexample'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example.no'); | ||||
|         testProxyUrl(env, '', 'http://a.b.example'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=*example (substring suffix)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = '*example'; | ||||
|         const t = getProxyForUrl('http://example', { env }); | ||||
|         testProxyUrl(env, '', 'http://example'); | ||||
|         testProxyUrl(env, '', 'http://example:80'); | ||||
|         testProxyUrl(env, '', 'http://example:1337'); | ||||
|         testProxyUrl(env, '', 'http://sub.example'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:80'); | ||||
|         testProxyUrl(env, '', 'http://sub.example:1337'); | ||||
|         testProxyUrl(env, '', 'http://prefexample'); | ||||
|         testProxyUrl(env, '', 'http://a.b.example'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://example.no'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://host/example'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=.*example (arbitrary wildcards are NOT supported)', | ||||
|         function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|             env.NO_PROXY = '.*example'; | ||||
|             testProxyUrl(env, 'http://proxy', 'http://example'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://sub.example'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://prefexample'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://x.prefexample'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://a.b.example'); | ||||
|         }); | ||||
|  | ||||
|     describe('no_proxy=[::1],[::2]:80,10.0.0.1,10.0.0.2:80 (IP addresses)', | ||||
|         function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|             env.NO_PROXY = '[::1],[::2]:80,10.0.0.1,10.0.0.2:80'; | ||||
|             testProxyUrl(env, '', 'http://[::1]/'); | ||||
|             testProxyUrl(env, '', 'http://[::1]:80/'); | ||||
|             testProxyUrl(env, '', 'http://[::1]:1337/'); | ||||
|  | ||||
|             testProxyUrl(env, '', 'http://[::2]/'); | ||||
|             testProxyUrl(env, '', 'http://[::2]:80/'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://[::2]:1337/'); | ||||
|  | ||||
|             testProxyUrl(env, '', 'http://10.0.0.1/'); | ||||
|             testProxyUrl(env, '', 'http://10.0.0.1:80/'); | ||||
|             testProxyUrl(env, '', 'http://10.0.0.1:1337/'); | ||||
|  | ||||
|             testProxyUrl(env, '', 'http://10.0.0.2/'); | ||||
|             testProxyUrl(env, '', 'http://10.0.0.2:80/'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://10.0.0.2:1337/'); | ||||
|  | ||||
|             testProxyUrl(env, 'http://proxy', 'http://10.0.0.3/'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://10.0.0.3:80/'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://10.0.0.3:1337/'); | ||||
|         }); | ||||
|  | ||||
|     describe('no_proxy=127.0.0.1/32 (CIDR is NOT supported)', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = '127.0.0.1/32'; | ||||
|         testProxyUrl(env, 'http://proxy', 'http://127.0.0.1'); | ||||
|         testProxyUrl(env, 'http://proxy', 'http://127.0.0.1/32'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy=127.0.0.1 does NOT match localhost', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|  | ||||
|         env.NO_PROXY = '127.0.0.1'; | ||||
|         testProxyUrl(env, '', 'http://127.0.0.1'); | ||||
|         // We're not performing DNS queries, so this shouldn't match. | ||||
|         testProxyUrl(env, 'http://proxy', 'http://localhost'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy with protocols that have a default port', function () { | ||||
|         const env = {}; | ||||
|         env.MQTT_PROXY = 'http://mqtt'; | ||||
|         env.MQTTS_PROXY = 'https://m_q_t_t_s_proxy'; | ||||
|         env.WS_PROXY = 'http://ws'; | ||||
|         env.WSS_PROXY = 'http://wss'; | ||||
|         env.HTTP_PROXY = 'http://http'; | ||||
|         env.HTTPS_PROXY = 'http://https'; | ||||
|         env.GOPHER_PROXY = 'http://gopher'; | ||||
|         env.FTP_PROXY = 'http://ftp'; | ||||
|         env.ALL_PROXY = 'http://all'; | ||||
|  | ||||
|         env.NO_PROXY = 'xxx:21,xxx:70,xxx:80,xxx:443,xxx:1880,xxx:8880'; | ||||
|  | ||||
|         testProxyUrl(env, '', 'http://xxx'); | ||||
|         testProxyUrl(env, '', 'http://xxx:80'); | ||||
|         testProxyUrl(env, 'http://http', 'http://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, '', 'ws://xxx'); | ||||
|         testProxyUrl(env, '', 'ws://xxx:80'); | ||||
|         testProxyUrl(env, 'http://ws', 'ws://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, '', 'https://xxx'); | ||||
|         testProxyUrl(env, '', 'https://xxx:443'); | ||||
|         testProxyUrl(env, 'http://https', 'https://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, '', 'wss://xxx'); | ||||
|         testProxyUrl(env, '', 'wss://xxx:443'); | ||||
|         testProxyUrl(env, 'http://wss', 'wss://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, '', 'gopher://xxx'); | ||||
|         testProxyUrl(env, '', 'gopher://xxx:70'); | ||||
|         testProxyUrl(env, 'http://gopher', 'gopher://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, '', 'ftp://xxx'); | ||||
|         testProxyUrl(env, '', 'ftp://xxx:21'); | ||||
|         testProxyUrl(env, 'http://ftp', 'ftp://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, '', 'mqtt://xxx'); | ||||
|         testProxyUrl(env, '', 'mqtt://xxx:1880'); | ||||
|         testProxyUrl(env, 'http://mqtt', 'mqtt://xxx:1337'); | ||||
|  | ||||
|         testProxyUrl(env, 'http://mqtt', 'mqtt://yyy'); | ||||
|         testProxyUrl(env, 'http://mqtt', 'mqtt://yyy:1880'); | ||||
|  | ||||
|         testProxyUrl(env, 'http://all', 'unknown://xxx'); | ||||
|         testProxyUrl(env, 'http://all', 'unknown://xxx:1234'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy should not be case-sensitive', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|         env.NO_PROXY = 'XXX,YYY,ZzZ'; | ||||
|  | ||||
|         testProxyUrl(env, '', 'http://xxx'); | ||||
|         testProxyUrl(env, '', 'http://XXX'); | ||||
|         testProxyUrl(env, '', 'http://yyy'); | ||||
|         testProxyUrl(env, '', 'http://YYY'); | ||||
|         testProxyUrl(env, '', 'http://ZzZ'); | ||||
|         testProxyUrl(env, '', 'http://zZz'); | ||||
|     }); | ||||
|  | ||||
|     describe('no_proxy should accept space separated entries', function () { | ||||
|         const env = {}; | ||||
|         env.HTTP_PROXY = 'http://proxy'; | ||||
|         env.NO_PROXY = 'X X X,Y Y Y,Z z Z'; | ||||
|  | ||||
|         testProxyUrl(env, '', 'http://x x x'); | ||||
|         testProxyUrl(env, '', 'http://X X X'); | ||||
|         testProxyUrl(env, '', 'http://y y y'); | ||||
|         testProxyUrl(env, '', 'http://Y Y Y'); | ||||
|         testProxyUrl(env, '', 'http://Z z Z'); | ||||
|         testProxyUrl(env, '', 'http://z Z z'); | ||||
|     }); | ||||
|  | ||||
|     describe('NPM proxy configuration', function () { | ||||
|         describe('npm_config_http_proxy should work', function () { | ||||
|             const env = {}; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_http_proxy = 'http://http-proxy'; | ||||
|  | ||||
|             testProxyUrl(env, '', 'https://example'); | ||||
|             testProxyUrl(env, 'http://http-proxy', 'http://example'); | ||||
|  | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_http_proxy = 'http://priority'; | ||||
|             testProxyUrl(env, 'http://priority', 'http://example'); | ||||
|         }); | ||||
|         // eslint-disable-next-line max-len | ||||
|         describe('npm_config_http_proxy should take precedence over HTTP_PROXY and npm_config_proxy', function () { | ||||
|             const env = {}; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_http_proxy = 'http://http-proxy'; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_proxy = 'http://unexpected-proxy'; | ||||
|             env.HTTP_PROXY = 'http://unexpected-proxy'; | ||||
|  | ||||
|             testProxyUrl(env, 'http://http-proxy', 'http://example'); | ||||
|         }); | ||||
|         describe('npm_config_https_proxy should work', function () { | ||||
|             const env = {}; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_http_proxy = 'http://unexpected.proxy'; | ||||
|             testProxyUrl(env, '', 'https://example'); | ||||
|  | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_https_proxy = 'http://https-proxy'; | ||||
|             testProxyUrl(env, 'http://https-proxy', 'https://example'); | ||||
|  | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_https_proxy = 'http://priority'; | ||||
|             testProxyUrl(env, 'http://priority', 'https://example'); | ||||
|         }); | ||||
|         // eslint-disable-next-line max-len | ||||
|         describe('npm_config_https_proxy should take precedence over HTTPS_PROXY and npm_config_proxy', function () { | ||||
|             const env = {}; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_https_proxy = 'http://https-proxy'; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_proxy = 'http://unexpected-proxy'; | ||||
|             env.HTTPS_PROXY = 'http://unexpected-proxy'; | ||||
|  | ||||
|             testProxyUrl(env, 'http://https-proxy', 'https://example'); | ||||
|         }); | ||||
|         describe('npm_config_proxy should work', function () { | ||||
|             const env = {}; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_proxy = 'http://http-proxy'; | ||||
|             testProxyUrl(env, 'http://http-proxy', 'http://example'); | ||||
|             testProxyUrl(env, 'http://http-proxy', 'https://example'); | ||||
|  | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_proxy = 'http://priority'; | ||||
|             testProxyUrl(env, 'http://priority', 'http://example'); | ||||
|             testProxyUrl(env, 'http://priority', 'https://example'); | ||||
|         }); | ||||
|         // eslint-disable-next-line max-len | ||||
|         describe('HTTP_PROXY and HTTPS_PROXY should take precedence over npm_config_proxy', function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://http-proxy'; | ||||
|             env.HTTPS_PROXY = 'http://https-proxy'; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_proxy = 'http://unexpected-proxy'; | ||||
|             testProxyUrl(env, 'http://http-proxy', 'http://example'); | ||||
|             testProxyUrl(env, 'http://https-proxy', 'https://example'); | ||||
|         }); | ||||
|         describe('npm_config_no_proxy should work', function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://proxy'; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_no_proxy = 'example'; | ||||
|  | ||||
|             testProxyUrl(env, '', 'http://example'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://otherwebsite'); | ||||
|         }); | ||||
|         // eslint-disable-next-line max-len | ||||
|         describe('npm_config_no_proxy should take precedence over NO_PROXY', function () { | ||||
|             const env = {}; | ||||
|             env.HTTP_PROXY = 'http://proxy'; | ||||
|             env.NO_PROXY = 'otherwebsite'; | ||||
|             // eslint-disable-next-line camelcase | ||||
|             env.npm_config_no_proxy = 'example'; | ||||
|  | ||||
|             testProxyUrl(env, '', 'http://example'); | ||||
|             testProxyUrl(env, 'http://proxy', 'http://otherwebsite'); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user