diff options
| author | Andrew Bulhak <andrew.bulhak@mullvad.net> | 2024-05-27 11:06:08 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2024-05-29 13:40:11 +0200 |
| commit | 61ca50af93d185c9b7563adf0740bc06446ca96e (patch) | |
| tree | 482954c41c8bfc5a816a0ef4a002bfc38590e585 | |
| parent | b26e90d1cd8bcf2730e1238656f907330d33f773 (diff) | |
| download | mullvadvpn-61ca50af93d185c9b7563adf0740bc06446ca96e.tar.xz mullvadvpn-61ca50af93d185c9b7563adf0740bc06446ca96e.zip | |
Break longer cases in reducer out into subfunctions
| -rw-r--r-- | ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift | 157 |
1 files changed, 90 insertions, 67 deletions
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift index 50379fdd63..3fc7d9f8b3 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift @@ -62,45 +62,10 @@ extension PacketTunnelActor { .startConnection(options.selectedRelay.map { .preSelected($0) } ?? .random), ] case .stop: - // a call of the reducer produces one state transition and a sequence of effects. In the app, a stop transitions to .disconnecting, shuts down various processes, and finally transitions to .disconnected. We currently do this by having an effect which acknowledges the completion of disconnection and just sets the state. This is a bit messy, and could possibly do with some rethinking. - switch state { - case let .connected(connState), let .connecting(connState), let .reconnecting(connState), - let .negotiatingPostQuantumKey(connState, _): - state = .disconnecting(connState) - return [ - .stopTunnelMonitor, - .stopDefaultPathObserver, - .stopTunnelAdapter, - .setDisconnectedState, - ] - case .error: - return [ - .stopDefaultPathObserver, - .stopTunnelAdapter, - .setDisconnectedState, - ] - - case .initial, .disconnected: - return [] - - case .disconnecting: - assertionFailure("stop(): out of order execution.") - return [] - } + return subreducerForStop(&state) case let .reconnect(nextRelay, reason: reason): - switch state { - case .disconnected, .disconnecting, .initial: - // There is no connection monitoring going on when exchanging keys. - // The procedure starts from scratch for each reconnection attempts. - return [] - case .connecting, .connected, .reconnecting, .error, .negotiatingPostQuantumKey: - if reason == .userInitiated { - return [.stopTunnelMonitor, .restartConnection(nextRelay, reason)] - } else { - return [.restartConnection(nextRelay, reason)] - } - } + return subreducerForReconnect(state, reason, nextRelay) case let .error(reason): // the transition from error to blocked state currently has side-effects, so will be handled as an effect for now. @@ -108,42 +73,14 @@ extension PacketTunnelActor { case let .notifyKeyRotated(lastKeyRotation): // the cacheActiveKey operation is currently effectful, starting a key-switch task within the mutation of state, so this is entirely done in an effect. Perhaps teasing effects out of state mutation is a future refactoring? - guard state.keyPolicy == .useCurrent else { return [] } - return [.cacheActiveKey(lastKeyRotation)] case .switchKey: - let oldKeyPolicy = state.keyPolicy - state.mutateKeyPolicy { keyPolicy in - if case .usePrior = keyPolicy { - keyPolicy = .useCurrent - } - } - if case .error = state { return [] } - return state.keyPolicy != oldKeyPolicy ? [.reconnect(.random)] : [] + return subreducerForSwitchKey(&state) case let .monitorEvent(event): - switch event { - case .connectionEstablished: - switch state { - case var .connecting(connState), var .reconnecting(connState): - // Reset connection attempt once successfully connected. - connState.connectionAttemptCount = 0 - state = .connected(connState) - - case .initial, .connected, .disconnecting, .disconnected, .error, .negotiatingPostQuantumKey: - break - } - return [] - case .connectionLost: - switch state { - case .connecting, .reconnecting, .connected: - return [.restartConnection(.random, .connectionLoss)] - case .initial, .disconnected, .disconnecting, .error, .negotiatingPostQuantumKey: - return [] - } - } + return subreducerForTunnelMonitorEvent(event, &state) case let .networkReachability(defaultPath): let newReachability = defaultPath.networkReachability @@ -155,6 +92,92 @@ extension PacketTunnelActor { } } + // Parts of the reducer path broken out for specific incoming events + + fileprivate static func subreducerForStop(_ state: inout State) -> [PacketTunnelActor.Effect] { + // a call of the reducer produces one state transition and a sequence of effects. In the app, a stop transitions to .disconnecting, shuts down various processes, and finally transitions to .disconnected. We currently do this by having an effect which acknowledges the completion of disconnection and just sets the state. This is a bit messy, and could possibly do with some rethinking. + switch state { + case let .connected(connState), let .connecting(connState), let .reconnecting(connState), + let .negotiatingPostQuantumKey(connState, _): + state = .disconnecting(connState) + return [ + .stopTunnelMonitor, + .stopDefaultPathObserver, + .stopTunnelAdapter, + .setDisconnectedState, + ] + case .error: + return [ + .stopDefaultPathObserver, + .stopTunnelAdapter, + .setDisconnectedState, + ] + + case .initial, .disconnected: + return [] + + case .disconnecting: + assertionFailure("stop(): out of order execution.") + return [] + } + } + + fileprivate static func subreducerForReconnect( + _ state: State, + _ reason: PacketTunnelActor.ReconnectReason, + _ nextRelay: NextRelay + ) -> [PacketTunnelActor.Effect] { + switch state { + case .disconnected, .disconnecting, .initial: + // There is no connection monitoring going on when exchanging keys. + // The procedure starts from scratch for each reconnection attempts. + return [] + case .connecting, .connected, .reconnecting, .error, .negotiatingPostQuantumKey: + if reason == .userInitiated { + return [.stopTunnelMonitor, .restartConnection(nextRelay, reason)] + } else { + return [.restartConnection(nextRelay, reason)] + } + } + } + + fileprivate static func subreducerForSwitchKey(_ state: inout State) -> [PacketTunnelActor.Effect] { + let oldKeyPolicy = state.keyPolicy + state.mutateKeyPolicy { keyPolicy in + if case .usePrior = keyPolicy { + keyPolicy = .useCurrent + } + } + if case .error = state { return [] } + return state.keyPolicy != oldKeyPolicy ? [.reconnect(.random)] : [] + } + + fileprivate static func subreducerForTunnelMonitorEvent( + _ event: TunnelMonitorEvent, + _ state: inout State + ) -> [PacketTunnelActor.Effect] { + switch event { + case .connectionEstablished: + switch state { + case var .connecting(connState), var .reconnecting(connState): + // Reset connection attempt once successfully connected. + connState.connectionAttemptCount = 0 + state = .connected(connState) + + case .initial, .connected, .disconnecting, .disconnected, .error, .negotiatingPostQuantumKey: + break + } + return [] + case .connectionLost: + switch state { + case .connecting, .reconnecting, .connected: + return [.restartConnection(.random, .connectionLoss)] + case .initial, .disconnected, .disconnecting, .error, .negotiatingPostQuantumKey: + return [] + } + } + } + func runReducer(_ event: Event) -> [Effect] { PacketTunnelActor.reducer(&state, event) } |
