Skip to content

Commit

Permalink
Add known failure store
Browse files Browse the repository at this point in the history
  • Loading branch information
quanganhdo committed May 14, 2024
1 parent 9906b94 commit 1b43462
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public enum NetworkProtectionError: LocalizedError, CustomNSError {
// Subscription errors
case vpnAccessRevoked

// Login item errors
case loginItemVersionMismatched

// Unhandled error
case unhandledError(function: String, line: Int, error: Error)

Expand Down Expand Up @@ -94,6 +97,7 @@ public enum NetworkProtectionError: LocalizedError, CustomNSError {
case .failedToParseRedeemResponse: return 111
case .invalidAuthToken: return 112
case .serverListInconsistency: return 113
case .loginItemVersionMismatched: return 114
// 200+ - Keychain errors
case .failedToCastKeychainValueToData: return 300
case .keychainReadError: return 201
Expand Down Expand Up @@ -138,7 +142,8 @@ public enum NetworkProtectionError: LocalizedError, CustomNSError {
.wireGuardDnsResolution,
.startWireGuardBackend,
.noAuthTokenFound,
.vpnAccessRevoked:
.vpnAccessRevoked,
.loginItemVersionMismatched:
return [:]
case .failedToFetchServerList(let error),
.failedToFetchRegisteredServers(let error),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public enum DebugCommand: Codable {
case removeSystemExtension
case removeVPNConfiguration
case sendTestNotification
case simulateKnownFailure
case disableConnectOnDemandAndShutDown
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public enum NetworkProtectionClientError: CustomNSError, NetworkProtectionErrorC
case failedToParseRedeemResponse(Error)
case invalidAuthToken
case accessDenied
case loginItemVersionMismatched

var networkProtectionError: NetworkProtectionError {
switch self {
Expand All @@ -58,6 +59,7 @@ public enum NetworkProtectionClientError: CustomNSError, NetworkProtectionErrorC
case .failedToParseRedeemResponse(let error): return .failedToParseRedeemResponse(error)
case .invalidAuthToken: return .invalidAuthToken
case .accessDenied: return .vpnAccessRevoked
case .loginItemVersionMismatched: return .loginItemVersionMismatched
}
}

Expand All @@ -77,6 +79,7 @@ public enum NetworkProtectionClientError: CustomNSError, NetworkProtectionErrorC
case .failedToParseRedeemResponse: return 11
case .invalidAuthToken: return 12
case .accessDenied: return 13
case .loginItemVersionMismatched: return 14
}
}

Expand All @@ -96,7 +99,8 @@ public enum NetworkProtectionClientError: CustomNSError, NetworkProtectionErrorC
.invalidInviteCode,
.failedToRetrieveAuthToken,
.invalidAuthToken,
.accessDenied:
.accessDenied,
.loginItemVersionMismatched:
return [:]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public enum NetworkProtectionNotification: String {
// Error Events
case tunnelErrorChanged
case controllerErrorChanged
case knownFailureUpdated

// New Status Observer
case requestStatusUpdate
Expand Down
2 changes: 2 additions & 0 deletions Sources/NetworkProtection/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,8 @@ open class PacketTunnelProvider: NEPacketTunnelProvider {
handleExpireRegistrationKey(completionHandler: completionHandler)
case .sendTestNotification:
handleSendTestNotification(completionHandler: completionHandler)
case .simulateKnownFailure:
completionHandler?(nil)
case .disableConnectOnDemandAndShutDown:
Task { [weak self] in
await self?.attemptShutdown {
Expand Down
38 changes: 38 additions & 0 deletions Sources/NetworkProtection/Status/KnownFailure.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// KnownFailure.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

@objc
final public class KnownFailure: NSObject, Codable {
public let domain: String
public let code: Int
public let localizedDescription: String

public init?(_ error: Error?) {
guard let nsError = error as? NSError else { return nil }

domain = nsError.domain
code = nsError.code
localizedDescription = nsError.localizedDescription
}

public override var description: String {
"Error domain=\(domain) code=\(code)\n\(localizedDescription)"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// KnownFailureObserver.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Combine
import Foundation
import NetworkExtension

public protocol KnownFailureObserver {
var publisher: AnyPublisher<KnownFailure?, Never> { get }
var recentValue: KnownFailure? { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//

Check failure on line 1 in Sources/NetworkProtection/Status/KnownFailureObserver/KnownFailureObserverThroughDistributedNotifications.swift

View workflow job for this annotation

GitHub Actions / Run SwiftLint

Header comments should be consistent with project patterns (file_header)
// KnownFailureObserver.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Combine
import Foundation
import NetworkExtension
import NotificationCenter

public class KnownFailureObserverThroughDistributedNotifications: KnownFailureObserver {
public lazy var publisher = subject.eraseToAnyPublisher()
public var recentValue: KnownFailure? {
subject.value
}

private let subject = CurrentValueSubject<KnownFailure?, Never>(nil)

private let distributedNotificationCenter: DistributedNotificationCenter
private var cancellable: AnyCancellable?

public init(distributedNotificationCenter: DistributedNotificationCenter = .default()) {
self.distributedNotificationCenter = distributedNotificationCenter

start()
}

private func start() {
cancellable = distributedNotificationCenter.publisher(for: .knownFailureUpdated).sink { [weak self] notification in
self?.handleKnownFailureUpdated(notification)
}
}

private func handleKnownFailureUpdated(_ notification: Notification) {
if let object = notification.object as? String,
let data = object.data(using: .utf8),
let failure = try? JSONDecoder().decode(KnownFailure.self, from: data) {
subject.send(failure)
} else {
subject.send(nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public protocol NetworkProtectionStatusReporter {
var connectivityIssuesObserver: ConnectivityIssueObserver { get }
var controllerErrorMessageObserver: ControllerErrorMesssageObserver { get }
var dataVolumeObserver: DataVolumeObserver { get }
var knownFailureObserver: KnownFailureObserver { get }

func forceRefresh()
}
Expand Down Expand Up @@ -70,6 +71,7 @@ public final class DefaultNetworkProtectionStatusReporter: NetworkProtectionStat
public let connectivityIssuesObserver: ConnectivityIssueObserver
public let controllerErrorMessageObserver: ControllerErrorMesssageObserver
public let dataVolumeObserver: DataVolumeObserver
public let knownFailureObserver: KnownFailureObserver

// MARK: - Init & deinit

Expand All @@ -79,6 +81,7 @@ public final class DefaultNetworkProtectionStatusReporter: NetworkProtectionStat
connectivityIssuesObserver: ConnectivityIssueObserver,
controllerErrorMessageObserver: ControllerErrorMesssageObserver,
dataVolumeObserver: DataVolumeObserver,
knownFailureObserver: KnownFailureObserver,
distributedNotificationCenter: DistributedNotificationCenter = .default()) {

self.statusObserver = statusObserver
Expand All @@ -87,6 +90,7 @@ public final class DefaultNetworkProtectionStatusReporter: NetworkProtectionStat
self.connectivityIssuesObserver = connectivityIssuesObserver
self.controllerErrorMessageObserver = controllerErrorMessageObserver
self.dataVolumeObserver = dataVolumeObserver
self.knownFailureObserver = knownFailureObserver
self.distributedNotificationCenter = distributedNotificationCenter

start()
Expand Down

0 comments on commit 1b43462

Please sign in to comment.