Skip to content

Add traffic stats callback to FFI (iOS + Android)#134

Open
AYastrebov wants to merge 1 commit into
cfal:masterfrom
AYastrebov:feat/traffic-stats-pr
Open

Add traffic stats callback to FFI (iOS + Android)#134
AYastrebov wants to merge 1 commit into
cfal:masterfrom
AYastrebov:feat/traffic-stats-pr

Conversation

@AYastrebov
Copy link
Copy Markdown
Contributor

@AYastrebov AYastrebov commented May 5, 2026

Summary

  • Wire cumulative upload/download byte counters through the TUN server via a new src/tun/traffic.rs module
  • TCP bytes counted after copy_bidirectional completes (both directions)
  • UDP bytes counted per-message in destination tasks (download on read, upload on write)
  • 1-second tokio::time::interval timer in run_tun_server invokes the platform callback with current totals
  • iOS: shoes_start now takes a ShoesTrafficCallback C function pointer parameter
  • Android: ShoesNative.start now takes a TrafficListener interface parameter

Architecture

src/tun/traffic.rs provides:

  • Two AtomicU64 globals (UPLOAD_BYTES / DOWNLOAD_BYTES)
  • RwLock<Option<TrafficCallback>> for the platform callback
  • Helpers: add_upload_bytes, add_download_bytes, reset_traffic_counters, set/clear_traffic_callback, report_traffic

Platform notes

  • iOS: C function pointers can't capture context — the Swift side uses a private weak var activeProvider global as an indirect capture; the closure reads it at call time
  • Android: TrafficListener stored as GlobalRef<JObject>, JVM attached per call via attach_current_thread

Files changed

File Change
src/tun/traffic.rs New module — counters, callback, tests
src/tun/mod.rs 1s reporting timer in TUN event loop
src/tun/udp_manager.rs Per-message byte counting
src/ffi/ios.rs ShoesTrafficCallback parameter
src/ffi/android.rs TrafficListener JNI integration
include/shoes.h C header updated
ShoesNative.kt Kotlin interface + JNI bridge

Test plan

  • cargo test --lib tun::traffic — 4 unit tests pass (counters, callback, clear, no-panic)
  • iOS: verify ShoesTrafficCallback is invoked every ~1s with increasing byte counts
  • Android: verify TrafficListener.onTrafficUpdate fires with correct totals

🤖 Generated with Claude Code

@AYastrebov AYastrebov force-pushed the feat/traffic-stats-pr branch 2 times, most recently from 358795b to d9bffc6 Compare May 5, 2026 11:10
@AYastrebov AYastrebov marked this pull request as ready for review May 5, 2026 12:22
Comment thread src/tun/mod.rs Outdated

match result {
Ok((client_to_remote, remote_to_client)) => {
traffic::add_upload_bytes(client_to_remote);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one downside to this is that it waits for copy_bidirectional to finish - which means if it's a large download, you won't see it immediately and it won't be useful if we ever want to display speeds. we probably need some wiring inside of copy_bidirectional to do better.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Fixed — replaced the post-hoc counting with a TrafficCountingStream wrapper that intercepts poll_read/poll_write on the local TUN connection. Bytes are now reported to the atomic counters in real time as they flow through copy_bidirectional, so long-lived connections show traffic immediately and this would support speed display in the future.

The TCP counting lines in mod.rs are replaced with:

let mut counting = traffic::TrafficCountingStream::new(connection);
let result = tokio::io::copy_bidirectional(&mut counting, &mut remote).await;

UDP counting remains per-message (already incremental).

@AYastrebov AYastrebov force-pushed the feat/traffic-stats-pr branch 2 times, most recently from 26f5a78 to e289cfc Compare May 18, 2026 09:11
Wire cumulative upload/download byte counters through the TUN server.
TCP bytes are counted after copy_bidirectional completes; UDP bytes are
counted per-message in destination tasks. A 1-second timer in the TUN
event loop invokes the platform callback with current totals.

iOS: shoes_start now takes a ShoesTrafficCallback parameter.
Android: ShoesNative.start now takes a TrafficListener parameter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@AYastrebov AYastrebov force-pushed the feat/traffic-stats-pr branch from e289cfc to 8096e26 Compare May 18, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants