@onekeyfe/react-native-sni-connect 3.0.87 → 3.0.89

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.
@@ -190,10 +190,7 @@ final class SniConnectClient {
190
190
  private let tasksQueue = DispatchQueue(label: "com.onekey.sni.connect.tasks", attributes: .concurrent)
191
191
  private let requestLimiter = SniConnectRequestLimiter.shared
192
192
 
193
- private struct SessionKey: Hashable {
194
- let hostname: String
195
- let ip: String
196
- }
193
+ private typealias SessionKey = SniConnectEndpointKey
197
194
 
198
195
  private struct SessionLease {
199
196
  let session: URLSession
@@ -404,15 +401,15 @@ final class SniConnectClient {
404
401
  }
405
402
  }
406
403
 
407
- private static func makeURLSession(for key: SessionKey) throws -> ManagedSession {
404
+ private static func makeURLSession(hostname: String, ip: String) throws -> ManagedSession {
408
405
  let resources = try SniConnectPinnedTransport.makeResources(
409
- hostname: key.hostname,
410
- ip: key.ip
406
+ hostname: hostname,
407
+ ip: ip
411
408
  )
412
409
  SniConnectLog.info(SniConnectLog.event("sni_transport_config", [
413
- ("hostname", key.hostname),
414
- ("ipHash", SniConnectLog.shortHash(key.ip)),
415
- ("ipFamily", SniConnectLog.ipFamily(key.ip)),
410
+ ("hostname", hostname),
411
+ ("ipHash", SniConnectLog.shortHash(ip)),
412
+ ("ipFamily", SniConnectLog.ipFamily(ip)),
416
413
  ("proxyMode", "no_proxy"),
417
414
  ("pinnedResolver", true),
418
415
  ("protocol", "http1"),
@@ -430,7 +427,7 @@ final class SniConnectClient {
430
427
  }
431
428
 
432
429
  private func sessionLease(for config: RequestConfig) async throws -> SessionLease {
433
- let key = SessionKey(hostname: config.hostname.lowercased(), ip: config.ip)
430
+ let key = SessionKey(hostname: config.hostname, ip: config.ip)
434
431
  while true {
435
432
  let acquisition = try Self.sessionsQueue.sync { () throws -> SessionAcquisition in
436
433
  Self.pendingResolverSlots.removeAll { $0.isReleased }
@@ -448,7 +445,7 @@ final class SniConnectClient {
448
445
  return .waitForResolverSlots(pendingResolverSlots)
449
446
  }
450
447
 
451
- let managedSession = try Self.makeURLSession(for: key)
448
+ let managedSession = try Self.makeURLSession(hostname: key.hostname, ip: config.ip)
452
449
  Self.sessionCache[key] = managedSession
453
450
  Self.markSessionUsed(key)
454
451
  return .ready(Self.retainSession(managedSession.session))
@@ -460,7 +457,7 @@ final class SniConnectClient {
460
457
  case .waitForResolverSlots(let resolverLeases):
461
458
  SniConnectLog.info(SniConnectLog.event("sni_resolver_slot_wait", [
462
459
  ("hostname", key.hostname),
463
- ("ipHash", SniConnectLog.shortHash(key.ip)),
460
+ ("ipHash", SniConnectLog.shortHash(config.ip)),
464
461
  ("waitCount", resolverLeases.count),
465
462
  ]))
466
463
  for resolverLease in resolverLeases {
@@ -511,7 +508,7 @@ final class SniConnectClient {
511
508
  }
512
509
  SniConnectLog.info(SniConnectLog.event("sni_cache_evict", [
513
510
  ("hostname", evictedKey.hostname),
514
- ("ipHash", SniConnectLog.shortHash(evictedKey.ip)),
511
+ ("ipHash", SniConnectLog.shortHash(evictedKey.canonicalIP)),
515
512
  ("cacheSize", sessionCache.count),
516
513
  ("limit", Self.maxCachedSessions),
517
514
  ("reason", forPendingInsert ? "max_cached_sessions_pending_insert" : "max_cached_sessions"),
@@ -47,6 +47,16 @@ struct SniConnectResolverConfig: Equatable {
47
47
  let ip: String
48
48
  }
49
49
 
50
+ struct SniConnectEndpointKey: Hashable {
51
+ let hostname: String
52
+ let canonicalIP: String
53
+
54
+ init(hostname: String, ip: String) {
55
+ self.hostname = hostname.lowercased()
56
+ canonicalIP = SniConnectValidation.canonicalIPKey(ip)
57
+ }
58
+ }
59
+
50
60
  enum SniConnectCoreError: Error {
51
61
  case resourceLimit(String)
52
62
  }
@@ -55,9 +65,9 @@ final class SniConnectPinnedResolverRegistry {
55
65
  static let defaultMaxEntries = 32
56
66
 
57
67
  private let maxEntries: Int
58
- private var classesByKey: [String: AnyClass] = [:]
68
+ private var classesByKey: [SniConnectEndpointKey: AnyClass] = [:]
59
69
  private var configsByClassName: [String: SniConnectResolverConfig] = [:]
60
- private var referenceCountsByKey: [String: Int] = [:]
70
+ private var referenceCountsByKey: [SniConnectEndpointKey: Int] = [:]
61
71
  private var reusableClasses: [AnyClass] = []
62
72
  private var allocatedClassNames: Set<String> = []
63
73
 
@@ -78,8 +88,7 @@ final class SniConnectPinnedResolverRegistry {
78
88
  ip: String,
79
89
  allocateClass: () -> AnyClass
80
90
  ) throws -> AnyClass {
81
- let normalizedHost = hostname.lowercased()
82
- let key = Self.key(hostname: normalizedHost, ip: ip)
91
+ let key = SniConnectEndpointKey(hostname: hostname, ip: ip)
83
92
  if let resolverClass = classesByKey[key] {
84
93
  referenceCountsByKey[key, default: 0] += 1
85
94
  return resolverClass
@@ -93,7 +102,7 @@ final class SniConnectPinnedResolverRegistry {
93
102
  let className = NSStringFromClass(resolverClass)
94
103
  allocatedClassNames.insert(className)
95
104
  classesByKey[key] = resolverClass
96
- configsByClassName[className] = SniConnectResolverConfig(hostname: normalizedHost, ip: ip)
105
+ configsByClassName[className] = SniConnectResolverConfig(hostname: key.hostname, ip: ip)
97
106
  referenceCountsByKey[key] = 1
98
107
  return resolverClass
99
108
  }
@@ -114,7 +123,7 @@ final class SniConnectPinnedResolverRegistry {
114
123
  }
115
124
 
116
125
  func release(hostname: String, ip: String) {
117
- let key = Self.key(hostname: hostname.lowercased(), ip: ip)
126
+ let key = SniConnectEndpointKey(hostname: hostname, ip: ip)
118
127
  guard let referenceCount = referenceCountsByKey[key] else {
119
128
  return
120
129
  }
@@ -137,10 +146,6 @@ final class SniConnectPinnedResolverRegistry {
137
146
  configsByClassName.removeAll()
138
147
  referenceCountsByKey.removeAll()
139
148
  }
140
-
141
- private static func key(hostname: String, ip: String) -> String {
142
- return "\(hostname)|\(ip)"
143
- }
144
149
  }
145
150
 
146
151
  enum SniConnectResponseText {
@@ -450,6 +450,36 @@ final class SniConnectValidationTests: XCTestCase {
450
450
  XCTAssertEqual(registry.resolve(domain: "example.net", resolverClass: reused), "93.184.216.36")
451
451
  }
452
452
 
453
+ func testEndpointCachesReuseEquivalentIPv6Spellings() throws {
454
+ final class ResolverA: NSObject {}
455
+ final class ResolverB: NSObject {}
456
+ let registry = SniConnectPinnedResolverRegistry(maxEntries: 2)
457
+ let compressedIP = "2606:4700:4700::1111"
458
+ let expandedIP = "2606:4700:4700:0:0:0:0:1111"
459
+
460
+ XCTAssertEqual(
461
+ SniConnectEndpointKey(hostname: "Example.com", ip: compressedIP),
462
+ SniConnectEndpointKey(hostname: "example.com", ip: expandedIP)
463
+ )
464
+
465
+ let first: AnyClass = try registry.resolverClass(hostname: "Example.com", ip: compressedIP) {
466
+ ResolverA.self
467
+ }
468
+ let same: AnyClass = try registry.resolverClass(hostname: "example.com", ip: expandedIP) {
469
+ XCTFail("Equivalent IPv6 spellings must reuse the resolver entry")
470
+ return ResolverB.self
471
+ }
472
+
473
+ XCTAssertTrue(first === same)
474
+ XCTAssertEqual(registry.entryCount, 1)
475
+ XCTAssertEqual(registry.allocatedClassCount, 1)
476
+
477
+ registry.release(hostname: "example.com", ip: expandedIP)
478
+ XCTAssertEqual(registry.entryCount, 1)
479
+ registry.release(hostname: "example.com", ip: compressedIP)
480
+ XCTAssertEqual(registry.entryCount, 0)
481
+ }
482
+
453
483
  func testResponseTextDecodePreservesOriginalJsonText() throws {
454
484
  let json = "{ \"b\": 1, \"a\": [true, null] }\n"
455
485
  XCTAssertEqual(SniConnectResponseText.decode(Data(json.utf8)), json)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-sni-connect",
3
- "version": "3.0.87",
3
+ "version": "3.0.89",
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",
@@ -64,8 +64,8 @@
64
64
  "@eslint/compat": "^1.3.2",
65
65
  "@eslint/eslintrc": "^3.3.1",
66
66
  "@eslint/js": "^9.35.0",
67
- "@react-native/babel-preset": "0.83.0",
68
- "@react-native/eslint-config": "0.83.0",
67
+ "@react-native/babel-preset": "0.86.2",
68
+ "@react-native/eslint-config": "0.86.2",
69
69
  "@release-it/conventional-changelog": "^10.0.1",
70
70
  "@types/jest": "^29.5.14",
71
71
  "@types/react": "^19.2.0",
@@ -77,8 +77,8 @@
77
77
  "jest": "^29.7.0",
78
78
  "lefthook": "^2.0.3",
79
79
  "prettier": "^2.8.8",
80
- "react": "19.2.0",
81
- "react-native": "patch:react-native@npm%3A0.83.0#~/.yarn/patches/react-native-npm-0.83.0-577d0f2d83.patch",
80
+ "react": "19.2.3",
81
+ "react-native": "patch:react-native@npm%3A0.86.2#~/.yarn/patches/react-native-npm-0.86.2-c9d546616f.patch",
82
82
  "react-native-builder-bob": "^0.40.17",
83
83
  "release-it": "^19.0.4",
84
84
  "turbo": "^2.5.6",