@onekeyfe/react-native-network-throttle 3.0.82 → 3.0.84-alpha.0

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 CHANGED
@@ -2,8 +2,16 @@
2
2
 
3
3
  React Native native network throttle for OneKey iOS and Android development settings.
4
4
 
5
- Current scope is an RN HTTP response latency gate. It does not emulate download
6
- throughput, upload throughput, offline mode, WebView traffic, or third-party
7
- native networking stacks.
5
+ Current scope is RN HTTP(S) latency and upload/download throughput. It does not
6
+ emulate offline mode, WebView traffic, or third-party native networking stacks.
7
+
8
+ `throttleUrlHosts` is an allowlist: when it is non-empty, only requests whose
9
+ host matches are throttled, and everything else is left untouched. An entry is
10
+ either an exact host or `*.example.com`, which matches sub-domains at any depth
11
+ but not the bare apex. An empty allowlist throttles nothing.
12
+
13
+ Hosts are registered additively for the lifetime of the native process, so
14
+ independently initialized React Native runtimes cannot clear each other's
15
+ configuration.
8
16
 
9
17
  This package only owns native request throttling. Product settings, persistence, and UI controls should remain in the host app.
@@ -4,6 +4,7 @@ import android.content.Context
4
4
  import android.util.Log
5
5
  import com.facebook.react.bridge.Arguments
6
6
  import com.facebook.react.bridge.ReadableMap
7
+ import com.facebook.react.bridge.ReadableType
7
8
  import com.facebook.react.bridge.WritableMap
8
9
  import com.facebook.react.modules.network.OkHttpClientProvider
9
10
  import java.io.IOException
@@ -11,6 +12,8 @@ import java.io.InterruptedIOException
11
12
  import java.util.concurrent.TimeUnit
12
13
  import java.util.concurrent.atomic.AtomicBoolean
13
14
  import java.util.concurrent.atomic.AtomicLong
15
+ import java.util.concurrent.atomic.AtomicReference
16
+ import okhttp3.HttpUrl
14
17
  import okhttp3.Interceptor
15
18
  import okhttp3.MediaType
16
19
  import okhttp3.OkHttpClient
