mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-27 23:00:55 +00:00
228 lines
5.0 KiB
Go
228 lines
5.0 KiB
Go
package tcp
|
|
|
|
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
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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()
|
|
if elapsed < 0.1 {
|
|
return // Avoid division by zero or too frequent updates
|
|
}
|
|
|
|
currentIn := atomic.LoadInt64(&s.totalBytesIn)
|
|
currentOut := atomic.LoadInt64(&s.totalBytesOut)
|
|
|
|
deltaIn := currentIn - s.lastBytesIn
|
|
deltaOut := currentOut - s.lastBytesOut
|
|
|
|
s.speedIn = int64(float64(deltaIn) / elapsed)
|
|
s.speedOut = int64(float64(deltaOut) / elapsed)
|
|
|
|
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 StatsSnapshot struct {
|
|
TotalBytesIn int64
|
|
TotalBytesOut int64
|
|
TotalBytes int64
|
|
TotalRequests 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() StatsSnapshot {
|
|
s.speedMu.Lock()
|
|
speedIn := s.speedIn
|
|
speedOut := s.speedOut
|
|
s.speedMu.Unlock()
|
|
|
|
totalIn := atomic.LoadInt64(&s.totalBytesIn)
|
|
totalOut := atomic.LoadInt64(&s.totalBytesOut)
|
|
|
|
return StatsSnapshot{
|
|
TotalBytesIn: totalIn,
|
|
TotalBytesOut: totalOut,
|
|
TotalBytes: totalIn + totalOut,
|
|
TotalRequests: atomic.LoadInt64(&s.totalRequests),
|
|
SpeedIn: speedIn,
|
|
SpeedOut: speedOut,
|
|
Uptime: time.Since(s.startTime),
|
|
}
|
|
}
|
|
|
|
// 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:])
|
|
}
|