@onekeyfe/react-native-range-downloader 3.0.81-alpha.1 → 3.0.81-alpha.11

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.
Files changed (43) hide show
  1. package/ReactNativeRangeDownloader.podspec +1 -2
  2. package/android/build.gradle +0 -1
  3. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +12 -5
  4. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweep.kt +111 -0
  5. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +194 -214
  6. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +21 -21
  7. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +39 -0
  8. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDeadlineTest.kt +26 -0
  9. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweepTest.kt +107 -0
  10. package/ios/FirmwareArtifactStore.swift +667 -435
  11. package/ios/RangeDownloadLogic.swift +317 -0
  12. package/ios/ReactNativeRangeDownloader.swift +60 -484
  13. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +4 -7
  14. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
  15. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +7 -6
  16. package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +7 -7
  17. package/nitrogen/generated/android/c++/JFirmwareArtifactReceipt.hpp +7 -3
  18. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +0 -19
  19. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +0 -1
  20. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +2 -2
  21. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +3 -3
  22. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReceipt.kt +6 -3
  23. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +0 -4
  24. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +15 -0
  25. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +0 -3
  26. package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +0 -11
  27. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +32 -13
  28. package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +39 -8
  29. package/nitrogen/generated/ios/swift/FirmwareArtifactReceipt.swift +13 -2
  30. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +0 -1
  31. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +0 -19
  32. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +6 -5
  33. package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +9 -9
  34. package/nitrogen/generated/shared/c++/FirmwareArtifactReceipt.hpp +6 -2
  35. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +0 -1
  36. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +0 -4
  37. package/package.json +1 -2
  38. package/src/ReactNativeRangeDownloader.nitro.ts +7 -11
  39. package/ios/FirmwareBackgroundSessionEventRouter.swift +0 -17
  40. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReconcileParams.hpp +0 -76
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReconcileParams.kt +0 -38
  42. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReconcileParams.swift +0 -48
  43. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReconcileParams.hpp +0 -76
@@ -9,6 +9,9 @@ import java.io.File
9
9
  import java.security.MessageDigest
10
10
  import java.util.concurrent.CopyOnWriteArrayList
11
11
  import java.util.concurrent.atomic.AtomicLong
12
+ import kotlinx.coroutines.CoroutineScope
13
+ import kotlinx.coroutines.Dispatchers
14
+ import kotlinx.coroutines.SupervisorJob
12
15
 
13
16
  // P1: Nitro adapter for the Android concurrent multi-range downloader.
14
17
  //
@@ -34,6 +37,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
34
37
 
35
38
  private val listeners = CopyOnWriteArrayList<Listener>()
36
39
  private val nextListenerId = AtomicLong(1)
40
+ private val firmwareArtifactScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
37
41
 
38
42
  // Active downloads keyed by "channel|taskId" so cancel/discardArtifacts can
39
43
  // flip the abort flag + stop the worker pool BEFORE deleting files, instead of
@@ -268,8 +272,8 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
268
272
 
