@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
package/README.md
CHANGED
|
@@ -14,7 +14,21 @@ This package is published as part of the OneKey `app-modules` workspace:
|
|
|
14
14
|
yarn add @onekeyfe/react-native-sni-connect
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
iOS:
|
|
17
|
+
iOS: add Aliyun's spec repo and make `EMASCurl` modular in your app Podfile,
|
|
18
|
+
then run `pod install`:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
source 'https://github.com/CocoaPods/Specs.git'
|
|
22
|
+
source 'https://github.com/aliyun/aliyun-specs.git'
|
|
23
|
+
|
|
24
|
+
target 'YourApp' do
|
|
25
|
+
# React Native autolinks SniConnect. This explicit pod keeps Swift
|
|
26
|
+
# `import EMASCurl` visible when building static libraries.
|
|
27
|
+
pod 'EMASCurl', :modular_headers => true
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Android autolinks.
|
|
18
32
|
|
|
19
33
|
## Usage
|
|
20
34
|
|
|
@@ -24,8 +38,15 @@ import {
|
|
|
24
38
|
cancelRequest,
|
|
25
39
|
cancelAllRequests,
|
|
26
40
|
clearDNSCache,
|
|
41
|
+
isProxyActiveForUrl,
|
|
27
42
|
} from '@onekeyfe/react-native-sni-connect';
|
|
28
43
|
|
|
44
|
+
const proxyActive = await isProxyActiveForUrl('https://example.com/api/v1/ping');
|
|
45
|
+
if (proxyActive) {
|
|
46
|
+
// Product adapters should avoid entering SNI mode when a per-URL/system proxy
|
|
47
|
+
// is active. Low-level SNI requests still bypass proxies directly.
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
const res = await request({
|
|
30
51
|
// requestId is optional; required only if you want to cancel the request.
|
|
31
52
|
requestId: 'health-check-1',
|
|
@@ -47,9 +68,31 @@ await cancelAllRequests();
|
|
|
47
68
|
await clearDNSCache();
|
|
48
69
|
```
|
|
49
70
|
|
|
71
|
+
`multiValueHeaders` preserves repeated response headers when the native transport
|
|
72
|
+
exposes them as raw entries. On iOS, EMASCurl 1.5.5 currently hands this module a
|
|
73
|
+
`HTTPURLResponse` dictionary view, so duplicate header names may already be
|
|
74
|
+
collapsed by the transport before this adapter can observe them. Full duplicate
|
|
75
|
+
header preservation on iOS requires an EMASCurl version that exposes the raw
|
|
76
|
+
response header list.
|
|
77
|
+
|
|
78
|
+
## Specification
|
|
79
|
+
|
|
80
|
+
The behavior of the SNI connect module is governed by a normative,
|
|
81
|
+
platform-agnostic standard that all implementations (iOS, Android, Node/Desktop,
|
|
82
|
+
shared JS adapters, and any future platform) must conform to:
|
|
83
|
+
|
|
84
|
+
**-> [OneKey SNI Connect Standard (OSCS)](./SPEC.md)**
|
|
85
|
+
|
|
86
|
+
Any change to request validation, destination pinning, TLS validation, redirect
|
|
87
|
+
handling, cancellation, response shape, or cache behavior must be checked
|
|
88
|
+
against OSCS.
|
|
89
|
+
When an implementation and the standard disagree, the implementation is wrong.
|
|
90
|
+
|
|
50
91
|
### Security notes
|
|
51
92
|
|
|
52
93
|
- The request scheme is always `https` on port `443`; `path` cannot override scheme, host or port.
|
|
94
|
+
- `isProxyActiveForUrl(url)` is a preflight probe for adapters. It does not enable proxying;
|
|
95
|
+
native SNI transport still bypasses system proxy configuration.
|
|
53
96
|
- `ip` must be an IPv4/IPv6 literal that routes to a public destination; loopback, private,
|
|
54
97
|
link-local (incl. cloud metadata), CGNAT, multicast and reserved ranges are rejected.
|
|
55
98
|
- Header names/values containing CR/LF/control characters are rejected; the `Host` header is
|
package/SniConnect.podspec
CHANGED
|
@@ -16,14 +16,13 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
s.static_framework = true
|
|
17
17
|
|
|
18
18
|
s.source_files = [
|
|
19
|
-
"ios
|
|
20
|
-
"ios
|
|
19
|
+
"ios/*.{swift}",
|
|
20
|
+
"ios/*.{m,mm}"
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
s.dependency 'React-Core'
|
|
24
24
|
s.dependency 'React-jsi'
|
|
25
25
|
s.dependency 'React-callinvoker'
|
|
26
|
-
s.dependency 'React-Codegen'
|
|
27
26
|
s.dependency 'EMASCurl', '1.5.5'
|
|
28
27
|
|
|
29
28
|
s.pod_target_xcconfig = {
|
package/android/build.gradle
CHANGED
|
@@ -40,6 +40,26 @@ internal object SniConnectLogger {
|
|
|
40
40
|
@JvmStatic
|
|
41
41
|
fun error(message: String) = log("error", message, android.util.Log.ERROR)
|
|
42
42
|
|
|
43
|
+
fun event(name: String, vararg fields: Pair<String, Any?>): String =
|
|
44
|
+
(listOf("event" to name) + fields).joinToString(separator = " ") { (key, value) ->
|
|
45
|
+
"$key=${sanitize(value)}"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fun shortHash(value: String?): String {
|
|
49
|
+
if (value.isNullOrEmpty()) return "none"
|
|
50
|
+
var hash = 0xcbf29ce484222325UL
|
|
51
|
+
value.encodeToByteArray().forEach { byte ->
|
|
52
|
+
hash = hash xor byte.toUByte().toULong()
|
|
53
|
+
hash *= 0x100000001b3UL
|
|
54
|
+
}
|
|
55
|
+
return hash.toString(16).padStart(16, '0').take(12)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fun elapsedMs(startedAtMs: Long): Long =
|
|
59
|
+
(android.os.SystemClock.elapsedRealtime() - startedAtMs).coerceAtLeast(0)
|
|
60
|
+
|
|
61
|
+
fun ipFamily(ip: String): String = if (ip.contains(":")) "ipv6" else "ipv4"
|
|
62
|
+
|
|
43
63
|
private fun log(level: String, message: String, androidLogLevel: Int) {
|
|
44
64
|
val method = methods?.get(level)
|
|
45
65
|
if (method != null) {
|
|
@@ -50,6 +70,17 @@ internal object SniConnectLogger {
|
|
|
50
70
|
// Fall through to android.util.Log
|
|
51
71
|
}
|
|
52
72
|
}
|
|
53
|
-
|
|
73
|
+
try {
|
|
74
|
+
android.util.Log.println(androidLogLevel, TAG, message)
|
|
75
|
+
} catch (_: RuntimeException) {
|
|
76
|
+
// Android JVM unit tests do not mock android.util.Log.
|
|
77
|
+
}
|
|
54
78
|
}
|
|
79
|
+
|
|
80
|
+
private fun sanitize(value: Any?): String =
|
|
81
|
+
value?.toString()
|
|
82
|
+
?.replace('\n', '_')
|
|
83
|
+
?.replace('\r', '_')
|
|
84
|
+
?.replace(' ', '_')
|
|
85
|
+
?: "none"
|
|
55
86
|
}
|