@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.
- package/ReactNativeRangeDownloader.podspec +1 -2
- package/android/build.gradle +0 -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 +194 -214
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +21 -21
- 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 +667 -435
- package/ios/RangeDownloadLogic.swift +317 -0
- package/ios/ReactNativeRangeDownloader.swift +60 -484
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +4 -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++/JFirmwareArtifactReceipt.hpp +7 -3
- 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/FirmwareArtifactReceipt.kt +6 -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/FirmwareArtifactReceipt.swift +13 -2
- 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++/FirmwareArtifactReceipt.hpp +6 -2
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +0 -1
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +0 -4
- package/package.json +1 -2
- package/src/ReactNativeRangeDownloader.nitro.ts +7 -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
|
@@ -2,6 +2,16 @@ import Foundation
|
|
|
2
2
|
import NitroModules
|
|
3
3
|
import ReactNativeNativeLogger
|
|
4
4
|
|
|
5
|
+
private func settledFirmwareArtifactPromise<T>(
|
|
6
|
+
_ operation: () throws -> T
|
|
7
|
+
) -> Promise<T> {
|
|
8
|
+
do {
|
|
9
|
+
return Promise.resolved(withResult: try operation())
|
|
10
|
+
} catch {
|
|
11
|
+
return Promise.rejected(withError: error)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
// MARK: - Nitro HybridObject entry point
|
|
6
16
|
//
|
|
7
17
|
// Thin Nitro shim over `RangeDownloader.shared`. The heavy lifting — concurrent
|
|
@@ -98,8 +108,8 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
98
108
|
|
|
99
109
|
func getFirmwareArtifactCapabilities() throws -> FirmwareArtifactCapabilities {
|
|
100
110
|
FirmwareArtifactCapabilities(
|
|
101
|
-
firmwareArtifactProtocolVersion:
|
|
102
|
-
supportedRouteTypes: ["domain"
|
|
111
|
+
firmwareArtifactProtocolVersion: 4,
|
|
112
|
+
supportedRouteTypes: ["domain"],
|
|
103
113
|
supportsArchiveMaterialization: true,
|
|
104
114
|
maxReadBytes: Double(FirmwareArtifactStore.maxReadBytes)
|
|
105
115
|
)
|
|
@@ -108,12 +118,27 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
108
118
|
func downloadFirmwareArtifact(
|
|
109
119
|
params: FirmwareArtifactDownloadParams
|
|
110
120
|
) throws -> Promise<FirmwareArtifactReceipt> {
|
|
111
|
-
|
|
121
|
+
do {
|
|
122
|
+
if let artifact = try FirmwareArtifactStore.shared.cachedArtifact(params) {
|
|
123
|
+
return Promise.resolved(
|
|
124
|
+
withResult: FirmwareArtifactReceipt(
|
|
125
|
+
artifactRef: artifact.artifactRef,
|
|
126
|
+
size: Double(artifact.size),
|
|
127
|
+
sha256: artifact.sha256,
|
|
128
|
+
expectedSha256Verified: params.expectedSha256 != nil
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
return Promise.rejected(withError: error)
|
|
134
|
+
}
|
|
135
|
+
return Promise.async {
|
|
112
136
|
let artifact = try await FirmwareArtifactStore.shared.download(params)
|
|
113
137
|
return FirmwareArtifactReceipt(
|
|
114
138
|
artifactRef: artifact.artifactRef,
|
|
115
139
|
size: Double(artifact.size),
|
|
116
|
-
sha256: artifact.sha256
|
|
140
|
+
sha256: artifact.sha256,
|
|
141
|
+
expectedSha256Verified: params.expectedSha256 != nil
|
|
117
142
|
)
|
|
118
143
|
}
|
|
119
144
|
}
|
|
@@ -121,7 +146,14 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
121
146
|
func cancelFirmwareArtifactDownloads(
|
|
122
147
|
params: FirmwareArtifactCancelParams
|
|
123
148
|
) throws -> Promise<Void> {
|
|
124
|
-
|
|
149
|
+
guard FirmwareArtifactStore.isSafeIdentifier(params.transactionId) else {
|
|
150
|
+
return Promise.rejected(
|
|
151
|
+
withError: FirmwareArtifactStoreError.invalidInput(
|
|
152
|
+
"Invalid firmware transactionId"
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
return Promise.async {
|
|
125
157
|
try await FirmwareArtifactStore.shared.cancelDownloads(
|
|
126
158
|
transactionId: params.transactionId
|
|
127
159
|
)
|
|
@@ -131,7 +163,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
131
163
|
func discardFirmwareArtifact(
|
|
132
164
|
params: FirmwareArtifactRefParams
|
|
133
165
|
) throws -> Promise<Void> {
|
|
134
|
-
|
|
166
|
+
settledFirmwareArtifactPromise {
|
|
135
167
|
try FirmwareArtifactStore.shared.discard(artifactRef: params.artifactRef)
|
|
136
168
|
}
|
|
137
169
|
}
|
|
@@ -139,7 +171,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
139
171
|
func openFirmwareArtifact(
|
|
140
172
|
params: FirmwareArtifactRefParams
|
|
141
173
|
) throws -> Promise<FirmwareArtifactReaderInfo> {
|
|
142
|
-
|
|
174
|
+
settledFirmwareArtifactPromise {
|
|
143
175
|
let reader = try FirmwareArtifactStore.shared.open(
|
|
144
176
|
artifactRef: params.artifactRef
|
|
145
177
|
)
|
|
@@ -153,16 +185,18 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
153
185
|
func readFirmwareArtifact(
|
|
154
186
|
params: FirmwareArtifactReaderReadParams
|
|
155
187
|
) throws -> Promise<ArrayBuffer> {
|
|
156
|
-
|
|
188
|
+
settledFirmwareArtifactPromise {
|
|
157
189
|
guard
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
190
|
+
let offset = firmwareArtifactExactInt64(
|
|
191
|
+
params.offset,
|
|
192
|
+
minimum: 0,
|
|
193
|
+
maximum: FirmwareArtifactStore.maxArtifactBytes
|
|
194
|
+
),
|
|
195
|
+
let length = firmwareArtifactExactInt64(
|
|
196
|
+
params.length,
|
|
197
|
+
minimum: 1,
|
|
198
|
+
maximum: firmwareArtifactMaxReadBytes
|
|
199
|
+
)
|
|
166
200
|
else {
|
|
167
201
|
throw FirmwareArtifactStoreError.readerInvalid(
|
|
168
202
|
"Invalid firmware artifact read"
|
|
@@ -170,8 +204,8 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
170
204
|
}
|
|
171
205
|
let data = try FirmwareArtifactStore.shared.read(
|
|
172
206
|
readerId: params.readerId,
|
|
173
|
-
offset:
|
|
174
|
-
length: Int(
|
|
207
|
+
offset: offset,
|
|
208
|
+
length: Int(length)
|
|
175
209
|
)
|
|
176
210
|
return try ArrayBuffer.copy(data: data)
|
|
177
211
|
}
|
|
@@ -180,7 +214,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
180
214
|
func closeFirmwareArtifact(
|
|
181
215
|
params: FirmwareArtifactReaderCloseParams
|
|
182
216
|
) throws -> Promise<Void> {
|
|
183
|
-
|
|
217
|
+
settledFirmwareArtifactPromise {
|
|
184
218
|
try FirmwareArtifactStore.shared.close(readerId: params.readerId)
|
|
185
219
|
}
|
|
186
220
|
}
|
|
@@ -188,7 +222,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
188
222
|
func materializeFirmwareArchive(
|
|
189
223
|
params: FirmwareArchiveMaterializeParams
|
|
190
224
|
) throws -> Promise<FirmwareArchiveMaterializeResult> {
|
|
191
|
-
|
|
225
|
+
settledFirmwareArtifactPromise {
|
|
192
226
|
let entries = try FirmwareArtifactStore.shared.materializeArchive(
|
|
193
227
|
leaseRef: params.leaseRef,
|
|
194
228
|
artifactRef: params.archiveArtifactRef,
|
|
@@ -201,7 +235,8 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
201
235
|
receipt: FirmwareArtifactReceipt(
|
|
202
236
|
artifactRef: entry.artifact.artifactRef,
|
|
203
237
|
size: Double(entry.artifact.size),
|
|
204
|
-
sha256: entry.artifact.sha256
|
|
238
|
+
sha256: entry.artifact.sha256,
|
|
239
|
+
expectedSha256Verified: params.expectedEntries != nil
|
|
205
240
|
)
|
|
206
241
|
)
|
|
207
242
|
}
|
|
@@ -212,7 +247,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
212
247
|
func createFirmwareArtifactLease(
|
|
213
248
|
params: FirmwareArtifactLeaseCreateParams
|
|
214
249
|
) throws -> Promise<FirmwareArtifactLease> {
|
|
215
|
-
|
|
250
|
+
settledFirmwareArtifactPromise {
|
|
216
251
|
FirmwareArtifactLease(
|
|
217
252
|
leaseRef: try FirmwareArtifactStore.shared.createLease(
|
|
218
253
|
transactionId: params.transactionId
|
|
@@ -224,7 +259,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
224
259
|
func retainFirmwareArtifact(
|
|
225
260
|
params: FirmwareArtifactLeaseRetainParams
|
|
226
261
|
) throws -> Promise<Void> {
|
|
227
|
-
|
|
262
|
+
settledFirmwareArtifactPromise {
|
|
228
263
|
try FirmwareArtifactStore.shared.retain(
|
|
229
264
|
leaseRef: params.leaseRef,
|
|
230
265
|
artifactRef: params.artifactRef
|
|
@@ -235,7 +270,7 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
235
270
|
func releaseFirmwareArtifactLease(
|
|
236
271
|
params: FirmwareArtifactLeaseReleaseParams
|
|
237
272
|
) throws -> Promise<Void> {
|
|
238
|
-
|
|
273
|
+
settledFirmwareArtifactPromise {
|
|
239
274
|
try FirmwareArtifactStore.shared.releaseLease(
|
|
240
275
|
leaseRef: params.leaseRef,
|
|
241
276
|
disposition: params.disposition
|
|
@@ -243,18 +278,8 @@ class ReactNativeRangeDownloader: HybridReactNativeRangeDownloaderSpec {
|
|
|
243
278
|
}
|
|
244
279
|
}
|
|
245
280
|
|
|
246
|
-
func reconcileFirmwareArtifactLeases(
|
|
247
|
-
params: FirmwareArtifactLeaseReconcileParams
|
|
248
|
-
) throws -> Promise<Void> {
|
|
249
|
-
Promise.async {
|
|
250
|
-
try FirmwareArtifactStore.shared.reconcileLeases(
|
|
251
|
-
activeLeaseRefs: params.activeLeaseRefs
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
281
|
func sweepFirmwareArtifactOrphans() throws -> Promise<FirmwareArtifactSweepResult> {
|
|
257
|
-
|
|
282
|
+
settledFirmwareArtifactPromise {
|
|
258
283
|
let result = try FirmwareArtifactStore.shared.sweepOrphans()
|
|
259
284
|
return FirmwareArtifactSweepResult(
|
|
260
285
|
deletedFiles: Double(result.deletedFiles),
|
|
@@ -323,79 +348,10 @@ extension RangeFallbackClass {
|
|
|
323
348
|
}
|
|
324
349
|
}
|
|
325
350
|
|
|
326
|
-
private struct FirmwareBackgroundTaskDescriptor: Codable, Equatable {
|
|
327
|
-
let schemaVersion: Int
|
|
328
|
-
let taskId: String
|
|
329
|
-
let transactionId: String
|
|
330
|
-
let leaseRef: String
|
|
331
|
-
let expectedSize: Int64
|
|
332
|
-
let expectedSha256: String
|
|
333
|
-
let hostname: String
|
|
334
|
-
let deadlineAt: TimeInterval
|
|
335
|
-
|
|
336
|
-
var key: String {
|
|
337
|
-
"\(leaseRef)|\(taskId)|\(expectedSha256)"
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
func hasSameArtifactIdentity(
|
|
341
|
-
as other: FirmwareBackgroundTaskDescriptor
|
|
342
|
-
) -> Bool {
|
|
343
|
-
taskId == other.taskId &&
|
|
344
|
-
transactionId == other.transactionId &&
|
|
345
|
-
leaseRef == other.leaseRef &&
|
|
346
|
-
expectedSize == other.expectedSize &&
|
|
347
|
-
expectedSha256 == other.expectedSha256 &&
|
|
348
|
-
hostname == other.hostname
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
private enum FirmwareBackgroundDownloadError: LocalizedError {
|
|
353
|
-
case cancelled
|
|
354
|
-
case invalidTask
|
|
355
|
-
case deadlineExceeded
|
|
356
|
-
case redirectRejected
|
|
357
|
-
case responseRejected
|
|
358
|
-
case sizeRejected
|
|
359
|
-
case transferFailed
|
|
360
|
-
|
|
361
|
-
var errorDescription: String? {
|
|
362
|
-
switch self {
|
|
363
|
-
case .cancelled:
|
|
364
|
-
return "ARTIFACT_CANCELLED: firmware background download was cancelled"
|
|
365
|
-
case .invalidTask:
|
|
366
|
-
return "ARTIFACT_PROTOCOL_INVALID: firmware background task is invalid"
|
|
367
|
-
case .deadlineExceeded:
|
|
368
|
-
return "ARTIFACT_DEADLINE_EXCEEDED: firmware download exceeded its deadline"
|
|
369
|
-
case .redirectRejected:
|
|
370
|
-
return "ARTIFACT_REDIRECT_REJECTED: firmware redirect changed canonical identity"
|
|
371
|
-
case .responseRejected:
|
|
372
|
-
return "ARTIFACT_PROTOCOL_INVALID: firmware response is invalid"
|
|
373
|
-
case .sizeRejected:
|
|
374
|
-
return "ARTIFACT_PROTOCOL_INVALID: firmware artifact size is invalid"
|
|
375
|
-
case .transferFailed:
|
|
376
|
-
return "ARTIFACT_NETWORK_FAILED: firmware background transfer failed"
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
351
|
public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
382
352
|
|
|
383
353
|
public static let shared = RangeDownloader()
|
|
384
354
|
|
|
385
|
-
public static func routeFirmwareBackgroundEvents(
|
|
386
|
-
identifier: String,
|
|
387
|
-
completionHandler: @escaping () -> Void
|
|
388
|
-
) -> Bool {
|
|
389
|
-
guard identifier == sessionIdentifier(for: .firmware) else {
|
|
390
|
-
return false
|
|
391
|
-
}
|
|
392
|
-
shared.attachBackgroundEvents(
|
|
393
|
-
identifier: identifier,
|
|
394
|
-
completionHandler: completionHandler
|
|
395
|
-
)
|
|
396
|
-
return true
|
|
397
|
-
}
|
|
398
|
-
|
|
399
355
|
/// Posted by the AppDelegate from
|
|
400
356
|
/// application(_:handleEventsForBackgroundURLSession:completionHandler:).
|
|
401
357
|
/// userInfo: ["identifier": String, "completionHandler": () -> Void].
|
|
@@ -432,11 +388,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
432
388
|
/// notification), keyed by session identifier, so we can call each back once
|
|
433
389
|
/// all its queued background events have been delivered.
|
|
434
390
|
private var backgroundCompletionHandlers: [String: () -> Void] = [:]
|
|
435
|
-
private var firmwareWaiters: [
|
|
436
|
-
String: [CheckedContinuation<StoredFirmwareArtifact, Error>]
|
|
437
|
-
] = [:]
|
|
438
|
-
private var firmwareTaskErrors: [Int: Error] = [:]
|
|
439
|
-
private var completedFirmwareTasks: Set<Int> = []
|
|
440
391
|
|
|
441
392
|
// Per-run mutable state.
|
|
442
393
|
private final class RunState {
|
|
@@ -694,240 +645,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
694
645
|
return (r, decoded.segIndex)
|
|
695
646
|
}
|
|
696
647
|
|
|
697
|
-
private static let firmwareTaskDescriptionPrefix = "firmware-v1:"
|
|
698
|
-
|
|
699
|
-
private static func encodeFirmwareTaskDescription(
|
|
700
|
-
_ descriptor: FirmwareBackgroundTaskDescriptor
|
|
701
|
-
) throws -> String {
|
|
702
|
-
let data = try JSONEncoder().encode(descriptor)
|
|
703
|
-
return firmwareTaskDescriptionPrefix + data.base64EncodedString()
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
private static func decodeFirmwareTaskDescription(
|
|
707
|
-
_ description: String?
|
|
708
|
-
) -> FirmwareBackgroundTaskDescriptor? {
|
|
709
|
-
guard
|
|
710
|
-
let description,
|
|
711
|
-
description.hasPrefix(firmwareTaskDescriptionPrefix),
|
|
712
|
-
let data = Data(
|
|
713
|
-
base64Encoded: String(
|
|
714
|
-
description.dropFirst(firmwareTaskDescriptionPrefix.count)
|
|
715
|
-
)
|
|
716
|
-
),
|
|
717
|
-
data.count <= 2048,
|
|
718
|
-
let descriptor = try? JSONDecoder().decode(
|
|
719
|
-
FirmwareBackgroundTaskDescriptor.self,
|
|
720
|
-
from: data
|
|
721
|
-
),
|
|
722
|
-
descriptor.schemaVersion == 1,
|
|
723
|
-
descriptor.taskId.range(
|
|
724
|
-
of: "^[A-Za-z0-9._-]{1,100}$",
|
|
725
|
-
options: .regularExpression
|
|
726
|
-
) != nil,
|
|
727
|
-
descriptor.transactionId.range(
|
|
728
|
-
of: "^[A-Za-z0-9._:-]{1,160}$",
|
|
729
|
-
options: .regularExpression
|
|
730
|
-
) != nil,
|
|
731
|
-
descriptor.leaseRef.range(
|
|
732
|
-
of: "^fwlease:[a-f0-9-]{36}$",
|
|
733
|
-
options: .regularExpression
|
|
734
|
-
) != nil,
|
|
735
|
-
descriptor.expectedSize > 0,
|
|
736
|
-
descriptor.expectedSize <= 512 * 1024 * 1024,
|
|
737
|
-
descriptor.expectedSha256.range(
|
|
738
|
-
of: "^[a-f0-9]{64}$",
|
|
739
|
-
options: .regularExpression
|
|
740
|
-
) != nil,
|
|
741
|
-
!descriptor.hostname.isEmpty,
|
|
742
|
-
descriptor.hostname.count <= 253,
|
|
743
|
-
descriptor.deadlineAt.isFinite,
|
|
744
|
-
descriptor.deadlineAt > 0
|
|
745
|
-
else {
|
|
746
|
-
return nil
|
|
747
|
-
}
|
|
748
|
-
return descriptor
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
func downloadFirmwareArtifact(
|
|
752
|
-
params: FirmwareArtifactDownloadParams
|
|
753
|
-
) async throws -> StoredFirmwareArtifact {
|
|
754
|
-
guard
|
|
755
|
-
params.routeType == "domain",
|
|
756
|
-
let url = URL(string: params.url),
|
|
757
|
-
let hostname = url.host?.lowercased()
|
|
758
|
-
else {
|
|
759
|
-
throw FirmwareBackgroundDownloadError.invalidTask
|
|
760
|
-
}
|
|
761
|
-
let deadlineSeconds = params.overallDeadlineSeconds ?? 180
|
|
762
|
-
guard
|
|
763
|
-
deadlineSeconds.isFinite,
|
|
764
|
-
deadlineSeconds > 0,
|
|
765
|
-
deadlineSeconds <= 24 * 60 * 60
|
|
766
|
-
else {
|
|
767
|
-
throw FirmwareBackgroundDownloadError.invalidTask
|
|
768
|
-
}
|
|
769
|
-
let descriptor = FirmwareBackgroundTaskDescriptor(
|
|
770
|
-
schemaVersion: 1,
|
|
771
|
-
taskId: params.taskId,
|
|
772
|
-
transactionId: params.transactionId,
|
|
773
|
-
leaseRef: params.leaseRef,
|
|
774
|
-
expectedSize: Int64(params.expectedSize),
|
|
775
|
-
expectedSha256: params.expectedSha256.lowercased(),
|
|
776
|
-
hostname: hostname,
|
|
777
|
-
deadlineAt: Date().timeIntervalSince1970 + deadlineSeconds
|
|
778
|
-
)
|
|
779
|
-
guard !FirmwareArtifactStore.shared.isTransactionCancelled(
|
|
780
|
-
descriptor.transactionId
|
|
781
|
-
) else {
|
|
782
|
-
throw FirmwareBackgroundDownloadError.cancelled
|
|
783
|
-
}
|
|
784
|
-
if let stored = try FirmwareArtifactStore.shared.storedArtifact(
|
|
785
|
-
expectedSize: descriptor.expectedSize,
|
|
786
|
-
expectedSha256: descriptor.expectedSha256
|
|
787
|
-
) {
|
|
788
|
-
return stored
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
return try await withCheckedThrowingContinuation { continuation in
|
|
792
|
-
lock.withLockValue {
|
|
793
|
-
firmwareWaiters[descriptor.key, default: []].append(continuation)
|
|
794
|
-
}
|
|
795
|
-
Task { [weak self] in
|
|
796
|
-
await self?.reconcileOrStartFirmwareTask(
|
|
797
|
-
descriptor: descriptor,
|
|
798
|
-
url: url
|
|
799
|
-
)
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
func cancelFirmwareArtifactDownloads(
|
|
805
|
-
transactionId: String
|
|
806
|
-
) async throws {
|
|
807
|
-
guard transactionId.range(
|
|
808
|
-
of: "^[A-Za-z0-9._:-]{1,160}$",
|
|
809
|
-
options: .regularExpression
|
|
810
|
-
) != nil else {
|
|
811
|
-
throw FirmwareBackgroundDownloadError.invalidTask
|
|
812
|
-
}
|
|
813
|
-
let session = session(forChannel: .firmware, segmentCount: 1)
|
|
814
|
-
let tasks = await allTasks(in: session)
|
|
815
|
-
for task in tasks {
|
|
816
|
-
guard
|
|
817
|
-
let descriptor = Self.decodeFirmwareTaskDescription(
|
|
818
|
-
task.taskDescription
|
|
819
|
-
),
|
|
820
|
-
descriptor.transactionId == transactionId
|
|
821
|
-
else {
|
|
822
|
-
continue
|
|
823
|
-
}
|
|
824
|
-
recordFirmwareTaskError(
|
|
825
|
-
taskIdentifier: task.taskIdentifier,
|
|
826
|
-
error: FirmwareBackgroundDownloadError.cancelled
|
|
827
|
-
)
|
|
828
|
-
task.cancel()
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
private func reconcileOrStartFirmwareTask(
|
|
833
|
-
descriptor: FirmwareBackgroundTaskDescriptor,
|
|
834
|
-
url: URL
|
|
835
|
-
) async {
|
|
836
|
-
guard !FirmwareArtifactStore.shared.isTransactionCancelled(
|
|
837
|
-
descriptor.transactionId
|
|
838
|
-
) else {
|
|
839
|
-
finishFirmwareTask(
|
|
840
|
-
key: descriptor.key,
|
|
841
|
-
result: .failure(FirmwareBackgroundDownloadError.cancelled)
|
|
842
|
-
)
|
|
843
|
-
return
|
|
844
|
-
}
|
|
845
|
-
let session = session(forChannel: .firmware, segmentCount: 1)
|
|
846
|
-
let tasks = await allTasks(in: session)
|
|
847
|
-
var matchingTaskFound = false
|
|
848
|
-
for task in tasks {
|
|
849
|
-
guard
|
|
850
|
-
let candidate = Self.decodeFirmwareTaskDescription(
|
|
851
|
-
task.taskDescription
|
|
852
|
-
)
|
|
853
|
-
else {
|
|
854
|
-
continue
|
|
855
|
-
}
|
|
856
|
-
if candidate.hasSameArtifactIdentity(as: descriptor) {
|
|
857
|
-
matchingTaskFound = true
|
|
858
|
-
} else if candidate.taskId == descriptor.taskId {
|
|
859
|
-
task.cancel()
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
if matchingTaskFound {
|
|
863
|
-
return
|
|
864
|
-
}
|
|
865
|
-
do {
|
|
866
|
-
if let stored = try FirmwareArtifactStore.shared.storedArtifact(
|
|
867
|
-
expectedSize: descriptor.expectedSize,
|
|
868
|
-
expectedSha256: descriptor.expectedSha256
|
|
869
|
-
) {
|
|
870
|
-
finishFirmwareTask(
|
|
871
|
-
key: descriptor.key,
|
|
872
|
-
result: .success(stored)
|
|
873
|
-
)
|
|
874
|
-
return
|
|
875
|
-
}
|
|
876
|
-
guard Date().timeIntervalSince1970 < descriptor.deadlineAt else {
|
|
877
|
-
throw FirmwareBackgroundDownloadError.deadlineExceeded
|
|
878
|
-
}
|
|
879
|
-
guard !FirmwareArtifactStore.shared.isTransactionCancelled(
|
|
880
|
-
descriptor.transactionId
|
|
881
|
-
) else {
|
|
882
|
-
throw FirmwareBackgroundDownloadError.cancelled
|
|
883
|
-
}
|
|
884
|
-
var request = URLRequest(url: url)
|
|
885
|
-
request.cachePolicy = .reloadIgnoringLocalCacheData
|
|
886
|
-
request.timeoutInterval = max(
|
|
887
|
-
1,
|
|
888
|
-
descriptor.deadlineAt - Date().timeIntervalSince1970
|
|
889
|
-
)
|
|
890
|
-
request.setValue("identity", forHTTPHeaderField: "Accept-Encoding")
|
|
891
|
-
let task = session.downloadTask(with: request)
|
|
892
|
-
task.taskDescription = try Self.encodeFirmwareTaskDescription(
|
|
893
|
-
descriptor
|
|
894
|
-
)
|
|
895
|
-
task.resume()
|
|
896
|
-
} catch {
|
|
897
|
-
finishFirmwareTask(
|
|
898
|
-
key: descriptor.key,
|
|
899
|
-
result: .failure(error)
|
|
900
|
-
)
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
private func allTasks(in session: URLSession) async -> [URLSessionTask] {
|
|
905
|
-
await withCheckedContinuation { continuation in
|
|
906
|
-
session.getAllTasks { tasks in
|
|
907
|
-
continuation.resume(returning: tasks)
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
private func finishFirmwareTask(
|
|
913
|
-
key: String,
|
|
914
|
-
result: Result<StoredFirmwareArtifact, Error>
|
|
915
|
-
) {
|
|
916
|
-
let continuations: [
|
|
917
|
-
CheckedContinuation<StoredFirmwareArtifact, Error>
|
|
918
|
-
] = lock.withLockValue {
|
|
919
|
-
firmwareWaiters.removeValue(forKey: key) ?? []
|
|
920
|
-
}
|
|
921
|
-
for continuation in continuations {
|
|
922
|
-
switch result {
|
|
923
|
-
case let .success(artifact):
|
|
924
|
-
continuation.resume(returning: artifact)
|
|
925
|
-
case let .failure(error):
|
|
926
|
-
continuation.resume(throwing: error)
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
648
|
// MARK: - Public entry
|
|
932
649
|
|
|
933
650
|
/// Downloads [urlString] into [filePath] using concurrent background ranges.
|
|
@@ -1463,28 +1180,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
1463
1180
|
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
|
|
1464
1181
|
didWriteData bytesWritten: Int64, totalBytesWritten: Int64,
|
|
1465
1182
|
totalBytesExpectedToWrite: Int64) {
|
|
1466
|
-
if let descriptor = Self.decodeFirmwareTaskDescription(
|
|
1467
|
-
downloadTask.taskDescription
|
|
1468
|
-
) {
|
|
1469
|
-
let exceedsBound =
|
|
1470
|
-
totalBytesWritten > descriptor.expectedSize ||
|
|
1471
|
-
(
|
|
1472
|
-
totalBytesExpectedToWrite != NSURLSessionTransferSizeUnknown &&
|
|
1473
|
-
totalBytesExpectedToWrite != descriptor.expectedSize
|
|
1474
|
-
)
|
|
1475
|
-
let expired =
|
|
1476
|
-
Date().timeIntervalSince1970 >= descriptor.deadlineAt
|
|
1477
|
-
if exceedsBound || expired {
|
|
1478
|
-
recordFirmwareTaskError(
|
|
1479
|
-
taskIdentifier: downloadTask.taskIdentifier,
|
|
1480
|
-
error: exceedsBound
|
|
1481
|
-
? FirmwareBackgroundDownloadError.sizeRejected
|
|
1482
|
-
: FirmwareBackgroundDownloadError.deadlineExceeded
|
|
1483
|
-
)
|
|
1484
|
-
downloadTask.cancel()
|
|
1485
|
-
}
|
|
1486
|
-
return
|
|
1487
|
-
}
|
|
1488
1183
|
guard let desc = downloadTask.taskDescription,
|
|
1489
1184
|
let (state, idx) = run(for: desc) else { return }
|
|
1490
1185
|
lock.lock()
|
|
@@ -1515,53 +1210,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
1515
1210
|
|
|
1516
1211
|
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
|
|
1517
1212
|
didFinishDownloadingTo location: URL) {
|
|
1518
|
-
if let descriptor = Self.decodeFirmwareTaskDescription(
|
|
1519
|
-
downloadTask.taskDescription
|
|
1520
|
-
) {
|
|
1521
|
-
guard firmwareTaskError(
|
|
1522
|
-
taskIdentifier: downloadTask.taskIdentifier
|
|
1523
|
-
) == nil else {
|
|
1524
|
-
return
|
|
1525
|
-
}
|
|
1526
|
-
do {
|
|
1527
|
-
guard
|
|
1528
|
-
!FirmwareArtifactStore.shared.isTransactionCancelled(
|
|
1529
|
-
descriptor.transactionId
|
|
1530
|
-
),
|
|
1531
|
-
Date().timeIntervalSince1970 < descriptor.deadlineAt,
|
|
1532
|
-
let response = downloadTask.response as? HTTPURLResponse,
|
|
1533
|
-
response.statusCode == 200,
|
|
1534
|
-
response.url?.scheme?.lowercased() == "https",
|
|
1535
|
-
response.url?.host?.lowercased() == descriptor.hostname,
|
|
1536
|
-
response.url?.port == nil || response.url?.port == 443
|
|
1537
|
-
else {
|
|
1538
|
-
throw FirmwareBackgroundDownloadError.responseRejected
|
|
1539
|
-
}
|
|
1540
|
-
let artifact = try FirmwareArtifactStore.shared.acceptBackgroundDownload(
|
|
1541
|
-
temporaryURL: location,
|
|
1542
|
-
leaseRef: descriptor.leaseRef,
|
|
1543
|
-
transactionId: descriptor.transactionId,
|
|
1544
|
-
expectedSize: descriptor.expectedSize,
|
|
1545
|
-
expectedSha256: descriptor.expectedSha256
|
|
1546
|
-
)
|
|
1547
|
-
lock.withLockValue {
|
|
1548
|
-
firmwareTaskErrors.removeValue(
|
|
1549
|
-
forKey: downloadTask.taskIdentifier
|
|
1550
|
-
)
|
|
1551
|
-
completedFirmwareTasks.insert(downloadTask.taskIdentifier)
|
|
1552
|
-
}
|
|
1553
|
-
finishFirmwareTask(
|
|
1554
|
-
key: descriptor.key,
|
|
1555
|
-
result: .success(artifact)
|
|
1556
|
-
)
|
|
1557
|
-
} catch {
|
|
1558
|
-
recordFirmwareTaskError(
|
|
1559
|
-
taskIdentifier: downloadTask.taskIdentifier,
|
|
1560
|
-
error: error
|
|
1561
|
-
)
|
|
1562
|
-
}
|
|
1563
|
-
return
|
|
1564
|
-
}
|
|
1565
1213
|
guard let desc = downloadTask.taskDescription,
|
|
1566
1214
|
let (state, idx) = run(for: desc) else { return }
|
|
1567
1215
|
let ranges = lock.withLockValue { state.ranges }
|
|
@@ -1660,39 +1308,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
1660
1308
|
}
|
|
1661
1309
|
|
|
1662
1310
|
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
1663
|
-
if let descriptor = Self.decodeFirmwareTaskDescription(
|
|
1664
|
-
task.taskDescription
|
|
1665
|
-
) {
|
|
1666
|
-
let completion: (completed: Bool, error: Error?) =
|
|
1667
|
-
lock.withLockValue {
|
|
1668
|
-
let completed = completedFirmwareTasks.remove(
|
|
1669
|
-
task.taskIdentifier
|
|
1670
|
-
) != nil
|
|
1671
|
-
let recordedError = firmwareTaskErrors.removeValue(
|
|
1672
|
-
forKey: task.taskIdentifier
|
|
1673
|
-
)
|
|
1674
|
-
return (completed, recordedError)
|
|
1675
|
-
}
|
|
1676
|
-
if completion.completed {
|
|
1677
|
-
return
|
|
1678
|
-
}
|
|
1679
|
-
finishFirmwareTask(
|
|
1680
|
-
key: descriptor.key,
|
|
1681
|
-
result: .failure(
|
|
1682
|
-
completion.error ??
|
|
1683
|
-
(
|
|
1684
|
-
FirmwareArtifactStore.shared.isTransactionCancelled(
|
|
1685
|
-
descriptor.transactionId
|
|
1686
|
-
)
|
|
1687
|
-
? FirmwareBackgroundDownloadError.cancelled
|
|
1688
|
-
: nil
|
|
1689
|
-
) ??
|
|
1690
|
-
error ??
|
|
1691
|
-
FirmwareBackgroundDownloadError.transferFailed
|
|
1692
|
-
)
|
|
1693
|
-
)
|
|
1694
|
-
return
|
|
1695
|
-
}
|
|
1696
1311
|
guard let desc = task.taskDescription,
|
|
1697
1312
|
let (state, idx) = run(for: desc) else { return }
|
|
1698
1313
|
let ranges = lock.withLockValue { state.ranges }
|
|
@@ -1868,26 +1483,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
1868
1483
|
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
1869
1484
|
newRequest request: URLRequest,
|
|
1870
1485
|
completionHandler: @escaping (URLRequest?) -> Void) {
|
|
1871
|
-
if let descriptor = Self.decodeFirmwareTaskDescription(
|
|
1872
|
-
task.taskDescription
|
|
1873
|
-
) {
|
|
1874
|
-
guard
|
|
1875
|
-
request.url?.scheme?.lowercased() == "https",
|
|
1876
|
-
request.url?.host?.lowercased() == descriptor.hostname,
|
|
1877
|
-
request.url?.port == nil || request.url?.port == 443,
|
|
1878
|
-
request.url?.user == nil,
|
|
1879
|
-
request.url?.password == nil
|
|
1880
|
-
else {
|
|
1881
|
-
recordFirmwareTaskError(
|
|
1882
|
-
taskIdentifier: task.taskIdentifier,
|
|
1883
|
-
error: FirmwareBackgroundDownloadError.redirectRejected
|
|
1884
|
-
)
|
|
1885
|
-
completionHandler(nil)
|
|
1886
|
-
return
|
|
1887
|
-
}
|
|
1888
|
-
completionHandler(request)
|
|
1889
|
-
return
|
|
1890
|
-
}
|
|
1891
1486
|
if request.url?.scheme?.lowercased() != "https" {
|
|
1892
1487
|
OneKeyLog.error("RangeDownloader", "blocked redirect to non-HTTPS URL")
|
|
1893
1488
|
if let desc = task.taskDescription, let (state, _) = run(for: desc) {
|
|
@@ -1899,25 +1494,6 @@ public final class RangeDownloader: NSObject, URLSessionDownloadDelegate {
|
|
|
1899
1494
|
}
|
|
1900
1495
|
}
|
|
1901
1496
|
|
|
1902
|
-
private func recordFirmwareTaskError(
|
|
1903
|
-
taskIdentifier: Int,
|
|
1904
|
-
error: Error
|
|
1905
|
-
) {
|
|
1906
|
-
lock.withLockValue {
|
|
1907
|
-
if firmwareTaskErrors[taskIdentifier] == nil {
|
|
1908
|
-
firmwareTaskErrors[taskIdentifier] = error
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
private func firmwareTaskError(
|
|
1914
|
-
taskIdentifier: Int
|
|
1915
|
-
) -> Error? {
|
|
1916
|
-
lock.withLockValue {
|
|
1917
|
-
firmwareTaskErrors[taskIdentifier]
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
|
|
1921
1497
|
/// Called on the session delegate queue when all background events for this
|
|
1922
1498
|
/// session have been delivered after a background relaunch.
|
|
1923
1499
|
public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
|