@onekeyfe/react-native-network-throttle 3.0.81 → 3.0.82-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,13 @@
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
+ `bypassUrlOrigins` excludes exact HTTP(S) origins from all throttling. Origins
9
+ are canonicalized with their effective port and registered additively for the
10
+ lifetime of the native process. This allows independently initialized React
11
+ Native runtimes to register local development servers without clearing each
12
+ other's configuration.
8
13
 
9
14
  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,9 @@ 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
17
+ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
14
18
  import okhttp3.Interceptor
15
19
  import okhttp3.MediaType
16
20
  import okhttp3.OkHttpClient
@@ -36,6 +40,7 @@ internal object NetworkThrottle {
36
40
  private val downloadBps = AtomicLong(DEFAULT_THROUGHPUT_BPS.toLong())
37
41
  private val uploadBps = AtomicLong(DEFAULT_THROUGHPUT_BPS.toLong())
38
42
  private val installed = AtomicBoolean(false)
43
+ private val bypassUrlOrigins = AtomicReference<Set<String>>(emptySet())
39
44
 
40
45
  fun install(context: Context) {
41
46
  if (!installed.compareAndSet(false, true)) {
@@ -85,6 +90,19 @@ internal object NetworkThrottle {
85
90
  if (nextUploadBps <= 0) {
86
91
  nextUploadBps = DEFAULT_THROUGHPUT_BPS.toLong()
87
92
  }
93
+ if (config.hasKey("bypassUrlOrigins") && !config.isNull("bypassUrlOrigins")) {
94
+ val origins = config.getArray("bypassUrlOrigins")
95
+ val normalizedOrigins = buildSet {
96
+ if (origins != null) {
97
+ for (index in 0 until origins.size()) {
98
+ if (origins.getType(index) == ReadableType.String) {
99
+ normalizeOrigin(origins.getString(index))?.let(::add)
100
+ }
101
+ }
102
+ }
103
+ }
104
+ bypassUrlOrigins.updateAndGet { current -> current + normalizedOrigins }
105
+ }
88
106
 
89
107
  enabled.set(nextEnabled)
90
108
  latencyNanos.set((nextLatencyMs * 1_000_000.0).toLong())
@@ -104,6 +122,9 @@ internal object NetworkThrottle {
104
122
  map.putDouble("latencyMs", latencyNanos.get() / 1_000_000.0)
105
123
  map.putDouble("downloadBps", downloadBps.get().toDouble())
106
124
  map.putDouble("uploadBps", uploadBps.get().toDouble())
125
+ val origins = Arguments.createArray()
126
+ bypassUrlOrigins.get().sorted().forEach(origins::pushString)
127
+ map.putArray("bypassUrlOrigins", origins)
107
128
  return map
108
129
  }
109
130
 
@@ -111,6 +132,21 @@ internal object NetworkThrottle {
111
132
  private fun getDownloadBps(): Long = if (enabled.get()) downloadBps.get() else 0L
112
133
  private fun getUploadBps(): Long = if (enabled.get()) uploadBps.get() else 0L
113
134
 
135
+ private fun canonicalOrigin(url: HttpUrl): String =
136
+ HttpUrl.Builder()
137
+ .scheme(url.scheme)
138
+ .host(url.host)
139
+ .port(url.port)
140
+ .build()
141
+ .toString()
142
+ .removeSuffix("/")
143
+
144
+ private fun normalizeOrigin(value: String?): String? =
145
+ value?.toHttpUrlOrNull()?.let(::canonicalOrigin)
146
+
147
+ private fun shouldBypass(requestUrl: HttpUrl): Boolean =
148
+ bypassUrlOrigins.get().contains(canonicalOrigin(requestUrl))
149
+
114
150
  private fun sleepNanos(delayNanos: Long) {
115
151
  if (delayNanos <= 0) {
116
152
  return
@@ -201,9 +237,12 @@ internal object NetworkThrottle {
201
237
 
202
238
  private class ThrottleInterceptor : Interceptor {
203
239
  override fun intercept(chain: Interceptor.Chain): Response {
240
+ val request = chain.request()
241
+ if (shouldBypass(request.url)) {
242
+ return chain.proceed(request)
243
+ }
204
244
  val requestStartNanos = System.nanoTime()
205
245
  val delayNanos = getLatencyNanos()
206
- val request = chain.request()
207
246
  val requestBody = request.body
208
247
  val activeUploadBps = getUploadBps()
209
248
  val throttledRequest =
@@ -10,16 +10,32 @@ 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 *OneKeyNetworkThrottleCanonicalOrigin(NSURL *url)
14
+ {
15
+ NSString *scheme = url.scheme.lowercaseString;
16
+ NSString *host = url.host.lowercaseString;
17
+ if ((!([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])) || host.length == 0) {
18
+ return nil;
19
+ }
20
+ NSURLComponents *components = [[NSURLComponents alloc] init];
21
+ components.scheme = scheme;
22
+ components.host = host;
23
+ components.port = url.port ?: @([scheme isEqualToString:@"https"] ? 443 : 80);
24
+ return components.string;
25
+ }
26
+
13
27
  @interface OneKeyNetworkThrottleState : NSObject
14
28
  + (NSDictionary *)currentConfig;
15
29
  + (BOOL)isEnabled;
16
30
  + (NSTimeInterval)latencyMs;
17
31
  + (NSInteger)downloadBps;
18
32
  + (NSInteger)uploadBps;
33
+ + (BOOL)shouldBypassURL:(NSURL *)url;
19
34
  + (NSDictionary *)setEnabled:(BOOL)enabled
20
35
  latencyMs:(NSTimeInterval)latencyMs
21
36
  downloadBps:(NSInteger)downloadBps
22
- uploadBps:(NSInteger)uploadBps;
37
+ uploadBps:(NSInteger)uploadBps
38
+ bypassUrlOrigins:(NSArray *)bypassUrlOrigins;
23
39
  @end
24
40
 
25
41
  @implementation OneKeyNetworkThrottleState
@@ -28,18 +44,25 @@ static atomic_bool _oneKeyNetworkThrottleEnabled = ATOMIC_VAR_INIT(false);
28
44
  static atomic_llong _oneKeyNetworkThrottleLatencyMicros = ATOMIC_VAR_INIT(562500);
29
45
  static atomic_llong _oneKeyNetworkThrottleDownloadBps = ATOMIC_VAR_INIT(102 * 1024);
30
46
  static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024);
47
+ static NSSet<NSString *> *_oneKeyNetworkThrottleBypassOrigins;
31
48
 
32
49
  + (NSDictionary *)currentConfig
33
50
  {
34
51
  BOOL enabled = atomic_load_explicit(&_oneKeyNetworkThrottleEnabled, memory_order_acquire);
35
52
  NSTimeInterval latencyMs =
36
53
  ((NSTimeInterval)atomic_load_explicit(&_oneKeyNetworkThrottleLatencyMicros, memory_order_relaxed)) / 1000.0;
54
+ NSArray<NSString *> *bypassUrlOrigins = nil;
55
+ @synchronized (self) {
56
+ bypassUrlOrigins = [[_oneKeyNetworkThrottleBypassOrigins ?: [NSSet set] allObjects]
57
+ sortedArrayUsingSelector:@selector(compare:)];
58
+ }
37
59
  return @{
38
60
  @"enabled": @(enabled),
39
61
  @"profile": OneKeyNetworkThrottleProfileSlow4G,
40
62
  @"latencyMs": @(latencyMs),
41
63
  @"downloadBps": @([self downloadBps]),
42
- @"uploadBps": @([self uploadBps])
64
+ @"uploadBps": @([self uploadBps]),
65
+ @"bypassUrlOrigins": bypassUrlOrigins
43
66
  };
44
67
  }
45
68
 
@@ -63,6 +86,17 @@ static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024
63
86
  return (NSInteger)atomic_load_explicit(&_oneKeyNetworkThrottleUploadBps, memory_order_relaxed);
64
87
  }
65
88
 
89
+ + (BOOL)shouldBypassURL:(NSURL *)url
90
+ {
91
+ NSString *origin = OneKeyNetworkThrottleCanonicalOrigin(url);
92
+ if (origin == nil) {
93
+ return NO;
94
+ }
95
+ @synchronized (self) {
96
+ return [_oneKeyNetworkThrottleBypassOrigins containsObject:origin];
97
+ }
98
+ }
99
+
66
100
  + (NSInteger)normalizeThroughputBps:(NSInteger)throughputBps
67
101
  {
68
102
  return throughputBps > 0 ? throughputBps : OneKeyNetworkThrottleDefaultThroughputBps;
@@ -72,10 +106,29 @@ static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024
72
106
  latencyMs:(NSTimeInterval)latencyMs
73
107
  downloadBps:(NSInteger)downloadBps
74
108
  uploadBps:(NSInteger)uploadBps
109
+ bypassUrlOrigins:(NSArray *)bypassUrlOrigins
75
110
  {
76
111
  NSTimeInterval normalizedLatencyMs = latencyMs > 0 ? latencyMs : OneKeyNetworkThrottleDefaultLatencyMs;
77
112
  NSInteger normalizedDownloadBps = [self normalizeThroughputBps:downloadBps];
78
113
  NSInteger normalizedUploadBps = [self normalizeThroughputBps:uploadBps];
114
+ if ([bypassUrlOrigins isKindOfClass:[NSArray class]]) {
115
+ NSMutableSet<NSString *> *normalizedOrigins = [NSMutableSet set];
116
+ for (id value in bypassUrlOrigins) {
117
+ if (![value isKindOfClass:[NSString class]]) {
118
+ continue;
119
+ }
120
+ NSString *origin = OneKeyNetworkThrottleCanonicalOrigin([NSURL URLWithString:(NSString *)value]);
121
+ if (origin != nil) {
122
+ [normalizedOrigins addObject:origin];
123
+ }
124
+ }
125
+ @synchronized (self) {
126
+ NSMutableSet<NSString *> *nextOrigins =
127
+ [_oneKeyNetworkThrottleBypassOrigins mutableCopy] ?: [NSMutableSet set];
128
+ [nextOrigins unionSet:normalizedOrigins];
129
+ _oneKeyNetworkThrottleBypassOrigins = [nextOrigins copy];
130
+ }
131
+ }
79
132
  atomic_store_explicit(
80
133
  &_oneKeyNetworkThrottleLatencyMicros,
81
134
  (long long)llround(normalizedLatencyMs * 1000.0),
@@ -137,6 +190,9 @@ static atomic_llong _oneKeyNetworkThrottleUploadBps = ATOMIC_VAR_INIT(102 * 1024
137
190
  if ([NSURLProtocol propertyForKey:OneKeyNetworkThrottleHandledKey inRequest:request]) {
138
191
  return NO;
139
192
  }
193
+ if ([OneKeyNetworkThrottleState shouldBypassURL:request.URL]) {
194
+ return NO;
195
+ }
140
196
  NSString *scheme = request.URL.scheme.lowercaseString;
141
197
  return [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"];
142
198
  }
@@ -517,7 +573,14 @@ RCT_REMAP_METHOD(setConfig, setConfig:(NSDictionary *)config resolver:(RCTPromis
517
573
  uploadBpsValue != nil && uploadBpsValue != [NSNull null]
518
574
  ? [uploadBpsValue integerValue]
519
575
  : [OneKeyNetworkThrottleState uploadBps];
520
- resolve([OneKeyNetworkThrottleState setEnabled:enabled latencyMs:latencyMs downloadBps:downloadBps uploadBps:uploadBps]);
576
+ id bypassUrlOriginsValue = config[@"bypassUrlOrigins"];
577
+ NSArray *bypassUrlOrigins = [bypassUrlOriginsValue isKindOfClass:[NSArray class]] ? bypassUrlOriginsValue : nil;
578
+ resolve([OneKeyNetworkThrottleState
579
+ setEnabled:enabled
580
+ latencyMs:latencyMs
581
+ downloadBps:downloadBps
582
+ uploadBps:uploadBps
583
+ bypassUrlOrigins:bypassUrlOrigins]);
521
584
  }
522
585
 
523
586
  @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
+ bypassUrlOrigins: config.bypassUrlOrigins ?? []
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
+ bypassUrlOrigins: config.bypassUrlOrigins ?? currentConfig.bypassUrlOrigins ?? []
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
+ bypassUrlOrigins: 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.81",
3
+ "version": "3.0.82-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",
@@ -142,5 +142,6 @@
142
142
  }
143
143
  }
144
144
  }
145
- }
145
+ },
146
+ "stableVersion": "3.0.81"
146
147
  }
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
+ bypassUrlOrigins: 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
+ 'bypassUrlOrigins'
23
+ > & {
24
+ bypassUrlOrigins?: 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
+ bypassUrlOrigins: config.bypassUrlOrigins ?? [],
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
+ bypassUrlOrigins:
74
+ config.bypassUrlOrigins ?? currentConfig.bypassUrlOrigins ?? [],
51
75
  });
76
+ return normalizeNativeConfig(nativeConfig);
52
77
  },
53
78
  }
54
79
  : (new Proxy(