"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var url = require("url"); var Contracts = require("../Declarations/Contracts"); var Util = require("../Library/Util"); var RequestResponseHeaders = require("../Library/RequestResponseHeaders"); var RequestParser = require("./RequestParser"); var CorrelationIdManager = require("../Library/CorrelationIdManager"); /** * Helper class to read data from the request/response objects and convert them into the telemetry contract */ var HttpDependencyParser = (function (_super) { __extends(HttpDependencyParser, _super); function HttpDependencyParser(requestOptions, request) { var _this = _super.call(this) || this; if (request && request.method && requestOptions) { // The ClientRequest.method property isn't documented, but is always there. _this.method = request.method; _this.url = HttpDependencyParser._getUrlFromRequestOptions(requestOptions, request); _this.startTime = +new Date(); } return _this; } /** * Called when the ClientRequest emits an error event. */ HttpDependencyParser.prototype.onError = function (error) { this._setStatus(undefined, error); }; /** * Called when the ClientRequest emits a response event. */ HttpDependencyParser.prototype.onResponse = function (response) { this._setStatus(response.statusCode, undefined); this.correlationId = Util.getCorrelationContextTarget(response, RequestResponseHeaders.requestContextTargetKey); }; /** * Gets a dependency data contract object for a completed ClientRequest. */ HttpDependencyParser.prototype.getDependencyTelemetry = function (baseTelemetry, dependencyId) { var urlObject = url.parse(this.url); urlObject.search = undefined; urlObject.hash = undefined; var dependencyName = this.method.toUpperCase() + " " + urlObject.pathname; var remoteDependencyType = Contracts.RemoteDependencyDataConstants.TYPE_HTTP; var remoteDependencyTarget = urlObject.hostname; if (urlObject.port) { remoteDependencyTarget += ":" + urlObject.port; } if (this.correlationId) { remoteDependencyType = Contracts.RemoteDependencyDataConstants.TYPE_AI; if (this.correlationId !== CorrelationIdManager.correlationIdPrefix) { remoteDependencyTarget += " | " + this.correlationId; } } else { remoteDependencyType = Contracts.RemoteDependencyDataConstants.TYPE_HTTP; } var dependencyTelemetry = { id: dependencyId, name: dependencyName, data: this.url, duration: this.duration, success: this._isSuccess(), resultCode: this.statusCode ? this.statusCode.toString() : null, properties: this.properties || {}, dependencyTypeName: remoteDependencyType, target: remoteDependencyTarget }; if (baseTelemetry && baseTelemetry.time) { dependencyTelemetry.time = baseTelemetry.time; } else if (this.startTime) { dependencyTelemetry.time = new Date(this.startTime); } // We should keep any parameters the user passed in // Except the fields defined above in requestTelemetry, which take priority // Except the properties field, where they're merged instead, with baseTelemetry taking priority if (baseTelemetry) { // Copy missing fields for (var key in baseTelemetry) { if (!dependencyTelemetry[key]) { dependencyTelemetry[key] = baseTelemetry[key]; } } // Merge properties if (baseTelemetry.properties) { for (var key in baseTelemetry.properties) { dependencyTelemetry.properties[key] = baseTelemetry.properties[key]; } } } return dependencyTelemetry; }; /** * Builds a URL from request options, using the same logic as http.request(). This is * necessary because a ClientRequest object does not expose a url property. */ HttpDependencyParser._getUrlFromRequestOptions = function (options, request) { if (typeof options === 'string') { if (options.indexOf("http://") === 0 || options.indexOf("https://") === 0) { // protocol exists, parse normally options = url.parse(options); } else { // protocol not found, insert http/https where appropriate var parsed = url.parse(options); if (parsed.host === "443") { options = url.parse("https://" + options); } else { options = url.parse("http://" + options); } } } else if (options && typeof url.URL === 'function' && options instanceof url.URL) { return url.format(options); } else { // Avoid modifying the original options object. var originalOptions_1 = options; options = {}; if (originalOptions_1) { Object.keys(originalOptions_1).forEach(function (key) { options[key] = originalOptions_1[key]; }); } } // Oddly, url.format ignores path and only uses pathname and search, // so create them from the path, if path was specified if (options.path) { var parsedQuery = url.parse(options.path); options.pathname = parsedQuery.pathname; options.search = parsedQuery.search; } // Similarly, url.format ignores hostname and port if host is specified, // even if host doesn't have the port, but http.request does not work // this way. It will use the port if one is not specified in host, // effectively treating host as hostname, but will use the port specified // in host if it exists. if (options.host && options.port) { // Force a protocol so it will parse the host as the host, not path. // It is discarded and not used, so it doesn't matter if it doesn't match var parsedHost = url.parse("http://" + options.host); if (!parsedHost.port && options.port) { options.hostname = options.host; delete options.host; } } // Mix in default values used by http.request and others options.protocol = options.protocol || (request.agent && request.agent.protocol) || (request.protocol) || undefined; options.hostname = options.hostname || 'localhost'; return url.format(options); }; return HttpDependencyParser; }(RequestParser)); module.exports = HttpDependencyParser; //# sourceMappingURL=HttpDependencyParser.js.map