TeraRecoveryCompletion.swift (3203B)
1 import Foundation 2 import RadrootsKit 3 4 struct TeraRecoveryUploadReceipt: Sendable { 5 let parent: String 6 let revision: UInt64 7 let attempt: String 8 let uploadURL: String 9 let media: TeraPreparedMedia 10 let response: TeraAddBackgroundUploadReceipt 11 12 init(snapshot: RadrootsBackgroundTransferSnapshot, owner: TeraNativeUploadRecoveryOwner) throws { 13 guard snapshot.state == .awaitingVerification || snapshot.state == .completed, 14 let identity = TeraBackgroundUploadRequest.transferIdentity(snapshot.identifier), 15 identity.draftID == owner.id, identity.revision <= owner.revision, 16 let hash = snapshot.request.expectedSourceSHA256, 17 let media = owner.media.first(where: { $0.sha256 == hash }), 18 owner.media.filter({ $0.sha256 == hash }).count == 1, 19 let url = media.remoteURL, let uploadURL = owner.uploadURLs[url], 20 snapshot.request.remoteURL.absoluteString == uploadURL 21 || (owner.verifiedURLs.contains(url) && snapshot.request.remoteURL.absoluteString == url), 22 try TeraBackgroundUploadRequest.persistedRequestMatchesMedia(snapshot.request, media: media, uploadURL: uploadURL), 23 let response = snapshot.response, let status = UInt16(exactly: response.statusCode), 24 let body = response.body, 25 body.count <= 16384, (response.mediaType?.utf8.count ?? 0) <= 8192, 26 (response.contentEncoding?.utf8.count ?? 0) <= 8192, 27 body.count + (response.mediaType?.utf8.count ?? 0) + (response.contentEncoding?.utf8.count ?? 0) <= 16384 28 else { throw TeraNativeRecoveryFault.associationMismatch } 29 parent = identity.draftID 30 revision = identity.revision 31 attempt = identity.attempt 32 self.uploadURL = snapshot.request.remoteURL.absoluteString 33 self.media = media 34 self.response = .init(identifier: snapshot.identifier.rawValue, draftID: parent, expectedRevision: revision, 35 statusCode: status, mediaType: response.mediaType, contentEncoding: response.contentEncoding, body: body) 36 } 37 } 38 39 struct TeraRecoveryCompletionReceipt: Sendable, Equatable { 40 let parent: String 41 let attempt: String 42 let canonicalURL: String 43 let sha256: String 44 let mediaType: String 45 let byteSize: UInt64 46 let verifiedAtUnixMS: UInt64 47 48 func confirm(_ input: TeraRecoveryUploadReceipt) throws { 49 guard parent == input.parent, attempt == input.attempt, canonicalURL == input.media.remoteURL, 50 sha256 == input.media.sha256, mediaType == input.media.mediaType, byteSize == input.media.byteSize, 51 verifiedAtUnixMS > 0, verifiedAtUnixMS <= UInt64(Int64.max) else { throw TeraComposerAcknowledgment.unconfirmed } 52 } 53 } 54 55 extension TeraRuntimeClient { 56 func recoverNativeUpload(_ receipt: TeraRecoveryUploadReceipt, media: TeraPreparedMediaHandle) async throws -> TeraRecoveryCompletionReceipt { 57 try await addOperation("runtime.recovery.complete") { try await $0.recoverNativeUpload(receipt, media: media) } 58 } 59 } 60 61 extension TeraRuntimeBackend { 62 func recoverNativeUpload(_: TeraRecoveryUploadReceipt, media _: TeraPreparedMediaHandle) async throws -> TeraRecoveryCompletionReceipt { 63 throw TeraComposerAcknowledgment.unconfirmed 64 } 65 }