Files
pangolin/client/macos/Runner/StatsClient.swift
T

84 lines
3.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// StatsClient.swift app macOS
//
// PacketTunnel System Extension root app App Group
// root /var/root/... vs /Users/<user>/... app
// libbox command.sock NetworkExtension
// app NETunnelProviderSession.sendProviderMessage("stats")
// handleAppMessage JSON StatsCollector
//
// root/ socket pangolin/vpn/stats
// lib/bridge/vpn_bridge.dart onStats VpnChannel Dart
import Foundation
import NetworkExtension
final class StatsClient: NSObject {
/// VpnChannel statsSink
var onStats: (([String: Any]) -> Void)?
private let queue = DispatchQueue(label: "pangolin.stats.client")
private weak var session: NETunnelProviderSession?
private var timer: DispatchSourceTimer?
private var diagTick = 0 // : 5 urltest,
//
/// connected
func start(session: NETunnelProviderSession) {
queue.async { [weak self] in
guard let self else { return }
self.session = session
self.timer?.cancel()
let t = DispatchSource.makeTimerSource(queue: self.queue)
t.schedule(deadline: .now() + 0.2, repeating: 1.0)
t.setEventHandler { [weak self] in self?.poll() }
t.resume()
self.timer = t
NSLog("[pangolin/stats] polling via sendProviderMessage started")
}
}
func stop() {
queue.async { [weak self] in
guard let self else { return }
self.timer?.cancel()
self.timer = nil
self.session = nil
self.emit([
"uploadBytes": 0, "downloadBytes": 0,
"uploadSpeed": 0.0, "downloadSpeed": 0.0,
"urltestResults": [[String: Any]](),
])
}
}
private func poll() {
guard let session else { return }
do {
try session.sendProviderMessage(Data("stats".utf8)) { [weak self] resp in
guard let self, let resp, !resp.isEmpty else { return }
guard let obj = (try? JSONSerialization.jsonObject(with: resp)) as? [String: Any] else {
NSLog("[pangolin/stats] bad stats payload (%d bytes)", resp.count)
return
}
// : urltest ()
self.diagTick += 1
if self.diagTick % 5 == 0 {
let urls = (obj["urltestResults"] as? [[String: Any]]) ?? []
let pairs = urls.map { "\($0["tag"] ?? "?")=\($0["delayMs"] ?? -1)" }.joined(separator: ",")
NSLog("[pangolin/stats] dl=%@ ul=%@ urltest[%d]=%@",
"\(obj["downloadSpeed"] ?? 0)", "\(obj["uploadSpeed"] ?? 0)", urls.count, pairs)
}
self.emit(obj)
}
} catch {
NSLog("[pangolin/stats] sendProviderMessage failed: %@", error.localizedDescription)
}
}
private func emit(_ stats: [String: Any]) {
DispatchQueue.main.async { [weak self] in self?.onStats?(stats) }
}
}