269
273
  override fun getFirmwareArtifactCapabilities(): FirmwareArtifactCapabilities {
270
274
  return FirmwareArtifactCapabilities(
271
- firmwareArtifactProtocolVersion = 1.0,
272
- supportedRouteTypes = arrayOf("domain", "pinnedIp"),
275
+ firmwareArtifactProtocolVersion = 4.0,
276
+ supportedRouteTypes = arrayOf("domain"),
273
277
  supportsArchiveMaterialization = true,
274
278
  maxReadBytes = FirmwareArtifactStore.MAX_READ_BYTES.toDouble(),
275
279
  )
@@ -278,12 +282,13 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
278
282
  override fun downloadFirmwareArtifact(
279
283
  params: FirmwareArtifactDownloadParams,
280
284
  ): Promise<FirmwareArtifactReceipt> {
281
- return Promise.async {
285
+ return Promise.async(firmwareArtifactScope) {
282
286
  val artifact = FirmwareArtifactStore.download(params)
283
287
  FirmwareArtifactReceipt(
284
288
  artifactRef = artifact.artifactRef,
285
289
  size = artifact.size.toDouble(),
286
290
  sha256 = artifact.sha256,
291
+ expectedSha256Verified = params.expectedSha256 != null,
287
292
  )
288
293
  }
289
294
  }
@@ -291,7 +296,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
291
296
  override fun cancelFirmwareArtifactDownloads(
292
297
  params: FirmwareArtifactCancelParams,
293
298
  ): Promise<Unit> {
294
- return Promise.async {
299
+ return Promise.async(firmwareArtifactScope) {
295
300
  FirmwareArtifactStore.cancelDownloads(params.transactionId)
296
301
  }
297
302
  }
@@ -299,7 +304,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
299
304
  override fun discardFirmwareArtifact(
300
305
  params: FirmwareArtifactRefParams,
301
306
  ): Promise<Unit> {
302
- return Promise.async {
307
+ return Promise.async(firmwareArtifactScope) {
303
308
  FirmwareArtifactStore.discard(params.artifactRef)
304
309
  }
305
310
  }
@@ -307,7 +312,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
307
312
  override fun openFirmwareArtifact(
308
313
  params: FirmwareArtifactRefParams,
309
314
  ): Promise<FirmwareArtifactReaderInfo> {
310
- return Promise.async {
315
+ return Promise.async(firmwareArtifactScope) {
311
316
  val (readerId, size) = FirmwareArtifactStore.open(params.artifactRef)
312
317
  FirmwareArtifactReaderInfo(readerId = readerId, size = size.toDouble())
313
318
  }
@@ -316,13 +321,15 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
316
321
  override fun readFirmwareArtifact(
317
322
  params: FirmwareArtifactReaderReadParams,
318
323
  ): Promise<ArrayBuffer> {
319
- return Promise.async {
324
+ return Promise.async(firmwareArtifactScope) {
320
325
  require(
321
326
  params.offset.isFinite() &&
322
327
  params.offset >= 0 &&
328
+ params.offset <= FirmwareArtifactStore.MAX_ARTIFACT_BYTES.toDouble() &&
323
329
  params.offset.toLong().toDouble() == params.offset &&
324
330
  params.length.isFinite() &&
325
331
  params.length > 0 &&
332
+ params.length <= FirmwareArtifactStore.MAX_READ_BYTES.toDouble() &&
326
333
  params.length.toInt().toDouble() == params.length
327
334
  ) {
328
335
  "Invalid firmware artifact read"
@@ -340,7 +347,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
340
347
  override fun closeFirmwareArtifact(
341
348
  params: FirmwareArtifactReaderCloseParams,
342
349
  ): Promise<Unit> {
343
- return Promise.async {
350
+ return Promise.async(firmwareArtifactScope) {
344
351
  FirmwareArtifactStore.close(params.readerId)
345
352
  }
346
353
  }
@@ -348,7 +355,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
348
355
  override fun materializeFirmwareArchive(
349
356
  params: FirmwareArchiveMaterializeParams,
350
357
  ): Promise<FirmwareArchiveMaterializeResult> {
351
- return Promise.async {
358
+ return Promise.async(firmwareArtifactScope) {
352
359
  val artifacts = FirmwareArtifactStore.materializeArchive(
353
360
  params.leaseRef,
354
361
  params.archiveArtifactRef,
@@ -362,6 +369,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
362
369
  artifactRef = entry.artifact.artifactRef,
363
370
  size = entry.artifact.size.toDouble(),
364
371
  sha256 = entry.artifact.sha256,
372
+ expectedSha256Verified = params.expectedEntries != null,
365
373
  ),
366
374
  )
367
375
  }.toTypedArray(),
@@ -372,7 +380,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
372
380
  override fun createFirmwareArtifactLease(
373
381
  params: FirmwareArtifactLeaseCreateParams,
374
382
  ): Promise<FirmwareArtifactLease> {
375
- return Promise.async {
383
+ return Promise.async(firmwareArtifactScope) {
376
384
  FirmwareArtifactLease(
377
385
  leaseRef = FirmwareArtifactStore.createLease(params.transactionId),
378
386
  )
@@ -382,7 +390,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
382
390
  override fun retainFirmwareArtifact(
383
391
  params: FirmwareArtifactLeaseRetainParams,
384
392
  ): Promise<Unit> {
385
- return Promise.async {
393
+ return Promise.async(firmwareArtifactScope) {
386
394
  FirmwareArtifactStore.retain(params.leaseRef, params.artifactRef)
387
395
  }
388
396
  }
@@ -390,21 +398,13 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
390
398
  override fun releaseFirmwareArtifactLease(
391
399
  params: FirmwareArtifactLeaseReleaseParams,
392
400
  ): Promise<Unit> {
393
- return Promise.async {
401
+ return Promise.async(firmwareArtifactScope) {
394
402
  FirmwareArtifactStore.releaseLease(params.leaseRef, params.disposition)
395
403
  }
396
404
  }
397
405
 
398
- override fun reconcileFirmwareArtifactLeases(
399
- params: FirmwareArtifactLeaseReconcileParams,
400
- ): Promise<Unit> {
401
- return Promise.async {
402
- FirmwareArtifactStore.reconcileLeases(params.activeLeaseRefs)
403
- }
404
- }
405
-
406
406
  override fun sweepFirmwareArtifactOrphans(): Promise<FirmwareArtifactSweepResult> {
407
- return Promise.async {
407
+ return Promise.async(firmwareArtifactScope) {
408
408
  val (deletedFiles, deletedBytes) = FirmwareArtifactStore.sweepOrphans()
409
409
  FirmwareArtifactSweepResult(
410
410
  deletedFiles = deletedFiles.toDouble(),
@@ -1,10 +1,19 @@
1
1
  package com.margelo.nitro.reactnativerangedownloader
2
2
 
3
+ import java.io.File
4
+ import java.io.FileOutputStream
5
+ import java.util.zip.ZipEntry
6
+ import java.util.zip.ZipOutputStream
3
7
  import org.junit.Assert.assertEquals
4
8
  import org.junit.Assert.assertThrows
9
+ import org.junit.Rule
5
10
  import org.junit.Test
11
+ import org.junit.rules.TemporaryFolder
6
12
 
7
13
  class FirmwareArchiveRulesTest {
14
+ @get:Rule
15
+ val temporaryFolder = TemporaryFolder()
16
+
8
17
  private fun entry(
9
18
  artifactId: String = "resource-entry",
10
19
  entryName: String = "assets/icon.png",
@@ -72,4 +81,34 @@ class FirmwareArchiveRulesTest {
72
81
  )
73
82
  }
74
83
  }
84
+
85
+ @Test
86
+ fun acceptsPortableEntriesWithoutExpectedIntegrityMetadata() {
87
+ val archive = createArchive("assets/icon.png", byteArrayOf(1, 2, 3))
88
+
89
+ val entries = FirmwareArchiveRules.validateCentralDirectory(archive, null)
90
+
91
+ assertEquals(1, entries.size)
92
+ assertEquals("assets/icon.png", entries.single().name)
93
+ assertEquals(3, entries.single().uncompressedSize)
94
+ }
95
+
96
+ @Test
97
+ fun rejectsTraversalWithoutExpectedIntegrityMetadata() {
98
+ val archive = createArchive("../icon.png", byteArrayOf(1))
99
+
100
+ assertThrows(IllegalArgumentException::class.java) {
101
+ FirmwareArchiveRules.validateCentralDirectory(archive, null)
102
+ }
103
+ }
104
+
105
+ private fun createArchive(entryName: String, content: ByteArray): File {
106
+ val archive = temporaryFolder.newFile("firmware.zip")
107
+ ZipOutputStream(FileOutputStream(archive)).use { zip ->
108
+ zip.putNextEntry(ZipEntry(entryName))
109
+ zip.write(content)
110
+ zip.closeEntry()
111
+ }
112
+ return archive
113
+ }
75
114
  }
@@ -0,0 +1,26 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertThrows
5
+ import org.junit.Test
6
+
7
+ class FirmwareArtifactDeadlineTest {
8
+ @Test
9
+ fun defaultsToABoundedThreeMinuteDeadline() {
10
+ assertEquals(180.0, validateFirmwareDownloadDeadlineSeconds(null), 0.0)
11
+ }
12
+
13
+ @Test
14
+ fun preservesFractionalDeadlines() {
15
+ assertEquals(2.75, validateFirmwareDownloadDeadlineSeconds(2.75), 0.0)
16
+ }
17
+
18
+ @Test
19
+ fun rejectsUnboundedOrInvalidDeadlines() {
20
+ listOf(0.0, -1.0, Double.NaN, Double.POSITIVE_INFINITY, 86_400.1).forEach {
21
+ assertThrows(IllegalArgumentException::class.java) {
22
+ validateFirmwareDownloadDeadlineSeconds(it)
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,107 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import java.io.File
4
+ import org.junit.Assert.assertEquals
5
+ import org.junit.Assert.assertFalse
6
+ import org.junit.Assert.assertTrue
7
+ import org.junit.Rule
8
+ import org.junit.Test
9
+ import org.junit.rules.TemporaryFolder
10
+
11
+ class FirmwareArtifactOrphanSweepTest {
12
+ @get:Rule
13
+ val temporaryFolder = TemporaryFolder()
14
+
15
+ @Test
16
+ fun removesOnlyStaleRootScratchEntriesWithExactNamesAndTypes() {
17
+ val root = temporaryFolder.newFolder("firmware-artifact-sweep")
18
+ val nowMs = 2_000_000_000_000L
19
+ val staleAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS - 1
20
+ val freshAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS + 1
21
+ val staleArchive = File(
22
+ root,
23
+ "archive-00000000-0000-4000-8000-000000000001",
24
+ ).apply {
25
+ assertTrue(mkdirs())
26
+ File(this, "0.entry").writeText("archive")
27
+ }
28
+ val stalePromote = File(
29
+ root,
30
+ ".promote-00000000-0000-4000-8000-000000000002",
31
+ ).apply { writeText("promote") }
32
+ val freshArchive = File(
33
+ root,
34
+ "archive-00000000-0000-4000-8000-000000000003",
35
+ ).apply { assertTrue(mkdirs()) }
36
+ val freshPromote = File(
37
+ root,
38
+ ".promote-00000000-0000-4000-8000-000000000004",
39
+ ).apply { writeText("promote") }
40
+ val malformedArchive = File(
41
+ root,
42
+ "archive-00000000-0000-4000-8000-000000000005.extra",
43
+ ).apply { assertTrue(mkdirs()) }
44
+ val malformedPromote = File(
45
+ root,
46
+ ".promote-00000000-0000-4000-8000-000000000006.tmp",
47
+ ).apply { writeText("promote") }
48
+ val archiveNamedFile = File(
49
+ root,
50
+ "archive-00000000-0000-4000-8000-000000000007",
51
+ ).apply { writeText("promote") }
52
+ val promoteNamedDirectory = File(
53
+ root,
54
+ ".promote-00000000-0000-4000-8000-000000000008",
55
+ ).apply { assertTrue(mkdirs()) }
56
+ val nestedArchive = File(
57
+ File(root, "nested"),
58
+ "archive-00000000-0000-4000-8000-000000000009",
59
+ ).apply { assertTrue(mkdirs()) }
60
+
61
+ for (
62
+ entry in listOf(
63
+ staleArchive,
64
+ stalePromote,
65
+ malformedArchive,
66
+ malformedPromote,
67
+ archiveNamedFile,
68
+ promoteNamedDirectory,
69
+ nestedArchive,
70
+ )
71
+ ) {
72
+ assertTrue(entry.setLastModified(staleAt))
73
+ }
74
+ for (entry in listOf(freshArchive, freshPromote)) {
75
+ assertTrue(entry.setLastModified(freshAt))
76
+ }
77
+
78
+ val result = sweepFirmwareArtifactOrphansAtRoot(
79
+ root = root,
80
+ retainedSha256 = emptySet(),
81
+ activeSha256 = emptySet(),
82
+ openPaths = emptySet(),
83
+ nowMs = nowMs,
84
+ )
85
+
86
+ assertEquals(2, result.first)
87
+ assertEquals(14L, result.second)
88
+ assertFalse(staleArchive.exists())
89
+ assertFalse(stalePromote.exists())
90
+ for (
91
+ retainedEntry in listOf(
92
+ freshArchive,
93
+ freshPromote,
94
+ malformedArchive,
95
+ malformedPromote,
96
+ archiveNamedFile,
97
+ promoteNamedDirectory,
98
+ nestedArchive,
99
+ )
100
+ ) {
101
+ assertTrue(
102
+ "Unexpectedly removed ${retainedEntry.name}",
103
+ retainedEntry.exists(),
104
+ )
105
+ }
106
+ }
107
+ }