@@ -36,6 +39,7 @@ internal object NetworkThrottle {
36
39
  private val downloadBps = AtomicLong(DEFAULT_THROUGHPUT_BPS.toLong())
37
40
  private val uploadBps = AtomicLong(DEFAULT_THROUGHPUT_BPS.toLong())
38
41
  private val installed = AtomicBoolean(false)
42
+ private val throttleUrlHosts = AtomicReference<Set<String>>(emptySet())
39
43
 
40
44
  fun install(context: Context) {
41
45
  if (!installed.compareAndSet(false, true)) {
@@ -85,6 +89,19 @@ internal object NetworkThrottle {
85
89
  if (nextUploadBps <= 0) {
86
90
  nextUploadBps = DEFAULT_THROUGHPUT_BPS.toLong()
87
91
  }
92
+ if (config.hasKey("throttleUrlHosts") && !config.isNull("throttleUrlHosts")) {
93
+ val hosts = config.getArray("throttleUrlHosts")
94
+ val normalizedHosts = buildSet {
95
+ if (hosts != null) {
96
+ for (index in 0 until hosts.size()) {
97
+ if (hosts.getType(index) == ReadableType.String) {
98
+ normalizeHost(hosts.getString(index))?.let(::add)
99
+ }
100
+ }
101
+ }
102
+ }
103
+ throttleUrlHosts.updateAndGet { current -> current + normalizedHosts }
104
+ }
88
105
 
89
106
  enabled.set(nextEnabled)
90
107
  latencyNanos.set((nextLatencyMs * 1_000_000.0).toLong())
@@ -104,6 +121,9 @@ internal object NetworkThrottle {
104
121
  map.putDouble("latencyMs", latencyNanos.get() / 1_000_000.0)
105
122
  map.putDouble("downloadBps", downloadBps.get().toDouble())
106
123
  map.putDouble("uploadBps", uploadBps.get().toDouble())
124
+ val hosts = Arguments.createArray()
125
+ throttleUrlHosts.get().sorted().forEach(hosts::pushString)
126
+ map.putArray("throttleUrlHosts", hosts)
107
127
  return map
108
128
  }
109
129
 
@@ -111,6 +131,33 @@ internal object NetworkThrottle {
111
131
  private fun getDownloadBps(): Long = if (enabled.get()) downloadBps.get() else 0L
112
132
  private fun getUploadBps(): Long = if (enabled.get()) uploadBps.get() else 0L
113
133
 
134
+ private fun normalizeHost(value: String?): String? =
135
+ value?.trim()?.lowercase()?.takeIf { it.isNotEmpty() }
136
+
137
+ /**
138
+ * Hosts are matched as exact names, or as `*.example.com` which matches
139
+ * sub-domains at any depth but not the bare apex. This mirrors the URL
140
+ * patterns the desktop app installs, so both platforms throttle the same
141
+ * traffic.
142
+ */
143
+ private fun matchesHost(host: String, pattern: String): Boolean =
144
+ if (pattern.startsWith("*.")) {
145
+ host.endsWith(pattern.substring(1))
146
+ } else {
147
+ host == pattern
148
+ }
149
+
150
+ private fun shouldThrottle(requestUrl: HttpUrl): Boolean {
151
+ // The interceptor is installed in every build, so keep the empty case
152
+ // allocation-free. An empty allowlist throttles nothing.
153
+ val hosts = throttleUrlHosts.get()
154
+ if (hosts.isEmpty()) {
155
+ return false
156
+ }
157
+ val host = requestUrl.host.lowercase()
158
+ return hosts.any { matchesHost(host, it) }
159
+ }
160
+
114
161
  private fun sleepNanos(delayNanos: Long) {
115
162
  if (delayNanos <= 0) {
116
163
  return
@@ -201,9 +248,12 @@ internal object NetworkThrottle {
201
248
 
202
249
  private class ThrottleInterceptor : Interceptor {
203
250
  override fun intercept(chain: Interceptor.Chain): Response {
251
+ val request = chain.request()
252
+ if (!shouldThrottle(request.url)) {
253
+ return chain.proceed(request)
254
+ }
204
255
  val requestStartNanos = System.nanoTime()
205
256
  val delayNanos = getLatencyNanos()
206
- val request = chain.request()
207
257
  val requestBody = request.body
208
258
  val activeUploadBps = getUploadBps()
209
259
  val throttledRequest =
@@ -10,16 +10,35 @@ static const NSTimeInterval OneKeyNetworkThrottleDefaultLatencyMs = 562.5;
10
10
  static const NSInteger OneKeyNetworkThrottleDefaultThroughputBps = 102 * 1024;
11
11
  static const NSUInteger OneKeyNetworkThrottleMaxPendingDownloadBytes = 256 * 1024;
12
12
 
13
+ static NSString *OneKeyNetworkThrottleNormalizedHost(NSString *value)
14
+ {
15
+ NSString *host = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString;
16
+ return host.length > 0 ? host : nil;
17
+ }
18
+
19
+ // Hosts match as exact names, or as `*.example.com` which matches sub-domains
20
+ // at any depth but not the bare apex. This mirrors the URL patterns the
21
+ // desktop app installs, so both platforms throttle the same traffic.
22
+ static BOOL OneKeyNetworkThrottleHostMatches(NSString *host, NSString *pattern)
23
+ {
24
+ if ([pattern hasPrefix:@"*."]) {
25
+ return [host hasSuffix:[pattern substringFromIndex:1]];
26
+ }
27
+ return [host isEqualToString:pattern];
28
+ }
29
+
13
30
  @interface OneKeyNetworkThrottleState : NSObject
14
31
  + (NSDictionary *)currentConfig;
15
32
  + (BOOL)isEnabled;
16
33
  + (NSTimeInterval)latencyMs;
17
34
  + (NSInteger)downloadBps;
18
35
  + (NSInteger)uploadBps;
36
+ + (BOOL)shouldThrottleURL:(NSURL *)url;
19
37
  + (NSDictionary *)setEnabled:(BOOL)enabled
20
38
  latencyMs:(NSTimeInterval)latencyMs
21
39
  downloadBps:(NSInteger)downloadBps
22
- uploadBps:(NSInteger)uploadBps;
40
+ uploadBps:(NSInteger)uploadBps
41
+ throttleUrlHosts:(NSArray *)throttleUrlHosts;
23
42
  @end
24
43
 
25
44
  @implementation OneKeyNetworkThrottleState
@@ -28,18 +47,25 @@ static atomic_bool _oneKeyNetworkThrottleEnabled = ATOMIC_VAR_INIT(false);
28
47
  static atomic_llong _oneKeyNetworkThrottleLatencyMicros = ATOMIC_VAR_INIT(562500);
29
48
  static atomic_llong _oneKeyNetworkThrottleDownloadBps = ATOMIC_VAR_INIT(102 * 1024);
30
49
  static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024);
50
+ static NSSet<NSString *> *_oneKeyNetworkThrottleHosts;
31
51
 
32
52
  + (NSDictionary *)currentConfig
33
53
  {
34
54
  BOOL enabled = atomic_load_explicit(&_oneKeyNetworkThrottleEnabled, memory_order_acquire);
35
55
  NSTimeInterval latencyMs =
36
56
  ((NSTimeInterval)atomic_load_explicit(&_oneKeyNetworkThrottleLatencyMicros, memory_order_relaxed)) / 1000.0;
57
+ NSArray<NSString *> *throttleUrlHosts = nil;
58
+ @synchronized (self) {
59
+ throttleUrlHosts = [[_oneKeyNetworkThrottleHosts ?: [NSSet set] allObjects]
60
+ sortedArrayUsingSelector:@selector(compare:)];
61
+ }
37
62
  return @{
38
63
  @"enabled": @(enabled),
39
64
  @"profile": OneKeyNetworkThrottleProfileSlow4G,
40
65
  @"latencyMs": @(latencyMs),
41
66
  @"downloadBps": @([self downloadBps]),
42
- @"uploadBps": @([self uploadBps])
67
+ @"uploadBps": @([self uploadBps]),
68
+ @"throttleUrlHosts": throttleUrlHosts
43
69
  };
44
70
  }
45
71
 
@@ -63,6 +89,25 @@ static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024
63
89
  return (NSInteger)atomic_load_explicit(&_oneKeyNetworkThrottleUploadBps, memory_order_relaxed);
64
90
  }
65
91
 
92
+ + (BOOL)shouldThrottleURL:(NSURL *)url
93
+ {
94
+ NSString *host = OneKeyNetworkThrottleNormalizedHost(url.host);
95
+ if (host == nil) {
96
+ return NO;
97
+ }
98
+ // An empty allowlist throttles nothing.
99
+ NSSet<NSString *> *hosts = nil;
100
+ @synchronized (self) {
101
+ hosts = _oneKeyNetworkThrottleHosts;
102
+ }
103
+ for (NSString *pattern in hosts) {
104
+ if (OneKeyNetworkThrottleHostMatches(host, pattern)) {
105
+ return YES;
106
+ }
107
+ }
108
+ return NO;
109
+ }
110
+
66
111
  + (NSInteger)normalizeThroughputBps:(NSInteger)throughputBps
67
112
  {
68
113
  return throughputBps > 0 ? throughputBps : OneKeyNetworkThrottleDefaultThroughputBps;
@@ -72,10 +117,29 @@ static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024
72
117
  latencyMs:(NSTimeInterval)latencyMs
73
118
  downloadBps:(NSInteger)downloadBps
74
119
  uploadBps:(NSInteger)uploadBps
120
+ throttleUrlHosts:(NSArray *)throttleUrlHosts
75
121
  {
76
122
  NSTimeInterval normalizedLatencyMs = latencyMs > 0 ? latencyMs : OneKeyNetworkThrottleDefaultLatencyMs;
77
123
  NSInteger normalizedDownloadBps = [self normalizeThroughputBps:downloadBps];
78
124
  NSInteger normalizedUploadBps = [self normalizeThroughputBps:uploadBps];
125
+ if ([throttleUrlHosts isKindOfClass:[NSArray class]]) {
126
+ NSMutableSet<NSString *> *normalizedHosts = [NSMutableSet set];
127
+ for (id value in throttleUrlHosts) {
128
+ if (![value isKindOfClass:[NSString class]]) {
129
+ continue;
130
+ }
131
+ NSString *host = OneKeyNetworkThrottleNormalizedHost((NSString *)value);
132
+ if (host != nil) {
133
+ [normalizedHosts addObject:host];
134
+ }
135
+ }
136
+ @synchronized (self) {
137
+ NSMutableSet<NSString *> *nextHosts =
138
+ [_oneKeyNetworkThrottleHosts mutableCopy] ?: [NSMutableSet set];
139
+ [nextHosts unionSet:normalizedHosts];
140
+ _oneKeyNetworkThrottleHosts = [nextHosts copy];
141
+ }
142
+ }
79
143
  atomic_store_explicit(
80
144
  &_oneKeyNetworkThrottleLatencyMicros,
81
145
  (long long)llround(normalizedLatencyMs * 1000.0),
@@ -137,6 +201,9 @@ static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024
137
201
  if ([NSURLProtocol propertyForKey:OneKeyNetworkThrottleHandledKey inRequest:request]) {
138
202
  return NO;
139
203
  }
204
+ if (![OneKeyNetworkThrottleState shouldThrottleURL:request.URL]) {
205
+ return NO;
206
+ }
140
207
  NSString *scheme = request.URL.scheme.lowercaseString;
141
208
  return [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"];
142
209
  }
@@ -517,7 +584,14 @@ RCT_REMAP_METHOD(setConfig, setConfig:(NSDictionary *)config resolver:(RCTPromis
517
584
  uploadBpsValue != nil && uploadBpsValue != [NSNull null]
518
585
  ? [uploadBpsValue integerValue]
519
586
  : [OneKeyNetworkThrottleState uploadBps];
520
- resolve([OneKeyNetworkThrottleState setEnabled:enabled latencyMs:latencyMs downloadBps:downloadBps uploadBps:uploadBps]);
587
+ id throttleUrlHostsValue = config[@"throttleUrlHosts"];
588
+ NSArray *throttleUrlHosts = [throttleUrlHostsValue isKindOfClass:[NSArray class]] ? throttleUrlHostsValue : nil;
589
+ resolve([OneKeyNetworkThrottleState
590
+ setEnabled:enabled
591
+ latencyMs:latencyMs
592
+ downloadBps:downloadBps
593
+ uploadBps:uploadBps
594
+ throttleUrlHosts:throttleUrlHosts]);
521
595
  }
522
596
 
523
597
  @end
@@ -10,17 +10,25 @@ const LINKING_ERROR = `The package '@onekeyfe/react-native-network-throttle' doe
10
10
  default: ''
11
11
  }) + '- rebuild the app after installing the package';
12
12
  const nativeModule = NativeModules.OneKeyNetworkThrottle;
13
+ function normalizeNativeConfig(config) {
14
+ return {
15
+ ...config,
16
+ throttleUrlHosts: config.throttleUrlHosts ?? []
17
+ };
18
+ }
13
19
  export const NetworkThrottle = nativeModule ? {
14
- getConfig: () => nativeModule.getConfig(),
20
+ getConfig: async () => normalizeNativeConfig(await nativeModule.getConfig()),
15
21
  setConfig: async config => {
16
- const currentConfig = await nativeModule.getConfig();
17
- return nativeModule.setConfig({
22
+ const currentConfig = normalizeNativeConfig(await nativeModule.getConfig());
23
+ const nativeConfig = await nativeModule.setConfig({
18
24
  enabled: config.enabled ?? currentConfig.enabled,
19
25
  profile: config.profile ?? currentConfig.profile,
20
26
  latencyMs: config.latencyMs ?? currentConfig.latencyMs,
21
27
  downloadBps: config.downloadBps ?? currentConfig.downloadBps,
22
- uploadBps: config.uploadBps ?? currentConfig.uploadBps
28
+ uploadBps: config.uploadBps ?? currentConfig.uploadBps,
29
+ throttleUrlHosts: config.throttleUrlHosts ?? currentConfig.throttleUrlHosts ?? []
23
30
  });
31
+ return normalizeNativeConfig(nativeConfig);
24
32
  }
25
33
  } : new Proxy({}, {
26
34
  get() {
@@ -5,6 +5,7 @@ export type NetworkThrottleConfig = {
5
5
  latencyMs: number;
6
6
  downloadBps: number;
7
7
  uploadBps: number;
8
+ throttleUrlHosts: string[];
8
9
  };
9
10
  export declare const NETWORK_THROTTLE_SLOW_4G_LATENCY_MS = 562.5;
10
11
  export declare const NETWORK_THROTTLE_102_KIB_BPS: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-network-throttle",
3
- "version": "3.0.82",
3
+ "version": "3.0.84-alpha.0",
4
4
  "description": "react-native-network-throttle",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
package/src/index.tsx CHANGED
@@ -8,6 +8,7 @@ export type NetworkThrottleConfig = {
8
8
  latencyMs: number;
9
9
  downloadBps: number;
10
10
  uploadBps: number;
11
+ throttleUrlHosts: string[];
11
12
  };
12
13
 
13
14
  export const NETWORK_THROTTLE_SLOW_4G_LATENCY_MS = 562.5;
@@ -16,9 +17,18 @@ export const NETWORK_THROTTLE_SLOW_4G_DOWNLOAD_BPS =
16
17
  NETWORK_THROTTLE_102_KIB_BPS;
17
18
  export const NETWORK_THROTTLE_SLOW_4G_UPLOAD_BPS = NETWORK_THROTTLE_102_KIB_BPS;
18
19
 
20
+ type NativeNetworkThrottleConfig = Omit<
21
+ NetworkThrottleConfig,
22
+ 'throttleUrlHosts'
23
+ > & {
24
+ throttleUrlHosts?: string[];
25
+ };
26
+
19
27
  type NativeNetworkThrottleModule = {
20
- getConfig: () => Promise<NetworkThrottleConfig>;
21
- setConfig: (config: NetworkThrottleConfig) => Promise<NetworkThrottleConfig>;
28
+ getConfig: () => Promise<NativeNetworkThrottleConfig>;
29
+ setConfig: (
30
+ config: NativeNetworkThrottleConfig
31
+ ) => Promise<NativeNetworkThrottleConfig>;
22
32
  };
23
33
 
24
34
  export type NetworkThrottleModule = {
@@ -37,18 +47,33 @@ const nativeModule = NativeModules.OneKeyNetworkThrottle as
37
47
  | NativeNetworkThrottleModule
38
48
  | undefined;
39
49
 
50
+ function normalizeNativeConfig(
51
+ config: NativeNetworkThrottleConfig
52
+ ): NetworkThrottleConfig {
53
+ return {
54
+ ...config,
55
+ throttleUrlHosts: config.throttleUrlHosts ?? [],
56
+ };
57
+ }
58
+
40
59
  export const NetworkThrottle: NetworkThrottleModule = nativeModule
41
60
  ? {
42
- getConfig: () => nativeModule.getConfig(),
61
+ getConfig: async () =>
62
+ normalizeNativeConfig(await nativeModule.getConfig()),
43
63
  setConfig: async (config) => {
44
- const currentConfig = await nativeModule.getConfig();
45
- return nativeModule.setConfig({
64
+ const currentConfig = normalizeNativeConfig(
65
+ await nativeModule.getConfig()
66
+ );
67
+ const nativeConfig = await nativeModule.setConfig({
46
68
  enabled: config.enabled ?? currentConfig.enabled,
47
69
  profile: config.profile ?? currentConfig.profile,
48
70
  latencyMs: config.latencyMs ?? currentConfig.latencyMs,
49
71
  downloadBps: config.downloadBps ?? currentConfig.downloadBps,
50
72
  uploadBps: config.uploadBps ?? currentConfig.uploadBps,
73
+ throttleUrlHosts:
74
+ config.throttleUrlHosts ?? currentConfig.throttleUrlHosts ?? [],
51
75
  });
76
+ return normalizeNativeConfig(nativeConfig);
52
77
  },
53
78
  }
54
79
  : (new Proxy(