@launchdarkly/node-server-sdk
Version:
LaunchDarkly Server-Side SDK for Node.js
138 lines • 5.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const http = require("http");
const https = require("https");
const createHttpsProxyAgent = require("https-proxy-agent");
// No types for the event source.
// @ts-ignore
const launchdarkly_eventsource_1 = require("launchdarkly-eventsource");
const util_1 = require("util");
const zlib = require("zlib");
const NodeResponse_1 = require("./NodeResponse");
const gzip = (0, util_1.promisify)(zlib.gzip);
function processTlsOptions(tlsOptions) {
const options = {
ca: tlsOptions.ca,
cert: tlsOptions.cert,
checkServerIdentity: tlsOptions.checkServerIdentity,
ciphers: tlsOptions.ciphers,
// Our interface says object for the pfx object. But the node
// type is more strict. This is also true for the key and KeyObject.
// @ts-ignore
pfx: tlsOptions.pfx,
// @ts-ignore
key: tlsOptions.key,
passphrase: tlsOptions.passphrase,
rejectUnauthorized: tlsOptions.rejectUnauthorized,
secureProtocol: tlsOptions.secureProtocol,
servername: tlsOptions.servername,
};
// Node does not take kindly to undefined keys.
Object.keys(options).forEach((key) => {
if (options[key] === undefined) {
delete options[key];
}
});
return options;
}
function processProxyOptions(proxyOptions, additional = {}) {
var _a;
const protocol = ((_a = proxyOptions.scheme) === null || _a === void 0 ? void 0 : _a.startsWith('https')) ? 'https:' : 'http';
const parsedOptions = Object.assign({ port: proxyOptions.port, host: proxyOptions.host, protocol }, additional);
if (proxyOptions.auth) {
parsedOptions.headers = {
'Proxy-Authorization': `Basic ${Buffer.from(proxyOptions.auth).toString('base64')}`,
};
}
// Node does not take kindly to undefined keys.
Object.keys(parsedOptions).forEach((key) => {
if (parsedOptions[key] === undefined) {
delete parsedOptions[key];
}
});
return createHttpsProxyAgent(parsedOptions);
}
function createAgent(tlsOptions, proxyOptions, logger) {
var _a;
if (!((_a = proxyOptions === null || proxyOptions === void 0 ? void 0 : proxyOptions.auth) === null || _a === void 0 ? void 0 : _a.startsWith('https')) && tlsOptions) {
logger === null || logger === void 0 ? void 0 : logger.warn('Proxy configured with TLS options, but is not using an https auth.');
}
if (tlsOptions) {
const agentOptions = processTlsOptions(tlsOptions);
if (proxyOptions) {
return processProxyOptions(proxyOptions, agentOptions);
}
return new https.Agent(agentOptions);
}
if (proxyOptions) {
return processProxyOptions(proxyOptions);
}
return undefined;
}
class NodeRequests {
constructor(tlsOptions, proxyOptions, logger, enableEventCompression) {
this._hasProxy = false;
this._hasProxyAuth = false;
this._enableBodyCompression = false;
this._agent = createAgent(tlsOptions, proxyOptions, logger);
this._hasProxy = !!proxyOptions;
this._hasProxyAuth = !!(proxyOptions === null || proxyOptions === void 0 ? void 0 : proxyOptions.auth);
this._enableBodyCompression = !!enableEventCompression;
}
async fetch(url, options = {}) {
var _a, _b;
const isSecure = url.startsWith('https://');
const impl = isSecure ? https : http;
const headers = Object.assign({}, options.headers);
let bodyData = options.body;
// For get requests we are going to automatically support compressed responses.
// Note this does not affect SSE as the event source is not using this fetch implementation.
if (((_a = options.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'get') {
headers['accept-encoding'] = 'gzip';
}
// For post requests we are going to support compressed post bodies if the
// enableEventCompression config setting is true and the compressBodyIfPossible
// option is true.
else if (this._enableBodyCompression &&
!!options.compressBodyIfPossible &&
((_b = options.method) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'post' &&
options.body) {
headers['content-encoding'] = 'gzip';
bodyData = await gzip(Buffer.from(options.body, 'utf8'));
}
return new Promise((resolve, reject) => {
const req = impl.request(url, {
timeout: options.timeout,
headers,
method: options.method,
agent: this._agent,
}, (res) => resolve(new NodeResponse_1.default(res)));
if (bodyData) {
req.write(bodyData);
}
req.on('error', (err) => {
reject(err);
});
req.end();
});
}
createEventSource(url, eventSourceInitDict) {
const expandedOptions = Object.assign(Object.assign({}, eventSourceInitDict), { agent: this._agent, tlsParams: this._tlsOptions, maxBackoffMillis: 30 * 1000, jitterRatio: 0.5 });
return new launchdarkly_eventsource_1.EventSource(url, expandedOptions);
}
getEventSourceCapabilities() {
return {
readTimeout: true,
headers: true,
customMethod: true,
};
}
usingProxy() {
return this._hasProxy;
}
usingProxyAuth() {
return this._hasProxyAuth;
}
}
exports.default = NodeRequests;
//# sourceMappingURL=NodeRequests.js.map