From c8f594a44adb1b07f2bb1272bc509717b022d938 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 20 Jun 2020 22:05:27 +0800 Subject: [PATCH] optimize stream api retry logic && format code # Conflicts: # ClashX/AppDelegate.swift --- ClashX/AppDelegate.swift | 4 ++ ClashX/Basic/String+Extension.swift | 2 +- ClashX/General/ApiRequest.swift | 63 ++++++++++++------- ClashX/General/Managers/ConfigManager.swift | 4 +- .../Managers/RemoteControlManager.swift | 49 +++++++-------- .../ExternalControlViewController.swift | 36 +++++------ .../Views/ProxyGroupSpeedTestMenuItem.swift | 6 +- 7 files changed, 87 insertions(+), 77 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 88bd907bb..2e5c701a5 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -469,6 +469,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } } + + if RemoteControlManager.selectConfig != nil { + resetStreamApi() + } } } diff --git a/ClashX/Basic/String+Extension.swift b/ClashX/Basic/String+Extension.swift index 1642897ad..0d94f3416 100644 --- a/ClashX/Basic/String+Extension.swift +++ b/ClashX/Basic/String+Extension.swift @@ -9,7 +9,7 @@ import Foundation extension String { func isUrlVaild() -> Bool { - guard count > 0 else {return false} + guard count > 0 else { return false } guard let url = URL(string: self) else { return false } guard url.host != nil, diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index 75c09e497..d9170d374 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -66,11 +66,13 @@ class ApiRequest { private var trafficWebSocket: WebSocket? private var loggingWebSocket: WebSocket? - private var trafficWebSocketRetryCount = 0 - private var loggingWebSocketRetryCount = 0 + private var trafficWebSocketRetryDelay: TimeInterval = 1 + private var loggingWebSocketRetryDelay: TimeInterval = 1 + private var trafficWebSocketRetryTimer: Timer? + private var loggingWebSocketRetryTimer: Timer? private var alamoFireManager: Session - + static func useDirectApi() -> Bool { if ConfigManager.shared.overrideApiURL != nil { return true @@ -327,22 +329,23 @@ extension ApiRequest { } func resetLogStreamApi() { - loggingWebSocketRetryCount = 0 + loggingWebSocketRetryTimer?.invalidate() + loggingWebSocketRetryTimer = nil + loggingWebSocketRetryDelay = 1 requestLog() } func resetTrafficStreamApi() { - trafficWebSocketRetryCount = 0 + trafficWebSocketRetryTimer?.invalidate() + trafficWebSocketRetryTimer = nil + trafficWebSocketRetryDelay = 1 requestTrafficInfo() } private func requestTrafficInfo() { + trafficWebSocketRetryTimer?.invalidate() + trafficWebSocketRetryTimer = nil trafficWebSocket?.disconnect(forceTimeout: 0, closeCode: 0) - trafficWebSocketRetryCount += 1 - if trafficWebSocketRetryCount > 5 { - NSUserNotificationCenter.default.postStreamApiConnectFail(api: "Traffic") - return - } let socket = WebSocket(url: URL(string: ConfigManager.apiUrl.appending("/traffic"))!) @@ -355,12 +358,9 @@ extension ApiRequest { } private func requestLog() { + loggingWebSocketRetryTimer?.invalidate() + loggingWebSocketRetryTimer = nil loggingWebSocket?.disconnect() - loggingWebSocketRetryCount += 1 - if loggingWebSocketRetryCount > 5 { - NSUserNotificationCenter.default.postStreamApiConnectFail(api: "Log") - return - } let uriString = "/logs?level=".appending(ConfigManager.selectLoggingApiLevel.rawValue) let socket = WebSocket(url: URL(string: ConfigManager.apiUrl.appending(uriString))!) @@ -378,8 +378,10 @@ extension ApiRequest: WebSocketDelegate { func websocketDidConnect(socket: WebSocketClient) { guard let webSocket = socket as? WebSocket else { return } if webSocket == trafficWebSocket { + trafficWebSocketRetryDelay = 1 Logger.log("trafficWebSocket did Connect", level: .debug) } else { + loggingWebSocketRetryDelay = 1 Logger.log("loggingWebSocket did Connect", level: .debug) } } @@ -390,15 +392,28 @@ extension ApiRequest: WebSocketDelegate { } Logger.log(err.localizedDescription, level: .error) - DispatchQueue.global().asyncAfter(deadline: .now() + 1) { - guard let webSocket = socket as? WebSocket else { return } - if webSocket == self.trafficWebSocket { - Logger.log("trafficWebSocket did disconnect", level: .debug) - self.requestTrafficInfo() - } else { - Logger.log("loggingWebSocket did disconnect", level: .debug) - self.requestLog() - } + + guard let webSocket = socket as? WebSocket else { return } + + if webSocket == trafficWebSocket { + Logger.log("trafficWebSocket did disconnect", level: .debug) + trafficWebSocketRetryTimer?.invalidate() + trafficWebSocketRetryTimer = + Timer.scheduledTimer(withTimeInterval: trafficWebSocketRetryDelay, repeats: false, block: { + [weak self] _ in + if self?.trafficWebSocket?.isConnected == true { return } + self?.requestTrafficInfo() + }) + trafficWebSocketRetryDelay *= 2 + } else { + Logger.log("loggingWebSocket did disconnect", level: .debug) + loggingWebSocketRetryTimer = + Timer.scheduledTimer(withTimeInterval: loggingWebSocketRetryDelay, repeats: false, block: { + [weak self] _ in + if self?.loggingWebSocket?.isConnected == true { return } + self?.requestLog() + }) + loggingWebSocketRetryDelay *= 2 } } diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index 69b5d1c69..6006f766d 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -16,8 +16,8 @@ class ConfigManager { private let disposeBag = DisposeBag() var apiPort = "8080" var apiSecret: String = "" - var overrideApiURL:URL? - var overrideSecret:String? + var overrideApiURL: URL? + var overrideSecret: String? var currentConfig: ClashConfig? { get { diff --git a/ClashX/General/Managers/RemoteControlManager.swift b/ClashX/General/Managers/RemoteControlManager.swift index aeea2890e..ac0492317 100644 --- a/ClashX/General/Managers/RemoteControlManager.swift +++ b/ClashX/General/Managers/RemoteControlManager.swift @@ -8,17 +8,17 @@ import Cocoa -class RemoteControl:Codable { +class RemoteControl: Codable { let name: String let url: String let secret: String - let uuid:String - - init(name:String, url:String,secret:String) { + let uuid: String + + init(name: String, url: String, secret: String) { self.name = name self.url = url self.secret = secret - self.uuid = UUID().uuidString + uuid = UUID().uuidString } } @@ -32,9 +32,9 @@ class RemoteControlManager { updateMenuItems() } } - + static var selectConfig: RemoteControl? - private static var menuSeparator:NSMenuItem? + private static var menuSeparator: NSMenuItem? static func loadConfig() -> [RemoteControl] { if let savedConfigs = UserDefaults.standard.object(forKey: "kRemoteControls") as? Data { @@ -47,16 +47,15 @@ class RemoteControlManager { } return [] } - - - static func setupMenuItem(separator:NSMenuItem) { + + static func setupMenuItem(separator: NSMenuItem) { menuSeparator = separator updateMenuItems() updateDropDownMenuItems() } - + static func updateMenuItems() { - guard let separator = menuSeparator,let menu = separator.menu else {return} + guard let separator = menuSeparator, let menu = separator.menu else { return } let idx = menu.index(of: separator) for _ in 0.. ExternalControlMenuItem { return ExternalControlMenuItem(title: "None") } diff --git a/ClashX/ViewControllers/ExternalControlViewController.swift b/ClashX/ViewControllers/ExternalControlViewController.swift index 9a6e4c1d7..a6bae2170 100644 --- a/ClashX/ViewControllers/ExternalControlViewController.swift +++ b/ClashX/ViewControllers/ExternalControlViewController.swift @@ -12,12 +12,12 @@ class ExternalControlViewController: NSViewController { @IBOutlet var tableView: NSTableView! @IBOutlet var addButton: NSButton! @IBOutlet var deleteButton: NSButton! - + override func viewDidLoad() { super.viewDidLoad() updateButtonStatus() } - + func updateButtonStatus() { let selectIdx = tableView.selectedRow if selectIdx == -1 { @@ -25,7 +25,7 @@ class ExternalControlViewController: NSViewController { return } } - + @IBAction func actionAdd(_ sender: Any) { let alertView = NSAlert() alertView.addButton(withTitle: NSLocalizedString("OK", comment: "")) @@ -46,19 +46,18 @@ class ExternalControlViewController: NSViewController { RemoteControlManager.configs.append(model) tableView.reloadData() } - + @IBAction func actionDelete(_ sender: Any) { RemoteControlManager.configs.safeRemove(at: tableView.selectedRow) tableView.reloadData() } - } -extension ExternalControlViewController: NSTableViewDataSource,NSTableViewDelegate { +extension ExternalControlViewController: NSTableViewDataSource, NSTableViewDelegate { func numberOfRows(in tableView: NSTableView) -> Int { return RemoteControlManager.configs.count } - + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { guard let config = RemoteControlManager.configs[safe: row] else { return nil } @@ -86,27 +85,25 @@ extension ExternalControlViewController: NSTableViewDataSource,NSTableViewDelega } } - - class ExternalControlAddView: NSView { let urlTextField = NSTextField() let secretField = NSTextField() let nameField = NSTextField() - - let urlLabel = NSTextField(labelWithString:"Api URL:") - let nameLabel = NSTextField(labelWithString:"Name:") - let secretLabel = NSTextField(labelWithString:"Secret:") - + + let urlLabel = NSTextField(labelWithString: "Api URL:") + let nameLabel = NSTextField(labelWithString: "Name:") + let secretLabel = NSTextField(labelWithString: "Secret:") + override init(frame frameRect: NSRect) { super.init(frame: frameRect) frame = NSRect(x: 0, y: 0, width: 300, height: 85) setupView() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func setupView() { addSubview(urlTextField) addSubview(secretField) @@ -139,14 +136,11 @@ class ExternalControlAddView: NSView { nameField.leadingAnchor.constraint(equalTo: urlTextField.leadingAnchor), nameLabel.centerYAnchor.constraint(equalTo: nameField.centerYAnchor), nameLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), - nameField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor,constant: 5) + nameField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 5), ]) - } - - + func isVaild() -> Bool { return urlTextField.stringValue.isUrlVaild() && nameLabel.stringValue.count > 0 } - } diff --git a/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift b/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift index 01d1f6a34..928a8ad72 100644 --- a/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift +++ b/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift @@ -114,15 +114,15 @@ fileprivate class ProxyGroupSpeedTestMenuItemView: MenuItemBaseView { label.stringValue = NSLocalizedString("Testing", comment: "") enclosingMenuItem?.isEnabled = false setNeedsDisplay() - + for provider in providers { testGroup.enter() - + ApiRequest.healthCheck(proxy: provider) { testGroup.leave() } } - + testGroup.notify(queue: .main) { [weak self] in guard let self = self, let menu = self.enclosingMenuItem else { return }