103 lines
3.1 KiB
Swift
103 lines
3.1 KiB
Swift
|
import Foundation
|
||
|
|
||
|
struct DomainState: Codable {
|
||
|
|
||
|
/// The date of the last successful update
|
||
|
let date: Date
|
||
|
|
||
|
/// The last registered addresses
|
||
|
let addresses: Set<String>
|
||
|
|
||
|
/**
|
||
|
Number of successive errors signaled by the server.
|
||
|
|
||
|
Needed to prevent excessive requests and being flagged as abuse.
|
||
|
*/
|
||
|
let serverErrorCount: Int
|
||
|
|
||
|
/**
|
||
|
Number of errors on the client side.
|
||
|
|
||
|
Errors here will not force the algorithm to back off.
|
||
|
*/
|
||
|
let clientErrorCount: Int
|
||
|
|
||
|
/**
|
||
|
The time to wait before the next update attempt
|
||
|
*/
|
||
|
let currentWaitPeriod: TimeInterval
|
||
|
|
||
|
let lastResult: DNSChangeResponse
|
||
|
|
||
|
init(date: Date, addresses: Set<String>, serverErrorCount: Int, clientErrorCount: Int, currentWaitPeriod: TimeInterval, lastResult: DNSChangeResponse) {
|
||
|
self.date = date
|
||
|
self.addresses = addresses
|
||
|
self.serverErrorCount = serverErrorCount
|
||
|
self.clientErrorCount = clientErrorCount
|
||
|
self.currentWaitPeriod = currentWaitPeriod
|
||
|
self.lastResult = lastResult
|
||
|
}
|
||
|
|
||
|
init(initial date: Date) {
|
||
|
self.date = date
|
||
|
self.addresses = []
|
||
|
self.serverErrorCount = 0
|
||
|
self.clientErrorCount = 0
|
||
|
self.currentWaitPeriod = 0
|
||
|
self.lastResult = .success
|
||
|
}
|
||
|
|
||
|
var nextUpdate: Date {
|
||
|
date.addingTimeInterval(currentWaitPeriod)
|
||
|
}
|
||
|
|
||
|
|
||
|
private static let formatter: DateFormatter = {
|
||
|
let formatter = DateFormatter()
|
||
|
formatter.dateStyle = .short
|
||
|
formatter.timeStyle = .short
|
||
|
return formatter
|
||
|
}()
|
||
|
|
||
|
func logEntry(domain: String) -> String {
|
||
|
"\(domain);\(DomainState.formatter.string(from: date));\(addressList);\(serverErrorCount);\(clientErrorCount);\(currentWaitPeriod);\(lastResult.rawValue)"
|
||
|
}
|
||
|
|
||
|
var addressList: String {
|
||
|
addresses.sortedList
|
||
|
}
|
||
|
|
||
|
func needsUpdate(at now: Date, to addresses: Set<String>) -> Bool {
|
||
|
guard now >= nextUpdate else {
|
||
|
return false
|
||
|
}
|
||
|
return !self.addresses.subtracting(addresses).isEmpty
|
||
|
}
|
||
|
|
||
|
func updated(_ now: Date, from result: DNSChangeResponse, expecting addresses: Set<String>) -> DomainState {
|
||
|
let addresses = result.isSuccessStatus ? addresses : self.addresses
|
||
|
let serverError = result.isServerError ? serverErrorCount + 1 : 0
|
||
|
let clientError = result.isClientError ? clientErrorCount + 1 : 0
|
||
|
let waitTime = result.delayTime(previous: currentWaitPeriod)
|
||
|
return .init(
|
||
|
date: now,
|
||
|
addresses: addresses,
|
||
|
serverErrorCount: serverError,
|
||
|
clientErrorCount: clientError,
|
||
|
currentWaitPeriod: waitTime,
|
||
|
lastResult: result)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension DomainState: Equatable {
|
||
|
|
||
|
static func == (lhs: DomainState, rhs: DomainState) -> Bool {
|
||
|
lhs.date == rhs.date &&
|
||
|
lhs.addresses == rhs.addresses &&
|
||
|
lhs.clientErrorCount == rhs.clientErrorCount &&
|
||
|
lhs.serverErrorCount == rhs.serverErrorCount &&
|
||
|
lhs.currentWaitPeriod == rhs.currentWaitPeriod &&
|
||
|
lhs.lastResult == rhs.lastResult
|
||
|
}
|
||
|
}
|