diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-02-04 13:46:43 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-02-08 10:55:21 +0100 |
| commit | 588752b3a054a9ea43c1ca932ca6a8f44be3a2bc (patch) | |
| tree | 3bf9066109df3b474736de9d0dd364a8e0ab4bae | |
| parent | 64aa3a07471d850e8b6f4c5ac950486eaad0e70e (diff) | |
| download | mullvadvpn-588752b3a054a9ea43c1ca932ca6a8f44be3a2bc.tar.xz mullvadvpn-588752b3a054a9ea43c1ca932ca6a8f44be3a2bc.zip | |
Pick random relay when all relays within the list have zero weight.
Fix roulette algorithm to skip relays with zero weight.
| -rw-r--r-- | ios/MullvadVPN/RelaySelector.swift | 40 |
1 files changed, 31 insertions, 9 deletions
diff --git a/ios/MullvadVPN/RelaySelector.swift b/ios/MullvadVPN/RelaySelector.swift index a3f9f7e474..f0bb12ce7f 100644 --- a/ios/MullvadVPN/RelaySelector.swift +++ b/ios/MullvadVPN/RelaySelector.swift @@ -36,16 +36,11 @@ enum RelaySelector {} extension RelaySelector { static func evaluate(relays: REST.ServerRelaysResponse, constraints: RelayConstraints) -> RelaySelectorResult? { - let filteredRelays = Self.applyConstraints(constraints, relays: Self.parseRelaysResponse(relays)) - let totalWeight = filteredRelays.reduce(0) { $0 + $1.relay.weight } + let filteredRelays = applyConstraints(constraints, relays: Self.parseRelaysResponse(relays)) - guard totalWeight > 0 else { return nil } - guard var i = (0...totalWeight).randomElement() else { return nil } - - let relayWithLocation = filteredRelays.first { (relayWithLocation) -> Bool in - i -= relayWithLocation.relay.weight - return i <= 0 - }.unsafelyUnwrapped + guard let relayWithLocation = pickRandomRelay(relays: filteredRelays) else { + return nil + } guard let port = relays.wireguard.portRanges.randomElement()?.randomElement() else { return nil @@ -93,6 +88,33 @@ extension RelaySelector { } } + private static func pickRandomRelay(relays: [RelayWithLocation]) -> RelayWithLocation? { + let totalWeight = relays.reduce(0) { accummulatedWeight, relayWithLocation in + return accummulatedWeight + relayWithLocation.relay.weight + } + + // Return random relay when all relays within the list have zero weight. + guard totalWeight > 0 else { + return relays.randomElement() + } + + // Pick a random number in the range 1 - totalWeight. This choses the relay with a + // non-zero weight. + var i = (1...totalWeight).randomElement()! + + let randomRelay = relays.first { (relayWithLocation) -> Bool in + let (result, isOverflow) = i.subtractingReportingOverflow(relayWithLocation.relay.weight) + + i = isOverflow ? 0 : result + + return i == 0 + } + + precondition(randomRelay != nil, "At least one relay must've had a weight above 0") + + return randomRelay + } + private static func parseRelaysResponse(_ response: REST.ServerRelaysResponse) -> [RelayWithLocation] { return response.wireguard.relays.compactMap { (serverRelay) -> RelayWithLocation? in guard let serverLocation = response.locations[serverRelay.location] else { return nil } |
