Skip to content

Commit

Permalink
RUMM-2759 Replace firstPartHosts
Browse files Browse the repository at this point in the history
  • Loading branch information
maciejburda committed Dec 2, 2022
1 parent 8e2e35b commit 70a5d10
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 81 deletions.
16 changes: 6 additions & 10 deletions Sources/Datadog/Core/FeaturesConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@ internal struct FeaturesConfiguration {
}

struct URLSessionAutoInstrumentation {
/// First party hosts defined by the user.
let userDefinedFirstPartyHosts: Set<String>

/// First party hosts defined by the user with custom tracing header types.
let userDefinedHostsWithHeaderTypes: [String: Set<TracingHeaderType>]
let userDefinedHostsWithHeaderTypes: FirstPartyHosts

/// URLs used internally by the SDK - they are not instrumented.
let sdkInternalURLs: Set<String>
Expand Down Expand Up @@ -213,7 +210,7 @@ extension FeaturesConfiguration {
}

var sanitizedHosts: Set<String> = []
if let firstPartyHosts = configuration.firstPartyHosts {
if let firstPartyHosts = configuration.firstPartyHostsWithHeaderTypes?.hosts {
sanitizedHosts = hostsSanitizer.sanitized(
hosts: firstPartyHosts,
warningMessage: "The first party host configured for Datadog SDK is not valid"
Expand Down Expand Up @@ -258,18 +255,17 @@ extension FeaturesConfiguration {
}
}

var sanitizedHostsWithHeaderTypes: [String: Set<TracingHeaderType>] = [:]
if let hostsWithHeaderTypes = configuration.firstPartyHostsWithHeaderTypes {
var sanitizedHostsWithHeaderTypes: FirstPartyHosts = [:]
if let firstPartyHosts = configuration.firstPartyHostsWithHeaderTypes {
sanitizedHostsWithHeaderTypes = hostsSanitizer.sanitized(
hostsWithHeaderTypes: hostsWithHeaderTypes,
firstPartyHosts: firstPartyHosts,
warningMessage: "The first party host with header types configured for Datadog SDK is not valid"
)
}

if configuration.firstPartyHosts != nil {
if configuration.firstPartyHostsWithHeaderTypes?.hosts != nil {
if configuration.tracingEnabled || configuration.rumEnabled {
urlSessionAutoInstrumentation = URLSessionAutoInstrumentation(
userDefinedFirstPartyHosts: sanitizedHosts,
userDefinedHostsWithHeaderTypes: sanitizedHostsWithHeaderTypes,
sdkInternalURLs: [
logsEndpoint.url,
Expand Down
10 changes: 5 additions & 5 deletions Sources/Datadog/Core/Utils/HostsSanitizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import Foundation
internal protocol HostsSanitizing {
func sanitized(hosts: Set<String>, warningMessage: String) -> Set<String>
func sanitized(
hostsWithHeaderTypes: [String: Set<TracingHeaderType>],
firstPartyHosts: FirstPartyHosts,
warningMessage: String
) -> [String: Set<TracingHeaderType>]
) -> FirstPartyHosts
}

internal struct HostsSanitizer: HostsSanitizing {
Expand Down Expand Up @@ -60,12 +60,12 @@ internal struct HostsSanitizer: HostsSanitizing {
}

func sanitized(
hostsWithHeaderTypes: [String: Set<TracingHeaderType>],
firstPartyHosts: FirstPartyHosts,
warningMessage: String
) -> [String: Set<TracingHeaderType>] {
) -> FirstPartyHosts {
var warnings: [String] = []

let sanitized: [String: Set<TracingHeaderType>] = hostsWithHeaderTypes.reduce(into: [:]) { partialResult, item in
let sanitized: FirstPartyHosts = firstPartyHosts.reduce(into: [:]) { partialResult, item in
let host = item.key
if host.range(of: urlRegex, options: .regularExpression) != nil {
// if an URL is given instead of the host, take its `host` part
Expand Down
13 changes: 4 additions & 9 deletions Sources/Datadog/DatadogConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,7 @@ extension Datadog {
private(set) var rumEndpoint: RUMEndpoint

private(set) var serviceName: String?
private(set) var firstPartyHosts: Set<String>?
private(set) var firstPartyHostsWithHeaderTypes: [String: Set<TracingHeaderType>]?
private(set) var firstPartyHostsWithHeaderTypes: FirstPartyHosts?
private(set) var logEventMapper: LogEventMapper?
private(set) var spanEventMapper: SpanEventMapper?
private(set) var loggingSamplingRate: Float
Expand Down Expand Up @@ -343,7 +342,6 @@ extension Datadog {
tracesEndpoint: .us1,
rumEndpoint: .us1,
serviceName: nil,
firstPartyHosts: nil,
firstPartyHostsWithHeaderTypes: nil,
spanEventMapper: nil,
loggingSamplingRate: 100.0,
Expand Down Expand Up @@ -530,16 +528,13 @@ extension Datadog {
///
/// - Parameter firstPartyHosts: empty set by default
public func trackURLSession(firstPartyHosts: Set<String> = []) -> Builder {
configuration.firstPartyHosts = firstPartyHosts
configuration.firstPartyHostsWithHeaderTypes = firstPartyHosts.reduce(into: [:], { partialResult, host in
return trackURLSession(firstPartyHostsWithHeaderTypes: firstPartyHosts.reduce(into: [:], { partialResult, host in
partialResult[host] = .init(arrayLiteral: .dd)
})
return self
}))
}

public func trackURLSession(firstPartyHostsWithHeaderTypes: [String: Set<TracingHeaderType>]) -> Builder {
public func trackURLSession(firstPartyHostsWithHeaderTypes: FirstPartyHosts) -> Builder {
configuration.firstPartyHostsWithHeaderTypes = firstPartyHostsWithHeaderTypes
configuration.firstPartyHosts = Set(firstPartyHostsWithHeaderTypes.keys)
return self
}

Expand Down
1 change: 0 additions & 1 deletion Sources/Datadog/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,6 @@ public class Logger: LoggerProtocol {
internal var sendLogsToDatadog = true
internal var consoleLogFormat: ConsoleLogFormat? = nil
internal var datadogReportingThreshold: LogLevel = .debug
internal var samplingRate: Float?

/// Sets the service name that will appear in logs.
/// - Parameter serviceName: the service name (default value is set to application bundle identifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ public enum TracingHeaderType: Hashable {
case b3m
case w3c
}

public typealias FirstPartyHosts = [String: Set<TracingHeaderType>]

extension FirstPartyHosts {
var hosts: Set<String> {
return Set(keys)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@
import Foundation

internal struct TracingHeaderTypesProvider {
private let hostsWithHeaderTypes: [String: Set<TracingHeaderType>]
private let defaultValue: TracingHeaderType = .dd
private let firstPartyHosts: FirstPartyHosts

init(
hostsWithHeaderTypes: [String: Set<TracingHeaderType>]
firstPartyHosts: FirstPartyHosts
) {
self.hostsWithHeaderTypes = hostsWithHeaderTypes
self.firstPartyHosts = firstPartyHosts
}

func tracingHeaderTypes(for url: URL?) -> Set<TracingHeaderType> {
for (key, value) in hostsWithHeaderTypes {
for (key, value) in firstPartyHosts {
let regex = "^(.*\\.)*[.]?\(NSRegularExpression.escapedPattern(for: key))$"
if url?.host?.range(of: regex, options: .regularExpression) != nil {
return value
}
if url?.absoluteString.range(of: regex, options: .regularExpression) != nil {
return value
}
}
return .init(arrayLiteral: defaultValue)
return .init()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ public class URLSessionInterceptor: URLSessionInterceptorType {
configuration: FeaturesConfiguration.URLSessionAutoInstrumentation,
handler: URLSessionInterceptionHandler
) {
self.defaultFirstPartyURLsFilter = FirstPartyURLsFilter(hosts: configuration.userDefinedFirstPartyHosts)
self.defaultFirstPartyURLsFilter = FirstPartyURLsFilter(hosts: configuration.userDefinedHostsWithHeaderTypes.hosts)
self.internalURLsFilter = InternalURLsFilter(urls: configuration.sdkInternalURLs)
self.tracingHeaderTypesProvider = TracingHeaderTypesProvider(
hostsWithHeaderTypes: configuration.userDefinedHostsWithHeaderTypes
firstPartyHosts: configuration.userDefinedHostsWithHeaderTypes
)
self.handler = handler
self.tracingSampler = configuration.tracingSampler
Expand Down
51 changes: 29 additions & 22 deletions Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,10 @@ class FeaturesConfigurationTests: XCTestCase {
let randomCustomTracesEndpoint: URL? = Bool.random() ? .mockRandom() : nil
let randomCustomRUMEndpoint: URL? = Bool.random() ? .mockRandom() : nil

let firstPartyHosts: Set<String> = ["example.com", "foo.eu"]
let firstPartyHostsWithHeaderTypes: FirstPartyHosts = [
"example.com": .init(arrayLiteral: .dd),
"foo.eu": .init(arrayLiteral: .dd)
]
let expectedSDKInternalURLs: Set<String> = [
randomCustomLogsEndpoint?.absoluteString ?? randomDatadogEndpoint.logsEndpoint.url,
randomCustomTracesEndpoint?.absoluteString ?? randomDatadogEndpoint.tracesEndpoint.url,
Expand All @@ -580,7 +583,7 @@ class FeaturesConfigurationTests: XCTestCase {
func createConfiguration(
tracingEnabled: Bool,
rumEnabled: Bool,
firstPartyHosts: Set<String>?
firstPartyHostsWithHeaderTypes: FirstPartyHosts?
) throws -> FeaturesConfiguration {
try FeaturesConfiguration(
configuration: .mockWith(
Expand All @@ -590,7 +593,7 @@ class FeaturesConfigurationTests: XCTestCase {
customLogsEndpoint: randomCustomLogsEndpoint,
customTracesEndpoint: randomCustomTracesEndpoint,
customRUMEndpoint: randomCustomRUMEndpoint,
firstPartyHosts: firstPartyHosts
firstPartyHostsWithHeaderTypes: firstPartyHostsWithHeaderTypes
),
appContext: .mockAny()
)
Expand All @@ -600,9 +603,9 @@ class FeaturesConfigurationTests: XCTestCase {
var configuration = try createConfiguration(
tracingEnabled: true,
rumEnabled: true,
firstPartyHosts: firstPartyHosts
firstPartyHostsWithHeaderTypes: firstPartyHostsWithHeaderTypes
)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.userDefinedFirstPartyHosts, firstPartyHosts)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.userDefinedHostsWithHeaderTypes, firstPartyHostsWithHeaderTypes)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.sdkInternalURLs, expectedSDKInternalURLs)
XCTAssertTrue(configuration.urlSessionAutoInstrumentation!.instrumentTracing)
XCTAssertTrue(configuration.urlSessionAutoInstrumentation!.instrumentRUM)
Expand All @@ -611,9 +614,9 @@ class FeaturesConfigurationTests: XCTestCase {
configuration = try createConfiguration(
tracingEnabled: true,
rumEnabled: false,
firstPartyHosts: firstPartyHosts
firstPartyHostsWithHeaderTypes: firstPartyHostsWithHeaderTypes
)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.userDefinedFirstPartyHosts, firstPartyHosts)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.userDefinedHostsWithHeaderTypes, firstPartyHostsWithHeaderTypes)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.sdkInternalURLs, expectedSDKInternalURLs)
XCTAssertTrue(configuration.urlSessionAutoInstrumentation!.instrumentTracing)
XCTAssertFalse(configuration.urlSessionAutoInstrumentation!.instrumentRUM)
Expand All @@ -622,9 +625,9 @@ class FeaturesConfigurationTests: XCTestCase {
configuration = try createConfiguration(
tracingEnabled: false,
rumEnabled: true,
firstPartyHosts: firstPartyHosts
firstPartyHostsWithHeaderTypes: firstPartyHostsWithHeaderTypes
)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.userDefinedFirstPartyHosts, firstPartyHosts)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.userDefinedHostsWithHeaderTypes, firstPartyHostsWithHeaderTypes)
XCTAssertEqual(configuration.urlSessionAutoInstrumentation?.sdkInternalURLs, expectedSDKInternalURLs)
XCTAssertFalse(configuration.urlSessionAutoInstrumentation!.instrumentTracing)
XCTAssertTrue(configuration.urlSessionAutoInstrumentation!.instrumentRUM)
Expand All @@ -633,7 +636,7 @@ class FeaturesConfigurationTests: XCTestCase {
configuration = try createConfiguration(
tracingEnabled: true,
rumEnabled: true,
firstPartyHosts: nil
firstPartyHostsWithHeaderTypes: nil
)
XCTAssertNil(
configuration.urlSessionAutoInstrumentation,
Expand All @@ -644,7 +647,7 @@ class FeaturesConfigurationTests: XCTestCase {
configuration = try createConfiguration(
tracingEnabled: true,
rumEnabled: true,
firstPartyHosts: []
firstPartyHostsWithHeaderTypes: [:]
)
XCTAssertNotNil(
configuration.urlSessionAutoInstrumentation,
Expand All @@ -658,7 +661,7 @@ class FeaturesConfigurationTests: XCTestCase {
configuration: .mockWith(
tracingEnabled: .random(),
rumEnabled: true,
firstPartyHosts: ["foo.com"],
firstPartyHostsWithHeaderTypes: ["foo.com": .init(arrayLiteral: .dd)],
rumResourceAttributesProvider: { _, _, _, _ in [:] }
),
appContext: .mockAny()
Expand All @@ -667,7 +670,7 @@ class FeaturesConfigurationTests: XCTestCase {
configuration: .mockWith(
tracingEnabled: .random(),
rumEnabled: true,
firstPartyHosts: ["foo.com"],
firstPartyHostsWithHeaderTypes: ["foo.com": .init(arrayLiteral: .dd)],
rumResourceAttributesProvider: nil
),
appContext: .mockAny()
Expand All @@ -685,7 +688,7 @@ class FeaturesConfigurationTests: XCTestCase {

_ = try FeaturesConfiguration(
configuration: .mockWith(
firstPartyHosts: nil,
firstPartyHostsWithHeaderTypes: nil,
rumResourceAttributesProvider: { _, _, _, _ in nil }
),
appContext: .mockAny()
Expand Down Expand Up @@ -742,15 +745,19 @@ class FeaturesConfigurationTests: XCTestCase {
defer { consolePrint = { print($0) } }

// Given
let firstPartyHosts: Set<String> = ["first-party.com"]
let firstPartyHostsWithHeaderTypes: FirstPartyHosts = ["first-party.com": .init(arrayLiteral: .dd)]

// When
let tracingEnabled = false
let rumEnabled = false

// Then
let configuration = try FeaturesConfiguration(
configuration: .mockWith(tracingEnabled: tracingEnabled, rumEnabled: rumEnabled, firstPartyHosts: firstPartyHosts),
configuration: .mockWith(
tracingEnabled: tracingEnabled,
rumEnabled: rumEnabled,
firstPartyHostsWithHeaderTypes: firstPartyHostsWithHeaderTypes
),
appContext: .mockAny()
)

Expand All @@ -770,23 +777,23 @@ class FeaturesConfigurationTests: XCTestCase {

func testWhenFirstPartyHostsAreProvided_itPassesThemToSanitizer() throws {
// When
let firstPartyHosts: Set<String> = [
"https://first-party.com",
"http://api.first-party.com",
"https://first-party.com/v2/api"
let firstPartyHostsWithHeaderTypes: FirstPartyHosts = [
"https://first-party.com": .init(arrayLiteral: .dd),
"http://api.first-party.com": .init(arrayLiteral: .dd),
"https://first-party.com/v2/api": .init(arrayLiteral: .dd)
]

// Then
let mockHostsSanitizer = MockHostsSanitizer()
_ = try FeaturesConfiguration(
configuration: .mockWith(rumEnabled: true, firstPartyHosts: firstPartyHosts),
configuration: .mockWith(rumEnabled: true, firstPartyHostsWithHeaderTypes: firstPartyHostsWithHeaderTypes),
appContext: .mockAny(),
hostsSanitizer: mockHostsSanitizer
)

XCTAssertEqual(mockHostsSanitizer.sanitizations.count, 1)
let sanitization = try XCTUnwrap(mockHostsSanitizer.sanitizations.first)
XCTAssertEqual(sanitization.hosts, firstPartyHosts)
XCTAssertEqual(sanitization.hosts, firstPartyHostsWithHeaderTypes.hosts)
XCTAssertEqual(sanitization.warningMessage, "The first party host configured for Datadog SDK is not valid")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DatadogConfigurationBuilderTests: XCTestCase {
XCTAssertEqual(configuration.tracesEndpoint, .us1)
XCTAssertEqual(configuration.rumEndpoint, .us1)
XCTAssertNil(configuration.serviceName)
XCTAssertNil(configuration.firstPartyHosts)
XCTAssertNil(configuration.firstPartyHostsWithHeaderTypes)
XCTAssertNil(configuration.logEventMapper)
XCTAssertNil(configuration.spanEventMapper)
XCTAssertEqual(configuration.loggingSamplingRate, 100.0)
Expand Down Expand Up @@ -154,7 +154,7 @@ class DatadogConfigurationBuilderTests: XCTestCase {
XCTAssertEqual(configuration.customLogsEndpoint, URL(string: "https://api.custom.logs/")!)
XCTAssertEqual(configuration.customTracesEndpoint, URL(string: "https://api.custom.traces/")!)
XCTAssertEqual(configuration.customRUMEndpoint, URL(string: "https://api.custom.rum/")!)
XCTAssertEqual(configuration.firstPartyHosts, ["example.com"])
XCTAssertEqual(configuration.firstPartyHostsWithHeaderTypes, ["example.com": .init(arrayLiteral: .dd)])
XCTAssertEqual(configuration.loggingSamplingRate, 66)
XCTAssertEqual(configuration.tracingSamplingRate, 75)
XCTAssertEqual(configuration.rumSessionsSamplingRate, 42.5)
Expand Down Expand Up @@ -202,7 +202,7 @@ class DatadogConfigurationBuilderTests: XCTestCase {

let configuration = builder.build()

XCTAssertEqual(configuration.firstPartyHosts, ["example.com"])
XCTAssertEqual(configuration.firstPartyHostsWithHeaderTypes, ["example.com": .init(arrayLiteral: .dd)])
XCTAssertEqual(configuration.logsEndpoint, .eu1)
XCTAssertEqual(configuration.tracesEndpoint, .eu1)
XCTAssertEqual(configuration.rumEndpoint, .eu1)
Expand Down
Loading

0 comments on commit 70a5d10

Please sign in to comment.