fix command
This commit is contained in:
parent
5813107425
commit
49750099ea
@ -149,7 +149,7 @@ actor SDLContextActor {
|
||||
do {
|
||||
try await self.runRootBody()
|
||||
} catch is CancellationError {
|
||||
if let terminalError = await self.consumeTerminalError() {
|
||||
if let terminalError = self.consumeTerminalError() {
|
||||
SDLLogger.fatal("[SDLContext] root task stopped by terminal error: \(terminalError)", category: .context)
|
||||
result = .failure(terminalError)
|
||||
} else {
|
||||
@ -163,7 +163,7 @@ actor SDLContextActor {
|
||||
}
|
||||
|
||||
await self.cleanupRoot()
|
||||
await self.finishRootTask(id: rootTaskID)
|
||||
self.finishRootTask(id: rootTaskID)
|
||||
try result.get()
|
||||
}
|
||||
self.rootTaskID = rootTaskID
|
||||
@ -196,6 +196,27 @@ actor SDLContextActor {
|
||||
self.terminalError = nil
|
||||
}
|
||||
|
||||
public func recoverAfterWake() async throws {
|
||||
SDLLogger.log("[SDLContext] recoverAfterWake requested", category: .context)
|
||||
|
||||
guard self.rootTask != nil else {
|
||||
throw TunnelError.invalidContext
|
||||
}
|
||||
|
||||
try await self.readySignal.wait(timeout: .seconds(30))
|
||||
|
||||
guard let dataCipher = self.dataCipher else {
|
||||
throw TunnelError.invalidContext
|
||||
}
|
||||
|
||||
let prunedSessions = await self.sessionManager.pruneExpiredSessions()
|
||||
await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: dataCipher)
|
||||
await self.packetInboundActor.updateRuntime(config: self.config, dataCipher: dataCipher)
|
||||
try await self.tunNetworkManager.apply(settings: .init(config: self.config), dnsServer: DNSHelper.dnsServer)
|
||||
|
||||
SDLLogger.log("[SDLContext] recoverAfterWake completed, prunedSessions: \(prunedSessions)", category: .context)
|
||||
}
|
||||
|
||||
private func runRootBody() async throws {
|
||||
self.prepareTunnelNotifier()
|
||||
|
||||
|
||||
@ -8,6 +8,16 @@
|
||||
import Foundation
|
||||
|
||||
final class SDLContextBootstrap: @unchecked Sendable {
|
||||
private typealias StartCompletion = (Error?) -> Void
|
||||
private typealias StopCompletion = () -> Void
|
||||
private typealias WakeCompletion = (Error?) -> Void
|
||||
|
||||
private enum BootstrapCommand {
|
||||
case start(config: SDLConfiguration, rsaCipher: CCRSACipher, completion: StartCompletion)
|
||||
case stop(clearRuntimeConfiguration: Bool, completion: StopCompletion)
|
||||
case recoverAfterWake(completion: WakeCompletion)
|
||||
}
|
||||
|
||||
private enum RuntimeState {
|
||||
case idle
|
||||
case starting
|
||||
@ -22,9 +32,22 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
||||
private var rsaCipher: CCRSACipher?
|
||||
private var contextActor: SDLContextActor?
|
||||
private var startCompletionHandler: ((Error?) -> Void)?
|
||||
private let commandContinuation: AsyncStream<BootstrapCommand>.Continuation
|
||||
private var commandWorker: Task<Void, Never>?
|
||||
|
||||
init(provider: PacketTunnelProvider) {
|
||||
let commandPair = AsyncStream.makeStream(of: BootstrapCommand.self, bufferingPolicy: .unbounded)
|
||||
|
||||
self.provider = provider
|
||||
self.commandContinuation = commandPair.continuation
|
||||
self.commandWorker = Task { [weak self, stream = commandPair.stream] in
|
||||
await self?.runCommandLoop(stream)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.commandContinuation.finish()
|
||||
self.commandWorker?.cancel()
|
||||
}
|
||||
|
||||
func startCached(completionHandler: @escaping (Error?) -> Void) {
|
||||
@ -43,6 +66,46 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
||||
}
|
||||
|
||||
func start(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
|
||||
self.submit(.start(config: config, rsaCipher: rsaCipher, completion: completionHandler))
|
||||
}
|
||||
|
||||
func stop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
|
||||
self.submit(.stop(clearRuntimeConfiguration: clearRuntimeConfiguration, completion: completionHandler))
|
||||
}
|
||||
|
||||
func recoverAfterWake(completionHandler: @escaping (Error?) -> Void) {
|
||||
self.submit(.recoverAfterWake(completion: completionHandler))
|
||||
}
|
||||
|
||||
func currentContextActor() -> SDLContextActor? {
|
||||
self.runtimeLock.lock()
|
||||
let contextActor = self.contextActor
|
||||
self.runtimeLock.unlock()
|
||||
return contextActor
|
||||
}
|
||||
|
||||
private func submit(_ command: BootstrapCommand) {
|
||||
self.commandContinuation.yield(command)
|
||||
}
|
||||
|
||||
private func runCommandLoop(_ stream: AsyncStream<BootstrapCommand>) async {
|
||||
for await command in stream {
|
||||
self.handle(command)
|
||||
}
|
||||
}
|
||||
|
||||
private func handle(_ command: BootstrapCommand) {
|
||||
switch command {
|
||||
case .start(let config, let rsaCipher, let completion):
|
||||
self.handleStart(config: config, rsaCipher: rsaCipher, completionHandler: completion)
|
||||
case .stop(let clearRuntimeConfiguration, let completion):
|
||||
self.handleStop(clearRuntimeConfiguration: clearRuntimeConfiguration, completionHandler: completion)
|
||||
case .recoverAfterWake(let completion):
|
||||
self.handleRecoverAfterWake(completionHandler: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleStart(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
|
||||
guard let provider = self.provider else {
|
||||
SDLLogger.fatal("[SDLContextBootstrap] start rejected: provider released", category: .app)
|
||||
completionHandler(TunnelError.invalidContext)
|
||||
@ -84,7 +147,7 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
func stop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
|
||||
private func handleStop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
|
||||
self.runtimeLock.lock()
|
||||
let contextActor = self.contextActor
|
||||
let startCompletionHandler = self.startCompletionHandler
|
||||
@ -122,11 +185,50 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
func currentContextActor() -> SDLContextActor? {
|
||||
private func handleRecoverAfterWake(completionHandler: @escaping (Error?) -> Void) {
|
||||
self.runtimeLock.lock()
|
||||
let runtimeState = self.runtimeState
|
||||
let contextActor = self.contextActor
|
||||
let config = self.config
|
||||
let rsaCipher = self.rsaCipher
|
||||
self.runtimeLock.unlock()
|
||||
return contextActor
|
||||
|
||||
switch runtimeState {
|
||||
case .running:
|
||||
guard let contextActor else {
|
||||
SDLLogger.fatal("[SDLContextBootstrap] recoverAfterWake failed: missing running context", category: .app)
|
||||
completionHandler(TunnelError.invalidContext)
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
do {
|
||||
try await contextActor.recoverAfterWake()
|
||||
completionHandler(nil)
|
||||
} catch {
|
||||
SDLLogger.fatal("[SDLContextBootstrap] recoverAfterWake failed: \(error)", category: .app)
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
||||
case .idle:
|
||||
guard let config, let rsaCipher else {
|
||||
SDLLogger.fatal("[SDLContextBootstrap] recoverAfterWake failed: missing cached runtime configuration", category: .app)
|
||||
completionHandler(TunnelError.invalidConfiguration)
|
||||
return
|
||||
}
|
||||
|
||||
SDLLogger.log("[SDLContextBootstrap] recoverAfterWake will start cached context", category: .app)
|
||||
self.handleStart(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
|
||||
|
||||
case .starting:
|
||||
SDLLogger.log("[SDLContextBootstrap] recoverAfterWake ignored while context is starting", category: .app)
|
||||
completionHandler(nil)
|
||||
|
||||
case .stopping:
|
||||
SDLLogger.log("[SDLContextBootstrap] recoverAfterWake ignored while context is stopping", category: .app)
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func finishContextStart(_ contextActor: SDLContextActor, error: Error?) {
|
||||
|
||||
@ -56,21 +56,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
}
|
||||
|
||||
override func sleep(completionHandler: @escaping () -> Void) {
|
||||
SDLLogger.fatal("[PacketTunnelProvider] sleep requested, will stop current context", category: .app)
|
||||
self.contextBootstrap.stop(clearRuntimeConfiguration: false) {
|
||||
SDLLogger.log("[PacketTunnelProvider] sleep", category: .app)
|
||||
SDLLogger.log("[PacketTunnelProvider] sleep requested", category: .app)
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
|
||||
override func wake() {
|
||||
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
|
||||
self.contextBootstrap.startCached { err in
|
||||
self.contextBootstrap.recoverAfterWake { err in
|
||||
if let err {
|
||||
SDLLogger.fatal("[PacketTunnelProvider] wakeup start failed: \(err)", category: .app)
|
||||
SDLLogger.log("[PacketTunnelProvider] wakeup start failed: \(err.localizedDescription)", category: .app)
|
||||
SDLLogger.fatal("[PacketTunnelProvider] wakeup recovery failed: \(err)", category: .app)
|
||||
SDLLogger.log("[PacketTunnelProvider] wakeup recovery failed: \(err.localizedDescription)", category: .app)
|
||||
} else {
|
||||
SDLLogger.log("[PacketTunnelProvider] wakeup and try start", category: .app)
|
||||
SDLLogger.log("[PacketTunnelProvider] wakeup recovery completed", category: .app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,15 @@ actor SessionManager {
|
||||
self.publishSnapshot()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func pruneExpiredSessions() -> Int {
|
||||
let oldCount = self.sessionCount()
|
||||
self.sessions = self.validSessions()
|
||||
let newCount = self.sessionCount()
|
||||
self.publishSnapshot()
|
||||
return oldCount - newCount
|
||||
}
|
||||
|
||||
nonisolated func snapshot() -> SessionSnapshot {
|
||||
return self.snapshotPublisher.current()
|
||||
}
|
||||
@ -83,12 +92,21 @@ actor SessionManager {
|
||||
self.snapshotPublisher.publish(self.compileSnapshot())
|
||||
}
|
||||
|
||||
private func compileSnapshot() -> SessionSnapshot {
|
||||
private func validSessions() -> [Data: [Session.AddressType: Session]] {
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
let sessions = self.sessions.compactMapValues { peerSessions in
|
||||
return self.sessions.compactMapValues { peerSessions in
|
||||
let validSessions = peerSessions.filter { $0.value.lastTimestamp + self.ttl >= timestamp }
|
||||
return validSessions.isEmpty ? nil : validSessions
|
||||
}
|
||||
return SessionSnapshot(sessions: sessions)
|
||||
}
|
||||
|
||||
private func sessionCount() -> Int {
|
||||
return self.sessions.values.reduce(0) { count, peerSessions in
|
||||
count + peerSessions.count
|
||||
}
|
||||
}
|
||||
|
||||
private func compileSnapshot() -> SessionSnapshot {
|
||||
return SessionSnapshot(sessions: self.validSessions())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#! /bin/sh
|
||||
#!/bin/sh
|
||||
|
||||
log stream --predicate 'subsystem == "com.jihe.punchnet.debug"' --info --style compact
|
||||
log stream --style compact --predicate 'subsystem == "com.jihe.punchnet.tun"'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user