@onekeyfe/react-native-sni-connect 3.0.70 → 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 -3
- 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 +512 -175
- 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
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import SniConnectValidationCore
|
|
3
|
+
|
|
4
|
+
final class SniConnectValidationTests: XCTestCase {
|
|
5
|
+
|
|
6
|
+
func testAcceptsValidRequestBoundaryValues() throws {
|
|
7
|
+
XCTAssertNoThrow(try SniConnectValidation.validateRequestId("req-1"))
|
|
8
|
+
XCTAssertNoThrow(try SniConnectValidation.validateTimeout(120_000))
|
|
9
|
+
XCTAssertNoThrow(try SniConnectValidation.validateBody(String(repeating: "a", count: 1024 * 1024)))
|
|
10
|
+
XCTAssertNoThrow(try SniConnectValidation.validatePublicIP("93.184.216.34"))
|
|
11
|
+
XCTAssertNoThrow(try SniConnectValidation.validatePublicIP("2001:4860:4860::8888"))
|
|
12
|
+
XCTAssertNoThrow(try SniConnectValidation.validateHostname("api.example.com"))
|
|
13
|
+
|
|
14
|
+
XCTAssertEqual(try SniConnectValidation.normalizeMethod(" get "), "GET")
|
|
15
|
+
XCTAssertEqual(try SniConnectValidation.normalizePath(""), "/")
|
|
16
|
+
XCTAssertEqual(try SniConnectValidation.normalizePath("v1?q=1"), "/v1?q=1")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func testRejectsIpLiteralHostnames() {
|
|
20
|
+
assertValidationFails {
|
|
21
|
+
try SniConnectValidation.validateHostname("93.184.216.34")
|
|
22
|
+
}
|
|
23
|
+
assertValidationFails {
|
|
24
|
+
try SniConnectValidation.validateHostname("2001:4860:4860::8888")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func testRejectsMalformedHostnames() {
|
|
29
|
+
[
|
|
30
|
+
"",
|
|
31
|
+
"-example.com",
|
|
32
|
+
"example-.com",
|
|
33
|
+
"example..com",
|
|
34
|
+
"bad_host.example",
|
|
35
|
+
"https://example.com",
|
|
36
|
+
"example.com:443",
|
|
37
|
+
String(repeating: "a", count: 64) + ".example.com",
|
|
38
|
+
String(repeating: "a", count: 250) + ".com",
|
|
39
|
+
].forEach { hostname in
|
|
40
|
+
assertValidationFails {
|
|
41
|
+
try SniConnectValidation.validateHostname(hostname)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func testRejectsUnsafeIpv4Destinations() {
|
|
47
|
+
[
|
|
48
|
+
"example.com",
|
|
49
|
+
"93.184.216.34:443",
|
|
50
|
+
" 93.184.216.34",
|
|
51
|
+
"10.0.0.1",
|
|
52
|
+
"127.0.0.1",
|
|
53
|
+
"100.64.0.1",
|
|
54
|
+
"169.254.169.254",
|
|
55
|
+
"172.16.0.1",
|
|
56
|
+
"192.168.1.1",
|
|
57
|
+
"192.0.2.1",
|
|
58
|
+
"198.18.0.1",
|
|
59
|
+
"198.51.100.1",
|
|
60
|
+
"203.0.113.1",
|
|
61
|
+
"224.0.0.1",
|
|
62
|
+
"255.255.255.255",
|
|
63
|
+
].forEach { ip in
|
|
64
|
+
assertValidationFails {
|
|
65
|
+
try SniConnectValidation.validatePublicIP(ip)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func testRejectsUnsafeIpv6DestinationsAndTransitionForms() {
|
|
71
|
+
[
|
|
72
|
+
"::",
|
|
73
|
+
"::1",
|
|
74
|
+
"fe80::1",
|
|
75
|
+
"fc00::1",
|
|
76
|
+
"ff00::1",
|
|
77
|
+
"100::1",
|
|
78
|
+
"2001::1",
|
|
79
|
+
"2001:2::1",
|
|
80
|
+
"2001:db8::1",
|
|
81
|
+
"2002:0a00:0001::1",
|
|
82
|
+
"::ffff:10.0.0.1",
|
|
83
|
+
"64:ff9b::10.0.0.1",
|
|
84
|
+
"64:ff9b:1::1",
|
|
85
|
+
"2001:4860:4860::8888%en0",
|
|
86
|
+
"[2001:4860:4860::8888]",
|
|
87
|
+
].forEach { ip in
|
|
88
|
+
assertValidationFails {
|
|
89
|
+
try SniConnectValidation.validatePublicIP(ip)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func testRejectsUnsupportedMethodsAndUnsafePaths() {
|
|
95
|
+
["TRACE", "CONNECT", "", "GET\n"].forEach { method in
|
|
96
|
+
assertValidationFails {
|
|
97
|
+
_ = try SniConnectValidation.normalizeMethod(method)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
[
|
|
102
|
+
"https://example.com",
|
|
103
|
+
"http://example.com",
|
|
104
|
+
"//example.com/path",
|
|
105
|
+
"javascript:alert(1)",
|
|
106
|
+
"/path\nInjected: yes",
|
|
107
|
+
"/" + String(repeating: "a", count: 8192),
|
|
108
|
+
].forEach { path in
|
|
109
|
+
assertValidationFails {
|
|
110
|
+
_ = try SniConnectValidation.normalizePath(path)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func testEnforcesRequestIdTimeoutAndBodyLimits() {
|
|
116
|
+
assertValidationFails {
|
|
117
|
+
try SniConnectValidation.validateRequestId("")
|
|
118
|
+
}
|
|
119
|
+
assertValidationFails {
|
|
120
|
+
try SniConnectValidation.validateRequestId(String(repeating: "x", count: 129))
|
|
121
|
+
}
|
|
122
|
+
assertValidationFails {
|
|
123
|
+
try SniConnectValidation.validateRequestId("req\n1")
|
|
124
|
+
}
|
|
125
|
+
assertValidationFails {
|
|
126
|
+
try SniConnectValidation.validateTimeout(0)
|
|
127
|
+
}
|
|
128
|
+
assertValidationFails {
|
|
129
|
+
try SniConnectValidation.validateTimeout(120_001)
|
|
130
|
+
}
|
|
131
|
+
assertValidationFails {
|
|
132
|
+
try SniConnectValidation.validateTimeout(.nan)
|
|
133
|
+
}
|
|
134
|
+
assertValidationFails {
|
|
135
|
+
try SniConnectValidation.validateBody(String(repeating: "a", count: 1024 * 1024 + 1))
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func testFiltersModuleOwnedHeadersAndRejectsUnsafeHeaders() throws {
|
|
140
|
+
let normalized = try SniConnectValidation.normalizeHeaders([
|
|
141
|
+
"Host": "evil.example",
|
|
142
|
+
"Content-Length": "9999",
|
|
143
|
+
"Accept-Encoding": "gzip",
|
|
144
|
+
"x-emascurl-config-id": "evil",
|
|
145
|
+
"X-Test": "ok",
|
|
146
|
+
])
|
|
147
|
+
|
|
148
|
+
XCTAssertFalse(normalized.keys.contains { $0.caseInsensitiveCompare("host") == .orderedSame })
|
|
149
|
+
XCTAssertFalse(normalized.keys.contains { $0.caseInsensitiveCompare("content-length") == .orderedSame })
|
|
150
|
+
XCTAssertFalse(normalized.keys.contains { $0.caseInsensitiveCompare("accept-encoding") == .orderedSame })
|
|
151
|
+
XCTAssertEqual(normalized["X-Test"], "ok")
|
|
152
|
+
|
|
153
|
+
[
|
|
154
|
+
["Connection": "close"],
|
|
155
|
+
["Proxy-Authorization": "secret"],
|
|
156
|
+
["Transfer-Encoding": "chunked"],
|
|
157
|
+
["Expect": "100-continue"],
|
|
158
|
+
[":authority": "evil.example"],
|
|
159
|
+
["Bad Header": "x"],
|
|
160
|
+
["X-Test": "line\nbreak"],
|
|
161
|
+
["X-Test": String(repeating: "x", count: 8 * 1024 + 1)],
|
|
162
|
+
].forEach { headers in
|
|
163
|
+
assertValidationFails {
|
|
164
|
+
_ = try SniConnectValidation.normalizeHeaders(headers)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
assertValidationFails {
|
|
169
|
+
_ = try SniConnectValidation.normalizeHeaders(Dictionary(uniqueKeysWithValues: (0...64).map { ("X-\($0)", "v") }))
|
|
170
|
+
}
|
|
171
|
+
assertValidationFails {
|
|
172
|
+
_ = try SniConnectValidation.normalizeHeaders(Dictionary(uniqueKeysWithValues: (0...4).map { ("X-\($0)", String(repeating: "x", count: 7 * 1024)) }))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
func testRejectsAmbiguousMethodBodyCombinations() throws {
|
|
177
|
+
assertValidationFails {
|
|
178
|
+
try SniConnectValidation.validateMethodBody(method: "GET", body: "")
|
|
179
|
+
}
|
|
180
|
+
assertValidationFails {
|
|
181
|
+
try SniConnectValidation.validateMethodBody(method: "HEAD", body: "payload")
|
|
182
|
+
}
|
|
183
|
+
assertValidationFails {
|
|
184
|
+
try SniConnectValidation.validateMethodBody(method: "POST", body: nil)
|
|
185
|
+
}
|
|
186
|
+
assertValidationFails {
|
|
187
|
+
try SniConnectValidation.validateMethodBody(method: "PUT", body: nil)
|
|
188
|
+
}
|
|
189
|
+
assertValidationFails {
|
|
190
|
+
try SniConnectValidation.validateMethodBody(method: "PATCH", body: nil)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
XCTAssertNoThrow(try SniConnectValidation.validateMethodBody(method: "POST", body: ""))
|
|
194
|
+
XCTAssertNoThrow(try SniConnectValidation.validateMethodBody(method: "DELETE", body: nil))
|
|
195
|
+
XCTAssertNoThrow(try SniConnectValidation.validateMethodBody(method: "OPTIONS", body: nil))
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
func testRequestLimiterEnforcesGlobalAndPerDestinationLimits() throws {
|
|
199
|
+
let limiter = SniConnectRequestLimiter(maxActiveRequests: 2, maxActiveRequestsPerPair: 1)
|
|
200
|
+
let firstToken = try limiter.acquire(hostname: "Example.com", ip: "93.184.216.34")
|
|
201
|
+
|
|
202
|
+
assertValidationFails {
|
|
203
|
+
_ = try limiter.acquire(hostname: "example.com", ip: "93.184.216.34")
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let secondToken = try limiter.acquire(hostname: "example.com", ip: "93.184.216.35")
|
|
207
|
+
assertValidationFails {
|
|
208
|
+
_ = try limiter.acquire(hostname: "example.net", ip: "93.184.216.36")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
firstToken.release()
|
|
212
|
+
let replacementToken = try limiter.acquire(hostname: "example.com", ip: "93.184.216.34")
|
|
213
|
+
firstToken.release()
|
|
214
|
+
secondToken.release()
|
|
215
|
+
replacementToken.release()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
func testResolverRegistryKeepsResolverClassUntilAllSessionsReleaseIt() throws {
|
|
219
|
+
final class ResolverA: NSObject {}
|
|
220
|
+
final class ResolverB: NSObject {}
|
|
221
|
+
var classes: [AnyClass] = [ResolverA.self, ResolverB.self]
|
|
222
|
+
let registry = SniConnectPinnedResolverRegistry(maxEntries: 2)
|
|
223
|
+
|
|
224
|
+
let first: AnyClass = try registry.resolverClass(hostname: "Example.com", ip: "93.184.216.34") {
|
|
225
|
+
classes.removeFirst()
|
|
226
|
+
}
|
|
227
|
+
let same: AnyClass = try registry.resolverClass(hostname: "example.com", ip: "93.184.216.34") {
|
|
228
|
+
XCTFail("Expected resolver class reuse for identical hostname/ip")
|
|
229
|
+
return ResolverB.self
|
|
230
|
+
}
|
|
231
|
+
XCTAssertTrue(first === same)
|
|
232
|
+
XCTAssertEqual(registry.resolve(domain: "example.com", resolverClass: first), "93.184.216.34")
|
|
233
|
+
XCTAssertEqual(registry.entryCount, 1)
|
|
234
|
+
XCTAssertEqual(registry.allocatedClassCount, 1)
|
|
235
|
+
|
|
236
|
+
_ = try registry.resolverClass(hostname: "example.com", ip: "93.184.216.35") {
|
|
237
|
+
classes.removeFirst()
|
|
238
|
+
}
|
|
239
|
+
XCTAssertEqual(registry.entryCount, 2)
|
|
240
|
+
XCTAssertEqual(registry.allocatedClassCount, 2)
|
|
241
|
+
assertValidationFails {
|
|
242
|
+
_ = try registry.resolverClass(hostname: "example.net", ip: "93.184.216.36") {
|
|
243
|
+
XCTFail("Bounded registry must not allocate past capacity")
|
|
244
|
+
return ResolverB.self
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
registry.release(hostname: "example.com", ip: "93.184.216.34")
|
|
249
|
+
XCTAssertEqual(registry.entryCount, 2)
|
|
250
|
+
XCTAssertEqual(registry.resolve(domain: "example.com", resolverClass: first), "93.184.216.34")
|
|
251
|
+
|
|
252
|
+
registry.release(hostname: "example.com", ip: "93.184.216.34")
|
|
253
|
+
XCTAssertEqual(registry.entryCount, 1)
|
|
254
|
+
|
|
255
|
+
registry.release(hostname: "example.com", ip: "93.184.216.35")
|
|
256
|
+
XCTAssertEqual(registry.entryCount, 0)
|
|
257
|
+
let reused: AnyClass = try registry.resolverClass(hostname: "example.net", ip: "93.184.216.36") {
|
|
258
|
+
XCTFail("Expected cleared resolver class to be reused")
|
|
259
|
+
return ResolverB.self
|
|
260
|
+
}
|
|
261
|
+
XCTAssertTrue(reused === ResolverA.self || reused === ResolverB.self)
|
|
262
|
+
XCTAssertEqual(registry.allocatedClassCount, 2)
|
|
263
|
+
XCTAssertEqual(registry.resolve(domain: "example.net", resolverClass: reused), "93.184.216.36")
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
func testResponseTextDecodePreservesOriginalJsonText() throws {
|
|
267
|
+
let json = "{ \"b\": 1, \"a\": [true, null] }\n"
|
|
268
|
+
XCTAssertEqual(SniConnectResponseText.decode(Data(json.utf8)), json)
|
|
269
|
+
XCTAssertEqual(SniConnectResponseText.decode(Data()), "")
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func testWallClockDeadlineReturnsFastOperation() async throws {
|
|
273
|
+
let value = try await SniConnectWallClockDeadline.run(timeoutMilliseconds: 1_000) {
|
|
274
|
+
return "ok"
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
XCTAssertEqual(value, "ok")
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
func testWallClockDeadlineTimesOutSlowOperation() async throws {
|
|
281
|
+
do {
|
|
282
|
+
_ = try await SniConnectWallClockDeadline.run(timeoutMilliseconds: 10) {
|
|
283
|
+
try await Task.sleep(nanoseconds: 100_000_000)
|
|
284
|
+
return "late"
|
|
285
|
+
}
|
|
286
|
+
XCTFail("Expected wall-clock deadline to throw")
|
|
287
|
+
} catch let error as SniConnectTimeout {
|
|
288
|
+
XCTAssertEqual(error, .deadlineExceeded)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
func testResponseHeaderMapsPreserveRawRepeatedSetCookieHeaders() throws {
|
|
293
|
+
let headerMaps = SniConnectResponseHeaders.make(rawHeaderFields: [
|
|
294
|
+
(name: "Content-Type", value: "application/json"),
|
|
295
|
+
(name: "Set-Cookie", value: "session=one; Path=/; HttpOnly"),
|
|
296
|
+
(name: "set-cookie", value: "theme=dark; Path=/; Secure"),
|
|
297
|
+
])
|
|
298
|
+
|
|
299
|
+
XCTAssertEqual(headerMaps.singleValueHeaders["content-type"], "application/json")
|
|
300
|
+
XCTAssertEqual(headerMaps.singleValueHeaders["set-cookie"], "theme=dark; Path=/; Secure")
|
|
301
|
+
XCTAssertEqual(headerMaps.multiValueHeaders["set-cookie"], [
|
|
302
|
+
"session=one; Path=/; HttpOnly",
|
|
303
|
+
"theme=dark; Path=/; Secure",
|
|
304
|
+
])
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
func testResponseHeaderMapsPreserveArrayBackedHeaderFields() throws {
|
|
308
|
+
let headerMaps = SniConnectResponseHeaders.make(from: [
|
|
309
|
+
"Set-Cookie": [
|
|
310
|
+
"session=one; Path=/; HttpOnly",
|
|
311
|
+
"theme=dark; Path=/; Secure",
|
|
312
|
+
],
|
|
313
|
+
])
|
|
314
|
+
|
|
315
|
+
XCTAssertEqual(headerMaps.singleValueHeaders["set-cookie"], "theme=dark; Path=/; Secure")
|
|
316
|
+
XCTAssertEqual(headerMaps.multiValueHeaders["set-cookie"], [
|
|
317
|
+
"session=one; Path=/; HttpOnly",
|
|
318
|
+
"theme=dark; Path=/; Secure",
|
|
319
|
+
])
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private func assertValidationFails(_ block: () throws -> Void, file: StaticString = #filePath, line: UInt = #line) {
|
|
323
|
+
XCTAssertThrowsError(try block(), file: file, line: line)
|
|
324
|
+
}
|
|
325
|
+
}
|
package/lib/module/index.js
CHANGED
|
@@ -13,4 +13,7 @@ export function cancelAllRequests() {
|
|
|
13
13
|
export function clearDNSCache() {
|
|
14
14
|
return NativeSniConnect.clearDNSCache();
|
|
15
15
|
}
|
|
16
|
+
export function isProxyActiveForUrl(url) {
|
|
17
|
+
return NativeSniConnect.isProxyActiveForUrl(url);
|
|
18
|
+
}
|
|
16
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -3,24 +3,44 @@ import type { Double, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
|
3
3
|
type HeaderMap = {
|
|
4
4
|
[key: string]: string;
|
|
5
5
|
};
|
|
6
|
-
|
|
6
|
+
type MultiValueHeaderMap = {
|
|
7
|
+
[key: string]: string[];
|
|
8
|
+
};
|
|
9
|
+
type SniConnectRequestBase = {
|
|
7
10
|
requestId?: string;
|
|
8
11
|
ip: string;
|
|
9
12
|
hostname: string;
|
|
10
|
-
method: string;
|
|
11
13
|
path: string;
|
|
12
14
|
headers: HeaderMap;
|
|
13
|
-
body?: string | null;
|
|
14
15
|
timeout: Double;
|
|
15
16
|
};
|
|
17
|
+
type NativeSniConnectRequest = SniConnectRequestBase & {
|
|
18
|
+
method: string;
|
|
19
|
+
body?: string | null;
|
|
20
|
+
};
|
|
21
|
+
export type SniConnectBodylessMethod = 'GET' | 'HEAD';
|
|
22
|
+
export type SniConnectRequiredBodyMethod = 'POST' | 'PUT' | 'PATCH';
|
|
23
|
+
export type SniConnectOptionalBodyMethod = 'DELETE' | 'OPTIONS';
|
|
24
|
+
export type SniConnectMethod = SniConnectBodylessMethod | SniConnectRequiredBodyMethod | SniConnectOptionalBodyMethod;
|
|
25
|
+
export type SniConnectRequest = (SniConnectRequestBase & {
|
|
26
|
+
method: SniConnectBodylessMethod;
|
|
27
|
+
body?: never;
|
|
28
|
+
}) | (SniConnectRequestBase & {
|
|
29
|
+
method: SniConnectRequiredBodyMethod;
|
|
30
|
+
body: string;
|
|
31
|
+
}) | (SniConnectRequestBase & {
|
|
32
|
+
method: SniConnectOptionalBodyMethod;
|
|
33
|
+
body?: string | null;
|
|
34
|
+
});
|
|
16
35
|
export type SniConnectResponse = {
|
|
17
36
|
data: string;
|
|
18
37
|
status: Int32;
|
|
19
38
|
statusText: string;
|
|
20
39
|
headers: HeaderMap;
|
|
40
|
+
multiValueHeaders?: MultiValueHeaderMap;
|
|
21
41
|
};
|
|
22
42
|
export interface Spec extends TurboModule {
|
|
23
|
-
request(config:
|
|
43
|
+
request(config: NativeSniConnectRequest): Promise<SniConnectResponse>;
|
|
24
44
|
cancelRequest(requestId: string): Promise<{
|
|
25
45
|
success: boolean;
|
|
26
46
|
}>;
|
|
@@ -30,6 +50,7 @@ export interface Spec extends TurboModule {
|
|
|
30
50
|
clearDNSCache(): Promise<{
|
|
31
51
|
success: boolean;
|
|
32
52
|
}>;
|
|
53
|
+
isProxyActiveForUrl(url: string): Promise<boolean>;
|
|
33
54
|
}
|
|
34
55
|
export type SniConnectModule = Spec;
|
|
35
56
|
declare const NativeSniConnect: SniConnectModule;
|
|
@@ -9,5 +9,6 @@ export declare function cancelAllRequests(): Promise<{
|
|
|
9
9
|
export declare function clearDNSCache(): Promise<{
|
|
10
10
|
success: boolean;
|
|
11
11
|
}>;
|
|
12
|
-
export
|
|
12
|
+
export declare function isProxyActiveForUrl(url: string): Promise<boolean>;
|
|
13
|
+
export type { SniConnectBodylessMethod, SniConnectMethod, SniConnectOptionalBodyMethod, SniConnectRequest, SniConnectRequiredBodyMethod, SniConnectResponse, } from './NativeSniConnect';
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-sni-connect",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.72",
|
|
4
4
|
"description": "A React Native library for SNI-based HTTP requests with DNS caching and request management",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
"typecheck": "tsc",
|
|
37
37
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
38
38
|
"test": "jest",
|
|
39
|
+
"test:android": "cd ../../example/react-native/android && ./gradlew :onekeyfe_react-native-sni-connect:testDebugUnitTest --no-daemon",
|
|
40
|
+
"test:ios": "swift test",
|
|
41
|
+
"test:native": "yarn test:ios && yarn test:android",
|
|
39
42
|
"release": "yarn prepare && npm whoami && npm publish --access public"
|
|
40
43
|
},
|
|
41
44
|
"keywords": [
|
|
@@ -109,6 +112,11 @@
|
|
|
109
112
|
"jsSrcsDir": "src",
|
|
110
113
|
"android": {
|
|
111
114
|
"javaPackageName": "com.sniconnect"
|
|
115
|
+
},
|
|
116
|
+
"ios": {
|
|
117
|
+
"modulesProvider": {
|
|
118
|
+
"SniConnect": "SniConnect"
|
|
119
|
+
}
|
|
112
120
|
}
|
|
113
121
|
},
|
|
114
122
|
"prettier": {
|
package/src/NativeSniConnect.ts
CHANGED
|
@@ -10,29 +10,60 @@ type HeaderMap = {
|
|
|
10
10
|
[key: string]: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
type MultiValueHeaderMap = {
|
|
14
|
+
[key: string]: string[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type SniConnectRequestBase = {
|
|
14
18
|
requestId?: string;
|
|
15
19
|
ip: string;
|
|
16
20
|
hostname: string;
|
|
17
|
-
method: string;
|
|
18
21
|
path: string;
|
|
19
22
|
headers: HeaderMap;
|
|
20
|
-
body?: string | null;
|
|
21
23
|
timeout: Double;
|
|
22
24
|
};
|
|
23
25
|
|
|
26
|
+
type NativeSniConnectRequest = SniConnectRequestBase & {
|
|
27
|
+
method: string;
|
|
28
|
+
body?: string | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type SniConnectBodylessMethod = 'GET' | 'HEAD';
|
|
32
|
+
export type SniConnectRequiredBodyMethod = 'POST' | 'PUT' | 'PATCH';
|
|
33
|
+
export type SniConnectOptionalBodyMethod = 'DELETE' | 'OPTIONS';
|
|
34
|
+
export type SniConnectMethod =
|
|
35
|
+
| SniConnectBodylessMethod
|
|
36
|
+
| SniConnectRequiredBodyMethod
|
|
37
|
+
| SniConnectOptionalBodyMethod;
|
|
38
|
+
|
|
39
|
+
export type SniConnectRequest =
|
|
40
|
+
| (SniConnectRequestBase & {
|
|
41
|
+
method: SniConnectBodylessMethod;
|
|
42
|
+
body?: never;
|
|
43
|
+
})
|
|
44
|
+
| (SniConnectRequestBase & {
|
|
45
|
+
method: SniConnectRequiredBodyMethod;
|
|
46
|
+
body: string;
|
|
47
|
+
})
|
|
48
|
+
| (SniConnectRequestBase & {
|
|
49
|
+
method: SniConnectOptionalBodyMethod;
|
|
50
|
+
body?: string | null;
|
|
51
|
+
});
|
|
52
|
+
|
|
24
53
|
export type SniConnectResponse = {
|
|
25
54
|
data: string;
|
|
26
55
|
status: Int32;
|
|
27
56
|
statusText: string;
|
|
28
57
|
headers: HeaderMap;
|
|
58
|
+
multiValueHeaders?: MultiValueHeaderMap;
|
|
29
59
|
};
|
|
30
60
|
|
|
31
61
|
export interface Spec extends TurboModule {
|
|
32
|
-
request(config:
|
|
62
|
+
request(config: NativeSniConnectRequest): Promise<SniConnectResponse>;
|
|
33
63
|
cancelRequest(requestId: string): Promise<{ success: boolean }>;
|
|
34
64
|
cancelAllRequests(): Promise<{ success: boolean }>;
|
|
35
65
|
clearDNSCache(): Promise<{ success: boolean }>;
|
|
66
|
+
isProxyActiveForUrl(url: string): Promise<boolean>;
|
|
36
67
|
}
|
|
37
68
|
|
|
38
69
|
const LINKING_ERROR =
|
package/src/index.tsx
CHANGED
|
@@ -23,4 +23,15 @@ export function clearDNSCache(): Promise<{ success: boolean }> {
|
|
|
23
23
|
return NativeSniConnect.clearDNSCache();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export
|
|
26
|
+
export function isProxyActiveForUrl(url: string): Promise<boolean> {
|
|
27
|
+
return NativeSniConnect.isProxyActiveForUrl(url);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type {
|
|
31
|
+
SniConnectBodylessMethod,
|
|
32
|
+
SniConnectMethod,
|
|
33
|
+
SniConnectOptionalBodyMethod,
|
|
34
|
+
SniConnectRequest,
|
|
35
|
+
SniConnectRequiredBodyMethod,
|
|
36
|
+
SniConnectResponse,
|
|
37
|
+
} from './NativeSniConnect';
|