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 b4d8b4b
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 210 deletions.
2 changes: 1 addition & 1 deletion lib/global_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ exports.setKeepAlive = function (value)
*
* @param {boolean} value
*/
exports.keepAlive = function (config) {
exports.getKeepAlive = function () {
return keepAlive;
};

Expand Down
90 changes: 32 additions & 58 deletions lib/http/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ const DEFAULT_REQUEST_TIMEOUT = 360000;
* @param connectionConfig
* @constructor
*/
function HttpClient(connectionConfig)
{
function HttpClient(connectionConfig) {
// save the connection config
this._connectionConfig = connectionConfig;
}

HttpClient.prototype.getConnectionConfig = function ()
{
HttpClient.prototype.getConnectionConfig = function () {
return this._connectionConfig;
};

Expand All @@ -34,8 +32,7 @@ HttpClient.prototype.getConnectionConfig = function ()
*
* @returns {Object} an object representing the request that was issued.
*/
HttpClient.prototype.request = function (options)
{
HttpClient.prototype.request = function (options) {
// validate input
Errors.assertInternal(Util.isObject(options));
Errors.assertInternal(Util.isString(options.method));
Expand All @@ -47,17 +44,16 @@ HttpClient.prototype.request = function (options)
Errors.assertInternal(!Util.exists(options.callback) ||
Util.isFunction(options.callback));

var headers;
var json;
var body;
var request;
let headers;
let json;
let body;
let request;

// normalize the headers
headers = normalizeHeaders(options.headers);

// create a function to send the request
var sendRequest = async function sendRequest()
{
let sendRequest = async function sendRequest() {
const url = options.url;

const timeout = options.timeout ||
Expand All @@ -67,7 +63,7 @@ HttpClient.prototype.request = function (options)
Logger.getInstance().trace(`CALL ${options.method} with timeout ${timeout}: ${url}`);
// build the basic request options

var requestOptions =
const requestOptions =
{
method: options.method,
url: url,
Expand All @@ -90,7 +86,7 @@ HttpClient.prototype.request = function (options)
if (this._connectionConfig.agentClass) {
mock = {
agentClass: this._connectionConfig.agentClass
}
};
}

// add the agent and proxy options
Expand Down Expand Up @@ -142,33 +138,25 @@ HttpClient.prototype.request = function (options)
// if a request body is specified and compression is enabled,
// try to compress the request body before sending the request
json = body = options.json;
if (body)
{
var bufferUncompressed = Buffer.from(JSON.stringify(body), 'utf8');
zlib.gzip(bufferUncompressed, null, function (err, bufferCompressed)
{
if (body) {
const bufferUncompressed = Buffer.from(JSON.stringify(body), 'utf8');
zlib.gzip(bufferUncompressed, null, function (err, bufferCompressed) {
// if the compression was successful
if (!err)
{
if (!err) {
json = undefined; // don't specify the 'json' option

// use the compressed buffer as the body and
// set the appropriate content encoding
body = bufferCompressed;
headers['Content-Encoding'] = 'gzip';
}
else
{
} else {
Logger.getInstance().warn('Could not compress request body.');
}

sendRequest();
});
}
else
{
if (body)
{
} else {
if (body) {
Logger.getInstance().trace('Request body compression disabled.');
}

Expand All @@ -178,10 +166,8 @@ HttpClient.prototype.request = function (options)
// return an externalized request object that only contains
// methods we're comfortable exposing to the outside world
return {
abort: function ()
{
if (request)
{
abort: function () {
if (request) {
request.abort();
}
}
Expand All @@ -195,8 +181,7 @@ HttpClient.prototype.request = function (options)
*
* @returns {*}
*/
HttpClient.prototype.getRequestModule = function ()
{
HttpClient.prototype.getRequestModule = function () {
return null;
};

Expand All @@ -208,8 +193,7 @@ HttpClient.prototype.getRequestModule = function ()
*
* @returns {*}
*/
HttpClient.prototype.getAgent = function (url, proxy, mock)
{
HttpClient.prototype.getAgent = function (url, proxy, mock) {
return null;
};

Expand All @@ -223,12 +207,10 @@ module.exports = HttpClient;
*
* @returns {Object}
*/
function normalizeHeaders(headers)
{
var ret = headers;
function normalizeHeaders(headers) {
let ret = headers;

if (Util.isObject(headers))
{
if (Util.isObject(headers)) {
ret = {
'user-agent': Util.userAgent
};
Expand All @@ -242,19 +224,14 @@ function normalizeHeaders(headers)
// browser-request will inject its own 'accept': 'application/json' header
// and the browser XMLHttpRequest object will concatenate the two values and
// send 'Accept': 'application/json, application/json' with the request
var headerNameLowerCase;
for (var headerName in headers)
{
if (headers.hasOwnProperty(headerName))
{
let headerNameLowerCase;
for (const headerName in headers) {
if (headers.hasOwnProperty(headerName)) {
headerNameLowerCase = headerName.toLowerCase();
if ((headerNameLowerCase === 'accept') ||
(headerNameLowerCase === 'content-type'))
{
(headerNameLowerCase === 'content-type')) {
ret[headerNameLowerCase] = headers[headerName];
}
else
{
} else {
ret[headerName] = headers[headerName];
}
}
Expand All @@ -273,13 +250,10 @@ function normalizeHeaders(headers)
*
* @return {Object}
*/
function normalizeResponse(response)
{
function normalizeResponse(response) {
// if the response doesn't already have a getResponseHeader() method, add one
if (response && !response.getResponseHeader)
{
response.getResponseHeader = function (header)
{
if (response && !response.getResponseHeader) {
response.getResponseHeader = function (header) {
return response.headers && response.headers[
Util.isString(header) ? header.toLowerCase() : header];
};
Expand Down
58 changes: 29 additions & 29 deletions lib/http/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,43 @@ 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}`
function getFromCacheOrCreate(agentClass, options, parsedUrl) {
const protocol = parsedUrl.protocol || 'http:';
const port = parsedUrl.port || (protocol === 'http:' ? '80' : '443');
const agentId = `${protocol}//${parsedUrl.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)
const agent = agentClass(options);
httpsAgentCache.set(agentId, agent);
Logger.getInstance().trace(`Create and add to cache new agent ${agentId}`);
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 +70,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 +87,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, url);
} else {
return getFromCacheOrCreate(HttpAgent, agentOptions, url);
agent = getFromCacheOrCreate(HttpAgent, agentOptions, parsedUrl);
}
return agent;
};

module.exports = NodeHttpClient;
5 changes: 0 additions & 5 deletions test/integration/ocsp_mock/testConnectionOcspMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,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
Loading

0 comments on commit b4d8b4b

Please sign in to comment.