blob: 4ca1d4d3c7646de7c1538f3d7a8591e92505d789 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
//
// UpdateAddressCacheOperation.swift
// MullvadVPN
//
// Created by pronebird on 08/12/2021.
// Copyright © 2021 Mullvad VPN AB. All rights reserved.
//
import Foundation
extension AddressCache {
enum CacheUpdateResult {
/// Operation was cancelled.
case cancelled
/// Address cache update was throttled as it was requested too early.
case throttled(_ lastUpdateDate: Date)
/// Failure to update address cache.
case failure(Error)
/// Address cache is successfully updated.
case success
var isTaskCompleted: Bool {
switch self {
case .cancelled, .failure:
return false
case .success, .throttled:
return true
}
}
}
class UpdateAddressCacheOperation: AsyncOperation {
typealias CompletionHandler = (_ result: CacheUpdateResult) -> Void
private let queue: DispatchQueue
private let restClient: REST.Client
private let store: AddressCache.Store
private let updateInterval: TimeInterval
private var completionHandler: CompletionHandler?
private var restCancellationHandle: Cancellable?
init(queue: DispatchQueue, restClient: REST.Client, store: AddressCache.Store, updateInterval: TimeInterval, completionHandler: CompletionHandler?) {
self.queue = queue
self.restClient = restClient
self.store = store
self.updateInterval = updateInterval
self.completionHandler = completionHandler
}
override func cancel() {
queue.async {
super.cancel()
self.restCancellationHandle?.cancel()
}
}
override func main() {
queue.async {
self.startUpdate()
}
}
private func startUpdate() {
guard !isCancelled else {
finish(with: .cancelled)
return
}
let lastUpdate = store.getLastUpdateDateAndWait()
let nextUpdate = Date(timeInterval: updateInterval, since: lastUpdate)
guard nextUpdate <= Date() else {
finish(with: .throttled(lastUpdate))
return
}
restCancellationHandle = restClient.getAddressList()
.execute(retryStrategy: .default) { restResult in
self.queue.async {
switch restResult {
case .success(let newEndpoints):
self.store.setEndpoints(newEndpoints) { error in
self.queue.async {
if let error = error {
self.finish(with: .failure(error))
} else {
self.finish(with: .success)
}
}
}
case .failure(let error):
if case URLError.cancelled = error {
self.finish(with: .cancelled)
} else {
self.finish(with: .failure(error))
}
}
}
}
}
private func finish(with result: CacheUpdateResult) {
completionHandler?(result)
completionHandler = nil
}
}
}
|