mirror of
https://github.com/DefinedNet/mobile_nebula.git
synced 2025-02-15 08:15:27 +00:00
Work on making DNUpdate use an async timer implementation
This commit is contained in:
parent
e4940d3e3a
commit
8ee9979b16
4 changed files with 93 additions and 72 deletions
|
@ -1,7 +1,7 @@
|
|||
import NetworkExtension
|
||||
import MobileNebula
|
||||
import os.log
|
||||
import NetworkExtension
|
||||
import SwiftyJSON
|
||||
import os.log
|
||||
|
||||
enum VPNStartError: Error {
|
||||
case noManagers
|
||||
|
@ -23,7 +23,6 @@ extension AppMessageError: LocalizedError {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME: marked as unchecked Sendable to allow sending `self.pathUpdate`, but we should refactor and re-enable linting.
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
||||
private var networkMonitor: NWPathMonitor?
|
||||
|
@ -56,7 +55,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
do {
|
||||
// Cannot use NETunnelProviderManager.loadAllFromPreferences() in earlier versions of iOS
|
||||
// TODO: Remove else once we drop support for iOS 16
|
||||
if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 17, minorVersion: 0, patchVersion: 0)) {
|
||||
if ProcessInfo().isOperatingSystemAtLeast(
|
||||
OperatingSystemVersion(majorVersion: 17, minorVersion: 0, patchVersion: 0))
|
||||
{
|
||||
manager = try await self.findManager()
|
||||
guard let foundManager = manager else {
|
||||
throw VPNStartError.couldNotFindManager
|
||||
|
@ -88,7 +89,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
// Make sure our ip is routed to the tun device
|
||||
var err: NSError?
|
||||
let ipNet = MobileNebulaParseCIDR(_site.cert!.cert.details.ips[0], &err)
|
||||
if (err != nil) {
|
||||
if err != nil {
|
||||
throw err!
|
||||
}
|
||||
tunnelNetworkSettings.ipv4Settings = NEIPv4Settings(addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR])
|
||||
|
@ -97,7 +98,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
// Add our unsafe routes
|
||||
try _site.unsafeRoutes.forEach { unsafeRoute in
|
||||
let ipNet = MobileNebulaParseCIDR(unsafeRoute.route, &err)
|
||||
if (err != nil) {
|
||||
if err != nil {
|
||||
throw err!
|
||||
}
|
||||
routes.append(NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR))
|
||||
|
@ -108,7 +109,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
|
||||
try await self.setTunnelNetworkSettings(tunnelNetworkSettings)
|
||||
var nebulaErr: NSError?
|
||||
self.nebula = MobileNebulaNewNebula(String(data: config, encoding: .utf8), key, self.site!.logFile, tunFD, &nebulaErr)
|
||||
self.nebula = MobileNebulaNewNebula(
|
||||
String(data: config, encoding: .utf8), key, self.site!.logFile, tunFD, &nebulaErr)
|
||||
self.startNetworkMonitor()
|
||||
|
||||
if nebulaErr != nil {
|
||||
|
@ -151,7 +153,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
throw VPNStartError.noProviderConfig
|
||||
}
|
||||
let id = mgrProviderConfig["id"] as? String
|
||||
if (id == targetID) {
|
||||
if id == targetID {
|
||||
return manager
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +225,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
defer {
|
||||
self.cancelTunnelWithError(error)
|
||||
}
|
||||
return try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error.localizedDescription)))
|
||||
return try? JSONEncoder().encode(
|
||||
IPCResponse.init(type: .error, message: JSON(error.localizedDescription)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,8 +248,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
error = AppMessageError.unknownIPCType(command: call.command)
|
||||
}
|
||||
|
||||
if (error != nil) {
|
||||
return try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error?.localizedDescription ?? "Unknown error")))
|
||||
if error != nil {
|
||||
return try? JSONEncoder().encode(
|
||||
IPCResponse.init(type: .error, message: JSON(error?.localizedDescription ?? "Unknown error")))
|
||||
} else {
|
||||
return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: data))
|
||||
}
|
||||
|
@ -307,4 +311,3 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,26 @@ class SiteList {
|
|||
#endif
|
||||
}
|
||||
|
||||
static func loadAllAsync() async -> Result<[String: Site], any Error> {
|
||||
await withCheckedContinuation { continuation in
|
||||
#if targetEnvironment(simulator)
|
||||
SiteList.loadAllFromFS { sites, err in
|
||||
if err != nil {
|
||||
continuation.resume(returning: Result.failure(err!))
|
||||
}
|
||||
continuation.resume(returning: Result.success(sites!))
|
||||
}
|
||||
#else
|
||||
SiteList.loadAllFromNETPM { sites, err in
|
||||
if err != nil {
|
||||
continuation.resume(returning: Result.failure(err!))
|
||||
}
|
||||
continuation.resume(returning: Result.success(sites!))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private static func loadAllFromFS(completion: @escaping ([String: Site]?, (any Error)?) -> Void) {
|
||||
let fileManager = FileManager.default
|
||||
var siteDirs: [URL]
|
||||
|
|
|
@ -26,15 +26,13 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
|||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
Task {
|
||||
for await site in dnUpdater.siteUpdates {
|
||||
for await site in await dnUpdater.siteUpdates {
|
||||
self.sites?.updateSite(site: site)
|
||||
// Send the refresh sites command on the main thread
|
||||
DispatchQueue.main.async {
|
||||
|
||||
// Signal to the main screen to reload
|
||||
self.ui?.invokeMethod("refreshSites", arguments: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let controller = window?.rootViewController as? FlutterViewController else {
|
||||
fatalError("rootViewController is not type FlutterViewController")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import os.log
|
||||
|
||||
class DNUpdater {
|
||||
actor DNUpdater {
|
||||
private let apiClient = APIClient()
|
||||
private let timer = RepeatingTimer(timeInterval: 15 * 60) // 15 * 60 is 15 minutes
|
||||
private let log = Logger(subsystem: "net.defined.mobileNebula", category: "DNUpdater")
|
||||
|
@ -18,7 +18,7 @@ class DNUpdater {
|
|||
return
|
||||
}
|
||||
|
||||
self.updateSite(site: site, onUpdate: onUpdate)
|
||||
await self.updateSite(site: site, onUpdate: onUpdate)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,20 @@ class DNUpdater {
|
|||
})
|
||||
}
|
||||
|
||||
// Site updates provides an async/await alternative to `.updateAllLoop` that doesn't require a sendable closure.
|
||||
// https://developer.apple.com/documentation/swift/asyncstream
|
||||
var siteUpdates: AsyncStream<Site> {
|
||||
AsyncStream { continuation in
|
||||
timer.eventHandler = {
|
||||
self.updateAll(onUpdate: { site in
|
||||
continuation.yield(site)
|
||||
})
|
||||
}
|
||||
timer.resume()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func updateAllLoop(onUpdate: @Sendable @escaping (Site) -> Void) {
|
||||
timer.eventHandler = {
|
||||
self.updateAll(onUpdate: onUpdate)
|
||||
|
@ -95,20 +109,6 @@ class DNUpdater {
|
|||
}
|
||||
}
|
||||
|
||||
extension DNUpdater {
|
||||
// Site updates provides an async/await alternative to `.updateAllLoop` that doesn't require a sendable closure.
|
||||
// https://developer.apple.com/documentation/swift/asyncstream
|
||||
var siteUpdates: AsyncStream<Site> {
|
||||
AsyncStream { continuation in
|
||||
self.updateAllLoop(onUpdate: { site in
|
||||
continuation.yield(site)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From https://medium.com/over-engineering/a-background-repeating-timer-in-swift-412cecfd2ef9
|
||||
class RepeatingTimer {
|
||||
|
||||
|
|
Loading…
Reference in a new issue