function captureOutgoingHTTPs()

in packages/core/lib/patchers/http_p.js [67:202]


  function captureOutgoingHTTPs(baseFunc, ...args) {
    let options;
    let callback;
    let hasUrl;
    let urlObj;

    let arg0 = args[0];
    if (typeof args[1] === 'object') {
      hasUrl = true;
      urlObj = typeof arg0 === 'string' ? new url.URL(arg0) : arg0;
      options = args[1],
      callback = args[2];
    } else {
      hasUrl = false;
      options = arg0;
      callback = args[1];
    }

    // Short circuit if the HTTP request has no options or is already being captured
    if (!options || (options.headers && (options.headers['X-Amzn-Trace-Id']))) {
      return baseFunc(...args);
    }

    // Case of calling a string URL without options, e.g.: http.request('http://amazon.com', callback)
    if (typeof options === 'string') {
      options = new url.URL(options);
    }

    if (!hasUrl) {
      urlObj = options;
    }

    const parent = contextUtils.resolveSegment(contextUtils.resolveManualSegmentParams(options));
    const hostname = options.hostname || options.host || urlObj.hostname || urlObj.host || 'Unknown host';

    if (!parent) {
      let output = '[ host: ' + hostname;
      output = options.method ? (output + ', method: ' + options.method) : output;
      output += ', path: ' + (urlObj.pathname || Utils.stripQueryStringFromPath(options.path)) + ' ]';

      if (!contextUtils.isAutomaticMode()) {
        logger.getLogger().info('Options for request ' + output +
          ' requires a segment object on the options params as "XRaySegment" for tracing in manual mode. Ignoring.');
      } else {
        logger.getLogger().info('Options for request ' + output +
          ' is missing the sub/segment context for automatic mode. Ignoring.');
      }

      // Options are not modified, only parsed for logging. We can pass in the original arguments.
      return baseFunc(...args);
    }

    let subsegment;
    if (parent.notTraced) {
      subsegment = parent.addNewSubsegmentWithoutSampling(hostname);
    } else {
      subsegment = parent.addNewSubsegment(hostname);
    }

    const root = parent.segment ? parent.segment : parent;
    subsegment.namespace = 'remote';

    if (!options.headers) {
      options.headers = {};
    }

    if (!parent.noOp) {
      options.headers['X-Amzn-Trace-Id'] = 'Root=' + root.trace_id + ';Parent=' + subsegment.id +
        ';Sampled=' + (subsegment.notTraced ? '0' : '1');
    }

    const errorCapturer = function errorCapturer(e) {
      if (subsegmentCallback) {
        subsegmentCallback(subsegment, this, null, e);
      }

      if (subsegment.http && subsegment.http.response) {
        if (Utils.getCauseTypeFromHttpStatus(subsegment.http.response.status) === 'error') {
          subsegment.addErrorFlag();
        }
        subsegment.close(e, true);
      } else {
        const madeItToDownstream = (e.code !== 'ECONNREFUSED');

        subsegment.addRemoteRequestData(this, null, madeItToDownstream && downstreamXRayEnabled);
        subsegment.close(e);
      }
    };

    const optionsCopy = Utils.objectWithoutProperties(options, ['Segment'], true);

    let req = baseFunc(...(hasUrl ? [arg0, optionsCopy] : [options]), function(res) {
      res.on('end', function() {
        if (subsegmentCallback) {
          subsegmentCallback(subsegment, this.req, res);
        }

        if (res.statusCode === 429) {
          subsegment.addThrottleFlag();
        }

        const cause = Utils.getCauseTypeFromHttpStatus(res.statusCode);

        if (cause) {
          subsegment[cause] = true;
        }

        subsegment.addRemoteRequestData(res.req, res, !!downstreamXRayEnabled);
        subsegment.close();
      });

      if (typeof callback === 'function') {
        if (contextUtils.isAutomaticMode()) {
          const session = contextUtils.getNamespace();

          session.run(function() {
            contextUtils.setSegment(subsegment);
            callback(res);
          });
        } else {
          callback(res);
        }
        // if no callback provided by user application, AND no explicit response listener
        // added by user application, then we consume the response so the 'end' event fires
        // See: https://nodejs.org/api/http.html#http_class_http_clientrequest
      } else if (res.req && res.req.listenerCount('response') === 0) {
        res.resume();
      }
    });

    // Use errorMonitor if available (in Node 12.17+), otherwise fall back to standard error listener
    // See: https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events_eventemitter_errormonitor
    req.on(events.errorMonitor || 'error', errorCapturer);

    return req;
  }