Skip to content

Commit

Permalink
optimize stream api retry logic && format code
Browse files Browse the repository at this point in the history
# Conflicts:
#	ClashX/AppDelegate.swift
  • Loading branch information
yichengchen committed Jul 10, 2020
1 parent 13f6da1 commit c8f594a
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 77 deletions.
4 changes: 4 additions & 0 deletions ClashX/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
}

if RemoteControlManager.selectConfig != nil {
resetStreamApi()
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion ClashX/Basic/String+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
63 changes: 39 additions & 24 deletions ClashX/General/ApiRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"))!)

Expand All @@ -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))!)
Expand All @@ -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)
}
}
Expand All @@ -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
}
}

Expand Down
4 changes: 2 additions & 2 deletions ClashX/General/Managers/ConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
49 changes: 23 additions & 26 deletions ClashX/General/Managers/RemoteControlManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand All @@ -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 {
Expand All @@ -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..<idx {
menu.removeItem(at: 0)
Expand All @@ -69,23 +68,22 @@ class RemoteControlManager {
item.action = #selector(didSelectMenuItem(sender:))
menu.insertItem(item, at: 0)
}

let item = ExternalControlMenuItem.createNoneItem()
item.target = self
item.action = #selector(didSelectMenuItem(sender:))
item.state = selectConfig == nil ? .on : .off
menu.insertItem(item, at: 0)

}

@objc static func didSelectMenuItem(sender: ExternalControlMenuItem) {
selectConfig = sender.model
updateRemoteControl()
updateMenuItems()
}

static func updateRemoteControl() {
if let config = selectConfig,let url = URL(string:config.url) {
if let config = selectConfig, let url = URL(string: config.url) {
ConfigManager.shared.overrideApiURL = url
ConfigManager.shared.overrideSecret = config.secret
} else {
Expand All @@ -99,34 +97,33 @@ class RemoteControlManager {
MenuItemFactory.recreateProxyMenuItems()
updateDropDownMenuItems()
}

static func updateDropDownMenuItems() {
let d = AppDelegate.shared
let enable = selectConfig == nil
d.statusMenu.autoenablesItems = enable
[d.copyExportCommandMenuItem,d.copyExportCommandExternalMenuItem,d.proxySettingMenuItem].forEach {
[d.copyExportCommandMenuItem, d.copyExportCommandExternalMenuItem, d.proxySettingMenuItem].forEach {
$0?.isEnabled = enable
}
TunManager.shared.refreshMenuItemStatus()
}

}

class ExternalControlMenuItem:NSMenuItem {
class ExternalControlMenuItem: NSMenuItem {
var model: RemoteControl?
init(model:RemoteControl) {
init(model: RemoteControl) {
super.init(title: model.name, action: nil, keyEquivalent: "")
self.model = model
}
private init(title:String) {

private init(title: String) {
super.init(title: title, action: nil, keyEquivalent: "")
}

required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

static func createNoneItem() -> ExternalControlMenuItem {
return ExternalControlMenuItem(title: "None")
}
Expand Down
36 changes: 15 additions & 21 deletions ClashX/ViewControllers/ExternalControlViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ 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 {
deleteButton.isEnabled = false
return
}
}

@IBAction func actionAdd(_ sender: Any) {
let alertView = NSAlert()
alertView.addButton(withTitle: NSLocalizedString("OK", comment: ""))
Expand All @@ -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 }

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}

}
6 changes: 3 additions & 3 deletions ClashX/Views/ProxyGroupSpeedTestMenuItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down

0 comments on commit c8f594a

Please sign in to comment.