import { getCurrentHub } from '@sentry/core' ; import { Integration , Span } from '@sentry/types' ; import { fill , logger , parseSemver } from '@sentry/utils' ; import * as http from 'http' ; import * as https from 'https' ; import { cleanSpanDescription , extractUrl , isSentryRequest , normalizeRequestArgs , RequestMethod , RequestMethodArgs , } from './utils/http' ; const NODE_VERSION = parseSemver ( process . versions . node ) ; /** http module integration */ export class Http implements Integration { inheritDoc public static id : string = 'Http' ; inheritDoc public name : string = Http . id ; inheritDoc private readonly _breadcrumbs : boolean ; inheritDoc private readonly _tracing : boolean ; inheritDoc public constructor ( options = { } ) { this . _breadcrumbs = typeof options . breadcrumbs === 'undefined' ? true : options . breadcrumbs ; this . _tracing = typeof options . tracing === 'undefined' ? false : options . tracing ; } inheritDoc public setupOnce ( ) { if ( ! this . _breadcrumbs && ! this . _tracing ) { return ; } const wrappedHandlerMaker = _createWrappedRequestMethodFactory ( this . _breadcrumbs , this . _tracing ) ; const httpModule = require ( 'http' ) ; fill ( httpModule , 'get' , wrappedHandlerMaker ) ; fill ( httpModule , 'request' , wrappedHandlerMaker ) ; if ( NODE_VERSION . major && NODE_VERSION . major > 8 ) { const httpsModule = require ( 'https' ) ; fill ( httpsModule , 'get' , wrappedHandlerMaker ) ; fill ( httpsModule , 'request' , wrappedHandlerMaker ) ; } } } type OriginalRequestMethod = RequestMethod ; type WrappedRequestMethod = RequestMethod ; type WrappedRequestMethodFactory = ( original : OriginalRequestMethod ) => WrappedRequestMethod ; param breadcrumbsEnabled param tracingEnabled returns function _createWrappedRequestMethodFactory ( breadcrumbsEnabled , tracingEnabled ) { return function wrappedRequestMethodFactory ( originalRequestMethod ) : WrappedRequestMethod { return function wrappedMethod ( this , ... args ) : http . ClientRequest { const httpModule = this ; const requestArgs = normalizeRequestArgs ( this , args ) ; const requestOptions = requestArgs [ 0 ] ; const requestUrl = extractUrl ( requestOptions ) ; if ( isSentryRequest ( requestUrl ) ) { return originalRequestMethod . apply ( httpModule , requestArgs ) ; } let span ; let parentSpan ; const scope = getCurrentHub ( ) . getScope ( ) ; if ( scope && tracingEnabled ) { parentSpan = scope . getSpan ( ) ; if ( parentSpan ) { span = parentSpan . startChild ( { description : `${ requestOptions . method || 'GET' } ${ requestUrl }` , op : 'http.client' , } ) ; const sentryTraceHeader = span . toTraceparent ( ) ; logger . log ( `[Tracing] Adding sentry-trace header ${ sentryTraceHeader } to outgoing request to ${ requestUrl }: ` ) ; requestOptions . headers = { ... requestOptions . headers , 'sentry-trace' : sentryTraceHeader } ; } } return originalRequestMethod . apply ( httpModule , requestArgs ) . once ( 'response' , function ( this , res ) : void { const req = this ; if ( breadcrumbsEnabled ) { addRequestBreadcrumb ( 'response' , requestUrl , req , res ) ; } if ( tracingEnabled && span ) { if ( res . statusCode ) { span . setHttpStatus ( res . statusCode ) ; } span . description = cleanSpanDescription ( span . description , requestOptions , req ) ; span . finish ( ) ; } } ) . once ( 'error' , function ( this ) : void { const req = this ; if ( breadcrumbsEnabled ) { addRequestBreadcrumb ( 'error' , requestUrl , req ) ; } if ( tracingEnabled && span ) { span . setHttpStatus ( 500 ) ; span . description = cleanSpanDescription ( span . description , requestOptions , req ) ; span . finish ( ) ; } } ) ; } ; } ; } /**
 * Captures Breadcrumb based on provided request/response pair
 */ function addRequestBreadcrumb ( event , url , req , res ? ) { if ( ! getCurrentHub ( ) . getIntegration ( Http ) ) { return ; } getCurrentHub ( ) . addBreadcrumb ( { category : 'http' , data : { method : req . method , status_code : res && res . statusCode , url , } , type : 'http' , } , { event , request : req , response : res , } ) ; }