UNPKG

snowflake-sdk

Version:
327 lines 13.9 kB
"use strict"; const Logger = require('./logger'); const Errors = require('./errors'); const Util = require('./util'); const GlobalConfig = require('./global_config'); const LoggingUtil = require('./logger/logging_util'); const ErrorCodes = Errors.codes; /** * @typedef {object} Proxy * @property {string} host - The host address of the proxy. * @property {string} protocol - The protocol used by the proxy (e.g., "http" or "https") * @property {string} user - The username for the proxy * @property {number} port - The port number. * @property {string} password - The password for the proxy * @property {string} noProxy - Optional list of domains that should bypass the prox */ /** * remove http:// or https:// from the input, e.g. used with proxy URL * @param input * @returns {string} */ exports.removeScheme = function (input) { return input.toString().replace(/(^\w+:|^)\/\//, ''); }; /** * Try to get the PROXY environmental variables * On Windows, envvar name is case-insensitive, but on *nix, it's case-sensitive * * Compare them with the proxy specified on the Connection, if any * Return with the log constructed from the components detection and comparison * If there's something to warn the user about, return that too * * @param {Proxy} agentOptions from agent creation * @returns {{messages: string, warnings: string}} log messages */ exports.getCompareAndLogEnvAndAgentProxies = function (agentOptions) { const envProxy = {}; const logMessages = { messages: '', warnings: '' }; envProxy.httpProxy = process.env.http_proxy || process.env.HTTP_PROXY; envProxy.httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY; envProxy.noProxy = process.env.no_proxy || process.env.NO_PROXY; envProxy.logHttpProxy = envProxy.httpProxy ? 'HTTP_PROXY: ' + envProxy.httpProxy : 'HTTP_PROXY: <unset>'; envProxy.logHttpsProxy = envProxy.httpsProxy ? 'HTTPS_PROXY: ' + envProxy.httpsProxy : 'HTTPS_PROXY: <unset>'; envProxy.logNoProxy = envProxy.noProxy ? 'NO_PROXY: ' + envProxy.noProxy : 'NO_PROXY: <unset>'; // log PROXY envvars if (envProxy.httpProxy || envProxy.httpsProxy) { logMessages.messages = logMessages.messages + ' // PROXY environment variables: ' + `${envProxy.logHttpProxy} ${envProxy.logHttpsProxy} ${envProxy.logNoProxy}.`; } // log proxy config on Connection, if any set if (agentOptions.host) { const proxyHostAndPort = agentOptions.host + ':' + agentOptions.port; const proxyProtocolHostAndPort = agentOptions.protocol ? ' protocol=' + agentOptions.protocol + ' proxy=' + proxyHostAndPort : ' proxy=' + proxyHostAndPort; const proxyUsername = agentOptions.user ? ' user=' + agentOptions.user : ''; const proxyString = `${Util.exists(agentOptions.user) ? `${agentOptions.user}:${agentOptions.password}@` : ''}${proxyHostAndPort}`.toLowerCase(); logMessages.messages = logMessages.messages + ` // Proxy configured in Agent:${proxyProtocolHostAndPort}${proxyUsername}`; // check if both the PROXY envvars and Connection proxy config is set // generate warnings if they are, and are also different if (envProxy.httpProxy && this.removeScheme(envProxy.httpProxy).toLowerCase() !== proxyString.toLowerCase()) { logMessages.warnings = logMessages.warnings + ` Using both the HTTP_PROXY (${this.describeProxy(this.getProxyFromEnv(false))})` + ` and the Connection proxy (${this.describeProxy(agentOptions)}), but with different values.` + ' If you experience connectivity issues, try unsetting one of them.'; } if (envProxy.httpsProxy && this.removeScheme(envProxy.httpsProxy).toLowerCase() !== proxyString.toLowerCase()) { logMessages.warnings = logMessages.warnings + ` Using both the HTTPS_PROXY (${this.describeProxy(this.getProxyFromEnv(true))})` + ` and the Connection proxy (${this.describeProxy(agentOptions)}) settings to connect, but with different values.` + ' If you experience connectivity issues, try unsetting one of them.'; } } logMessages.messages = logMessages.messages ? logMessages.messages : ' none.'; return logMessages; }; /** * Validate whether the proxy object has the appropriate information * * @param {Proxy} proxy * @returns {Proxy} */ exports.validateProxy = function (proxy) { const { host, port, noProxy, user, password } = proxy; // check for missing proxyHost Errors.checkArgumentExists(Util.exists(host), ErrorCodes.ERR_CONN_CREATE_MISSING_PROXY_HOST); // check for invalid proxyHost Errors.checkArgumentValid(Util.isString(host), ErrorCodes.ERR_CONN_CREATE_INVALID_PROXY_HOST); // check for missing proxyPort Errors.checkArgumentExists(Util.exists(port), ErrorCodes.ERR_CONN_CREATE_MISSING_PROXY_PORT); // check for invalid proxyPort Errors.checkArgumentValid(Util.isNumber(port), ErrorCodes.ERR_CONN_CREATE_INVALID_PROXY_PORT); if (Util.exists(noProxy)) { // check for invalid noProxy Errors.checkArgumentValid(Util.isString(noProxy), ErrorCodes.ERR_CONN_CREATE_INVALID_NO_PROXY); } if (Util.exists(user) || Util.exists(password)) { // check for missing proxyUser Errors.checkArgumentExists(Util.exists(user), ErrorCodes.ERR_CONN_CREATE_MISSING_PROXY_USER); // check for invalid proxyUser Errors.checkArgumentValid(Util.isString(user), ErrorCodes.ERR_CONN_CREATE_INVALID_PROXY_USER); // check for missing proxyPassword Errors.checkArgumentExists(Util.exists(password), ErrorCodes.ERR_CONN_CREATE_MISSING_PROXY_PASS); // check for invalid proxyPassword Errors.checkArgumentValid(Util.isString(password), ErrorCodes.ERR_CONN_CREATE_INVALID_PROXY_PASS); } else { delete proxy.user; delete proxy.password; } }; /** * Obtain the proxy information from the environment variable. * * @param {boolean} isHttps * @returns {Proxy} */ exports.getProxyFromEnv = function (isHttps = true) { const getDefaultPortIfNotSet = (proxyFromEnv) => { const isProxyProtocolHttps = proxyFromEnv.protocol === 'https:'; if (!proxyFromEnv.port) { return isProxyProtocolHttps ? 443 : 80; } else { return proxyFromEnv.port; } }; const protocol = isHttps ? 'https' : 'http'; let proxyFromEnv = Util.getEnvVar(`${protocol}_proxy`); if (!proxyFromEnv) { return null; } Logger.getInstance().debug(`Util.getProxyEnv: Using ${protocol.toUpperCase()}_PROXY from the environment variable`); if (proxyFromEnv.indexOf('://') === -1) { Logger.getInstance().info('Util.getProxyEnv: the protocol was missing from the environment proxy. Use the HTTP protocol.'); proxyFromEnv = 'http' + '://' + proxyFromEnv; } proxyFromEnv = new URL(proxyFromEnv); const port = getDefaultPortIfNotSet(proxyFromEnv); const proxy = { host: Util.validateEmptyString(proxyFromEnv.hostname), port: Number(port), user: Util.validateEmptyString(proxyFromEnv.username), password: Util.validateEmptyString(proxyFromEnv.password), protocol: Util.validateEmptyString(proxyFromEnv.protocol), noProxy: this.getNoProxyEnv(), useForOCSP: !isHttps, }; this.validateProxy(proxy); return proxy; }; /** * Obtain the no proxy information from the environment variable. * * @returns {string | undefined} */ exports.getNoProxyEnv = function () { const noProxy = Util.getEnvVar('no_proxy'); if (noProxy) { return noProxy.split(',').join('|'); } return undefined; }; /** * Extract the host from the destination URL to check whether the same agent already exists or not. * * @param {string} destination * @returns {string} */ exports.getHostFromURL = function (destination) { if (destination.indexOf('://') === -1) { destination = 'https' + '://' + destination; } try { return new URL(destination).hostname; } catch (err) { Logger.getInstance().error(`Failed to parse the destination to URL with the error: ${err}. Return destination as the host: ${destination}`); return destination; } }; /** * if proxy exists, return the proxy. If not and the useEnvProxy is true, return the proxy from the environment variable. * @param {Proxy} proxy * @param {string} moduleName * @param {string} isHttp * * @returns {Proxy} */ exports.getProxy = function (proxy, moduleName, isHttps) { if (!proxy && GlobalConfig.isEnvProxyActive()) { proxy = this.getProxyFromEnv(isHttps); if (proxy) { Logger.getInstance().debug(`${moduleName} loads the proxy info from the environment variable host: ${proxy.host}`); } } return proxy; }; /** * The proxy configuration fields in Azure are different from the proxy fields in the snowflake node.js driver. * Because of that, this function converts the snowflake proxy info to the Azure proxy info. * @param {Proxy} proxy * @returns {{host:string, port:number, user?:string, password?:string}}} */ exports.getAzureProxy = function (proxy) { const AzureProxy = { ...proxy, host: `${proxy.protocol}${proxy.protocol.endsWith(':') ? '' : ':'}//${proxy.host}`, }; delete AzureProxy.noProxy; delete AzureProxy.protocol; if (!Util.exists(AzureProxy.user) || !Util.exists(AzureProxy.password)) { delete AzureProxy.user; delete AzureProxy.password; } return AzureProxy; }; /** * Currently, there is no way to disable loading the proxy information from the environment path in Azure/blob. * To control this proxy option on the driver side, A temporary workaround is hide(remove) the environment proxy from the process * when the client is created (At this time, the client loads the proxy from the environment variables internally). * After the client is created, restore them with the 'restoreEnvironmentProxy' function. */ let envProxyList; const proxyEnvList = ['http_proxy', 'https_proxy', 'no_proxy']; exports.hideEnvironmentProxy = function () { if (GlobalConfig.isEnvProxyActive()) { return; } Logger.getInstance().debug('As the useEnvProxy option is disabled, the proxy environment variables are temporarily hidden during the creation of an Azure client'); envProxyList = []; for (const envVar of proxyEnvList) { saveProxyInfoInList(envVar); if (!Util.isWindows()) { saveProxyInfoInList(envVar.toUpperCase()); } } }; function saveProxyInfoInList(envVar) { const proxyEnv = process.env[envVar]; envProxyList.push(process.env[envVar]); delete process.env[envVar]; if (Util.exists(proxyEnv)) { Logger.getInstance().debug(`Temporarily exclude ${envVar} from the environment variable value: ${proxyEnv}`); } else { Logger.getInstance().debug(`${envVar} was not defined, nothing to do`); } } exports.restoreEnvironmentProxy = function () { if (GlobalConfig.isEnvProxyActive()) { return; } const iterator = envProxyList[Symbol.iterator](); let nextValue = iterator.next().value; for (const envVar of proxyEnvList) { if (Util.exists(nextValue)) { Logger.getInstance().debug(`The ${envVar} value exists with the value: ${nextValue} Restore back the proxy environment variable values`); process.env[envVar] = nextValue; } nextValue = iterator.next().value; if (!Util.isWindows()) { if (Util.exists(nextValue)) { Logger.getInstance().debug(`The ${envVar.toUpperCase()} value exists with the value: ${nextValue} Restore back the proxy environment variable values (for Non-Windows machine)`); process.env[envVar.toUpperCase()] = nextValue; } nextValue = iterator.next().value; } } Logger.getInstance().debug('An Azure client has been created. Restore back the proxy environment variable values'); }; /** * Provide the details of the proxy info. * @param proxy * @param destination {string | Regex } */ exports.isByPassProxy = function (proxy, destination) { if (proxy && proxy.noProxy) { const bypassList = proxy.noProxy.split('|'); return bypassList.find((noProxy) => { const host = noProxy.trim(); if (destination instanceof RegExp) { return destination.test(host); } let regexPattern = ''; for (let i = 0; i < host.length; i++) { const char = host[i]; if (char === '*' || (char === '.' && i === 0)) { regexPattern += '.*'; } else { regexPattern += Util.escapeRegex(char); } } const noProxyRegex = new RegExp(`^${regexPattern}$`); return noProxyRegex.test(destination); }); } }; /** * Provide the details of the proxy info. * @param proxy * @returns {string} */ exports.describeProxy = function (proxy) { if (Util.exists(proxy)) { return (`proxyHost: ${proxy.host}, proxyPort: ${proxy.port}, ` + `${Util.exists(proxy.user) ? `proxyUser: ${proxy.user}, proxyPassword is ${LoggingUtil.describePresence(proxy.password)}, ` : ''}` + `proxyProtocol: ${proxy.protocol}, noProxy: ${proxy.noProxy}`); } else { return 'proxy was not configured'; } }; //# sourceMappingURL=proxy_util.js.map