Skip to content

Commit

Permalink
SNOW-734920: Add possibility to configure keep alive value
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-pmotacki committed Sep 22, 2023
1 parent a6367ff commit 53a57b3
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 176 deletions.
5 changes: 2 additions & 3 deletions lib/global_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,7 @@ let keepAlive = true;
*
* @param {boolean} value
*/
exports.setKeepAlive = function (value)
{
exports.setKeepAlive = function (value) {
// validate input
Errors.assertInternal(Util.isBoolean(value));
keepAlive = value;
Expand All @@ -276,7 +275,7 @@ exports.setKeepAlive = function (value)
*
* @param {boolean} value
*/
exports.keepAlive = function (config) {
exports.getKeepAlive = function () {
return keepAlive;
};

Expand Down
6 changes: 3 additions & 3 deletions lib/http/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ HttpClient.prototype.request = function (options)
};

let mock;
if (this._connectionConfig.agentClass) {
if (this._connectionConfig.agentClass)
{
mock = {
agentClass: this._connectionConfig.agentClass
}
Expand Down Expand Up @@ -208,8 +209,7 @@ HttpClient.prototype.getRequestModule = function ()
*
* @returns {*}
*/
HttpClient.prototype.getAgent = function (url, proxy, mock)
{
HttpClient.prototype.getAgent = function (url, proxy, mock) {
return null;
};

Expand Down
77 changes: 44 additions & 33 deletions lib/http/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,54 @@ const Logger = require('../logger');
* @return {Number} number of milliseconds to wait before retrying again the request.
*/
NodeHttpClient.prototype.constructExponentialBackoffStrategy = function () {
var sleep = this._connectionConfig.getRetrySfStartingSleepTime();
var cap = this._connectionConfig.getRetrySfMaxSleepTime();
var base = 1;
let sleep = this._connectionConfig.getRetrySfStartingSleepTime();
const cap = this._connectionConfig.getRetrySfMaxSleepTime();
const base = 1;
sleep = Util.nextSleepTime(base, cap, sleep);
return sleep * 1000;
}
};

/**
* Creates a client that can be used to make requests in Node.js.
*
* @param {ConnectionConfig} connectionConfig
* @constructor
*/
function NodeHttpClient(connectionConfig)
{
function NodeHttpClient(connectionConfig) {
Base.apply(this, arguments);
}

Util.inherits(NodeHttpClient, Base);

const httpsAgentCache = new Map();

function getFromCacheOrCreate(agentClass, options, url) {
const parsed = Url.parse(url);
const protocol = parsed.protocol || 'http:';
const port = parsed.port || (protocol === 'http:' ? '80' : '443');
const agentId = `${protocol}//${parsed.hostname}:${port}/${options.keepAlive}`
if (httpsAgentCache.has(agentId)) {
Logger.getInstance().trace(`Get agent with id: ${agentId} from cache`);
return httpsAgentCache.get(agentId);
} else {
agent = agentClass(options)
function getFromCacheOrCreate(agentClass, options, parsedUrl) {
const protocol = parsedUrl.protocol;
const port = parsedUrl.port || (protocol === 'http:' ? '80' : '443');
const agentId = `${protocol}//${parsedUrl.hostname}:${port}`;

let agent ={}
function createAgent(agentClass, agentOptions) {
const agent = agentClass(agentOptions);
httpsAgentCache.set(agentId, agent);
Logger.getInstance().trace(`Create and add to cache new agent ${agentId}`);
return agent;
}

if (httpsAgentCache.has(agentId)) {
Logger.getInstance().trace(`Get agent with id: ${agentId} from cache`);
agent = httpsAgentCache.get(agentId);
// recreate agent if keepAlive parameter was changed
if (agent.keepAlive !== options.keepAlive) {
agent = createAgent(agentClass, options);
}
} else {
agent = createAgent(agentClass, options);
}
return agent;
}

function prepareProxyAgentOptions(agentOptions, proxy) {
Logger.getInstance().info(`Use proxy: ${JSON.stringify(proxy)}`);
agentOptions.host = proxy.host;
agentOptions.port = proxy.port;
agentOptions.protocol = proxy.protocol;
Expand All @@ -73,13 +81,13 @@ function prepareProxyAgentOptions(agentOptions, proxy) {

function isBypassProxy(proxy, url, bypassProxy) {
if (proxy && proxy.noProxy) {
let bypassList = proxy.noProxy.split("|");
const bypassList = proxy.noProxy.split('|');
for (let i = 0; i < bypassList.length; i++) {
host = bypassList[i].trim();
host = host.replace("*", ".*?");
let matches = url.match(host);
host = host.replace('*', '.*?');
const matches = url.match(host);
if (matches) {
Logger.getInstance().debug("bypassing proxy for %s", url);
Logger.getInstance().debug('bypassing proxy for %s', url);
return true;
}
}
Expand All @@ -90,28 +98,31 @@ function isBypassProxy(proxy, url, bypassProxy) {
/**
* @inheritDoc
*/
NodeHttpClient.prototype.getAgent = function (url, proxy, mock, defaultAgentOptions) {
const isHttps = Url.parse(url).protocol === 'https:';
let agentOptions = {keepAlive: GlobalConfig.keepAlive()};
var bypassProxy = isBypassProxy(proxy, url);

NodeHttpClient.prototype.getAgent = function (url, proxy, mock) {
const agentOptions = {keepAlive: GlobalConfig.getKeepAlive()};
if (mock) {
return mock.agentClass(agentOptions)
return mock.agentClass(agentOptions);
}

const parsedUrl = Url.parse(url);
const isHttps = parsedUrl.protocol === 'https:';
const bypassProxy = isBypassProxy(proxy, url);
let agent = {};

if (isHttps) {
if (proxy && !bypassProxy) {
const proxyAgentOptions = prepareProxyAgentOptions(agentOptions, proxy)
return getFromCacheOrCreate(HttpsProxyAgent, proxyAgentOptions, url);
prepareProxyAgentOptions(agentOptions, proxy);
agent = getFromCacheOrCreate(HttpsProxyAgent, agentOptions, parsedUrl);
} else {
return getFromCacheOrCreate(HttpsAgent, agentOptions, url);
agent = getFromCacheOrCreate(HttpsAgent, agentOptions, parsedUrl);
}
} else if (proxy && !bypassProxy) {
const proxyAgentOptions = prepareProxyAgentOptions(agentOptions, proxy);
return getFromCacheOrCreate(HttpAgent, proxyAgentOptions, url);
prepareProxyAgentOptions(agentOptions, proxy);
agent = getFromCacheOrCreate(HttpAgent, agentOptions, parsedUrl);
} else {
return getFromCacheOrCreate(HttpAgent, agentOptions, url);
agent = getFromCacheOrCreate(HttpAgent, agentOptions, parsedUrl);
}
return agent;
};

module.exports = NodeHttpClient;
32 changes: 13 additions & 19 deletions test/integration/ocsp_mock/testConnectionOcspMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ const connOption = require('../connectionOptions');
const Errors = require('../../../lib/errors');
const ErrorCodes = Errors.codes;
const HttpsMockAgent = require('./https_ocsp_mock_agent');
const {configureLogger} = require("../../configureLogger");
const testUtil = require("../testUtil");

function cloneConnOption(connOption)
{
Expand All @@ -26,11 +24,6 @@ describe('Connection test with OCSP Mock', function ()
{
const valid = cloneConnOption(connOption.valid);
const isHttps = valid.accessUrl.startsWith("https");
before(async () =>
{
configureLogger('TRACE');
});


function connect(errcode, connection, callback)
{
Expand Down Expand Up @@ -83,36 +76,37 @@ describe('Connection test with OCSP Mock', function ()
);
});

it('Connection failure with OCSP unknown error', function (done) {
it('Connection failure with OCSP unknown error', function (done)
{
valid.agentClass = HttpsMockAgent.HttpsMockAgentOcspUnkwown;
const connection = snowflake.createConnection(valid);

async.series([
function (callback) {
try {
connect(ErrorCodes.ERR_OCSP_UNKNOWN, connection, callback);
} catch (err){
console.log(`err ${err}`);
}

function (callback)
{
connect(ErrorCodes.ERR_OCSP_UNKNOWN, connection, callback);
},
function (callback) {
function (callback)
{
destroy(connection, callback);
}
],
done
);
});

it('Connection failure with invalid validity OCSP error', function (done) {
it('Connection failure with invalid validity OCSP error', function (done)
{
valid.agentClass = HttpsMockAgent.HttpsMockAgentOcspInvalid;
const connection = snowflake.createConnection(valid);

async.series([
function (callback) {
function (callback)
{
connect(ErrorCodes.ERR_OCSP_INVALID_VALIDITY, connection, callback);
},
function (callback) {
function (callback)
{
destroy(connection, callback);
}
],
Expand Down
115 changes: 0 additions & 115 deletions test/integration/testKeepAlive.js

This file was deleted.

4 changes: 1 addition & 3 deletions test/integration/testLargeResultSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ describe('Large result Set Tests', function ()
});
});

describe('SNOW-743920: Large result set with ~35 chunks', function () {
describe('SNOW-743920:Large result set with ~35 chunks', function () {
let connection;
const tableName = 'test_table';
const sourceRowCount = 251002;
Expand All @@ -263,7 +263,6 @@ describe('SNOW-743920: Large result set with ~35 chunks', function () {

before(async () => {
connection = testUtil.createConnection();
configureLogger('TRACE');
await testUtil.connectAsync(connection);
// setting ROWS_PER_RESULTSET causes invalid, not encoded chunks from GCP
// await testUtil.executeCmdAsync(connection, 'alter session set ROWS_PER_RESULTSET = 1000000');
Expand All @@ -273,7 +272,6 @@ describe('SNOW-743920: Large result set with ~35 chunks', function () {
});

after(async () => {
configureLogger('ERROR');
await testUtil.dropTablesIgnoringErrorsAsync(connection, [tableName]);
await testUtil.destroyConnectionAsync(connection);
});
Expand Down
Loading

0 comments on commit 53a57b3

Please sign in to comment.