snowflake-sdk
Version:
Node.js driver for Snowflake
327 lines • 13.9 kB
JavaScript
;
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