@onekeyfe/react-native-sni-connect 3.0.71 → 3.0.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -1
- package/SniConnect.podspec +2 -2
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/sniconnect/SniConnectLogger.kt +32 -1
- package/android/src/main/java/com/sniconnect/SniConnectModule.kt +508 -68
- package/android/src/main/java/com/sniconnect/SniConnectValidation.kt +259 -13
- package/android/src/test/java/com/sniconnect/SniConnectValidationTest.kt +257 -0
- package/ios/SniConnect.mm +15 -0
- package/ios/SniConnect.swift +119 -22
- package/ios/SniConnectClient.swift +511 -174
- package/ios/SniConnectCore.swift +228 -0
- package/ios/SniConnectLog.swift +39 -0
- package/ios/SniConnectValidation.swift +243 -1
- package/ios/Tests/SniConnectValidationTests/SniConnectValidationTests.swift +325 -0
- package/lib/module/index.js +3 -0
- package/lib/typescript/src/NativeSniConnect.d.ts +25 -4
- package/lib/typescript/src/index.d.ts +2 -1
- package/package.json +9 -1
- package/src/NativeSniConnect.ts +35 -4
- package/src/index.tsx +12 -1
package/ios/SniConnect.swift
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import CFNetwork
|
|
2
3
|
import React
|
|
3
4
|
|
|
4
5
|
private enum SniConnectError: Error {
|
|
@@ -25,7 +26,12 @@ final class SniConnectImpl: NSObject {
|
|
|
25
26
|
let parsed = try Self.parseDictionary(config)
|
|
26
27
|
handleRequest(config: parsed, resolve: resolve, reject: reject)
|
|
27
28
|
} catch {
|
|
28
|
-
SniConnectLog.error("
|
|
29
|
+
SniConnectLog.error(SniConnectLog.event("sni_request_result", [
|
|
30
|
+
("result", "error"),
|
|
31
|
+
("code", "SNI_INVALID_CONFIG"),
|
|
32
|
+
("nativeErrorClass", String(describing: type(of: error))),
|
|
33
|
+
("stage", "parse_config"),
|
|
34
|
+
]))
|
|
29
35
|
reject("SNI_INVALID_CONFIG", "\(error)", error)
|
|
30
36
|
}
|
|
31
37
|
}
|
|
@@ -38,29 +44,41 @@ final class SniConnectImpl: NSObject {
|
|
|
38
44
|
do {
|
|
39
45
|
try Self.validate(config)
|
|
40
46
|
} catch {
|
|
41
|
-
SniConnectLog.error("
|
|
47
|
+
SniConnectLog.error(SniConnectLog.event("sni_request_result", [
|
|
48
|
+
("result", "error"),
|
|
49
|
+
("code", "SNI_INVALID_CONFIG"),
|
|
50
|
+
("nativeErrorClass", String(describing: type(of: error))),
|
|
51
|
+
("stage", "validate_config"),
|
|
52
|
+
("requestIdHash", SniConnectLog.shortHash(config.requestId)),
|
|
53
|
+
("hostname", config.hostname.lowercased()),
|
|
54
|
+
("ipHash", SniConnectLog.shortHash(config.ip)),
|
|
55
|
+
("ipFamily", SniConnectLog.ipFamily(config.ip)),
|
|
56
|
+
("method", config.method.uppercased()),
|
|
57
|
+
("timeoutMs", Int(config.effectiveTotalTimeout)),
|
|
58
|
+
]))
|
|
42
59
|
reject("SNI_INVALID_CONFIG", "\(error)", error)
|
|
43
60
|
return
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
// Create the task and register it synchronously before JS can cancel it.
|
|
64
|
+
// Unregistering is owned by the result waiter below, so even a task that
|
|
65
|
+
// completes immediately cannot unregister before it has been registered.
|
|
47
66
|
let task = Task { () -> SniConnectClient.Response in
|
|
48
67
|
return try await client.performRequest(config: config)
|
|
49
68
|
}
|
|
50
69
|
|
|
51
|
-
|
|
52
|
-
if let requestId = config.requestId {
|
|
53
|
-
client.registerTask(task, for: requestId)
|
|
54
|
-
}
|
|
70
|
+
let registrationToken = client.registerTask(task, for: config.requestId)
|
|
55
71
|
|
|
56
72
|
// Handle the task result asynchronously
|
|
57
73
|
Task {
|
|
74
|
+
defer {
|
|
75
|
+
client.unregisterTask(requestId: config.requestId, token: registrationToken)
|
|
76
|
+
}
|
|
77
|
+
|
|
58
78
|
do {
|
|
59
79
|
let result = try await task.value
|
|
60
|
-
let responseBody = Self.serializeResponseData(result.data)
|
|
61
|
-
|
|
62
80
|
resolve([
|
|
63
|
-
"data":
|
|
81
|
+
"data": result.data,
|
|
64
82
|
"status": result.status,
|
|
65
83
|
"statusText": result.statusText,
|
|
66
84
|
"headers": result.headers,
|
|
@@ -71,7 +89,17 @@ final class SniConnectImpl: NSObject {
|
|
|
71
89
|
} catch is CancellationError {
|
|
72
90
|
reject("SNI_CANCELLED", "Request cancelled", nil)
|
|
73
91
|
} catch {
|
|
74
|
-
SniConnectLog.error("
|
|
92
|
+
SniConnectLog.error(SniConnectLog.event("sni_request_result", [
|
|
93
|
+
("result", "error"),
|
|
94
|
+
("code", "SNI_UNKNOWN_ERROR"),
|
|
95
|
+
("nativeErrorClass", String(describing: type(of: error))),
|
|
96
|
+
("requestIdHash", SniConnectLog.shortHash(config.requestId)),
|
|
97
|
+
("hostname", config.hostname.lowercased()),
|
|
98
|
+
("ipHash", SniConnectLog.shortHash(config.ip)),
|
|
99
|
+
("ipFamily", SniConnectLog.ipFamily(config.ip)),
|
|
100
|
+
("method", config.method.uppercased()),
|
|
101
|
+
("timeoutMs", Int(config.effectiveTotalTimeout)),
|
|
102
|
+
]))
|
|
75
103
|
reject("SNI_UNKNOWN_ERROR", error.localizedDescription, error)
|
|
76
104
|
}
|
|
77
105
|
}
|
|
@@ -83,8 +111,8 @@ final class SniConnectImpl: NSObject {
|
|
|
83
111
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
84
112
|
reject: @escaping RCTPromiseRejectBlock
|
|
85
113
|
) {
|
|
86
|
-
client.cancelRequest(requestId: requestId)
|
|
87
|
-
resolve(["success":
|
|
114
|
+
let success = client.cancelRequest(requestId: requestId)
|
|
115
|
+
resolve(["success": success])
|
|
88
116
|
}
|
|
89
117
|
|
|
90
118
|
@objc
|
|
@@ -105,6 +133,19 @@ final class SniConnectImpl: NSObject {
|
|
|
105
133
|
resolve(["success": true])
|
|
106
134
|
}
|
|
107
135
|
|
|
136
|
+
@objc
|
|
137
|
+
public func isProxyActiveForUrl(
|
|
138
|
+
_ url: String,
|
|
139
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
140
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
141
|
+
) {
|
|
142
|
+
do {
|
|
143
|
+
resolve(try Self.isProxyActive(forUrl: url))
|
|
144
|
+
} catch {
|
|
145
|
+
reject("SNI_INVALID_URL", "\(error)", error)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
108
149
|
private static func parseDictionary(_ dictionary: NSDictionary) throws -> SniConnectClient.RequestConfig {
|
|
109
150
|
guard let ip = dictionary["ip"] as? String, !ip.isEmpty else {
|
|
110
151
|
throw SniConnectError.invalidConfig("Missing ip")
|
|
@@ -141,22 +182,78 @@ final class SniConnectImpl: NSObject {
|
|
|
141
182
|
private static func validate(_ config: SniConnectClient.RequestConfig) throws {
|
|
142
183
|
try SniConnectValidation.validatePublicIP(config.ip)
|
|
143
184
|
try SniConnectValidation.validateHostname(config.hostname)
|
|
144
|
-
try SniConnectValidation.
|
|
145
|
-
|
|
185
|
+
_ = try SniConnectValidation.normalizeHeaders(config.headers)
|
|
186
|
+
let method = try SniConnectValidation.normalizeMethod(config.method)
|
|
146
187
|
_ = try SniConnectValidation.normalizePath(config.path)
|
|
188
|
+
try SniConnectValidation.validateRequestId(config.requestId)
|
|
189
|
+
try SniConnectValidation.validateTimeout(config.effectiveTotalTimeout)
|
|
190
|
+
try SniConnectValidation.validateBody(config.body)
|
|
191
|
+
try SniConnectValidation.validateMethodBody(method: method, body: config.body)
|
|
147
192
|
}
|
|
148
193
|
|
|
149
|
-
private static func
|
|
150
|
-
|
|
151
|
-
|
|
194
|
+
private static func isProxyActive(forUrl urlString: String) throws -> Bool {
|
|
195
|
+
let startedAt = Date()
|
|
196
|
+
guard let url = URL(string: urlString),
|
|
197
|
+
let scheme = url.scheme?.lowercased(),
|
|
198
|
+
["http", "https"].contains(scheme),
|
|
199
|
+
let host = url.host else {
|
|
200
|
+
SniConnectLog.warn(SniConnectLog.event("proxy_preflight", [
|
|
201
|
+
("platform", "ios"),
|
|
202
|
+
("scheme", "unknown"),
|
|
203
|
+
("host", "unknown"),
|
|
204
|
+
("result", "invalid_url"),
|
|
205
|
+
("source", "CFNetwork"),
|
|
206
|
+
("proxyCount", 0),
|
|
207
|
+
("elapsedMs", SniConnectLog.elapsedMs(since: startedAt)),
|
|
208
|
+
]))
|
|
209
|
+
throw SniConnectError.invalidConfig("Invalid URL")
|
|
152
210
|
}
|
|
153
211
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
212
|
+
guard let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() else {
|
|
213
|
+
SniConnectLog.info(SniConnectLog.event("proxy_preflight", [
|
|
214
|
+
("platform", "ios"),
|
|
215
|
+
("scheme", scheme),
|
|
216
|
+
("host", host),
|
|
217
|
+
("result", false),
|
|
218
|
+
("source", "CFNetwork"),
|
|
219
|
+
("proxyCount", 0),
|
|
220
|
+
("elapsedMs", SniConnectLog.elapsedMs(since: startedAt)),
|
|
221
|
+
]))
|
|
222
|
+
return false
|
|
158
223
|
}
|
|
159
224
|
|
|
160
|
-
|
|
225
|
+
let proxies = CFNetworkCopyProxiesForURL(url as CFURL, settings).takeRetainedValue() as NSArray
|
|
226
|
+
var proxyTypes: [String] = []
|
|
227
|
+
for proxy in proxies {
|
|
228
|
+
guard let proxyDictionary = proxy as? NSDictionary,
|
|
229
|
+
let type = proxyDictionary[kCFProxyTypeKey] as? String else {
|
|
230
|
+
continue
|
|
231
|
+
}
|
|
232
|
+
proxyTypes.append(type)
|
|
233
|
+
if type != (kCFProxyTypeNone as String) {
|
|
234
|
+
SniConnectLog.info(SniConnectLog.event("proxy_preflight", [
|
|
235
|
+
("platform", "ios"),
|
|
236
|
+
("scheme", scheme),
|
|
237
|
+
("host", host),
|
|
238
|
+
("result", true),
|
|
239
|
+
("source", "CFNetwork"),
|
|
240
|
+
("proxyCount", proxies.count),
|
|
241
|
+
("proxyTypes", proxyTypes.joined(separator: ",")),
|
|
242
|
+
("elapsedMs", SniConnectLog.elapsedMs(since: startedAt)),
|
|
243
|
+
]))
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
SniConnectLog.info(SniConnectLog.event("proxy_preflight", [
|
|
248
|
+
("platform", "ios"),
|
|
249
|
+
("scheme", scheme),
|
|
250
|
+
("host", host),
|
|
251
|
+
("result", false),
|
|
252
|
+
("source", "CFNetwork"),
|
|
253
|
+
("proxyCount", proxies.count),
|
|
254
|
+
("proxyTypes", proxyTypes.joined(separator: ",")),
|
|
255
|
+
("elapsedMs", SniConnectLog.elapsedMs(since: startedAt)),
|
|
256
|
+
]))
|
|
257
|
+
return false
|
|
161
258
|
}
|
|
162
259
|
}
|