text: 'use strict';
  var DateTimeValue_1 = require('../configuration/DateTimeValue');
  var glimpse_node_common_1 = require('@glimpse/glimpse-node-common');
  var Tracing_1 = require('../tracing/Tracing');
  var HttpHelper_1 = require('../util/HttpHelper');
  var HttpEvents_1 = require('../tracing/module_instrumentors/HttpEvents');
  var url = require('url');
  var _ = require('lodash');
  var ClientRequestInspector = (function () {
      function ClientRequestInspector() {
          this.requests = {};
          this.listeners = {};
      }
      ClientRequestInspector.prototype.normalizeOptions = function (options, req) {
          // Normalize to a copy of the original options
          if (typeof options === 'string') {
              options = url.parse(options);
          }
          options = _.assign({}, options);
          // 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;
          }
          // Simiarly, url.format ignores hostname and path 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. Fun times.
          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 || req.agent.protocol;
          options.hostname = options.hostname || 'localhost';
          return options;
      };
      ClientRequestInspector.prototype.numOutstandingRequests = function () {
          return Object.keys(this.requests).length;
      };
      ClientRequestInspector.prototype.before = function (masterData) {
          // TODO: https://github.com/Glimpse/Glimpse.Node/issues/307
          // Add support for base64 encoding non-text content by setting the encoding here
          var encoding = 'utf8';
          for (var i = 0; i < masterData.requestBodyChunks.length; i++) {
              if (Buffer.isBuffer(masterData.requestBodyChunks[i])) {
                  masterData.requestBodyChunks[i] = masterData.requestBodyChunks[i].toString();
              }
          }
          var payload = {
              protocol: {
                  identifier: masterData.options.protocol.replace(':', '').toLowerCase(),
                  // This value is hard coded in Node: https://github.com/nodejs/node/blob/d0582ef9e19e8ed941b0a585c935ad11919080ee/lib/_http_client.js#L114
                  version: '1.1'
              },
              url: url.format(masterData.options),
              // The method property isn't documented in the typings definition, for some reason
              //tslint:disable-next-line:no-any
              method: masterData.request.method,
              startTime: masterData.startTimeStamp,
              // Note: this uses a private field on the request object. Sadly, there isn't another way to get these currently.
              //tslint:disable-next-line:no-any
              headers: masterData.request._headers,
              isAjax: false,
              clientIp: '127.0.0.1',
              body: {
                  size: masterData.requestBodyLength,
                  encoding: encoding,
                  content: masterData.requestBodyChunks.join(''),
                  isTruncated: masterData.requestBodyLength > HttpHelper_1.HttpHelper.getMaxBodySize(this.agent.providers.configSettings)
              },
              timing: {
                  requestStart: masterData.requestStartTimeStamp
              },
              source: 'Server',
              correlationId: masterData.correlationId
          };
          this.agent.broker.createAndSendMessage(payload, ['data-http-request'], undefined, HttpHelper_1.HttpHelper.getContext(masterData.request));
      };
      ClientRequestInspector.prototype.after = function (masterData) {
          // TODO: https://github.com/Glimpse/Glimpse.Node/issues/307
          // Add support for base64 encoding non-text content by setting the encoding here
          var encoding = 'utf8';
          for (var i = 0; i < masterData.responseBodyChunks.length; i++) {
              if (Buffer.isBuffer(masterData.responseBodyChunks[i])) {
                  masterData.responseBodyChunks[i] = masterData.responseBodyChunks[i].toString();
              }
          }
          var payload = {
              // res.url doesn't seem to be populated in practice
              url: masterData.response.url || url.format(masterData.options),
              headers: masterData.response.headers,
              statusCode: masterData.response.statusCode,
              endTime: masterData.endTimeStamp,
              duration: masterData.duration,
              body: {
                  size: masterData.responseBodyLength,
                  encoding: encoding,
                  content: masterData.responseBodyChunks.join(''),
                  isTruncated: masterData.responseBodyLength > HttpHelper_1.HttpHelper.getMaxBodySize(this.agent.providers.configSettings)
              },
              timing: {
                  requestStart: masterData.requestStartTimeStamp,
                  responseStart: masterData.responseStartTimeStamp,
                  responseEnd: this.getCurrentTimeStamp()
              },
              source: 'Server',
              correlationId: masterData.correlationId
          };
          this.agent.broker.createAndSendMessage(payload, ['data-http-response'], undefined, HttpHelper_1.HttpHelper.getContext(masterData.response));
      };
      ClientRequestInspector.prototype.init = function (agent, errorReportingService) {
          var _this = this;
          this.agent = agent;
          this.errorReportingService = errorReportingService;
          this.listeners = (_a = {},
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_REQUEST_CREATED] = function (event) { return _this.onRequestCreated(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_REQUEST_DATA_SENT] = function (event) { return _this.onRequestDataSent(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_REQUEST_END] = function (event) { return _this.onRequestEnd(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_REQUEST_ERROR] = function (event) { return _this.onRequestError(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_RESPONSE_RECEIVED] = function (event) { return _this.onResponseReceived(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_RESPONSE_DATA_RECEIVED] = function (event) { return _this.onResponseDataReceived(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_RESPONSE_END] = function (event) { return _this.onResponseEnd(event); },
              _a[HttpEvents_1.EVENT_HTTP_CLIENT_RESPONSE_ERROR] = function (event) { return _this.onResponseError(event); },
              _a
          );
          for (var event_1 in this.listeners) {
              if (!this.listeners.hasOwnProperty(event_1)) {
                  continue;
              }
              Tracing_1.default.onAlways(event_1, this.listeners[event_1]);
          }
          var _a;
      };
      ClientRequestInspector.prototype.removeEventListeners = function () {
          for (var event_2 in this.listeners) {
              if (!this.listeners.hasOwnProperty(event_2)) {
                  continue;
              }
              Tracing_1.default.removeEventListener(event_2, this.listeners[event_2]);
          }
      };
      ClientRequestInspector.prototype.getCurrentTimeStamp = function () {
          return DateTimeValue_1.DateTimeValue.fromUnixMillisecondTimestamp(Date.now(), process.hrtime()).format();
      };
      ClientRequestInspector.prototype.onRequestCreated = function (event) {
          var eventData = event.data;
          this.requests[eventData.id] = {
              startTime: event.time,
              startTimeStamp: DateTimeValue_1.DateTimeValue.fromUnixMillisecondTimestamp(event.timestamp, event.time).format(),
              endTimeStamp: '',
              duration: 0,
              options: this.normalizeOptions(eventData.options, eventData.req),
              request: eventData.req,
              response: undefined,
              requestBodyChunks: [],
              requestBodyLength: 0,
              responseBodyChunks: [],
              responseBodyLength: 0,
              requestStartTimeStamp: '',
              responseStartTimeStamp: '',
              correlationId: eventData.id
          };
      };
      ClientRequestInspector.prototype.onRequestDataSent = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          // Save part or all of the chunk to the set of chunks,
          // truncating if necessary to keep the set under the
          // max body size
          var originalChunkLength = eventData.chunk.length;
          var normalizedChunk = eventData.chunk;
          var maxBodySize = HttpHelper_1.HttpHelper.getMaxBodySize(this.agent.providers.configSettings);
          if (masterData.requestBodyLength < maxBodySize) {
              if (masterData.requestBodyLength + originalChunkLength >= maxBodySize) {
                  normalizedChunk = normalizedChunk.slice(0, maxBodySize - masterData.requestBodyLength);
              }
              masterData.requestBodyChunks.push(normalizedChunk);
          }
          masterData.requestBodyLength += originalChunkLength;
      };
      ClientRequestInspector.prototype.onRequestEnd = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          masterData.requestStartTimeStamp = this.getCurrentTimeStamp();
          this.before(masterData);
      };
      ClientRequestInspector.prototype.onRequestError = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          delete this.requests[eventData.id];
          this.errorReportingService.reportError(glimpse_node_common_1.createHttpClientError(eventData.error));
      };
      ClientRequestInspector.prototype.onResponseReceived = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          masterData.responseStartTimeStamp = this.getCurrentTimeStamp();
      };
      ClientRequestInspector.prototype.onResponseDataReceived = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          // Save part or all of the chunk to the set of chunks,
          // truncating if necessary to keep the set under the
          // max body size
          var originalChunkLength = eventData.chunk.length;
          var normalizedChunk = eventData.chunk;
          var maxBodySize = HttpHelper_1.HttpHelper.getMaxBodySize(this.agent.providers.configSettings);
          if (masterData.responseBodyLength < maxBodySize) {
              if (masterData.responseBodyLength + originalChunkLength >= maxBodySize) {
                  normalizedChunk = normalizedChunk.slice(0, maxBodySize - masterData.responseBodyLength);
              }
              masterData.responseBodyChunks.push(normalizedChunk);
          }
          masterData.responseBodyLength += originalChunkLength;
      };
      ClientRequestInspector.prototype.onResponseEnd = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          masterData.endTimeStamp = DateTimeValue_1.DateTimeValue.fromUnixMillisecondTimestamp(event.timestamp, event.time).format();
          masterData.duration = (event.time[0] * 1e9 + event.time[1] -
              masterData.startTime[0] * 1e9 - masterData.startTime[1]) / 1e6;
          masterData.response = eventData.res;
          this.after(masterData);
          delete this.requests[eventData.id];
      };
      ClientRequestInspector.prototype.onResponseError = function (event) {
          var eventData = event.data;
          var masterData = this.requests[eventData.id];
          if (!masterData) {
              throw new Error('Internal error: could not find associated master data');
          }
          delete this.requests[eventData.id];
          this.errorReportingService.reportError(glimpse_node_common_1.createHttpClientError(eventData.error));
      };
      return ClientRequestInspector;
  }());
  exports.ClientRequestInspector = ClientRequestInspector;
  
  //# sourceMappingURL=../../maps/inspectors/ClientRequestInspector.js.map
  