Skip to content

Commit 255476f

Browse files
committed
Prototype iOS changes
1 parent da975c1 commit 255476f

File tree

7 files changed

+158
-25
lines changed

7 files changed

+158
-25
lines changed

platform/swift/source/CaptureRustBridge.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,17 @@ bool capture_runtime_bool_variable_value(logger_id logger_id, const char *variab
318318
*/
319319
uint32_t capture_runtime_uint32_variable_value(logger_id logger_id, const char *variable_name, uint32_t default_value);
320320

321+
/*
322+
* Returns the value of a string runtime variable via client runtime configuration.
323+
*
324+
* @param logger_id the logger to check the variable value for.
325+
* @param variable_name the name of the string variable to check.
326+
* @param default_value the default value to use when the relevant configuration entry is missing.
327+
*
328+
* @returns the value of the string variable.
329+
*/
330+
NSString * capture_runtime_string_variable_value(logger_id logger_id, const char *variable_name, const char *default_value);
331+
321332
/*
322333
* Records the session replay record capture screen duration.
323334
*

platform/swift/source/Logging.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
import Foundation
99

10+
protocol RuntimeVariableLoading {
11+
func runtimeValue<T: RuntimeValue>(_ variable: RuntimeVariable<T>) -> T
12+
}
13+
1014
/// A Capture SDK logger interface.
1115
public protocol Logging {
1216
/// Retrieves the session ID.

platform/swift/source/RuntimeVariable.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ protocol RuntimeValue {}
1313

1414
extension Bool: RuntimeValue {}
1515
extension UInt32: RuntimeValue {}
16+
extension String: RuntimeValue {}
1617

1718
/// The runtime variable.
1819
struct RuntimeVariable<T: RuntimeValue> {
@@ -31,6 +32,9 @@ extension RuntimeVariable {
3132
} else if T.self == UInt32.self {
3233
// swiftlint:disable:next force_cast
3334
capture_runtime_uint32_variable_value(loggerID, self.name, self.defaultValue as! UInt32) as! T
35+
} else if T.self == String.self {
36+
// swiftlint:disable:next force_cast
37+
capture_runtime_string_variable_value(loggerID, self.name, self.defaultValue as! String) as! T
3438
} else {
3539
fatalError("unsupported runtime variable type")
3640
}
@@ -111,3 +115,10 @@ extension RuntimeVariable<UInt32> {
111115
defaultValue: 90
112116
)
113117
}
118+
119+
extension RuntimeVariable<String> {
120+
static let tracePropagationMode = RuntimeVariable(
121+
name: "client_feature.ios.trace_propagation_mode",
122+
defaultValue: "w3c"
123+
)
124+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// capture-sdk - bitdrift's client SDK
2+
// Copyright Bitdrift, Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a source available license that can be found in the
5+
// LICENSE file or at:
6+
// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt
7+
8+
import Foundation
9+
import Security
10+
11+
enum URLSessionTracePropagationMode {
12+
case w3c
13+
case b3Single
14+
case b3Multi
15+
case disabled
16+
17+
init(runtimeValue: String) {
18+
switch runtimeValue
19+
.trimmingCharacters(in: .whitespacesAndNewlines)
20+
.lowercased()
21+
{
22+
case "b3-single":
23+
self = .b3Single
24+
case "b3-multi":
25+
self = .b3Multi
26+
case "w3c"
27+
self = .w3c
28+
default:
29+
self = .disabled
30+
}
31+
}
32+
}
33+
34+
enum URLSessionTracePropagation {
35+
static let traceIDField = "_trace_id"
36+
static let traceIDHeader = "x-capture-span-trace-id"
37+
static let legacyTraceIDHeader = "x-capture-span-trace-field-trace_id"
38+
static let traceparentHeader = "traceparent"
39+
static let b3Header = "b3"
40+
static let xB3TraceIDHeader = "X-B3-TraceId"
41+
static let xB3SpanIDHeader = "X-B3-SpanId"
42+
static let xB3SampledHeader = "X-B3-Sampled"
43+
}
44+
45+
struct URLSessionTraceContext {
46+
let traceID: String
47+
let spanID: String
48+
49+
static func make() -> URLSessionTraceContext {
50+
URLSessionTraceContext(traceID: Self.hexString(byteCount: 16), spanID: Self.hexString(byteCount: 8))
51+
}
52+
53+
private static func hexString(byteCount: Int) -> String {
54+
var bytes = [UInt8](repeating: 0, count: byteCount)
55+
let status = SecRandomCopyBytes(kSecRandomDefault, byteCount, &bytes)
56+
if status != errSecSuccess {
57+
for index in bytes.indices {
58+
bytes[index] = UInt8.random(in: UInt8.min ... UInt8.max)
59+
}
60+
}
61+
62+
let hexDigits = Array("0123456789abcdef".utf8)
63+
var output = [UInt8]()
64+
output.reserveCapacity(byteCount * 2)
65+
for byte in bytes {
66+
output.append(hexDigits[Int(byte >> 4)])
67+
output.append(hexDigits[Int(byte & 0x0f)])
68+
}
69+
70+
return String(decoding: output, as: UTF8.self)
71+
}
72+
}

platform/swift/source/integrations/url_session/URLSessionTaskTracker.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ final class URLSessionTaskTracker {
2929

3030
static let shared = URLSessionTaskTracker()
3131

32+
static func enrichExtraFields(
33+
_ extraFields: Fields?,
34+
with traceContext: URLSessionTraceContext?
35+
) -> Fields?
36+
{
37+
guard let traceContext else {
38+
return extraFields
39+
}
40+
41+
var updatedExtraFields = extraFields ?? [:]
42+
updatedExtraFields[URLSessionTracePropagation.traceIDField] = traceContext.traceID
43+
return updatedExtraFields
44+
}
45+
3246
/// Ensures the given task type is supported by our current network instrumentation. Some of these don't
3347
/// have all properties we
3448
/// access, and those are only known at runtime. To play safe, we only check the positive case here we
@@ -62,6 +76,7 @@ final class URLSessionTaskTracker {
6276
if let originalRequest = task.originalRequest {
6377
extraFields = URLSessionIntegration.shared.requestFieldProvider?.provideExtraFields(for: originalRequest)
6478
}
79+
extraFields = Self.enrichExtraFields(extraFields, with: task.cap_traceContext)
6580
guard let requestInfo = HTTPRequestInfo(task: task, extraFields: extraFields) else {
6681
return
6782
}
@@ -92,6 +107,7 @@ final class URLSessionTaskTracker {
92107
for: httpURLResponse
93108
)
94109
}
110+
extraFields = Self.enrichExtraFields(extraFields, with: task.cap_traceContext)
95111
let responseInfo = HTTPResponseInfo(
96112
requestInfo: requestInfo,
97113
response: httpResponse,
@@ -100,6 +116,7 @@ final class URLSessionTaskTracker {
100116
)
101117

102118
URLSessionIntegration.shared.logger?.log(responseInfo, file: nil, line: nil, function: nil)
119+
task.cap_traceContext = nil
103120
}
104121
}
105122
}

platform/swift/source/integrations/url_session/extensions/URLSessionTask+Extensions.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import Foundation
99

1010
private var kRequestInfoKey: UInt8 = 0
11+
private var kTraceContextKey: UInt8 = 0
1112

1213
extension URLSessionTask {
1314
/// The HTTP Request Info associated with a given `URLSessionTask`.
@@ -18,4 +19,11 @@ extension URLSessionTask {
1819
objc_setAssociatedObject(self, &kRequestInfoKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
1920
}
2021
}
22+
23+
var cap_traceContext: URLSessionTraceContext? {
24+
get { objc_getAssociatedObject(self, &kTraceContextKey) as? URLSessionTraceContext }
25+
set {
26+
objc_setAssociatedObject(self, &kTraceContextKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
27+
}
28+
}
2129
}

platform/swift/source/src/bridge.rs

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,20 @@ use anyhow::anyhow;
1717
use bd_api::{Platform, PlatformNetworkManager, PlatformNetworkStream, StreamEvent};
1818
use bd_crash_handler::{CrashReportHook, CrashReportInfo};
1919
use bd_error_reporter::reporter::{
20-
handle_unexpected,
21-
with_handle_unexpected,
22-
with_handle_unexpected_or,
23-
MetadataErrorReporter,
20+
handle_unexpected, with_handle_unexpected, with_handle_unexpected_or, MetadataErrorReporter,
2421
UnexpectedErrorHandler,
2522
};
2623
use bd_logger::{
27-
Block,
28-
CaptureSession,
29-
LogAttributesOverrides,
30-
LogFieldKind,
31-
LogFields,
32-
LogLevel,
33-
MetadataProvider,
34-
ReportProcessingSession,
24+
Block, CaptureSession, LogAttributesOverrides, LogFieldKind, LogFields, LogLevel,
25+
MetadataProvider, ReportProcessingSession,
3526
};
3627
use bd_noop_network::NoopNetwork;
3728
use bd_proto::flatbuffers::report::bitdrift_public::fbs::issue_reporting::v_1;
3829
use bd_proto::protos::logging::payload::LogType;
3930
use objc::rc::StrongPtr;
4031
use objc::runtime::Object;
4132
use platform_shared::javascript_error::{
42-
persist_javascript_error_report,
43-
AppMetadata,
44-
DeviceMetadata,
33+
persist_javascript_error_report, AppMetadata, DeviceMetadata,
4534
};
4635
use platform_shared::metadata::{self, Mobile};
4736
use platform_shared::{LoggerHolder, LoggerId};
@@ -257,7 +246,7 @@ impl<W: StreamWriter + Send> PlatformNetworkStream for SwiftNetworkStream<W> {
257246
return Ok(());
258247
}
259248

260-
slice = &slice[written ..];
249+
slice = &slice[written..];
261250
}
262251
}
263252
}
@@ -570,15 +559,13 @@ extern "C" fn capture_create_logger(
570559
static_metadata,
571560
start_in_sleep_mode,
572561
})
573-
.with_crash_report_hook(
574-
if issue_callback_configuration.is_null() {
575-
None
576-
} else {
577-
Some(Arc::new(IssueCallbackConfigurationHandle::new(
578-
issue_callback_configuration,
579-
)))
580-
},
581-
)
562+
.with_crash_report_hook(if issue_callback_configuration.is_null() {
563+
None
564+
} else {
565+
Some(Arc::new(IssueCallbackConfigurationHandle::new(
566+
issue_callback_configuration,
567+
)))
568+
})
582569
.with_internal_logger(true)
583570
.build()
584571
.map(|(logger, _, future, _)| LoggerHolder::new(logger, future))?;
@@ -672,6 +659,29 @@ extern "C" fn capture_runtime_uint32_variable_value(
672659
)
673660
}
674661

662+
#[no_mangle]
663+
extern "C" fn capture_runtime_string_variable_value(
664+
logger_id: LoggerId<'_>,
665+
variable_name: *const c_char,
666+
default_value: *const c_char,
667+
) -> *const Object {
668+
with_handle_unexpected_or(
669+
move || {
670+
let variable_name = unsafe { CStr::from_ptr(variable_name) }.to_str()?;
671+
let default_value = unsafe { CStr::from_ptr(default_value) }
672+
.to_str()?
673+
.to_string();
674+
let value = logger_id
675+
.runtime_snapshot()
676+
.get_string(variable_name, default_value);
677+
678+
Ok(make_nsstring(&value)?.autorelease())
679+
},
680+
make_empty_nsstring().autorelease(),
681+
"swift runtime string value",
682+
)
683+
}
684+
675685
#[no_mangle]
676686
extern "C" fn capture_write_log(
677687
logger_id: LoggerId<'_>,

0 commit comments

Comments
 (0)