From e78c0a28aa9932df540db2b4563c548643db9969 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Thu, 12 Mar 2026 11:13:08 +0000 Subject: [PATCH] Filter out requests originating at the JS layer from iOS network instrumentation --- example/ios/Podfile.lock | 265 ++---------------- packages/core/ios/Sources/Attributes.swift | 5 + .../Sources/DdSdkNativeInitialization.swift | 9 +- packages/core/ios/Tests/DdSdkTests.swift | 25 ++ packages/core/ios/Tests/RUMMocks.swift | 15 + .../distributedTracing/headers.ts | 2 + .../requestProxy/XHRProxy/XHRProxy.ts | 11 +- .../XHRProxy/__tests__/XHRProxy.test.ts | 55 +++- 8 files changed, 135 insertions(+), 252 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 550beabd9..ec7d99796 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -21,26 +21,7 @@ PODS: - DatadogRUM (= 3.8.2) - DatadogTrace (= 3.8.2) - DatadogWebViewTracking (= 3.8.2) - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - DatadogSDKReactNative/Tests (3.1.2): - DatadogCore (= 3.8.2) - DatadogCrashReporting (= 3.8.2) @@ -49,26 +30,7 @@ PODS: - DatadogRUM (= 3.8.2) - DatadogTrace (= 3.8.2) - DatadogWebViewTracking (= 3.8.2) - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - DatadogSDKReactNativeSessionReplay (3.1.2): - DatadogSDKReactNative - DatadogSessionReplay (= 3.8.2) @@ -120,52 +82,14 @@ PODS: - DatadogInternal (= 3.8.2) - DatadogSDKReactNative - DatadogWebViewTracking (= 3.8.2) - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - DatadogSDKReactNativeWebView/Tests (3.1.2): - DatadogInternal (= 3.8.2) - DatadogSDKReactNative - DatadogWebViewTracking (= 3.8.2) - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - react-native-webview - - React-NativeModulesApple - - React-RCTFabric - React-RCTText - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - DatadogSessionReplay (3.8.2): - DatadogInternal (= 3.8.2) - DatadogTrace (3.8.2): @@ -233,7 +157,6 @@ PODS: - React-RCTText (= 0.76.9) - React-RCTVibration (= 0.76.9) - React-callinvoker (0.76.9) - - React-Codegen (0.1.0) - React-Core (0.76.9): - glog - hermes-engine @@ -1476,71 +1399,7 @@ PODS: - react-native-crash-tester (0.2.3): - React-Core - react-native-safe-area-context (5.1.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - react-native-safe-area-context/common (= 5.1.0) - - react-native-safe-area-context/fabric (= 5.1.0) - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-safe-area-context/common (5.1.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-safe-area-context/fabric (5.1.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - react-native-safe-area-context/common - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - react-native-webview (13.14.2): - DoubleConversion - glog @@ -1835,87 +1694,20 @@ PODS: - React-perflogger - React-utils (= 0.76.9) - ReactNativeNavigation (8.0.0-snapshot.1658): - - DoubleConversion - - glog - - hermes-engine - HMSegmentedControl - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React - - React-Codegen - React-Core - React-CoreModules - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - React-RCTImage - React-RCTText - - React-rendererdebug - - React-rncore - - React-runtimeexecutor - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - ReactNativeNavigation/Core (= 8.0.0-snapshot.1658) - - Yoga - ReactNativeNavigation/Core (8.0.0-snapshot.1658): - - DoubleConversion - - glog - - hermes-engine - HMSegmentedControl - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React - - React-Codegen - React-Core - React-CoreModules - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - React-RCTImage - React-RCTText - - React-rendererdebug - - React-rncore - - React-runtimeexecutor - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - RNCAsyncStorage (2.2.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - RNGestureHandler (2.24.0): - DoubleConversion - glog @@ -1938,29 +1730,6 @@ PODS: - ReactCommon/turbomodule/core - Yoga - RNScreens (4.5.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.10.14.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-RCTImage - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNScreens/common (= 4.5.0) - - Yoga - - RNScreens/common (4.5.0): - DoubleConversion - glog - hermes-engine @@ -2080,7 +1849,6 @@ SPEC REPOS: - HMSegmentedControl - KSCrash - OpenTelemetry-Swift-Api - - React-Codegen - SocketRocket EXTERNAL SOURCES: @@ -2242,9 +2010,9 @@ SPEC CHECKSUMS: DatadogInternal: a4640f67e249c9b4608e806cf1c8c10b17f66626 DatadogLogs: 55e2c1c403e92f952f8ce451854c81f476b04d89 DatadogRUM: 738b6b448df9af58ba0455451e628ac81040be04 - DatadogSDKReactNative: 182ec2015f201c853b707b527868258562f2c237 - DatadogSDKReactNativeSessionReplay: 5d7a92be2396b614593f13878fd150481648ed26 - DatadogSDKReactNativeWebView: acc25a85d0328070ff3cc184b30694d15319772c + DatadogSDKReactNative: deaa371701ba3e0e5587aec673642988698fced7 + DatadogSDKReactNativeSessionReplay: bfdcb981d5d10e18f710b0ff7d1bc0a3de872bda + DatadogSDKReactNativeWebView: 18c3b1908f4221cf5aa824c9156d93d6133f7946 DatadogSessionReplay: 4edcb9997acc8c378048932ffe0dd1e6bf2c18ca DatadogTrace: faefc639bc2dd11a8ebbf9cbddf830b68eb62d1d DatadogWebViewTracking: 50afac044e278ac0165c082b6b65dd8327fdefb4 @@ -2263,21 +2031,20 @@ SPEC CHECKSUMS: RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f React: 4641770499c39f45d4e7cde1eba30e081f9d8a3d React-callinvoker: 4bef67b5c7f3f68db5929ab6a4d44b8a002998ea - React-Codegen: 4b8b4817cea7a54b83851d4c1f91f79aa73de30a React-Core: 0a06707a0b34982efc4a556aff5dae4b22863455 React-CoreModules: 907334e94314189c2e5eed4877f3efe7b26d85b0 React-cxxreact: 3a1d5e8f4faa5e09be26614e9c8bbcae8d11b73d React-debug: 817160c07dc8d24d020fbd1eac7b3558ffc08964 - React-defaultsnativemodule: 814830ccbc3fb08d67d0190e63b179ee4098c67b - React-domnativemodule: 270acf94bd0960b026bc3bfb327e703665d27fb4 + React-defaultsnativemodule: a965cb39fb0a79276ab611793d39f52e59a9a851 + React-domnativemodule: d647f94e503c62c44f54291334b1aa22a30fa08b React-Fabric: 64586dc191fc1c170372a638b8e722e4f1d0a09b React-FabricComponents: b0ebd032387468ea700574c581b139f57a7497fb React-FabricImage: 81f0e0794caf25ad1224fa406d288fbc1986607f React-featureflags: f2792b067a351d86fdc7bec23db3b9a2f2c8d26c - React-featureflagsnativemodule: 0d7091ae344d6160c0557048e127897654a5c00f + React-featureflagsnativemodule: 95a02d895475de8ace78fedd76143866838bb720 React-graphics: cbebe910e4a15b65b0bff94a4d3ed278894d6386 React-hermes: ec18c10f5a69d49fb9b5e17ae95494e9ea13d4d3 - React-idlecallbacksnativemodule: 6b84add48971da9c40403bd1860d4896462590f2 + React-idlecallbacksnativemodule: 0c1ae840cc5587197cd926a3cb76828ad059d116 React-ImageManager: f2a4c01c2ccb2193e60a20c135da74c7ca4d36f2 React-jserrorhandler: 61d205b5a7cbc57fed3371dd7eed48c97f49fc64 React-jsi: 95f7676103137861b79b0f319467627bcfa629ee @@ -2286,19 +2053,19 @@ SPEC CHECKSUMS: React-jsitracing: 3758cdb155ea7711f0e77952572ea62d90c69f0b React-logger: dbca7bdfd4aa5ef69431362bde6b36d49403cb20 React-Mapbuffer: 6efad4a606c1fae7e4a93385ee096681ef0300dc - React-microtasksnativemodule: a645237a841d733861c70b69908ab4a1707b52ad + React-microtasksnativemodule: 8732b71aa66045da4bb341ddee1bb539f71e5f38 react-native-crash-tester: 3ffaa64141427ca362079cb53559fe9a532487ae - react-native-safe-area-context: 511649fbcdb7b88d29660aa5c0936b3cd03c935d - react-native-webview: 7e0507e49529e26404dd5a37db31f2f8ad3d19b9 + react-native-safe-area-context: 04803a01f39f31cc6605a5531280b477b48f8a88 + react-native-webview: 1e12de2fad74c17b4f8b1b53ebd1e3baa0148d71 React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 React-NativeModulesApple: 958d4f6c5c2ace4c0f427cf7ef82e28ae6538a22 React-perflogger: 9b4f13c0afe56bc7b4a0e93ec74b1150421ee22d React-performancetimeline: 359db1cb889aa0282fafc5838331b0987c4915a9 React-RCTActionSheet: aacf2375084dea6e7c221f4a727e579f732ff342 React-RCTAnimation: d8c82deebebe3aaf7a843affac1b57cb2dc073d4 - React-RCTAppDelegate: 1774aa421a29a41a704ecaf789811ef73c4634b6 + React-RCTAppDelegate: 6c0377d9c4058773ea7073bb34bb9ebd6ddf5a84 React-RCTBlob: 70a58c11a6a3500d1a12f2e51ca4f6c99babcff8 - React-RCTFabric: 731cda82aed592aacce2d32ead69d78cde5d9274 + React-RCTFabric: 7eb6dd2c8fda98cb860a572e3f4e4eb60d62c89e React-RCTImage: 5e9d655ba6a790c31e3176016f9b47fd0978fbf0 React-RCTLinking: 2a48338252805091f7521eaf92687206401bdf2a React-RCTNetwork: 0c1282b377257f6b1c81934f72d8a1d0c010e4c3 @@ -2317,10 +2084,10 @@ SPEC CHECKSUMS: React-utils: 54df9ada708578c8ad40d92895d6fed03e0e8a9e ReactCodegen: 21a52ccddc6479448fc91903a437dd23ddc7366c ReactCommon: bfd3600989d79bc3acbe7704161b171a1480b9fd - ReactNativeNavigation: 3ddd319cc013c5e2b3bbe90b1f7fadaa5e8aa66f - RNCAsyncStorage: 8724c3be379a3d5a02ba83e276d79c2899f8d53c - RNGestureHandler: db5f279b66899cb96c8b9773c0eaac2117fe0e8a - RNScreens: 638e0b7f980df30dbb57e18d42c3b07b9fb4b92e + ReactNativeNavigation: 50c1eef68b821e7265eff3a391d27ed18fdce459 + RNCAsyncStorage: 23e56519cc41d3bade3c8d4479f7760cb1c11996 + RNGestureHandler: 950dfa674dbf481460ca389c65b9036ac4ab8ada + RNScreens: 606ab1cf68162f7ba0d049a31f2a84089a6fffb4 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a diff --git a/packages/core/ios/Sources/Attributes.swift b/packages/core/ios/Sources/Attributes.swift index 25dd8df20..80920ab2b 100644 --- a/packages/core/ios/Sources/Attributes.swift +++ b/packages/core/ios/Sources/Attributes.swift @@ -99,6 +99,11 @@ internal struct InternalConfigurationAttributes { /// Expects `NSString?` value static let versionSuffix = "_dd.version_suffix" + /// Header key set by the JS layer to indicate the request origin. + static let trackedByHeaderKey = "x-datadog-tracked-by" + /// Header value set by the JS layer to indicate the request was tracked by React Native. + static let trackedByHeaderValue = "react-native" + /// Custom attribute of the resource passed from bridge SDK. /// It flags the resource as dropped by the user so it can be dropped in the resource mapper. /// Expects `Bool` value. diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index 176ad7d9c..89d6f9caf 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -169,7 +169,14 @@ public class DdSdkNativeInitialization: NSObject { sampleRate: Float( configuration.rumConfiguration?.resourceTraceSampleRate ?? DefaultConfiguration.resourceTraceSampleRate) - ) + ), + resourceAttributesProvider: { request, _, _, _ in + let trackedBy = request.value(forHTTPHeaderField: InternalConfigurationAttributes.trackedByHeaderKey) + if trackedBy == InternalConfigurationAttributes.trackedByHeaderValue { + return [InternalConfigurationAttributes.dropResource: true] + } + return nil + } ) } diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index d5c65b9ef..c01d4b2d5 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -1556,6 +1556,31 @@ class DdSdkTests: XCTestCase { XCTAssertNotNil(mappedEvent) } + func testResourceAttributesProviderMarksTrackedByRequestsAsDropped() throws { + let rumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.firstPartyHosts = ["example.com": [.datadog]] + let configuration: DdSdkConfiguration = .mockAny(rumConfiguration: rumConfiguration) + + let ddConfig = DdSdkNativeInitialization().buildRumConfiguration( + configuration: configuration + ) + + let resourceAttributesProvider = try XCTUnwrap( + ddConfig.urlSessionTracking?.resourceAttributesProvider + ) + + // Request with x-datadog-tracked-by header should be marked for dropping + let trackedRequest = URLRequest.mockWith(headerFields: [InternalConfigurationAttributes.trackedByHeaderKey: InternalConfigurationAttributes.trackedByHeaderValue]) + let trackedAttributes = resourceAttributesProvider(trackedRequest, nil, nil, nil) + XCTAssertNotNil(trackedAttributes) + XCTAssertEqual(trackedAttributes?[InternalConfigurationAttributes.dropResource] as? Bool, true) + + // Request without the header should not be marked for dropping + let untrackedRequest = URLRequest.mockWith(headerFields: [:]) + let untrackedAttributes = resourceAttributesProvider(untrackedRequest, nil, nil, nil) + XCTAssertNil(untrackedAttributes) + } + func testDropsActionMarkedAsDropped() throws { let configuration: DdSdkConfiguration = .mockAny() diff --git a/packages/core/ios/Tests/RUMMocks.swift b/packages/core/ios/Tests/RUMMocks.swift index 01d0f9d0d..c07f0fa7f 100644 --- a/packages/core/ios/Tests/RUMMocks.swift +++ b/packages/core/ios/Tests/RUMMocks.swift @@ -513,3 +513,18 @@ extension RUMActionEvent: RandomMockable { ) } } + +// MARK: - URLRequest Mocks + +extension URLRequest { + static func mockWith( + url: URL = URL(string: "https://example.com")!, + headerFields: [String: String] = [:] + ) -> URLRequest { + var request = URLRequest(url: url) + for (key, value) in headerFields { + request.setValue(value, forHTTPHeaderField: key) + } + return request + } +} diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts index 4301fc027..2a75d9a20 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts @@ -8,6 +8,8 @@ */ export const SAMPLING_PRIORITY_HEADER_KEY = 'x-datadog-sampling-priority'; export const ORIGIN_HEADER_KEY = 'x-datadog-origin'; +export const TRACKED_BY_HEADER_KEY = 'x-datadog-tracked-by'; +export const TRACKED_BY_HEADER_VALUE = 'react-native'; export const ORIGIN_RUM = 'rum'; export const TRACE_ID_HEADER_KEY = 'x-datadog-trace-id'; export const PARENT_ID_HEADER_KEY = 'x-datadog-parent-id'; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts index 6636944f8..c1c4ddc37 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts @@ -13,7 +13,11 @@ import { import type { DdRumResourceTracingAttributes } from '../../distributedTracing/distributedTracingAttributes'; import { getTracingHeadersFromAttributes } from '../../distributedTracing/distributedTracingHeaders'; import { getTracingAttributes } from '../../distributedTracing/distributedTracing'; -import { BAGGAGE_HEADER_KEY } from '../../distributedTracing/headers'; +import { + BAGGAGE_HEADER_KEY, + TRACKED_BY_HEADER_KEY, + TRACKED_BY_HEADER_VALUE +} from '../../distributedTracing/headers'; import { DATADOG_GRAPH_QL_OPERATION_NAME_HEADER, DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER, @@ -161,6 +165,11 @@ const proxySend = (providers: XHRProxyProviders): void => { if (baggageHeader) { this.setRequestHeader(DATADOG_BAGGAGE_HEADER, baggageHeader); } + + this.setRequestHeader( + TRACKED_BY_HEADER_KEY, + TRACKED_BY_HEADER_VALUE + ); } proxyOnReadyStateChange(this, providers); diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index b82d73127..505201a7d 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -33,7 +33,9 @@ import { ORIGIN_HEADER_KEY, TRACESTATE_HEADER_KEY, TAGS_HEADER_KEY, - BAGGAGE_HEADER_KEY + BAGGAGE_HEADER_KEY, + TRACKED_BY_HEADER_KEY, + TRACKED_BY_HEADER_VALUE } from '../../../distributedTracing/headers'; import { DATADOG_GRAPH_QL_OPERATION_NAME_HEADER, @@ -452,6 +454,57 @@ describe('XHRProxy', () => { ); }); + it('adds the x-datadog-tracked-by header for first party host requests', async () => { + // GIVEN + const method = 'GET'; + const url = 'https://api.example.com/v2/user'; + xhrProxy.onTrackingStart({ + tracingSamplingRate: 100, + firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([ + { + match: 'api.example.com', + propagatorTypes: [PropagatorType.DATADOG] + } + ]) + }); + + // WHEN + const xhr = new XMLHttpRequestMock(); + xhr.open(method, url); + xhr.send(); + xhr.notifyResponseArrived(); + xhr.complete(200, 'ok'); + await flushPromises(); + + // THEN + expect(xhr.requestHeaders.get(TRACKED_BY_HEADER_KEY)).toBe( + TRACKED_BY_HEADER_VALUE + ); + }); + + it('adds the x-datadog-tracked-by header for non-first party host requests', async () => { + // GIVEN + const method = 'GET'; + const url = 'https://api.example.com/v2/user'; + xhrProxy.onTrackingStart({ + tracingSamplingRate: 100, + firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([]) + }); + + // WHEN + const xhr = new XMLHttpRequestMock(); + xhr.open(method, url); + xhr.send(); + xhr.notifyResponseArrived(); + xhr.complete(200, 'ok'); + await flushPromises(); + + // THEN + expect(xhr.requestHeaders.get(TRACKED_BY_HEADER_KEY)).toBe( + TRACKED_BY_HEADER_VALUE + ); + }); + it('adds tracecontext request headers when the host is instrumented with tracecontext and request is sampled', async () => { // GIVEN const method = 'GET';