5.7 KiB
SmartProxy Byte Counting Audit Report
Executive Summary
After a comprehensive audit of the SmartProxy codebase, I can confirm that byte counting is implemented correctly with no instances of double counting. Each byte transferred through the proxy is counted exactly once in each direction.
Byte Counting Implementation
1. Core Tracking Mechanisms
SmartProxy uses two complementary tracking systems:
-
Connection Records (
IConnectionRecord
):bytesReceived
: Total bytes received from clientbytesSent
: Total bytes sent to client
-
MetricsCollector:
- Global throughput tracking via
ThroughputTracker
- Per-connection byte tracking for route/IP metrics
- Called via
recordBytes(connectionId, bytesIn, bytesOut)
- Global throughput tracking via
2. Where Bytes Are Counted
Bytes are counted in only two files:
a) route-connection-handler.ts
- Line 351: TLS alert bytes when no SNI is provided
- Lines 1286-1301: Data forwarding callbacks in
setupBidirectionalForwarding()
b) http-proxy-bridge.ts
- Line 127: Initial TLS chunk for HttpProxy connections
- Lines 142-154: Data forwarding callbacks in
setupBidirectionalForwarding()
Connection Flow Analysis
1. Direct TCP Connection (No TLS)
Client → SmartProxy → Target Server
- Connection arrives at
RouteConnectionHandler.handleConnection()
- For non-TLS ports, immediately routes via
routeConnection()
setupDirectConnection()
creates target connectionsetupBidirectionalForwarding()
handles all data transfer:onClientData
:bytesReceived += chunk.length
+recordBytes(chunk.length, 0)
onServerData
:bytesSent += chunk.length
+recordBytes(0, chunk.length)
Result: ✅ Each byte counted exactly once
2. TLS Passthrough Connection
Client (TLS) → SmartProxy → Target Server (TLS)
- Connection waits for initial data to detect TLS
- TLS handshake detected, SNI extracted
- Route matched,
setupDirectConnection()
called - Initial chunk stored in
pendingData
(NOT counted yet) - On target connect,
pendingData
written to target (still not counted) setupBidirectionalForwarding()
counts ALL bytes including initial chunk
Result: ✅ Each byte counted exactly once
3. TLS Termination via HttpProxy
Client (TLS) → SmartProxy → HttpProxy (localhost) → Target Server
- TLS connection detected with
tls.mode = "terminate"
forwardToHttpProxy()
called:- Initial chunk:
bytesReceived += chunk.length
+recordBytes(chunk.length, 0)
- Initial chunk:
- Proxy connection created to HttpProxy on localhost
setupBidirectionalForwarding()
handles subsequent data
Result: ✅ Each byte counted exactly once
4. HTTP Connection via HttpProxy
Client (HTTP) → SmartProxy → HttpProxy (localhost) → Target Server
- Connection on configured HTTP port (
useHttpProxy
ports) - Same flow as TLS termination
- All byte counting identical to TLS termination
Result: ✅ Each byte counted exactly once
5. NFTables Forwarding
Client → [Kernel NFTables] → Target Server
- Connection detected, route matched with
forwardingEngine: 'nftables'
- Connection marked as
usingNetworkProxy = true
- NO application-level forwarding (kernel handles packet routing)
- NO byte counting in application layer
Result: ✅ No counting (correct - kernel handles everything)
Special Cases
PROXY Protocol
- PROXY protocol headers sent to backend servers are NOT counted in client metrics
- Only actual client data is counted
- Correct behavior: Protocol overhead is not client data
TLS Alerts
- TLS alerts (e.g., for missing SNI) are counted as sent bytes
- Correct behavior: Alerts are actual data sent to the client
Initial Chunks
- Direct connections: Stored in
pendingData
, counted when forwarded - HttpProxy connections: Counted immediately upon receipt
- Both approaches: Count each byte exactly once
Verification Methodology
-
Code Analysis: Searched for all instances of:
bytesReceived +=
andbytesSent +=
recordBytes()
calls- Data forwarding implementations
-
Flow Tracing: Followed data path for each connection type from entry to exit
-
Handler Review: Examined all forwarding handlers to ensure no additional counting
Findings
✅ No Double Counting Detected
- Each byte is counted exactly once in the direction it flows
- Connection records and metrics are updated consistently
- No overlapping or duplicate counting logic found
Areas of Excellence
- Centralized Counting: All byte counting happens in just two files
- Consistent Pattern: Uses
setupBidirectionalForwarding()
with callbacks - Clear Separation: Forwarding handlers don't interfere with proxy metrics
Recommendations
-
Debug Logging: Add optional debug logging to verify byte counts in production:
if (settings.debugByteCount) { logger.log('debug', `Bytes counted: ${connectionId} +${bytes} (total: ${record.bytesReceived})`); }
-
Unit Tests: Create specific tests to ensure byte counting accuracy:
- Test initial chunk handling
- Test PROXY protocol overhead exclusion
- Test HttpProxy forwarding accuracy
-
Protocol Overhead Tracking: Consider separately tracking:
- PROXY protocol headers
- TLS handshake bytes
- HTTP headers vs body
-
NFTables Documentation: Clearly document that NFTables-forwarded connections are not included in application metrics
Conclusion
SmartProxy's byte counting implementation is robust and accurate. The design ensures that each byte is counted exactly once, with clear separation between connection tracking and metrics collection. No remediation is required.