@onekeyfe/react-native-range-downloader 3.0.81-alpha.0 → 3.0.81-alpha.10
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/ReactNativeRangeDownloader.podspec +1 -1
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +12 -5
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweep.kt +111 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +185 -189
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +16 -20
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +39 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDeadlineTest.kt +26 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweepTest.kt +107 -0
- package/ios/FirmwareArtifactStore.swift +662 -408
- package/ios/RangeDownloadLogic.swift +297 -0
- package/ios/ReactNativeRangeDownloader.swift +42 -471
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +3 -7
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +7 -6
- package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +7 -7
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +0 -19
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +0 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +0 -4
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +15 -0
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +0 -3
- package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +0 -11
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +32 -13
- package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +39 -8
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +0 -1
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +0 -19
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +6 -5
- package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +9 -9
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +0 -1
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +0 -4
- package/package.json +2 -2
- package/src/ReactNativeRangeDownloader.nitro.ts +4 -11
- package/ios/FirmwareBackgroundSessionEventRouter.swift +0 -17
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReconcileParams.hpp +0 -76
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReconcileParams.kt +0 -38
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReconcileParams.swift +0 -48
- 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,7 +272,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
268
272
|
|
|
269
273
|
override fun getFirmwareArtifactCapabilities(): FirmwareArtifactCapabilities {
|
|
270
274
|
return FirmwareArtifactCapabilities(
|
|
271
|
-
firmwareArtifactProtocolVersion =
|
|
275
|
+
firmwareArtifactProtocolVersion = 3.0,
|
|
272
276
|
supportedRouteTypes = arrayOf("domain", "pinnedIp"),
|
|
273
277
|
supportsArchiveMaterialization = true,
|
|
274
278
|
maxReadBytes = FirmwareArtifactStore.MAX_READ_BYTES.toDouble(),
|
|
@@ -278,7 +282,7 @@ 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,
|
|
@@ -291,7 +295,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
291
295
|
override fun cancelFirmwareArtifactDownloads(
|
|
292
296
|
params: FirmwareArtifactCancelParams,
|
|
293
297
|
): Promise<Unit> {
|
|
294
|
-
return Promise.async {
|
|
298
|
+
return Promise.async(firmwareArtifactScope) {
|
|
295
299
|
FirmwareArtifactStore.cancelDownloads(params.transactionId)
|
|
296
300
|
}
|
|
297
301
|
}
|
|
@@ -299,7 +303,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
299
303
|
override fun discardFirmwareArtifact(
|
|
300
304
|
params: FirmwareArtifactRefParams,
|
|
301
305
|
): Promise<Unit> {
|
|
302
|
-
return Promise.async {
|
|
306
|
+
return Promise.async(firmwareArtifactScope) {
|
|
303
307
|
FirmwareArtifactStore.discard(params.artifactRef)
|
|
304
308
|
}
|
|
305
309
|
}
|
|
@@ -307,7 +311,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
307
311
|
override fun openFirmwareArtifact(
|
|
308
312
|
params: FirmwareArtifactRefParams,
|
|
309
313
|
): Promise<FirmwareArtifactReaderInfo> {
|
|
310
|
-
return Promise.async {
|
|
314
|
+
return Promise.async(firmwareArtifactScope) {
|
|
311
315
|
val (readerId, size) = FirmwareArtifactStore.open(params.artifactRef)
|
|
312
316
|
FirmwareArtifactReaderInfo(readerId = readerId, size = size.toDouble())
|
|
313
317
|
}
|
|
@@ -316,7 +320,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
316
320
|
override fun readFirmwareArtifact(
|
|
317
321
|
params: FirmwareArtifactReaderReadParams,
|
|
318
322
|
): Promise<ArrayBuffer> {
|
|
319
|
-
return Promise.async {
|
|
323
|
+
return Promise.async(firmwareArtifactScope) {
|
|
320
324
|
require(
|
|
321
325
|
params.offset.isFinite() &&
|
|
322
326
|
params.offset >= 0 &&
|
|
@@ -340,7 +344,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
340
344
|
override fun closeFirmwareArtifact(
|
|
341
345
|
params: FirmwareArtifactReaderCloseParams,
|
|
342
346
|
): Promise<Unit> {
|
|
343
|
-
return Promise.async {
|
|
347
|
+
return Promise.async(firmwareArtifactScope) {
|
|
344
348
|
FirmwareArtifactStore.close(params.readerId)
|
|
345
349
|
}
|
|
346
350
|
}
|
|
@@ -348,7 +352,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
348
352
|
override fun materializeFirmwareArchive(
|
|
349
353
|
params: FirmwareArchiveMaterializeParams,
|
|
350
354
|
): Promise<FirmwareArchiveMaterializeResult> {
|
|
351
|
-
return Promise.async {
|
|
355
|
+
return Promise.async(firmwareArtifactScope) {
|
|
352
356
|
val artifacts = FirmwareArtifactStore.materializeArchive(
|
|
353
357
|
params.leaseRef,
|
|
354
358
|
params.archiveArtifactRef,
|
|
@@ -372,7 +376,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
372
376
|
override fun createFirmwareArtifactLease(
|
|
373
377
|
params: FirmwareArtifactLeaseCreateParams,
|
|
374
378
|
): Promise<FirmwareArtifactLease> {
|
|
375
|
-
return Promise.async {
|
|
379
|
+
return Promise.async(firmwareArtifactScope) {
|
|
376
380
|
FirmwareArtifactLease(
|
|
377
381
|
leaseRef = FirmwareArtifactStore.createLease(params.transactionId),
|
|
378
382
|
)
|
|
@@ -382,7 +386,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
382
386
|
override fun retainFirmwareArtifact(
|
|
383
387
|
params: FirmwareArtifactLeaseRetainParams,
|
|
384
388
|
): Promise<Unit> {
|
|
385
|
-
return Promise.async {
|
|
389
|
+
return Promise.async(firmwareArtifactScope) {
|
|
386
390
|
FirmwareArtifactStore.retain(params.leaseRef, params.artifactRef)
|
|
387
391
|
}
|
|
388
392
|
}
|
|
@@ -390,21 +394,13 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
390
394
|
override fun releaseFirmwareArtifactLease(
|
|
391
395
|
params: FirmwareArtifactLeaseReleaseParams,
|
|
392
396
|
): Promise<Unit> {
|
|
393
|
-
return Promise.async {
|
|
397
|
+
return Promise.async(firmwareArtifactScope) {
|
|
394
398
|
FirmwareArtifactStore.releaseLease(params.leaseRef, params.disposition)
|
|
395
399
|
}
|
|
396
400
|
}
|
|
397
401
|
|
|
398
|
-
override fun reconcileFirmwareArtifactLeases(
|
|
399
|
-
params: FirmwareArtifactLeaseReconcileParams,
|
|
400
|
-
): Promise<Unit> {
|
|
401
|
-
return Promise.async {
|
|
402
|
-
FirmwareArtifactStore.reconcileLeases(params.activeLeaseRefs)
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
402
|
override fun sweepFirmwareArtifactOrphans(): Promise<FirmwareArtifactSweepResult> {
|
|
407
|
-
return Promise.async {
|
|
403
|
+
return Promise.async(firmwareArtifactScope) {
|
|
408
404
|
val (deletedFiles, deletedBytes) = FirmwareArtifactStore.sweepOrphans()
|
|
409
405
|
FirmwareArtifactSweepResult(
|
|
410
406
|
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
|
+
}
|