"use strict"; var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; var http = require("http"); var https = require("https"); var url = require("url"); var constants = require("constants"); var Logging = require("./Logging"); var RequestResponseHeaders = require("./RequestResponseHeaders"); var Util = (function () { function Util() { } /** * helper method to access userId and sessionId cookie */ Util.getCookie = function (name, cookie) { var value = ""; if (name && name.length && typeof cookie === "string") { var cookieName = name + "="; var cookies = cookie.split(";"); for (var i = 0; i < cookies.length; i++) { var cookie = cookies[i]; cookie = Util.trim(cookie); if (cookie && cookie.indexOf(cookieName) === 0) { value = cookie.substring(cookieName.length, cookies[i].length); break; } } } return value; }; /** * helper method to trim strings (IE8 does not implement String.prototype.trim) */ Util.trim = function (str) { if (typeof str === "string") { return str.replace(/^\s+|\s+$/g, ""); } else { return ""; } }; /** * Convert an array of int32 to Base64 (no '==' at the end). * MSB first. */ Util.int32ArrayToBase64 = function (array) { var toChar = function (v, i) { return String.fromCharCode((v >> i) & 0xFF); }; var int32AsString = function (v) { return toChar(v, 24) + toChar(v, 16) + toChar(v, 8) + toChar(v, 0); }; var x = array.map(int32AsString).join(""); var b = Buffer.from ? Buffer.from(x, "binary") : new Buffer(x, "binary"); var s = b.toString("base64"); return s.substr(0, s.indexOf("=")); }; /** * generate a random 32bit number (-0x80000000..0x7FFFFFFF). */ Util.random32 = function () { return (0x100000000 * Math.random()) | 0; }; /** * generate a random 32bit number (0x00000000..0xFFFFFFFF). */ Util.randomu32 = function () { return Util.random32() + 0x80000000; }; /** * generate W3C-compatible trace id * https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id */ Util.w3cTraceId = function () { var hexValues = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]; // rfc4122 version 4 UUID without dashes and with lowercase letters var oct = "", tmp; for (var a = 0; a < 4; a++) { tmp = Util.random32(); oct += hexValues[tmp & 0xF] + hexValues[tmp >> 4 & 0xF] + hexValues[tmp >> 8 & 0xF] + hexValues[tmp >> 12 & 0xF] + hexValues[tmp >> 16 & 0xF] + hexValues[tmp >> 20 & 0xF] + hexValues[tmp >> 24 & 0xF] + hexValues[tmp >> 28 & 0xF]; } // "Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively" var clockSequenceHi = hexValues[8 + (Math.random() * 4) | 0]; return oct.substr(0, 8) + oct.substr(9, 4) + "4" + oct.substr(13, 3) + clockSequenceHi + oct.substr(16, 3) + oct.substr(19, 12); }; Util.w3cSpanId = function () { return Util.w3cTraceId().substring(16); }; Util.isValidW3CId = function (id) { return id.length === 32 && id !== "00000000000000000000000000000000"; }; /** * Check if an object is of type Array */ Util.isArray = function (obj) { return Object.prototype.toString.call(obj) === "[object Array]"; }; /** * Check if an object is of type Error */ Util.isError = function (obj) { return obj instanceof Error; }; Util.isPrimitive = function (input) { var propType = typeof input; return propType === "string" || propType === "number" || propType === "boolean"; }; /** * Check if an object is of type Date */ Util.isDate = function (obj) { return Object.prototype.toString.call(obj) === "[object Date]"; }; /** * Convert ms to c# time span format */ Util.msToTimeSpan = function (totalms) { if (isNaN(totalms) || totalms < 0) { totalms = 0; } var sec = ((totalms / 1000) % 60).toFixed(7).replace(/0{0,4}$/, ""); var min = "" + Math.floor(totalms / (1000 * 60)) % 60; var hour = "" + Math.floor(totalms / (1000 * 60 * 60)) % 24; var days = Math.floor(totalms / (1000 * 60 * 60 * 24)); sec = sec.indexOf(".") < 2 ? "0" + sec : sec; min = min.length < 2 ? "0" + min : min; hour = hour.length < 2 ? "0" + hour : hour; var daysText = days > 0 ? days + "." : ""; return daysText + hour + ":" + min + ":" + sec; }; /** * Using JSON.stringify, by default Errors do not serialize to something useful: * Simplify a generic Node Error into a simpler map for customDimensions * Custom errors can still implement toJSON to override this functionality */ Util.extractError = function (err) { // Error is often subclassed so may have code OR id properties: // https://nodejs.org/api/errors.html#errors_error_code var looseError = err; return { message: err.message, code: looseError.code || looseError.id || "", }; }; /** * Manually call toJSON if available to pre-convert the value. * If a primitive is returned, then the consumer of this function can skip JSON.stringify. * This avoids double escaping of quotes for Date objects, for example. */ Util.extractObject = function (origProperty) { if (origProperty instanceof Error) { return Util.extractError(origProperty); } if (typeof origProperty.toJSON === "function") { return origProperty.toJSON(); } return origProperty; }; /** * Validate that an object is of type { [key: string]: string } */ Util.validateStringMap = function (obj) { if (typeof obj !== "object") { Logging.info("Invalid properties dropped from payload"); return; } var map = {}; for (var field in obj) { var property = ''; var origProperty = obj[field]; var propType = typeof origProperty; if (Util.isPrimitive(origProperty)) { property = origProperty.toString(); } else if (origProperty === null || propType === "undefined") { property = ""; } else if (propType === "function") { Logging.info("key: " + field + " was function; will not serialize"); continue; } else { var stringTarget = Util.isArray(origProperty) ? origProperty : Util.extractObject(origProperty); try { if (Util.isPrimitive(stringTarget)) { property = stringTarget; } else { property = JSON.stringify(stringTarget); } } catch (e) { property = origProperty.constructor.name.toString() + " (Error: " + e.message + ")"; Logging.info("key: " + field + ", could not be serialized"); } } map[field] = property.substring(0, Util.MAX_PROPERTY_LENGTH); } return map; }; /** * Checks if a request url is not on a excluded domain list * and if it is safe to add correlation headers */ Util.canIncludeCorrelationHeader = function (client, requestUrl) { var excludedDomains = client && client.config && client.config.correlationHeaderExcludedDomains; if (!excludedDomains || excludedDomains.length == 0 || !requestUrl) { return true; } for (var i = 0; i < excludedDomains.length; i++) { var regex = new RegExp(excludedDomains[i].replace(/\./g, "\.").replace(/\*/g, ".*")); if (regex.test(url.parse(requestUrl).hostname)) { return false; } } return true; }; Util.getCorrelationContextTarget = function (response, key) { var contextHeaders = response.headers && response.headers[RequestResponseHeaders.requestContextHeader]; if (contextHeaders) { var keyValues = contextHeaders.split(","); for (var i = 0; i < keyValues.length; ++i) { var keyValue = keyValues[i].split("="); if (keyValue.length == 2 && keyValue[0] == key) { return keyValue[1]; } } } }; /** * Generate request * * Proxify the request creation to handle proxy http * * @param {string} requestUrl url endpoint * @param {Object} requestOptions Request option * @param {Function} requestCallback callback on request * @returns {http.ClientRequest} request object */ Util.makeRequest = function (config, requestUrl, requestOptions, requestCallback) { if (requestUrl && requestUrl.indexOf('//') === 0) { requestUrl = 'https:' + requestUrl; } var requestUrlParsed = url.parse(requestUrl); var options = __assign({}, requestOptions, { host: requestUrlParsed.hostname, port: requestUrlParsed.port, path: requestUrlParsed.pathname }); var proxyUrl = undefined; if (requestUrlParsed.protocol === 'https:') { proxyUrl = config.proxyHttpsUrl || undefined; } if (requestUrlParsed.protocol === 'http:') { proxyUrl = config.proxyHttpUrl || undefined; } if (proxyUrl) { if (proxyUrl.indexOf('//') === 0) { proxyUrl = 'http:' + proxyUrl; } var proxyUrlParsed = url.parse(proxyUrl); // https is not supported at the moment if (proxyUrlParsed.protocol === 'https:') { Logging.info("Proxies that use HTTPS are not supported"); proxyUrl = undefined; } else { options = __assign({}, options, { host: proxyUrlParsed.hostname, port: proxyUrlParsed.port || "80", path: requestUrl, headers: __assign({}, options.headers, { Host: requestUrlParsed.hostname }) }); } } var isHttps = requestUrlParsed.protocol === 'https:' && !proxyUrl; if (isHttps && config.httpsAgent !== undefined) { options.agent = config.httpsAgent; } else if (!isHttps && config.httpAgent !== undefined) { options.agent = config.httpAgent; } else if (isHttps) { // HTTPS without a passed in agent. Use one that enforces our TLS rules options.agent = Util.tlsRestrictedAgent; } if (isHttps) { return https.request(options, requestCallback); } else { return http.request(options, requestCallback); } }; ; /** * Parse standard request-context header */ Util.safeIncludeCorrelationHeader = function (client, request, correlationHeader) { var header; // attempt to cast correlationHeader to string if (typeof correlationHeader === "string") { header = correlationHeader; } else if (correlationHeader instanceof Array) { header = correlationHeader.join(","); } else if (correlationHeader && typeof correlationHeader.toString === "function") { // best effort attempt: requires well-defined toString try { header = correlationHeader.toString(); } catch (err) { Logging.warn("Outgoing request-context header could not be read. Correlation of requests may be lost.", err, correlationHeader); } } if (header) { Util.addCorrelationIdHeaderFromString(client, request, header); } else { request.setHeader(RequestResponseHeaders.requestContextHeader, RequestResponseHeaders.requestContextSourceKey + "=" + client.config.correlationId); } }; Util.addCorrelationIdHeaderFromString = function (client, response, correlationHeader) { var components = correlationHeader.split(","); var key = RequestResponseHeaders.requestContextSourceKey + "="; var found = components.some(function (value) { return value.substring(0, key.length) === key; }); if (!found) { response.setHeader(RequestResponseHeaders.requestContextHeader, correlationHeader + "," + RequestResponseHeaders.requestContextSourceKey + "=" + client.config.correlationId); } }; Util.MAX_PROPERTY_LENGTH = 8192; Util.tlsRestrictedAgent = new https.Agent({ secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 }); return Util; }()); module.exports = Util; //# sourceMappingURL=Util.js.map