Skip to content

Commit

Permalink
Ability to read stats from tunnel. (#1085)
Browse files Browse the repository at this point in the history
* Ability to read stats from tunnel.

* added back stats.

* Added comments.

* ran swift-format.

* use userDefault observer for communication.

* Simplify stats methods.

* added logs.

* Use DispatchSource instead of timer.

* Use userid and token to user default.

* added more logs.

* fixed localisation issue on ios.

---------

Co-authored-by: atavism <[email protected]>
  • Loading branch information
jigar-f and atavism authored May 27, 2024
1 parent 8e111e9 commit 9f8bee2
Show file tree
Hide file tree
Showing 17 changed files with 245 additions and 101 deletions.
15 changes: 13 additions & 2 deletions internalsdk/ios/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type ConfigResult struct {
// uniquely identifies the current device. hardcodedProxies allows manually specifying
// a proxies.yaml configuration that overrides whatever we fetch from the cloud.
func Configure(configFolderPath string, userID int, proToken, deviceID string, refreshProxies bool, hardcodedProxies string) (*ConfigResult, error) {
log.Debugf("Configuring client for device '%v' at config path '%v'", deviceID, configFolderPath)
log.Debugf("Configuring client for device '%v' at config path '%v' userid '%v' token '%v'", deviceID, configFolderPath, userID, proToken)
defer log.Debug("Finished configuring client")
uc := userConfigFor(userID, proToken, deviceID)
cf := &configurer{
Expand All @@ -75,9 +75,18 @@ type configurer struct {
rt http.RoundTripper
}

// Important:
// This method is responsible for potentially delaying the UI startup process.
// Occasionally, the execution time of this method varies significantly, sometimes completing within 5 seconds, while other times taking more than 30 seconds.
// For instance, examples from my running
// config.go:167 Configured completed in 35.8970435s
// config.go:167 Configured completed in 4.0234035s
// config.go:176 Configured completed in 3.700574125s

// TODO: Implement a timeout mechanism to handle prolonged execution times and potentially execute this method in the background to maintain smooth UI startup performance.
func (cf *configurer) configure(userID int, proToken string, refreshProxies bool) (*ConfigResult, error) {
result := &ConfigResult{}

start := time.Now()
if err := cf.writeUserConfig(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -164,6 +173,8 @@ func (cf *configurer) configure(userID int, proToken string, refreshProxies bool
log.Debugf("Added %v", host)
}
}
seconds := time.Since(start).Seconds()
log.Debugf("Configured completed in %v seconds", seconds)

email.SetDefaultRecipient(global.ReportIssueEmail)

Expand Down
28 changes: 15 additions & 13 deletions internalsdk/ios/ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/getlantern/flashlight/v7/chained"
"github.com/getlantern/flashlight/v7/stats"
"github.com/getlantern/ipproxy"
"github.com/getlantern/lantern-client/internalsdk"
"github.com/getlantern/lantern-client/internalsdk/common"
)

Expand Down Expand Up @@ -104,6 +103,10 @@ type ClientWriter interface {
Close() error
}

type StatsTracker interface {
UpdateStats(string, string, string, int, int, bool)
}

type cw struct {
ipStack io.WriteCloser
client *iosClient
Expand Down Expand Up @@ -158,9 +161,10 @@ type iosClient struct {
clientWriter *cw
memoryAvailable int64
started time.Time
statsTracker StatsTracker
}

func Client(packetsOut Writer, udpDialer UDPDialer, memChecker MemChecker, configDir string, mtu int, capturedDNSHost, realDNSHost string) (ClientWriter, error) {
func Client(packetsOut Writer, udpDialer UDPDialer, memChecker MemChecker, configDir string, mtu int, capturedDNSHost, realDNSHost string, statsTracker StatsTracker) (ClientWriter, error) {
log.Debug("Creating new iOS client")
if mtu <= 0 {
log.Debug("Defaulting MTU to 1500")
Expand All @@ -177,6 +181,7 @@ func Client(packetsOut Writer, udpDialer UDPDialer, memChecker MemChecker, confi
capturedDNSHost: capturedDNSHost,
realDNSHost: realDNSHost,
started: time.Now(),
statsTracker: statsTracker,
}
optimizeMemoryUsage(&c.memoryAvailable)
go c.gcPeriodically()
Expand All @@ -190,28 +195,25 @@ func (c *iosClient) start() (ClientWriter, error) {
return nil, log.Errorf("error loading user config: %v", err)
}
log.Debugf("Running client at config path '%v'", c.configDir)
start := time.Now()
log.Debugf("User config process start at %v", start)
dialers, err := c.loadDialers()
if err != nil {
return nil, err
}
if len(dialers) == 0 {
return nil, errors.New("No dialers found")
}
tracker := stats.NewTracker()
dialer, err := bandit.NewWithStats(dialers, tracker)
if err != nil {
return nil, err
}
go func() {
tracker.AddListener(func(st stats.Stats) {
log.Debugf("Received stats: %v", st)
sessionModel, err := internalsdk.GetSessionModel()
if err == nil {
sessionModel.UpdateStats(
st.City,
st.Country,
st.CountryCode,
st.HTTPSUpgrades,
st.AdsBlocked,
st.HasSucceedingProxy)
}
start := time.Now()
log.Debugf("Stats update at %v", start)
c.statsTracker.UpdateStats(st.City, st.Country, st.CountryCode, st.HTTPSUpgrades, st.AdsBlocked, st.HasSucceedingProxy)
})
}()

Expand Down
24 changes: 12 additions & 12 deletions internalsdk/session_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@ const (
currentTermsVersion = 1
)

var (
instance *SessionModel
)

type SessionModelOpts struct {
DevelopmentMode bool
ProUser bool
Expand Down Expand Up @@ -96,7 +92,6 @@ func NewSessionModel(mdb minisql.DB, opts *SessionModelOpts) (*SessionModel, err
base.db.RegisterType(2000, &protos.Devices{})
}
m := &SessionModel{baseModel: base}
instance = m
m.proClient = pro.NewClient(fmt.Sprintf("https://%s", common.ProAPIHost), &pro.Opts{
HttpClient: proxied.DirectThenFrontedClient(dialTimeout),
UserConfig: func() common.UserConfig {
Expand All @@ -120,13 +115,6 @@ func NewSessionModel(mdb minisql.DB, opts *SessionModelOpts) (*SessionModel, err
return m, nil
}

func GetSessionModel() (*SessionModel, error) {
if instance == nil {
return nil, errors.New("SessionModel not initialized")
}
return instance, nil
}

func (m *SessionModel) doInvokeMethod(method string, arguments Arguments) (interface{}, error) {
switch method {
case "getBandwidth":
Expand Down Expand Up @@ -206,6 +194,18 @@ func (m *SessionModel) doInvokeMethod(method string, arguments Arguments) (inter
}
checkAdsEnabled(m)
return true, nil
case "updateStats":
city := arguments.Get("city").String()
country := arguments.Get("country").String()
serverCountryCode := arguments.Get("serverCountryCode").String()
httpsUpgrades := arguments.Get("httpsUpgrades").Int()
adsBlocked := arguments.Get("adsBlocked").Int()
hasSucceedingProxy := arguments.Get("hasSucceedingProxy").Bool()
err := m.UpdateStats(city, country, serverCountryCode, httpsUpgrades, adsBlocked, hasSucceedingProxy)
if err != nil {
return nil, err
}
return true, nil
default:
return m.methodNotImplemented(method)
}
Expand Down
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ SPEC CHECKSUMS:
Google-Mobile-Ads-SDK: 5a6d005a6cb5b5e8f4c7b69ca05cdea79c181139
google_mobile_ads: 5698dd1cd5a3cfc6688abec7db5428f237d6b1ac
GoogleUserMessagingPlatform: f131fa7978d2ba88d7426702b057c2cc318e6595
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
integration_test: 13825b8a9334a850581300559b8839134b124670
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
Expand All @@ -288,4 +288,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: f41df053e13795ca3bec060038c249ef06910792

COCOAPODS: 1.15.2
COCOAPODS: 1.14.3
4 changes: 4 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
0321C3F92AA9D8DF00D462C1 /* FlashlightManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0321C3F82AA9D8DF00D462C1 /* FlashlightManager.swift */; };
0324CC302AE279CF00490B46 /* SentryUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0324CC2F2AE279CF00490B46 /* SentryUtils.swift */; };
032A43582AA6F8AA00EF442B /* DnsDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032A43572AA6F8AA00EF442B /* DnsDetector.swift */; };
0332DD612BFF809C0007240D /* StatsTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0332DD602BFF809C0007240D /* StatsTracker.swift */; };
0333203B2AC698FA00333DA9 /* MockVPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0333203A2AC698FA00333DA9 /* MockVPNManager.swift */; };
03567DBB2AA9F15100A233EA /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB432AA8946400FB41B2 /* Constants.swift */; };
03567DBC2AA9F18D00A233EA /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0321C3F62AA9D84700D462C1 /* Process.swift */; };
Expand Down Expand Up @@ -99,6 +100,7 @@
0321C3F82AA9D8DF00D462C1 /* FlashlightManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlashlightManager.swift; sourceTree = "<group>"; };
0324CC2F2AE279CF00490B46 /* SentryUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUtils.swift; sourceTree = "<group>"; };
032A43572AA6F8AA00EF442B /* DnsDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DnsDetector.swift; sourceTree = "<group>"; };
0332DD602BFF809C0007240D /* StatsTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsTracker.swift; sourceTree = "<group>"; };
0333203A2AC698FA00333DA9 /* MockVPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVPNManager.swift; sourceTree = "<group>"; };
03567DBF2AA9F47200A233EA /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
03567DC22AA9F77000A233EA /* NEProviderStopReason.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NEProviderStopReason.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -249,6 +251,7 @@
03A1AB342AA890AB00FB41B2 /* PacketTunnelProvider.swift */,
03A1AB362AA890AB00FB41B2 /* Info.plist */,
03A1AB372AA890AB00FB41B2 /* Tunnel.entitlements */,
0332DD602BFF809C0007240D /* StatsTracker.swift */,
);
path = Tunnel;
sourceTree = "<group>";
Expand Down Expand Up @@ -667,6 +670,7 @@
03567DBE2AA9F31200A233EA /* Logger.swift in Sources */,
0308A13E2AAF43A10086157A /* UserNotificationsManager.swift in Sources */,
0308A1312AAEF3B00086157A /* Event.swift in Sources */,
0332DD612BFF809C0007240D /* StatsTracker.swift in Sources */,
03A1AB352AA890AB00FB41B2 /* PacketTunnelProvider.swift in Sources */,
03567DBB2AA9F15100A233EA /* Constants.swift in Sources */,
03567DC12AA9F47200A233EA /* FileUtils.swift in Sources */,
Expand Down
8 changes: 8 additions & 0 deletions ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
14 changes: 14 additions & 0 deletions ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"pins" : [
{
"identity" : "sqlite.swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/stephencelis/SQLite.swift.git",
"state" : {
"revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb",
"version" : "0.14.1"
}
}
],
"version" : 2
}
107 changes: 60 additions & 47 deletions ios/Runner/Lantern/Core/Vpn/VpnHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,13 @@ class VpnHelper: NSObject {
let constants: Constants
let flashlightManager: FlashlightManager
let vpnManager: VPNBase
var configFetchTimer: Timer!
// var configFetchTimer: Timer!
var hasConfiguredThisSession = false
var configFetchInProcess: Bool = false

private var configFetchTimer: DispatchSourceTimer?
private let fetchQueue = DispatchQueue(label: "org.getlantern.configfetch", qos: .background)

init(
constants: Constants,
fileManager: FileManager,
Expand Down Expand Up @@ -275,38 +278,71 @@ class VpnHelper: NSObject {
return
}
}
// Start the timer to fetch config periodically if connected
strongSelf.startConfigFetchTimer()
// Start the timer to fetch config periodically if connected
strongSelf.setUpConfigFetchTimer()
}
}
}

private func startConfigFetchTimer() {
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { return }
let time: TimeInterval = 60
strongSelf.configFetchTimer?.invalidate() // Invalidate any existing timer
logger.debug("Setting up config fetch timer with interval: \(time) seconds")
strongSelf.configFetchTimer = Timer.scheduledTimer(withTimeInterval: time, repeats: true) {
[weak self] _ in
guard let strongSelf = self else { return }
guard strongSelf.state == .connected else {
logger.debug("Skipping config fetch because state is not connected.")
return
}
logger.debug("Config fetch timer fired, fetching config...")
strongSelf.fetchConfig { result in
switch result {
case .success:
logger.debug("Auto-config fetch succeeded.")
case .failure(let error):
logger.error("Auto-config fetch failed: \(error.localizedDescription)")
}
// this use DispatchSource more efficient method
func setUpConfigFetchTimer() {
// Cancel any existing timer
configFetchTimer?.cancel()
configFetchTimer = nil

// Create a new DispatchSourceTimer
configFetchTimer = DispatchSource.makeTimerSource(queue: fetchQueue)

let time: Double = 60
configFetchTimer?.schedule(deadline: .now() + time, repeating: time)

configFetchTimer?.setEventHandler { [weak self] in
// Only auto-fetch new config when VPN is on
guard self?.state == .connected else {
logger.debug("Skipping config fetch because state is not connected.")
return
}
logger.debug("Config Fetch timer fired after \(time) seconds, fetching...")
self?.fetchConfig { result in
switch result {
case .success:
logger.debug("Auto-config fetch success")
case .failure(let error):
logger.error("Auto-config fetch failed: \(error.localizedDescription)")
}
}
}

// Start the timer
configFetchTimer?.resume()
}

// private func startConfigFetchTimer() {
// DispatchQueue.main.async { [weak self] in
// guard let strongSelf = self else { return }
// let time: TimeInterval = 60
// strongSelf.configFetchTimer?.invalidate() // Invalidate any existing timer
// logger.debug("Setting up config fetch timer with interval: \(time) seconds")
// strongSelf.configFetchTimer = Timer.scheduledTimer(withTimeInterval: time, repeats: true) {
// [weak self] _ in
// guard let strongSelf = self else { return }
// guard strongSelf.state == .connected else {
// logger.debug("Skipping config fetch because state is not connected.")
// return
// }
// logger.debug("Config fetch timer fired, fetching config...")
// strongSelf.fetchConfig { result in
// switch result {
// case .success:
// logger.debug("Auto-config fetch succeeded.")
// case .failure(let error):
// logger.error("Auto-config fetch failed: \(error.localizedDescription)")
// }
// }
// }
// }
// }

func fetchConfig(
refreshProxies: Bool = true, _ completion: @escaping (Result<Void, Swift.Error>) -> Void
) {
Expand All @@ -329,29 +365,6 @@ class VpnHelper: NSObject {
}
}

func setUpConfigFetchTimer() {
// set up timer on Main queue's runloop
// FlashlightManager will automatically use its designated goQueue when fetching
DispatchQueue.main.async { [weak self] in
let time: Double = 60
self?.configFetchTimer = Timer.scheduledTimer(
withTimeInterval: time, repeats: true,
block: { [weak self] _ in
// Only auto-fetch new config when VPN is on
guard self?.state == .connected else { return }
logger.debug("Config Fetch timer fired after \(time), fetching...")
self?.fetchConfig { result in
switch result {
case .success:
logger.debug("Auto-config fetch success")
case .failure(let error):
logger.error("Auto-config fetch failed: \(error.localizedDescription)")
}
}
})
}
}

private func messageNetExToUpdateExcludedIPs() {
logger.debug("Notifying network extension of updated config")
do {
Expand Down
Loading

0 comments on commit 9f8bee2

Please sign in to comment.