UNPKG

@sentry/core

Version:
282 lines (278 loc) 12.8 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const constants = require('./constants.js'); const debugBuild = require('../../debug-build.js'); const debugLogger = require('../../utils/debug-logger.js'); const currentScopes = require('../../currentScopes.js'); const hasSpansEnabled = require('../../utils/hasSpansEnabled.js'); const request = require('../../utils/request.js'); const patchRequestToCaptureBody = require('./patch-request-to-capture-body.js'); const url = require('../../utils/url.js'); const recordRequestSession = require('./record-request-session.js'); const propagationContext = require('../../utils/propagationContext.js'); const trace = require('../../tracing/trace.js'); const randomSafeContext = require('../../utils/randomSafeContext.js'); const attributes = require('@sentry/conventions/attributes'); const semanticAttributes = require('../../semanticAttributes.js'); const spanstatus = require('../../tracing/spanstatus.js'); const hasSpanStreamingEnabled = require('../../tracing/spans/hasSpanStreamingEnabled.js'); const spanNames = require('../../tracing/spans/spanNames.js'); const op = require('@sentry/conventions/op'); const filterCollectedUrl = require('../../utils/data-collection/filterCollectedUrl.js'); const INTEGRATION_NAME = "Http.Server"; const SPANS_INTEGRATION_NAME = "Http.SentryServerSpans"; const lastSentryEmitMap = /* @__PURE__ */ new WeakMap(); const kRequestMark = /* @__PURE__ */ Symbol.for("sentry_http_server_instrumented"); function markRequest(request) { return !request[kRequestMark] && (request[kRequestMark] = true); } function instrumentServer(options, server) { const currentEmit = server.emit; const instrumentedEmit = lastSentryEmitMap.get(server); if (currentEmit === instrumentedEmit) { return; } const newEmit = new Proxy(currentEmit, { apply(target, thisArg, args) { const [event, ...data] = args; if (event !== "request") { return target.apply(thisArg, args); } const client = currentScopes.getClient(); const [request$1, response] = data; if (!client || !markRequest(request$1)) { return target.apply(thisArg, args); } debugBuild.DEBUG_BUILD && debugLogger.debug.log(INTEGRATION_NAME, "Handling incoming request"); const isolationScope = currentScopes.getIsolationScope().clone(); isolationScope.setClient(client); const ipAddress = request$1.socket?.remoteAddress; const url$1 = request$1.url || "/"; const normalizedRequest = request.httpRequestToRequestData(request$1); const { maxRequestBodySize: configuredBodySize, ignoreRequestBody, sessions = true, sessionFlushingDelayMS = 6e4 } = options; const effectiveBodySize = configuredBodySize ?? (client.getDataCollectionOptions().httpBodies.includes("incomingRequest") ? "medium" : "none"); if (effectiveBodySize !== "none" && !ignoreRequestBody?.(url$1, request$1)) { patchRequestToCaptureBody.patchRequestToCaptureBody(request$1, isolationScope, effectiveBodySize, INTEGRATION_NAME); } isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress }); const httpMethod = (request$1.method || "GET").toUpperCase(); const httpTargetWithoutQueryFragment = url.stripUrlQueryAndFragment(url$1); const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`; isolationScope.setTransactionName(bestEffortTransactionName); if (sessions) { recordRequestSession.recordRequestSession(client, { requestIsolationScope: isolationScope, response, sessionFlushingDelayMS: sessionFlushingDelayMS ?? 6e4 }); } return currentScopes.withIsolationScope(isolationScope, () => { const sentryTrace = normalizedRequest.headers?.["sentry-trace"]; const baggage = normalizedRequest.headers?.["baggage"]; const sentryTraceValue = Array.isArray(sentryTrace) ? sentryTrace[0] : sentryTrace; return trace.continueTrace( { sentryTrace: sentryTraceValue, baggage: Array.isArray(baggage) ? baggage[0] : baggage }, () => { const propagationContext$1 = currentScopes.getCurrentScope().getPropagationContext(); propagationContext$1.propagationSpanId = propagationContext.generateSpanId(); if (!sentryTraceValue) { propagationContext$1.traceId = propagationContext.generateTraceId(); propagationContext$1.sampleRand = randomSafeContext.safeMathRandom(); } response.once("close", () => { isolationScope.setContext("response", { status_code: response.statusCode }); }); const wrap = options.wrapServerEmitRequest; let emitResult = false; if (wrap) { wrap(request$1, response, normalizedRequest, () => { emitResult = target.apply(thisArg, args); }); } else { emitResult = target.apply(thisArg, args); } return emitResult; } ); }); } }); lastSentryEmitMap.set(server, newEmit); server.emit = newEmit; } function getHttpServerSubscriptions(options) { const userWrap = options.wrapServerEmitRequest; const spanWrap = buildServerSpanWrap(options); const effectiveOptions = { ...options, wrapServerEmitRequest(request, response, normalizedRequest, next) { const clientOptions = currentScopes.getClient()?.getOptions(); const createSpans = options.spans ?? (clientOptions ? hasSpansEnabled.hasSpansEnabled(clientOptions) : false); if (createSpans) { spanWrap(request, response, normalizedRequest, next); } else if (userWrap) { userWrap(request, response, normalizedRequest, next); } else { next(); } } }; const onHttpServerRequest = (data) => { const { server } = data; instrumentServer(effectiveOptions, server); }; return { [constants.HTTP_ON_SERVER_REQUEST]: onHttpServerRequest }; } function buildServerSpanWrap(options) { const { wrapServerEmitRequest: userWrap, ignoreIncomingRequests, ignoreStaticAssets = true, onSpanCreated, errorMonitor = "error", onSpanEnd } = options; return (request$1, response, normalizedRequest, next) => { if (typeof __SENTRY_TRACING__ !== "undefined" && !__SENTRY_TRACING__) { return next(); } return userWrap ? userWrap(request$1, response, normalizedRequest, createSpan) : createSpan(); function createSpan() { const isolationScope = currentScopes.getIsolationScope(); const client = isolationScope.getClient(); if (!client) { return next(); } const dataCollectionOptions = client.getDataCollectionOptions(); if (shouldIgnoreSpansForIncomingRequest(request$1, { ignoreStaticAssets, ignoreIncomingRequests })) { debugBuild.DEBUG_BUILD && debugLogger.debug.log(SPANS_INTEGRATION_NAME, "Skipping span creation for incoming request", request$1.url); return next(); } const fullUrl = normalizedRequest.url || request$1.url || "/"; const urlObj = url.parseStringToURLObject(fullUrl); const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : url.stripUrlQueryAndFragment(fullUrl); const method = (request$1.method || "GET").toUpperCase(); const name = hasSpanStreamingEnabled.hasSpanStreamingEnabled(client) ? request$1.method?.toUpperCase() || spanNames.HTTP_SPAN_NAME_FALLBACK : `${method} ${httpTargetWithoutQueryFragment}`; const headers = request$1.headers; const userAgent = headers["user-agent"]; const ips = headers["x-forwarded-for"]; const httpVersion = request$1.httpVersion; const host = headers.host; const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || "localhost"; const scheme = fullUrl.startsWith("https") ? "https" : "http"; const { socket } = request$1; const { localAddress, localPort, remoteAddress, remotePort } = socket ?? {}; const collectClientAddress = client.getDataCollectionOptions().userInfo; const clientAddress = getForwardedClientAddress(ips) ?? remoteAddress; return trace.startSpanManual( { name, attributes: { // Sentry-specific attributes [attributes.SENTRY_OP]: op.HTTP_SERVER, [semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.server", [attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url", [attributes.SENTRY_KIND]: "server", // Network attributes [attributes.SERVER_ADDRESS]: hostname, [attributes.SERVER_PORT]: localPort, [attributes.NETWORK_LOCAL_ADDRESS]: localAddress, [attributes.NETWORK_LOCAL_PORT]: localPort, [attributes.CLIENT_ADDRESS]: collectClientAddress ? clientAddress : void 0, [attributes.CLIENT_PORT]: remotePort, [attributes.NETWORK_PEER_ADDRESS]: collectClientAddress ? remoteAddress : void 0, [attributes.NETWORK_PEER_PORT]: remotePort, [attributes.SENTRY_HTTP_PREFETCH]: isKnownPrefetchRequest(request$1) || void 0, [attributes.URL_FULL]: filterCollectedUrl.filterCollectedUrl(fullUrl, client), [attributes.URL_PATH]: urlObj?.pathname ?? httpTargetWithoutQueryFragment, [attributes.URL_QUERY]: filterCollectedUrl.filterCollectedUrlQuery(url.getUrlQuery(urlObj?.search), client), [attributes.URL_FRAGMENT]: url.getUrlFragment(urlObj?.hash), [attributes.HTTP_REQUEST_METHOD]: method, [attributes.NETWORK_PROTOCOL_NAME]: "http", [attributes.NETWORK_PROTOCOL_VERSION]: httpVersion, [attributes.USER_AGENT_ORIGINAL]: userAgent, [attributes.URL_SCHEME]: scheme, [attributes.NETWORK_TRANSPORT]: httpVersion?.toUpperCase() === "QUIC" ? "udp" : "tcp", "http.request.body.size": request.getContentLengthFromHeaders(request$1.headers), ...request.httpHeadersToSpanAttributes(normalizedRequest.headers || {}, dataCollectionOptions) } }, (span) => { onSpanCreated?.(span, request$1, response); let isEnded = false; function endSpan(status) { if (isEnded) { return; } isEnded = true; span.setAttributes({ "http.response.status_text": response.statusMessage?.toUpperCase(), [attributes.HTTP_RESPONSE_STATUS_CODE]: response.statusCode, ...request.httpHeadersToSpanAttributes(request.headersToDict(response.headers), dataCollectionOptions, "response") }); span.setStatus(status); onSpanEnd?.(span, request$1, response); span.end(); } response.once("close", () => { endSpan(spanstatus.getSpanStatusFromHttpCode(response.statusCode)); }); response.once(errorMonitor, () => { const httpStatus = spanstatus.getSpanStatusFromHttpCode(response.statusCode); endSpan(httpStatus.code === spanstatus.SPAN_STATUS_ERROR ? httpStatus : { code: spanstatus.SPAN_STATUS_ERROR }); }); next(); } ); } }; } function getForwardedClientAddress(forwardedFor) { return typeof forwardedFor === "string" ? forwardedFor.split(",")[0]?.trim() || void 0 : void 0; } function shouldIgnoreSpansForIncomingRequest(request, { ignoreStaticAssets, ignoreIncomingRequests }) { const urlPath = request.url; const method = request.method?.toUpperCase(); if (method === "OPTIONS" || method === "HEAD" || !urlPath) { return true; } if (ignoreStaticAssets && method === "GET" && isStaticAssetRequest(urlPath)) { return true; } if (ignoreIncomingRequests?.(urlPath, request)) { return true; } return false; } function isStaticAssetRequest(urlPath) { const path = url.stripUrlQueryAndFragment(urlPath); if (path.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) { return true; } if (path.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) { return true; } return false; } function isKnownPrefetchRequest(req) { return req.headers["next-router-prefetch"] === "1"; } exports.getHttpServerSubscriptions = getHttpServerSubscriptions; exports.instrumentServer = instrumentServer; exports.isStaticAssetRequest = isStaticAssetRequest; //# sourceMappingURL=server-subscription.js.map