Repository URL to install this package:
|
Version:
0.7.0 ▾
|
// (c) Copyright 2023 Supertenant Ltd. - all rights reserved.
// See LICENSE file in project root for license terms.
"use strict";const coreHttpModule=require("http"),coreHttpsModule=require("https"),URL=require("url").URL,tracingUtil=require("../../tracingUtil"),{dropLeadingQuestionMark,filterParams,sanitizeUrl,splitAndFilter}=require("../../../util/url"),{getExtraHeadersFromOptions,mergeExtraHeadersFromIncomingMessage,mergeExtraHeadersFromServerResponseOrClientRequest}=require("./captureHttpHeadersUtil"),constants=require("../../constants"),cls=require("../../cls"),url=require("url");let extraHttpHeadersToCapture,isActive=!1;function evaluateHeaderValue(headerValue,validator){if(null==headerValue)return!1;if("string"==typeof headerValue||"number"==typeof headerValue)return validator(String(headerValue));if(Array.isArray(headerValue)){var len=headerValue.length;for(let i=0;i<len;i++)if(!0===validator(headerValue[i]))return!0;return!1}}function shouldBeBypassed(parentSpan,options){var options=options&&options.headers,isAWSNodeJSHeader=evaluateHeaderValue(options&&options["User-Agent"]||options&&options["user-agent"],header=>-1<header.toLowerCase().indexOf("aws-sdk-nodejs")||-1<header.toLowerCase().indexOf("aws-sdk-js")),options=evaluateHeaderValue(options&&options.Host||options&&options.host,header=>null!==header.match(/^sqs\.(?:.+?)\./));return!!(parentSpan&&"sqs"===parentSpan.n&&isAWSNodeJSHeader&&options)}function instrument(coreModule,forceHttps){const originalRequest=coreModule.request;coreModule.request=function request(){let clientRequest;for(;0<arguments.length&&null==arguments[arguments.length-1];)arguments.length--;const originalArgs=new Array(arguments.length);for(let i=0;i<arguments.length;i++)originalArgs[i]=arguments[i];let urlArg=null,options=null,callback=null,callbackIndex=-1,w3cTraceContext=("string"==typeof originalArgs[0]||isUrlObject(originalArgs[0])?urlArg=originalArgs[0]:"object"==typeof originalArgs[0]&&(options=originalArgs[0]),options||"object"!=typeof originalArgs[1]||(options=originalArgs[1]),"function"==typeof originalArgs[1]&&(callback=originalArgs[1],callbackIndex=1),callback||"function"!=typeof originalArgs[2]||(callback=originalArgs[2],callbackIndex=2),cls.getW3cTraceContext());var skipTracingResult=cls.skipExitTracing({isActive:isActive,extendedResponse:!0,skipParentSpanCheck:!0,skipIsTracing:!0}),parentSpan=cls.getCurrentSpan()||cls.getReducedSpan();if(skipTracingResult.skip||!parentSpan||constants.isExitSpan(parentSpan)||shouldBeBypassed(parentSpan,options)){let traceLevelHeaderHasBeenAdded=!1;skipTracingResult.suppressed&&(traceLevelHeaderHasBeenAdded=tryToAddTraceLevelAddHeaderToOpts(options,"0",w3cTraceContext)),clientRequest=originalRequest.apply(coreModule,arguments),skipTracingResult.suppressed&&!traceLevelHeaderHasBeenAdded&&(clientRequest.setHeader(constants.traceLevelHeaderName,"0"),setW3cHeadersOnRequest(clientRequest,w3cTraceContext))}else cls.ns.run(()=>{const span=cls.startSpan("node.http.client",constants.EXIT);w3cTraceContext=cls.getW3cTraceContext();let completeCallUrl,params;urlArg&&"string"==typeof urlArg?(completeCallUrl=sanitizeUrl(urlArg),params=splitAndFilter(urlArg)):urlArg&&isUrlObject(urlArg)?(completeCallUrl=sanitizeUrl(url.format(urlArg)),params=dropLeadingQuestionMark(filterParams(urlArg.search))):options&&(urlAndQuery=constructFromUrlOpts(options,coreModule,forceHttps),completeCallUrl=urlAndQuery[0],params=urlAndQuery[1]),span.stack=tracingUtil.getStackTrace(request);var urlAndQuery=cls.ns.bind(function(res){span.data.http={method:clientRequest.method,url:completeCallUrl,status:res.statusCode,params:params};var headers=captureRequestHeaders(options,clientRequest,res);headers&&(span.data.http.header=headers),span.d=Date.now()-span.ts,span.ec=500<=res.statusCode?1:0,span.transmit(),callback&&callback(res)});0<=callbackIndex?originalArgs[callbackIndex]=urlAndQuery:originalArgs.push(urlAndQuery);let instanaHeadersHaveBeenAdded=!1;try{instanaHeadersHaveBeenAdded=tryToAddHeadersToOpts(options,span,w3cTraceContext),clientRequest=originalRequest.apply(coreModule,originalArgs)}catch(e){throw span.data.http={url:completeCallUrl,error:e?e.message:""},span.d=Date.now()-span.ts,span.ec=1,span.transmit(),e}cls.ns.bindEmitter(clientRequest),instanaHeadersHaveBeenAdded=instanaHeadersHaveBeenAdded||setHeadersOnRequest(clientRequest,span,w3cTraceContext);let isTimeout=!1;clientRequest.on("timeout",()=>{isTimeout=!0}),clientRequest.on("error",err=>{let errorMessage=err.message;isTimeout?(errorMessage="Timeout exceeded",clientRequest.aborted&&(errorMessage+=", request aborted")):clientRequest.aborted&&(errorMessage="Request aborted"),span.data.http={method:clientRequest.method,url:completeCallUrl,error:errorMessage},span.d=Date.now()-span.ts,span.ec=1,span.transmit()})});return clientRequest},coreModule.get=function(){var req=coreModule.request.apply(coreModule,arguments);return req.end(),req}}function constructFromUrlOpts(options,self,forceHttps){if(options.href)return[sanitizeUrl(options.href),splitAndFilter(options.href)];try{var agent=options.agent||self.agent,port=options.port||options.defaultPort||agent&&agent.defaultPort||80,protocol=(443===port?"https:":options.protocol)||agent&&agent.protocol||(forceHttps?"https:":"http:"),host=options.hostname||options.host||"localhost",path=options.path||"/";return[sanitizeUrl(protocol+`//${host}:`+port+path),splitAndFilter(path)]}catch(e){return[void 0,void 0]}}function isUrlObject(argument){return URL&&argument instanceof url.URL}function tryToAddHeadersToOpts(options,span,w3cTraceContext){return!!hasHeadersOption(options)&&(isItSafeToModifiyHeadersInOptions(options)&&(options.headers[constants.spanIdHeaderName]=span.s,options.headers[constants.traceIdHeaderName]=span.t,options.headers[constants.traceLevelHeaderName]="1",tryToAddW3cHeaderToOpts(options,w3cTraceContext)),!0)}function tryToAddTraceLevelAddHeaderToOpts(options,level,w3cTraceContext){return!!hasHeadersOption(options)&&(isItSafeToModifiyHeadersInOptions(options)&&(options.headers[constants.traceLevelHeaderName]=level,tryToAddW3cHeaderToOpts(options,w3cTraceContext)),!0)}function tryToAddW3cHeaderToOpts(options,w3cTraceContext){w3cTraceContext&&(options.headers[constants.w3cTraceParent]=w3cTraceContext.renderTraceParent(),w3cTraceContext.hasTraceState())&&(options.headers[constants.w3cTraceState]=w3cTraceContext.renderTraceState())}function hasHeadersOption(options){return options&&"object"==typeof options&&options.headers&&"object"==typeof options.headers}function setHeadersOnRequest(clientRequest,span,w3cTraceContext){return!!isItSafeToModifiyHeadersForRequest(clientRequest)&&(clientRequest.setHeader(constants.spanIdHeaderName,span.s),clientRequest.setHeader(constants.traceIdHeaderName,span.t),clientRequest.setHeader(constants.traceLevelHeaderName,"1"),setW3cHeadersOnRequest(clientRequest,w3cTraceContext),!0)}function setW3cHeadersOnRequest(clientRequest,w3cTraceContext){w3cTraceContext&&(clientRequest.setHeader(constants.w3cTraceParent,w3cTraceContext.renderTraceParent()),w3cTraceContext.hasTraceState())&&clientRequest.setHeader(constants.w3cTraceState,w3cTraceContext.renderTraceState())}function isItSafeToModifiyHeadersInOptions(options){var key,keys=Object.keys(options.headers);for(let i=0;i<keys.length;i++)if("authorization"===(key=keys[i]).toLowerCase()&&"string"==typeof options.headers[key]&&0===options.headers[key].indexOf("AWS"))return!1;return!0}function isItSafeToModifiyHeadersForRequest(clientRequest){clientRequest=clientRequest.getHeader("Authorization");return!clientRequest||0!==clientRequest.indexOf("AWS")}function captureRequestHeaders(options,clientRequest,response){options=getExtraHeadersFromOptions(options,extraHttpHeadersToCapture),options=mergeExtraHeadersFromServerResponseOrClientRequest(options,clientRequest,extraHttpHeadersToCapture);return mergeExtraHeadersFromIncomingMessage(options,response,extraHttpHeadersToCapture)}exports.init=function(config){instrument(coreHttpModule,!1),instrument(coreHttpsModule,!0),extraHttpHeadersToCapture=config.tracing.http.extraHttpHeadersToCapture},exports.updateConfig=function(config){extraHttpHeadersToCapture=config.tracing.http.extraHttpHeadersToCapture},exports.activate=function(extraConfig){extraConfig&&extraConfig.tracing&&extraConfig.tracing.http&&Array.isArray(extraConfig.tracing.http.extraHttpHeadersToCapture)&&(extraHttpHeadersToCapture=extraConfig.tracing.http.extraHttpHeadersToCapture),isActive=!0},exports.deactivate=function(){isActive=!1};