{"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { EventEmitter } from 'node:events';\nimport type { IncomingMessage, RequestOptions, Server, ServerResponse } from 'node:http';\nimport type { Socket } from 'node:net';\nimport { context, createContextKey, propagation } from '@opentelemetry/api';\nimport type { AggregationCounts, Client, Integration, IntegrationFn, Scope } from '@sentry/core';\nimport {\n addNonEnumerableProperty,\n debug,\n generateSpanId,\n getClient,\n getCurrentScope,\n getIsolationScope,\n httpRequestToRequestData,\n stripUrlQueryAndFragment,\n withIsolationScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { patchRequestToCaptureBody } from '../../utils/captureRequestBody';\n\ntype ServerEmit = typeof Server.prototype.emit;\n\n// Inlining this type to not depend on newer TS types\ninterface WeakRefImpl {\n deref(): T | undefined;\n}\n\ntype StartSpanCallback = (next: () => boolean) => boolean;\ntype RequestWithOptionalStartSpanCallback = IncomingMessage & {\n _startSpanCallback?: WeakRefImpl;\n};\n\nconst HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented');\nconst INTEGRATION_NAME = 'Http.Server';\n\nconst clientToRequestSessionAggregatesMap = new Map<\n Client,\n { [timestampRoundedToSeconds: string]: { exited: number; crashed: number; errored: number } }\n>();\n\n// We keep track of emit functions we wrapped, to avoid double wrapping\n// We do this instead of putting a non-enumerable property on the function, because\n// sometimes the property seems to be migrated to forks of the emit function, which we do not want to happen\n// This was the case in the nestjs-distributed-tracing E2E test\nconst wrappedEmitFns = new WeakSet();\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n sessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n}\n\n/**\n * Add a callback to the request object that will be called when the request is started.\n * The callback will receive the next function to continue processing the request.\n */\nexport function addStartSpanCallback(request: RequestWithOptionalStartSpanCallback, callback: StartSpanCallback): void {\n addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback));\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) => {\n const _options = {\n sessions: options.sessions ?? true,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60_000,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onHttpServerRequestStart = ((_data: unknown) => {\n const data = _data as { server: Server };\n\n instrumentServer(data.server, _options);\n }) satisfies ChannelListener;\n\n subscribe('http.server.request.start', onHttpServerRequestStart);\n },\n afterAllSetup(client) {\n if (DEBUG_BUILD && client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests\n * handled via the node `http` module.\n *\n * This version uses OpenTelemetry for context propagation and span management.\n *\n * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry\n */\nexport const httpServerIntegration = _httpServerIntegration as (\n options?: HttpServerIntegrationOptions,\n) => Integration & {\n name: 'HttpServer';\n setupOnce: () => void;\n};\n\n/**\n * Instrument a server to capture incoming requests.\n *\n */\nfunction instrumentServer(\n server: Server,\n {\n ignoreRequestBody,\n maxRequestBodySize,\n sessions,\n sessionFlushingDelayMS,\n }: {\n ignoreRequestBody?: (url: string, request: IncomingMessage) => boolean;\n maxRequestBodySize: 'small' | 'medium' | 'always' | 'none';\n sessions: boolean;\n sessionFlushingDelayMS: number;\n },\n): void {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalEmit: ServerEmit = server.emit;\n\n if (wrappedEmitFns.has(originalEmit)) {\n return;\n }\n\n const newEmit = new Proxy(originalEmit, {\n apply(target, thisArg, args: [event: string, ...args: unknown[]]) {\n // Only traces request events\n if (args[0] !== 'request') {\n return target.apply(thisArg, args);\n }\n\n const client = getClient();\n\n // Make sure we do not double execute our wrapper code, for edge cases...\n // Without this check, if we double-wrap emit, for whatever reason, you'd get two http.server spans (one the children of the other)\n if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY) || !client) {\n return target.apply(thisArg, args);\n }\n\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request');\n\n const isolationScope = getIsolationScope().clone();\n const request = args[1] as IncomingMessage;\n const response = args[2] as ServerResponse & { socket: Socket };\n\n const normalizedRequest = httpRequestToRequestData(request);\n\n // request.ip is non-standard but some frameworks set this\n const ipAddress = (request as { ip?: string }).ip || request.socket?.remoteAddress;\n\n const url = request.url || '/';\n if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) {\n patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME);\n }\n\n // Update the isolation scope, isolate this request\n isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress });\n\n // attempt to update the scope's `transactionName` based on the request URL\n // Ideally, framework instrumentations coming after the HttpInstrumentation\n // update the transactionName once we get a parameterized route.\n const httpMethod = (request.method || 'GET').toUpperCase();\n const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url);\n\n const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`;\n\n isolationScope.setTransactionName(bestEffortTransactionName);\n\n if (sessions && client) {\n recordRequestSession(client, {\n requestIsolationScope: isolationScope,\n response,\n sessionFlushingDelayMS: sessionFlushingDelayMS ?? 60_000,\n });\n }\n\n return withIsolationScope(isolationScope, () => {\n // Set a new propagationSpanId for this request\n // We rely on the fact that `withIsolationScope()` will implicitly also fork the current scope\n // This way we can save an \"unnecessary\" `withScope()` invocation\n getCurrentScope().getPropagationContext().propagationSpanId = generateSpanId();\n\n const ctx = propagation\n .extract(context.active(), normalizedRequest.headers)\n .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);\n\n return context.with(ctx, () => {\n // This is used (optionally) by the httpServerSpansIntegration to attach _startSpanCallback to the request object\n client.emit('httpServerRequest', request, response, normalizedRequest);\n\n const callback = (request as RequestWithOptionalStartSpanCallback)._startSpanCallback?.deref();\n if (callback) {\n return callback(() => target.apply(thisArg, args));\n }\n return target.apply(thisArg, args);\n });\n });\n },\n });\n\n wrappedEmitFns.add(newEmit);\n server.emit = newEmit;\n}\n\n/**\n * Starts a session and tracks it in the context of a given isolation scope.\n * When the passed response is finished, the session is put into a task and is\n * aggregated with other sessions that may happen in a certain time window\n * (sessionFlushingDelayMs).\n *\n * The sessions are always aggregated by the client that is on the current scope\n * at the time of ending the response (if there is one).\n */\n// Exported for unit tests\nexport function recordRequestSession(\n client: Client,\n {\n requestIsolationScope,\n response,\n sessionFlushingDelayMS,\n }: {\n requestIsolationScope: Scope;\n response: EventEmitter;\n sessionFlushingDelayMS?: number;\n },\n): void {\n requestIsolationScope.setSDKProcessingMetadata({\n requestSession: { status: 'ok' },\n });\n response.once('close', () => {\n const requestSession = requestIsolationScope.getScopeData().sdkProcessingMetadata.requestSession;\n\n if (client && requestSession) {\n DEBUG_BUILD && debug.log(`Recorded request session with status: ${requestSession.status}`);\n\n const roundedDate = new Date();\n roundedDate.setSeconds(0, 0);\n const dateBucketKey = roundedDate.toISOString();\n\n const existingClientAggregate = clientToRequestSessionAggregatesMap.get(client);\n const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0 };\n bucket[({ ok: 'exited', crashed: 'crashed', errored: 'errored' } as const)[requestSession.status]]++;\n\n if (existingClientAggregate) {\n existingClientAggregate[dateBucketKey] = bucket;\n } else {\n DEBUG_BUILD && debug.log('Opened new request session aggregate.');\n const newClientAggregate = { [dateBucketKey]: bucket };\n clientToRequestSessionAggregatesMap.set(client, newClientAggregate);\n\n const flushPendingClientAggregates = (): void => {\n clearTimeout(timeout);\n unregisterClientFlushHook();\n clientToRequestSessionAggregatesMap.delete(client);\n\n const aggregatePayload: AggregationCounts[] = Object.entries(newClientAggregate).map(\n ([timestamp, value]) => ({\n started: timestamp,\n exited: value.exited,\n errored: value.errored,\n crashed: value.crashed,\n }),\n );\n client.sendSession({ aggregates: aggregatePayload });\n };\n\n const unregisterClientFlushHook = client.on('flush', () => {\n DEBUG_BUILD && debug.log('Sending request session aggregate due to client flush');\n flushPendingClientAggregates();\n });\n const timeout = setTimeout(() => {\n DEBUG_BUILD && debug.log('Sending request session aggregate due to flushing schedule');\n flushPendingClientAggregates();\n }, sessionFlushingDelayMS).unref();\n }\n }\n });\n}\n"],"names":["createContextKey","addNonEnumerableProperty","subscribe","DEBUG_BUILD","debug","getClient","context","getIsolationScope","httpRequestToRequestData","patchRequestToCaptureBody","stripUrlQueryAndFragment","withIsolationScope","getCurrentScope","generateSpanId","propagation"],"mappings":"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"}