Skip to content

Commit

Permalink
Merge pull request #1843 from DataDog/ncreated/RUM-3183/fix-crash-on-…
Browse files Browse the repository at this point in the history
…accessing-allHTTPHeaderFields

RUM-3183 fix: Fix crash on accessing `request.allHTTPHeaderFields`
  • Loading branch information
ncreated authored May 22, 2024
2 parents ab338ea + fa145d1 commit 40ce508
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 27 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- [IMPROVEMENT] Crash errors now include up-to-date global RUM attributes. See [#1834][]
- [FEATURE] `DatadogTrace` now supports OpenTelemetry. See [#1828][]
- [FIX] Fix crash on accessing request.allHTTPHeaderFields. See [#1843][]
- [FEATURE] Support for trace context injection configuration to allow selective injection. See [#1835][]
- [FEATURE] `DatadogWebViewTracking` is now available for Obj-C. See [#1854][]
- [FEATURE] RUM "stop session", "get session ID" and "evaluate feature flag" APIs are now available for Obj-C. See [#1853][]
Expand Down Expand Up @@ -661,6 +662,7 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO
[#1774]: https://github.com/DataDog/dd-sdk-ios/pull/1774
[#1763]: https://github.com/DataDog/dd-sdk-ios/pull/1763
[#1767]: https://github.com/DataDog/dd-sdk-ios/pull/1767
[#1843]: https://github.com/DataDog/dd-sdk-ios/pull/1843
[#1798]: https://github.com/DataDog/dd-sdk-ios/pull/1798
[#1776]: https://github.com/DataDog/dd-sdk-ios/pull/1776
[#1834]: https://github.com/DataDog/dd-sdk-ios/pull/1834
Expand Down
12 changes: 12 additions & 0 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
614798A22A45A48F0095CB02 /* DatadogTrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2C1A55A29C4F2DF00946C31 /* DatadogTrace.framework */; };
614798A32A45A4980095CB02 /* DatadogTrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D25EE93429C4C3C300CE3839 /* DatadogTrace.framework */; };
6147E3B3270486920092BC9F /* TraceConfigurationE2ETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6147E3B2270486920092BC9F /* TraceConfigurationE2ETests.swift */; };
614A708E2BF754D800D9AF42 /* ImmutableRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614A708D2BF754D700D9AF42 /* ImmutableRequest.swift */; };
614A708F2BF754D800D9AF42 /* ImmutableRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614A708D2BF754D700D9AF42 /* ImmutableRequest.swift */; };
614B78ED296D7B63009C6B92 /* DatadogCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614B78EA296D7B63009C6B92 /* DatadogCoreTests.swift */; };
614B78EE296D7B63009C6B92 /* DatadogCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614B78EA296D7B63009C6B92 /* DatadogCoreTests.swift */; };
614B78F1296D7B63009C6B92 /* LowPowerModePublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614B78EC296D7B63009C6B92 /* LowPowerModePublisherTests.swift */; };
Expand All @@ -355,6 +357,8 @@
615192CE2BD6948B0005A782 /* HTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615192CC2BD6948B0005A782 /* HTTPHeadersWriterTests.swift */; };
615192D02BD6B7C90005A782 /* DatadogTracer+InjectAndExtract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615192CF2BD6B7C90005A782 /* DatadogTracer+InjectAndExtract.swift */; };
615192D12BD6B7C90005A782 /* DatadogTracer+InjectAndExtract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615192CF2BD6B7C90005A782 /* DatadogTracer+InjectAndExtract.swift */; };
6156A9072BF75A7C00DF66C3 /* ImmutableRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6156A9062BF75A7C00DF66C3 /* ImmutableRequestTests.swift */; };
6156A9082BF75A7C00DF66C3 /* ImmutableRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6156A9062BF75A7C00DF66C3 /* ImmutableRequestTests.swift */; };
61570005246AADFA00E96950 /* DatadogObjc.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61133BF0242397DA00786299 /* DatadogObjc.framework */; };
615A4A8324A3431600233986 /* Trace+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615A4A8224A3431600233986 /* Trace+objc.swift */; };
615A4A8924A34FD700233986 /* DDTracerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615A4A8824A34FD700233986 /* DDTracerTests.swift */; };
Expand Down Expand Up @@ -2332,6 +2336,7 @@
61494CB024C839460082C633 /* RUMResourceScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMResourceScope.swift; sourceTree = "<group>"; };
61494CB424C864680082C633 /* RUMResourceScopeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMResourceScopeTests.swift; sourceTree = "<group>"; };
61494CB924CB126F0082C633 /* RUMUserActionScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMUserActionScope.swift; sourceTree = "<group>"; };
614A708D2BF754D700D9AF42 /* ImmutableRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmutableRequest.swift; sourceTree = "<group>"; };
614B0A4A24EBC43D00A2A780 /* RUMUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMUser.swift; sourceTree = "<group>"; };
614B0A4E24EBDC6B00A2A780 /* RUMConnectivityInfoProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMConnectivityInfoProvider.swift; sourceTree = "<group>"; };
614B78EA296D7B63009C6B92 /* DatadogCoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatadogCoreTests.swift; sourceTree = "<group>"; };
Expand All @@ -2345,6 +2350,7 @@
615519252461BCE7002A85CF /* Datadog.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Datadog.xcconfig; sourceTree = "<group>"; };
615519262461BCE7002A85CF /* Datadog.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Datadog.local.xcconfig; sourceTree = "<group>"; };
61569894256D0E9A00C6AADA /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
6156A9062BF75A7C00DF66C3 /* ImmutableRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmutableRequestTests.swift; sourceTree = "<group>"; };
6156CB8D24DDA1B5008CB2B2 /* RUMContextProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMContextProvider.swift; sourceTree = "<group>"; };
615950EA291C029700470E0C /* SessionReplayDependencyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionReplayDependencyTests.swift; sourceTree = "<group>"; };
615950ED291C058F00470E0C /* SessionReplayDependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionReplayDependency.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5555,6 +5561,7 @@
D2160CC029C0DED100FAA9A5 /* URLSession */ = {
isa = PBXGroup;
children = (
614A708D2BF754D700D9AF42 /* ImmutableRequest.swift */,
D295A16429F299C9007C0E9A /* URLSessionInterceptor.swift */,
D2160CC129C0DED100FAA9A5 /* URLSessionTaskInterception.swift */,
3CBDE6732AA08C2F00F6A7B6 /* URLSessionInstrumentation.swift */,
Expand Down Expand Up @@ -6239,6 +6246,7 @@
D2BEEDAE2B335C400065F3AC /* URLSessionTaskSwizzlerTests.swift */,
D2BEEDB72B3360F50065F3AC /* URLSessionTaskDelegateSwizzlerTests.swift */,
D270CDDF2B46E5A50002EACD /* URLSessionDataDelegateSwizzlerTests.swift */,
6156A9062BF75A7C00DF66C3 /* ImmutableRequestTests.swift */,
);
path = NetworkInstrumentation;
sourceTree = "<group>";
Expand Down Expand Up @@ -8438,6 +8446,7 @@
D23039F5298D5236001A1FA3 /* AnyEncodable.swift in Sources */,
D2303A00298D5236001A1FA3 /* DatadogExtended.swift in Sources */,
D23039E6298D5236001A1FA3 /* Sysctl.swift in Sources */,
614A708E2BF754D800D9AF42 /* ImmutableRequest.swift in Sources */,
D2160CF429C0EDFC00FAA9A5 /* UploadPerformancePreset.swift in Sources */,
D23039E1298D5236001A1FA3 /* AppState.swift in Sources */,
D2DE63532A30A7CA00441A54 /* CoreRegistry.swift in Sources */,
Expand Down Expand Up @@ -9366,6 +9375,7 @@
D2DA2369298D57AA00C6C7E6 /* AnyEncodable.swift in Sources */,
D2DA236A298D57AA00C6C7E6 /* DatadogExtended.swift in Sources */,
D2DA236B298D57AA00C6C7E6 /* Sysctl.swift in Sources */,
614A708F2BF754D800D9AF42 /* ImmutableRequest.swift in Sources */,
D2160CF529C0EDFC00FAA9A5 /* UploadPerformancePreset.swift in Sources */,
D2DA236C298D57AA00C6C7E6 /* AppState.swift in Sources */,
D2DE63542A30A7CA00441A54 /* CoreRegistry.swift in Sources */,
Expand Down Expand Up @@ -9449,6 +9459,7 @@
D2216EC32A96649500ADAEC8 /* FeatureBaggageTests.swift in Sources */,
D2160CDC29C0DF6700FAA9A5 /* HostsSanitizerTests.swift in Sources */,
615192CD2BD6948B0005A782 /* HTTPHeadersWriterTests.swift in Sources */,
6156A9072BF75A7C00DF66C3 /* ImmutableRequestTests.swift in Sources */,
D2F44FB8299AA1DA0074B0D9 /* DataCompressionTests.swift in Sources */,
D2160CE029C0DF6700FAA9A5 /* URLSessionDelegateAsSuperclassTests.swift in Sources */,
D2EBEE3B29BA163E00B15732 /* B3HTTPHeadersReaderTests.swift in Sources */,
Expand Down Expand Up @@ -9496,6 +9507,7 @@
D2216EC42A96649700ADAEC8 /* FeatureBaggageTests.swift in Sources */,
D2160CDD29C0DF6700FAA9A5 /* HostsSanitizerTests.swift in Sources */,
615192CE2BD6948B0005A782 /* HTTPHeadersWriterTests.swift in Sources */,
6156A9082BF75A7C00DF66C3 /* ImmutableRequestTests.swift in Sources */,
D2F44FB9299AA1DB0074B0D9 /* DataCompressionTests.swift in Sources */,
D2160CE129C0DF6700FAA9A5 /* URLSessionDelegateAsSuperclassTests.swift in Sources */,
D2EBEE3F29BA163F00B15732 /* B3HTTPHeadersReaderTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ extension NetworkInstrumentationFeature {
interception.register(trace: traceContext)
}

if let origin = request.allHTTPHeaderFields?[TracingHTTPHeaders.originField] {
if let origin = request.ddOriginHeaderValue {
interception.register(origin: origin)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-Present Datadog, Inc.
*/

import Foundation

/// An immutable version of `URLRequest`.
///
/// Introduced in response to concerns raised in https://github.com/DataDog/dd-sdk-ios/issues/1638
/// it makes a copy of request attributes, safeguarding against potential thread safety issues arising from concurrent
/// mutations (see more context in https://github.com/DataDog/dd-sdk-ios/pull/1767 ).
public struct ImmutableRequest {
/// The URL of the request.
public let url: URL?
/// The HTTP method of the request.
public let httpMethod: String?
/// The value of `x-datadog-origin` header (if any).
public let ddOriginHeaderValue: String?
/// A reference to the original `URLRequest` object provided during initialization. Direct use is discouraged
/// due to thread safety concerns. Instead, necessary attributes should be accessed through `ImmutableRequest` fields.
public let unsafeOriginal: URLRequest

public init(request: URLRequest) {
self.url = request.url
self.httpMethod = request.httpMethod
// RUM-3183: As observed in https://github.com/DataDog/dd-sdk-ios/issues/1638, accessing `request.allHTTPHeaderFields` is not
// safe and can lead to crashes with undefined root cause. To avoid issues we should prefer `request.value(forHTTPHeaderField:)`
// when interacting with `URLRequest`.
self.ddOriginHeaderValue = request.value(forHTTPHeaderField: TracingHTTPHeaders.originField)
self.unsafeOriginal = request
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public struct URLSessionInterceptor {
// MARK: - Private

private func extractTraceID(from request: URLRequest) -> TraceID? {
guard let headers = request.allHTTPHeaderFields else {
guard let headers = request.allHTTPHeaderFields else { // swiftlint:disable:this unsafe_all_http_header_fields
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,30 +81,6 @@ public struct ResourceCompletion {
}
}

/// An immutable version of `URLRequest`.
///
/// Introduced in response to concerns raised in https://github.com/DataDog/dd-sdk-ios/issues/1638
/// it makes a copy of request attributes, safeguarding against potential thread safety issues arising from concurrent
/// mutations (see more context in https://github.com/DataDog/dd-sdk-ios/pull/1767 ).
public struct ImmutableRequest {
/// The URL of the request.
public let url: URL?
/// The HTTP method of the request.
public let httpMethod: String?
/// The HTTP header fields of the request.
public let allHTTPHeaderFields: [String: String]?
/// A reference to the original `URLRequest` object provided during initialization. Direct use is discouraged
/// due to thread safety concerns. Instead, necessary attributes should be accessed through `ImmutableRequest` fields.
public let unsafeOriginal: URLRequest

public init(request: URLRequest) {
self.url = request.url
self.httpMethod = request.httpMethod
self.allHTTPHeaderFields = request.allHTTPHeaderFields
self.unsafeOriginal = request
}
}

/// Encapsulates key metrics retrieved either from `URLSessionTaskMetrics` or any other relevant data source.
/// Reference: https://developer.apple.com/documentation/foundation/urlsessiontasktransactionmetrics
public struct ResourceMetrics {
Expand Down
4 changes: 3 additions & 1 deletion DatadogInternal/Sources/Upload/URLRequestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ public struct URLRequestBuilder {
}
}

request.allHTTPHeaderFields = headers
headers.forEach { field, value in
request.setValue(value, forHTTPHeaderField: field)
}
return request
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-Present Datadog, Inc.
*/

import XCTest
import TestUtilities
import DatadogInternal

class ImmutableRequestTests: XCTestCase {
func testReadingURL() {
let original: URLRequest = .mockWith(url: "https://example.com")
let immutable = ImmutableRequest(request: original)
XCTAssertEqual(immutable.url, original.url)
}

func testReadingHTTPMethod() {
let original: URLRequest = .mockWith(httpMethod: .mockRandom())
let immutable = ImmutableRequest(request: original)
XCTAssertEqual(immutable.httpMethod, original.httpMethod)
}

func testReadingDatadogOriginHeader() {
let expectedValue: String = .mockRandom(length: 128)
let original: URLRequest = .mockWith(
headers: [
TracingHTTPHeaders.originField: expectedValue
]
)
let immutable = ImmutableRequest(request: original)
XCTAssertEqual(immutable.ddOriginHeaderValue, expectedValue)
}

func testPreservingUnsafeOriginal() {
let original: URLRequest = .mockAny()
let immutable = ImmutableRequest(request: original)
XCTAssertEqual(immutable.unsafeOriginal, original)
}
}
9 changes: 9 additions & 0 deletions tools/lint/sources.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ custom_rules:
- doccomment
message: "`UIApplication.shared` is unavailable in some environments. Check `UIApplication.managedShared`."
severity: error
unsafe_all_http_header_fields: # prevents from using `URLRequest.allHTTPHeaderFields` API
included: Sources
name: "Unsafe API: `URLRequest.allHTTPHeaderFields`"
regex: '\.allHTTPHeaderFields'
excluded_match_kinds:
- comment
- doccomment
message: "`URLRequest.allHTTPHeaderFields` is considered unsafe. Prefer using `URLRequest.value(forHTTPHeaderField:)`. See full context in https://github.com/DataDog/dd-sdk-ios/pull/1843"
severity: error
required_reason_api_name: # prevents from declaring symbols that may conflict with Apple's Required Reason APIs
included: Sources
name: "Required Reason API Conflict"
Expand Down

0 comments on commit 40ce508

Please sign in to comment.