-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RUM-3183 fix: Fix crash on accessing request.allHTTPHeaderFields
#1843
Changes from all commits
1c252a0
9cb2383
8fe0422
fa145d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 It is IMO fine to ignore this warning here as Alternatively, we could use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, this is fine 👍 |
||
return nil | ||
} | ||
|
||
|
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) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🥇 |
||
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" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/suggestion I guess this could be merged with
URLSessionTaskInterception
, or an inner type.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this PR we just move this type to separate file (it was added earlier). While merging types is simple in code, updating tests is harder as it adds more permutations to cover. For the sake of isolated testing, let's keep it this way.