Track data count in shared UserDefaults

Default disabled (dataCountInterval = 0).
This commit is contained in:
Davide De Rosa 2019-03-28 00:04:35 +01:00
parent d03f1bd9af
commit 44fb5a5b48
4 changed files with 71 additions and 4 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Added
- Optional data count report via `TunnelKitProvider.Configuration.dataCount(in:)`.
### Fixed ### Fixed
- `checksEKU` not propagated to TunnelKitProvider. - `checksEKU` not propagated to TunnelKitProvider.

View File

@ -305,7 +305,9 @@ extension TunnelKitProvider {
static let debugLogFilename = "debug.log" static let debugLogFilename = "debug.log"
static let lastErrorKey = "LastTunnelKitError" static let lastErrorKey = "TunnelKitLastError"
fileprivate static let dataCountKey = "TunnelKitDataCount"
/** /**
Returns the URL of the latest debug log. Returns the URL of the latest debug log.
@ -358,6 +360,22 @@ extension TunnelKitProvider {
UserDefaults(suiteName: appGroup)?.removeObject(forKey: Configuration.lastErrorKey) UserDefaults(suiteName: appGroup)?.removeObject(forKey: Configuration.lastErrorKey)
} }
/**
Returns the most recent (received, sent) count in bytes.
- Parameter in: The app group where to locate the count pair.
- Returns: The bytes count pair, if any.
*/
public func dataCount(in appGroup: String) -> (Int, Int)? {
guard let rawValue = UserDefaults(suiteName: appGroup)?.dataCountArray else {
return nil
}
guard rawValue.count == 2 else {
return nil
}
return (rawValue[0], rawValue[1])
}
// MARK: API // MARK: API
/** /**
@ -574,3 +592,19 @@ extension EndpointProtocol: Codable {
try container.encode(rawValue) try container.encode(rawValue)
} }
} }
/// :nodoc:
public extension UserDefaults {
@objc public var dataCountArray: [Int]? {
get {
return array(forKey: TunnelKitProvider.Configuration.dataCountKey) as? [Int]
}
set {
set(newValue, forKey: TunnelKitProvider.Configuration.dataCountKey)
}
}
public func removeDataCountArray() {
removeObject(forKey: TunnelKitProvider.Configuration.dataCountKey)
}
}

View File

@ -72,6 +72,9 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
/// The number of link failures after which the tunnel is expected to die. /// The number of link failures after which the tunnel is expected to die.
public var maxLinkFailures = 3 public var maxLinkFailures = 3
/// The number of milliseconds between data count updates. Set to 0 to disable updates (default).
public var dataCountInterval = 0
// MARK: Constants // MARK: Constants
private let memoryLog = MemoryDestination() private let memoryLog = MemoryDestination()
@ -112,6 +115,8 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
private var pendingStopHandler: (() -> Void)? private var pendingStopHandler: (() -> Void)?
private var isCountingData = false
// MARK: NEPacketTunnelProvider (XPC queue) // MARK: NEPacketTunnelProvider (XPC queue)
/// :nodoc: /// :nodoc:
@ -194,6 +199,7 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
let proxy: SessionProxy let proxy: SessionProxy
do { do {
proxy = try SessionProxy(queue: tunnelQueue, configuration: cfg.sessionConfiguration, cachesURL: cachesURL) proxy = try SessionProxy(queue: tunnelQueue, configuration: cfg.sessionConfiguration, cachesURL: cachesURL)
refreshDataCount()
} catch let e { } catch let e {
completionHandler(e) completionHandler(e)
return return
@ -244,8 +250,7 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
response = memoryLog.description.data(using: .utf8) response = memoryLog.description.data(using: .utf8)
case .dataCount: case .dataCount:
if let proxy = proxy { if let proxy = proxy, let dataCount = proxy.dataCount() {
let dataCount = proxy.dataCount()
response = Data() response = Data()
response?.append(UInt64(dataCount.0)) // inbound response?.append(UInt64(dataCount.0)) // inbound
response?.append(UInt64(dataCount.1)) // outbound response?.append(UInt64(dataCount.1)) // outbound
@ -340,6 +345,22 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
cancelTunnelWithError(error) cancelTunnelWithError(error)
} }
} }
// MARK: Data counter (tunnel queue)
private func refreshDataCount() {
guard dataCountInterval > 0 else {
return
}
tunnelQueue.schedule(after: .milliseconds(dataCountInterval)) { [weak self] in
self?.refreshDataCount()
}
guard isCountingData, let proxy = proxy, let dataCount = proxy.dataCount() else {
defaults?.removeDataCountArray()
return
}
defaults?.dataCountArray = [dataCount.0, dataCount.1]
}
} }
extension TunnelKitProvider: GenericSocketDelegate { extension TunnelKitProvider: GenericSocketDelegate {
@ -461,12 +482,17 @@ extension TunnelKitProvider: SessionProxyDelegate {
self.pendingStartHandler?(nil) self.pendingStartHandler?(nil)
self.pendingStartHandler = nil self.pendingStartHandler = nil
} }
isCountingData = true
} }
/// :nodoc: /// :nodoc:
public func sessionDidStop(_: SessionProxy, shouldReconnect: Bool) { public func sessionDidStop(_: SessionProxy, shouldReconnect: Bool) {
log.info("Session did stop") log.info("Session did stop")
isCountingData = false
refreshDataCount()
reasserting = shouldReconnect reasserting = shouldReconnect
socket?.shutdown() socket?.shutdown()
} }

View File

@ -309,7 +309,10 @@ public class SessionProxy {
- Returns: The current data bytes count as a pair, inbound first. - Returns: The current data bytes count as a pair, inbound first.
*/ */
public func dataCount() -> (Int, Int) { public func dataCount() -> (Int, Int)? {
guard let _ = link else {
return nil
}
return controlChannel.currentDataCount() return controlChannel.currentDataCount()
} }