mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-26 14:21:17 +00:00
feat(tunnel): switch to yamux stream proxying and connection pooling
- Introduce pooled tunnel sessions (TunnelID/DataConnect) on client/server - Proxy HTTP/HTTPS via raw HTTP over yamux streams; pipe TCP streams directly - Move UI/stats into internal/shared; refactor CLI tunnel helpers; drop msgpack/hpack legacy
This commit is contained in:
77
internal/shared/stats/format.go
Normal file
77
internal/shared/stats/format.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package stats
|
||||
|
||||
// FormatBytes formats bytes to human readable string
|
||||
func FormatBytes(bytes int64) string {
|
||||
const (
|
||||
KB = 1024
|
||||
MB = KB * 1024
|
||||
GB = MB * 1024
|
||||
)
|
||||
|
||||
switch {
|
||||
case bytes >= GB:
|
||||
return formatFloat(float64(bytes)/float64(GB)) + " GB"
|
||||
case bytes >= MB:
|
||||
return formatFloat(float64(bytes)/float64(MB)) + " MB"
|
||||
case bytes >= KB:
|
||||
return formatFloat(float64(bytes)/float64(KB)) + " KB"
|
||||
default:
|
||||
return formatInt(bytes) + " B"
|
||||
}
|
||||
}
|
||||
|
||||
// FormatSpeed formats speed (bytes per second) to human readable string
|
||||
func FormatSpeed(bytesPerSec int64) string {
|
||||
if bytesPerSec == 0 {
|
||||
return "0 B/s"
|
||||
}
|
||||
return FormatBytes(bytesPerSec) + "/s"
|
||||
}
|
||||
|
||||
func formatFloat(f float64) string {
|
||||
if f >= 100 {
|
||||
return formatInt(int64(f))
|
||||
} else if f >= 10 {
|
||||
return formatOneDecimal(f)
|
||||
}
|
||||
return formatTwoDecimal(f)
|
||||
}
|
||||
|
||||
func formatInt(i int64) string {
|
||||
return intToStr(i)
|
||||
}
|
||||
|
||||
func formatOneDecimal(f float64) string {
|
||||
i := int64(f * 10)
|
||||
whole := i / 10
|
||||
frac := i % 10
|
||||
return intToStr(whole) + "." + intToStr(frac)
|
||||
}
|
||||
|
||||
func formatTwoDecimal(f float64) string {
|
||||
i := int64(f * 100)
|
||||
whole := i / 100
|
||||
frac := i % 100
|
||||
if frac < 10 {
|
||||
return intToStr(whole) + ".0" + intToStr(frac)
|
||||
}
|
||||
return intToStr(whole) + "." + intToStr(frac)
|
||||
}
|
||||
|
||||
func intToStr(i int64) string {
|
||||
if i == 0 {
|
||||
return "0"
|
||||
}
|
||||
if i < 0 {
|
||||
return "-" + intToStr(-i)
|
||||
}
|
||||
|
||||
var buf [20]byte
|
||||
pos := len(buf)
|
||||
for i > 0 {
|
||||
pos--
|
||||
buf[pos] = byte('0' + i%10)
|
||||
i /= 10
|
||||
}
|
||||
return string(buf[pos:])
|
||||
}
|
||||
184
internal/shared/stats/stats.go
Normal file
184
internal/shared/stats/stats.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TrafficStats tracks traffic statistics for a tunnel connection
|
||||
type TrafficStats struct {
|
||||
// Total bytes
|
||||
totalBytesIn int64
|
||||
totalBytesOut int64
|
||||
|
||||
// Request counts
|
||||
totalRequests int64
|
||||
activeConnections int64
|
||||
|
||||
// For speed calculation
|
||||
lastBytesIn int64
|
||||
lastBytesOut int64
|
||||
lastTime time.Time
|
||||
speedMu sync.Mutex
|
||||
|
||||
// Current speed (bytes per second)
|
||||
speedIn int64
|
||||
speedOut int64
|
||||
|
||||
// Start time
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// NewTrafficStats creates a new traffic stats tracker
|
||||
func NewTrafficStats() *TrafficStats {
|
||||
now := time.Now()
|
||||
return &TrafficStats{
|
||||
startTime: now,
|
||||
lastTime: now,
|
||||
}
|
||||
}
|
||||
|
||||
// AddBytesIn adds incoming bytes to the counter
|
||||
func (s *TrafficStats) AddBytesIn(n int64) {
|
||||
atomic.AddInt64(&s.totalBytesIn, n)
|
||||
}
|
||||
|
||||
// AddBytesOut adds outgoing bytes to the counter
|
||||
func (s *TrafficStats) AddBytesOut(n int64) {
|
||||
atomic.AddInt64(&s.totalBytesOut, n)
|
||||
}
|
||||
|
||||
// AddRequest increments the request counter
|
||||
func (s *TrafficStats) AddRequest() {
|
||||
atomic.AddInt64(&s.totalRequests, 1)
|
||||
}
|
||||
|
||||
func (s *TrafficStats) IncActiveConnections() {
|
||||
atomic.AddInt64(&s.activeConnections, 1)
|
||||
}
|
||||
|
||||
func (s *TrafficStats) DecActiveConnections() {
|
||||
v := atomic.AddInt64(&s.activeConnections, -1)
|
||||
if v < 0 {
|
||||
atomic.StoreInt64(&s.activeConnections, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTotalBytesIn returns total incoming bytes
|
||||
func (s *TrafficStats) GetTotalBytesIn() int64 {
|
||||
return atomic.LoadInt64(&s.totalBytesIn)
|
||||
}
|
||||
|
||||
// GetTotalBytesOut returns total outgoing bytes
|
||||
func (s *TrafficStats) GetTotalBytesOut() int64 {
|
||||
return atomic.LoadInt64(&s.totalBytesOut)
|
||||
}
|
||||
|
||||
// GetTotalRequests returns total request count
|
||||
func (s *TrafficStats) GetTotalRequests() int64 {
|
||||
return atomic.LoadInt64(&s.totalRequests)
|
||||
}
|
||||
|
||||
func (s *TrafficStats) GetActiveConnections() int64 {
|
||||
return atomic.LoadInt64(&s.activeConnections)
|
||||
}
|
||||
|
||||
// GetTotalBytes returns total bytes (in + out)
|
||||
func (s *TrafficStats) GetTotalBytes() int64 {
|
||||
return s.GetTotalBytesIn() + s.GetTotalBytesOut()
|
||||
}
|
||||
|
||||
// UpdateSpeed calculates current transfer speed
|
||||
// Should be called periodically (e.g., every second)
|
||||
func (s *TrafficStats) UpdateSpeed() {
|
||||
s.speedMu.Lock()
|
||||
defer s.speedMu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
elapsed := now.Sub(s.lastTime).Seconds()
|
||||
|
||||
// Require minimum interval of 100ms to avoid division issues
|
||||
if elapsed < 0.1 {
|
||||
return
|
||||
}
|
||||
|
||||
currentIn := atomic.LoadInt64(&s.totalBytesIn)
|
||||
currentOut := atomic.LoadInt64(&s.totalBytesOut)
|
||||
|
||||
deltaIn := currentIn - s.lastBytesIn
|
||||
deltaOut := currentOut - s.lastBytesOut
|
||||
|
||||
// Calculate instantaneous speed
|
||||
if deltaIn > 0 {
|
||||
s.speedIn = int64(float64(deltaIn) / elapsed)
|
||||
} else {
|
||||
// No new bytes - set speed to 0 immediately
|
||||
s.speedIn = 0
|
||||
}
|
||||
|
||||
if deltaOut > 0 {
|
||||
s.speedOut = int64(float64(deltaOut) / elapsed)
|
||||
} else {
|
||||
// No new bytes - set speed to 0 immediately
|
||||
s.speedOut = 0
|
||||
}
|
||||
|
||||
s.lastBytesIn = currentIn
|
||||
s.lastBytesOut = currentOut
|
||||
s.lastTime = now
|
||||
}
|
||||
|
||||
// GetSpeedIn returns current incoming speed in bytes per second
|
||||
func (s *TrafficStats) GetSpeedIn() int64 {
|
||||
s.speedMu.Lock()
|
||||
defer s.speedMu.Unlock()
|
||||
return s.speedIn
|
||||
}
|
||||
|
||||
// GetSpeedOut returns current outgoing speed in bytes per second
|
||||
func (s *TrafficStats) GetSpeedOut() int64 {
|
||||
s.speedMu.Lock()
|
||||
defer s.speedMu.Unlock()
|
||||
return s.speedOut
|
||||
}
|
||||
|
||||
// GetUptime returns how long the connection has been active
|
||||
func (s *TrafficStats) GetUptime() time.Duration {
|
||||
return time.Since(s.startTime)
|
||||
}
|
||||
|
||||
// Snapshot returns a snapshot of all stats
|
||||
type Snapshot struct {
|
||||
TotalBytesIn int64
|
||||
TotalBytesOut int64
|
||||
TotalBytes int64
|
||||
TotalRequests int64
|
||||
ActiveConnections int64
|
||||
SpeedIn int64 // bytes per second
|
||||
SpeedOut int64 // bytes per second
|
||||
Uptime time.Duration
|
||||
}
|
||||
|
||||
// GetSnapshot returns a snapshot of current stats
|
||||
func (s *TrafficStats) GetSnapshot() Snapshot {
|
||||
s.speedMu.Lock()
|
||||
speedIn := s.speedIn
|
||||
speedOut := s.speedOut
|
||||
s.speedMu.Unlock()
|
||||
|
||||
totalIn := atomic.LoadInt64(&s.totalBytesIn)
|
||||
totalOut := atomic.LoadInt64(&s.totalBytesOut)
|
||||
active := atomic.LoadInt64(&s.activeConnections)
|
||||
|
||||
return Snapshot{
|
||||
TotalBytesIn: totalIn,
|
||||
TotalBytesOut: totalOut,
|
||||
TotalBytes: totalIn + totalOut,
|
||||
TotalRequests: atomic.LoadInt64(&s.totalRequests),
|
||||
ActiveConnections: active,
|
||||
SpeedIn: speedIn,
|
||||
SpeedOut: speedOut,
|
||||
Uptime: time.Since(s.startTime),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user