🚀 Improve Tunnel Performance by Separating Control Plane and Data Plane
📌 Problem
The current tunnel implementation processes requests and responses using a JSON-based message approach, where the entire HTTP request/response body is read into memory before being forwarded.
Example:
body, err := io.ReadAll(resp.Body)
This leads to:
- ❌ High memory usage for large responses
- ❌ No streaming support (responses are buffered fully before sending)
- ❌ Increased latency
- ❌ Inefficient handling of large or continuous data (e.g., file downloads, SSE, etc.)
🧠 Root Cause
The current design mixes:
- Control Plane (metadata) → JSON (method, headers, path)
- Data Plane (actual payload) → also sent via JSON
This prevents leveraging Go's streaming capabilities.
💡 Proposed Solution
Separate responsibilities into:
1. Control Plane (JSON)
Used for:
- Handshake (
clientHello)
- Request metadata (method, path, headers)
- Logging / inspector events
2. Data Plane (Streaming via io.Copy)
Used for:
- Request body
- Response body
Example:
go io.Copy(localConn, stream) // request body
go io.Copy(stream, localConn) // response body
🎯 Benefits
- ✅ True streaming (no buffering entire payloads)
- ✅ Lower memory footprint
- ✅ Automatic backpressure handling (TCP-level)
- ✅ Better performance for large/continuous responses
- ✅ Aligns with real-world tunnel implementations (e.g., ngrok-like systems)
🔧 Suggested Changes
- Avoid using
io.ReadAll for HTTP bodies
- Introduce streaming between
yamux stream and local connection
- Keep JSON strictly for control messages only
- Optionally introduce a simple protocol to signal start/end of streams
⚠️ Considerations
- Need a way to distinguish control messages vs raw stream data
- Ensure proper connection lifecycle handling (close both sides on error)
- Maintain compatibility with existing inspector/logging system
📎 References
Current implementation: handleStream uses full buffering via io.ReadAll
Suggested approach: use io.Copy for streaming data paths
🧩 Summary
Move from a buffered JSON-based tunnel → to a streaming tunnel architecture by separating control and data planes.
This will significantly improve performance, scalability, and reliability.
🚀 Improve Tunnel Performance by Separating Control Plane and Data Plane
📌 Problem
The current tunnel implementation processes requests and responses using a JSON-based message approach, where the entire HTTP request/response body is read into memory before being forwarded.
Example:
This leads to:
🧠 Root Cause
The current design mixes:
This prevents leveraging Go's streaming capabilities.
💡 Proposed Solution
Separate responsibilities into:
1. Control Plane (JSON)
Used for:
clientHello)2. Data Plane (Streaming via
io.Copy)Used for:
Example:
🎯 Benefits
🔧 Suggested Changes
io.ReadAllfor HTTP bodiesyamuxstream and local connection📎 References
Current implementation:
handleStreamuses full buffering viaio.ReadAllSuggested approach: use
io.Copyfor streaming data paths🧩 Summary
Move from a buffered JSON-based tunnel → to a streaming tunnel architecture by separating control and data planes.
This will significantly improve performance, scalability, and reliability.