@onekeyfe/react-native-range-downloader 3.0.81-alpha.7 → 3.0.81-alpha.9
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 +132 -68
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +39 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweepTest.kt +107 -0
- package/ios/FirmwareArtifactStore.swift +242 -172
- package/ios/RangeDownloadLogic.swift +194 -36
- package/ios/ReactNativeRangeDownloader.swift +9 -423
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +3 -3
- 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/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/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +15 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +32 -13
- package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +39 -8
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +6 -5
- package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +9 -9
- package/package.json +2 -2
- package/src/ReactNativeRangeDownloader.nitro.ts +3 -3
- package/ios/FirmwareBackgroundSessionEventRouter.swift +0 -17
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import CommonCrypto
|
|
3
3
|
|
|
4
|
+
enum FirmwareArtifactStoreError: Error, CustomStringConvertible {
|
|
5
|
+
case invalidInput(String)
|
|
6
|
+
case downloadFailed(String)
|
|
7
|
+
case integrityMismatch(String)
|
|
8
|
+
case readerInvalid(String)
|
|
9
|
+
case archiveInvalid(String)
|
|
10
|
+
|
|
11
|
+
var description: String {
|
|
12
|
+
switch self {
|
|
13
|
+
case let .invalidInput(message), let .downloadFailed(message),
|
|
14
|
+
let .integrityMismatch(message), let .readerInvalid(message),
|
|
15
|
+
let .archiveInvalid(message):
|
|
16
|
+
return message
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
enum FirmwareArtifactDeadlineError: Error {
|
|
5
22
|
case exceeded
|
|
6
23
|
}
|
|
@@ -30,16 +47,32 @@ enum FirmwareArtifactWallClockDeadline {
|
|
|
30
47
|
|
|
31
48
|
func firmwareArtifactDownloadKey(
|
|
32
49
|
transactionId: String,
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
taskId: String,
|
|
51
|
+
expectedSize: Int64?,
|
|
52
|
+
expectedSha256: String?
|
|
35
53
|
) -> String {
|
|
36
|
-
"\(transactionId)|\(expectedSize)|\(expectedSha256)"
|
|
54
|
+
"\(transactionId)|\(taskId)|\(expectedSize.map(String.init) ?? "unknown")|\(expectedSha256 ?? "unknown")"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func firmwareArtifactDownloadToken(
|
|
58
|
+
expectedSha256: String?,
|
|
59
|
+
url: String
|
|
60
|
+
) -> String {
|
|
61
|
+
if let expectedSha256 {
|
|
62
|
+
return expectedSha256.lowercased()
|
|
63
|
+
}
|
|
64
|
+
let urlData = Data(url.utf8)
|
|
65
|
+
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
|
|
66
|
+
urlData.withUnsafeBytes {
|
|
67
|
+
_ = CC_SHA256($0.baseAddress, CC_LONG(urlData.count), &hash)
|
|
68
|
+
}
|
|
69
|
+
return hash.map { String(format: "%02x", $0) }.joined()
|
|
37
70
|
}
|
|
38
71
|
|
|
39
72
|
func firmwareArtifactPartialFileName(
|
|
40
73
|
transactionId: String,
|
|
41
74
|
taskId: String,
|
|
42
|
-
|
|
75
|
+
downloadToken: String
|
|
43
76
|
) -> String {
|
|
44
77
|
let transactionData = Data(transactionId.utf8)
|
|
45
78
|
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
|
|
@@ -50,13 +83,14 @@ func firmwareArtifactPartialFileName(
|
|
|
50
83
|
.prefix(8)
|
|
51
84
|
.map { String(format: "%02x", $0) }
|
|
52
85
|
.joined()
|
|
53
|
-
return "\(
|
|
86
|
+
return "\(downloadToken).\(taskId).\(transactionToken).partial"
|
|
54
87
|
}
|
|
55
88
|
|
|
56
89
|
func firmwareArtifactContentRangeIsValid(
|
|
57
90
|
_ value: String,
|
|
58
91
|
expectedStart: Int64,
|
|
59
|
-
expectedTotal: Int64
|
|
92
|
+
expectedTotal: Int64?,
|
|
93
|
+
maxBytes: Int64
|
|
60
94
|
) -> Bool {
|
|
61
95
|
guard value.lowercased().hasPrefix("bytes ") else {
|
|
62
96
|
return false
|
|
@@ -67,10 +101,11 @@ func firmwareArtifactContentRangeIsValid(
|
|
|
67
101
|
else {
|
|
68
102
|
return false
|
|
69
103
|
}
|
|
104
|
+
let totalMatches = expectedTotal.map { total == $0 } ?? (total > 0 && total <= maxBytes)
|
|
70
105
|
return bounds.start == expectedStart &&
|
|
71
106
|
bounds.end >= bounds.start &&
|
|
72
107
|
bounds.end < total &&
|
|
73
|
-
|
|
108
|
+
totalMatches
|
|
74
109
|
}
|
|
75
110
|
|
|
76
111
|
func firmwareArtifactResponseFits(
|
|
@@ -85,40 +120,163 @@ func firmwareArtifactResponseFits(
|
|
|
85
120
|
expectedContentLength <= maxBytes - baseOffset
|
|
86
121
|
}
|
|
87
122
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
case deadlineExceeded
|
|
92
|
-
case redirectRejected
|
|
93
|
-
case responseRejected
|
|
94
|
-
case sizeRejected
|
|
95
|
-
case tlsRejected
|
|
96
|
-
case transferFailed
|
|
123
|
+
let firmwareArtifactFinalGrace: TimeInterval = 24 * 60 * 60
|
|
124
|
+
let firmwareArtifactPartialGrace: TimeInterval = 7 * 24 * 60 * 60
|
|
125
|
+
let firmwareArtifactScratchGrace: TimeInterval = firmwareArtifactPartialGrace
|
|
97
126
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
func firmwareArtifactIdentifierIsSafe(_ value: String) -> Bool {
|
|
128
|
+
value.range(
|
|
129
|
+
of: "^[A-Za-z0-9._:-]{1,160}$",
|
|
130
|
+
options: .regularExpression
|
|
131
|
+
) != nil
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private func firmwareArtifactScratchNameIsValid(
|
|
135
|
+
_ name: String,
|
|
136
|
+
prefix: String
|
|
137
|
+
) -> Bool {
|
|
138
|
+
guard name.hasPrefix(prefix) else {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
let rawUUID = String(name.dropFirst(prefix.count))
|
|
142
|
+
guard
|
|
143
|
+
rawUUID.count == 36,
|
|
144
|
+
let uuid = UUID(uuidString: rawUUID)
|
|
145
|
+
else {
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
return uuid.uuidString.caseInsensitiveCompare(rawUUID) == .orderedSame
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private func firmwareArtifactEntrySize(
|
|
152
|
+
_ entryURL: URL,
|
|
153
|
+
values: URLResourceValues,
|
|
154
|
+
fileManager: FileManager
|
|
155
|
+
) -> Int64 {
|
|
156
|
+
if values.isRegularFile == true {
|
|
157
|
+
return Int64(values.fileSize ?? 0)
|
|
158
|
+
}
|
|
159
|
+
guard
|
|
160
|
+
values.isDirectory == true,
|
|
161
|
+
let enumerator = fileManager.enumerator(
|
|
162
|
+
at: entryURL,
|
|
163
|
+
includingPropertiesForKeys: [
|
|
164
|
+
.isRegularFileKey,
|
|
165
|
+
.isSymbolicLinkKey,
|
|
166
|
+
.fileSizeKey,
|
|
167
|
+
],
|
|
168
|
+
options: [],
|
|
169
|
+
errorHandler: nil
|
|
170
|
+
)
|
|
171
|
+
else {
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
var size: Int64 = 0
|
|
175
|
+
for case let childURL as URL in enumerator {
|
|
176
|
+
guard
|
|
177
|
+
let childValues = try? childURL.resourceValues(
|
|
178
|
+
forKeys: [.isRegularFileKey, .isSymbolicLinkKey, .fileSizeKey]
|
|
179
|
+
),
|
|
180
|
+
childValues.isRegularFile == true,
|
|
181
|
+
childValues.isSymbolicLink != true
|
|
182
|
+
else {
|
|
183
|
+
continue
|
|
116
184
|
}
|
|
185
|
+
size += Int64(childValues.fileSize ?? 0)
|
|
117
186
|
}
|
|
187
|
+
return size
|
|
188
|
+
}
|
|
118
189
|
|
|
119
|
-
|
|
120
|
-
|
|
190
|
+
func sweepFirmwareArtifactOrphansAtRoot(
|
|
191
|
+
_ rootURL: URL,
|
|
192
|
+
retainedSha256: Set<String>,
|
|
193
|
+
activeSha256: Set<String>,
|
|
194
|
+
openPaths: Set<String>,
|
|
195
|
+
now: Date = Date(),
|
|
196
|
+
fileManager: FileManager = .default
|
|
197
|
+
) throws -> (deletedFiles: Int, deletedBytes: Int64) {
|
|
198
|
+
var deletedFiles = 0
|
|
199
|
+
var deletedBytes: Int64 = 0
|
|
200
|
+
let resourceKeys: Set<URLResourceKey> = [
|
|
201
|
+
.isRegularFileKey,
|
|
202
|
+
.isDirectoryKey,
|
|
203
|
+
.isSymbolicLinkKey,
|
|
204
|
+
.contentModificationDateKey,
|
|
205
|
+
.fileSizeKey,
|
|
206
|
+
]
|
|
207
|
+
let entries = try fileManager.contentsOfDirectory(
|
|
208
|
+
at: rootURL,
|
|
209
|
+
includingPropertiesForKeys: Array(resourceKeys),
|
|
210
|
+
options: []
|
|
211
|
+
)
|
|
212
|
+
for entryURL in entries {
|
|
213
|
+
let name = entryURL.lastPathComponent
|
|
214
|
+
let values = try entryURL.resourceValues(forKeys: resourceKeys)
|
|
215
|
+
let isArchiveScratch = firmwareArtifactScratchNameIsValid(
|
|
216
|
+
name,
|
|
217
|
+
prefix: "archive-"
|
|
218
|
+
)
|
|
219
|
+
let isPromoteScratch = firmwareArtifactScratchNameIsValid(
|
|
220
|
+
name,
|
|
221
|
+
prefix: ".promote-"
|
|
222
|
+
)
|
|
223
|
+
let isScratchCandidate = values.isSymbolicLink != true &&
|
|
224
|
+
((isArchiveScratch && values.isDirectory == true) ||
|
|
225
|
+
(isPromoteScratch && values.isRegularFile == true))
|
|
226
|
+
if isScratchCandidate {
|
|
227
|
+
guard
|
|
228
|
+
let modifiedAt = values.contentModificationDate,
|
|
229
|
+
now.timeIntervalSince(modifiedAt) >= firmwareArtifactScratchGrace
|
|
230
|
+
else {
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
233
|
+
let size = firmwareArtifactEntrySize(
|
|
234
|
+
entryURL,
|
|
235
|
+
values: values,
|
|
236
|
+
fileManager: fileManager
|
|
237
|
+
)
|
|
238
|
+
try fileManager.removeItem(at: entryURL)
|
|
239
|
+
deletedFiles += 1
|
|
240
|
+
deletedBytes += size
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
guard
|
|
245
|
+
values.isRegularFile == true,
|
|
246
|
+
values.isSymbolicLink != true,
|
|
247
|
+
name.count >= 64
|
|
248
|
+
else {
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
let sha256 = String(name.prefix(64))
|
|
252
|
+
guard
|
|
253
|
+
sha256.range(of: "^[a-f0-9]{64}$", options: .regularExpression) != nil,
|
|
254
|
+
!retainedSha256.contains(sha256),
|
|
255
|
+
!activeSha256.contains(sha256),
|
|
256
|
+
!openPaths.contains(entryURL.path)
|
|
257
|
+
else {
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
let grace: TimeInterval
|
|
261
|
+
if name.hasSuffix(".bin") {
|
|
262
|
+
grace = firmwareArtifactFinalGrace
|
|
263
|
+
} else if name.hasSuffix(".partial") {
|
|
264
|
+
grace = firmwareArtifactPartialGrace
|
|
265
|
+
} else {
|
|
266
|
+
continue
|
|
267
|
+
}
|
|
268
|
+
guard
|
|
269
|
+
let modifiedAt = values.contentModificationDate,
|
|
270
|
+
now.timeIntervalSince(modifiedAt) >= grace
|
|
271
|
+
else {
|
|
272
|
+
continue
|
|
273
|
+
}
|
|
274
|
+
let size = Int64(values.fileSize ?? 0)
|
|
275
|
+
try fileManager.removeItem(at: entryURL)
|
|
276
|
+
deletedFiles += 1
|
|
277
|
+
deletedBytes += size
|
|
121
278
|
}
|
|
279
|
+
return (deletedFiles, deletedBytes)
|
|
122
280
|
}
|
|
123
281
|
|
|
124
282
|
// MARK: - Dependency-free RangeDownloader logic (OCDS §4 / §5)
|