blob: 81940acd21ea36c5cf89565b31cdebfa8e90015d (
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
|
//
// UpdateAddressCacheOperation.swift
// MullvadVPN
//
// Created by pronebird on 08/12/2021.
// Copyright © 2021 Mullvad VPN AB. All rights reserved.
//
import Foundation
extension AddressCache {
enum CacheUpdateResult {
/// Address cache update was throttled as it was requested too early.
case throttled(_ lastUpdateDate: Date)
/// Address cache is successfully updated.
case finished
}
class UpdateAddressCacheOperation: ResultOperation<CacheUpdateResult, Error> {
private let queue: DispatchQueue
private let restClient: REST.Client
private let store: AddressCache.Store
private let updateInterval: TimeInterval
private var requestTask: 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
super.init(completionQueue: queue, completionHandler: completionHandler)
}
override func main() {
queue.async {
self.startUpdate()
}
}
override func cancel() {
super.cancel()
queue.async {
self.requestTask?.cancel()
self.requestTask = nil
}
}
private func startUpdate() {
guard !isCancelled else {
finish(completion: .cancelled)
return
}
let lastUpdate = store.getLastUpdateDateAndWait()
let nextUpdate = Date(timeInterval: updateInterval, since: lastUpdate)
guard nextUpdate <= Date() else {
finish(completion: .success(.throttled(lastUpdate)))
return
}
requestTask = restClient.getAddressList(retryStrategy: .default) { completion in
self.queue.async {
self.handleResponse(completion)
}
}
}
private func handleResponse(_ completion: OperationCompletion<[AnyIPEndpoint], REST.Error>) {
switch completion {
case .success(let newEndpoints):
self.store.setEndpoints(newEndpoints) { error in
if let error = error {
self.finish(completion: .failure(error))
} else {
self.finish(completion: .success(.finished))
}
}
case .failure(let error):
if case URLError.cancelled = error {
self.finish(completion: .cancelled)
} else {
self.finish(completion: .failure(error))
}
case .cancelled:
self.finish(completion: .cancelled)
}
}
}
}
extension OperationCompletion where Success == AddressCache.CacheUpdateResult {
var isTaskCompleted: Bool {
switch self {
case .success:
return true
case .cancelled, .failure:
return false
}
}
}
|