Files
rose_cat707 0d84b07f68
CI / docker (push) Successful in 1m58s
feat: Wormhole 内网穿透与反向代理网关
Go + Vue 管理台:Agent 隧道、域名反代、IP 安全、访客验证与监控大盘。
Gitea Actions CI 多架构镜像;纯 Go SQLite、无 CGO Docker 构建。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 17:52:43 +08:00

75 lines
1.6 KiB
Go

package bridge
import (
"net"
"sync"
"time"
)
// rateLimitedConn limits read/write throughput to bytesPerSec (simple token bucket).
type rateLimitedConn struct {
net.Conn
bytesPerSec int64
mu sync.Mutex
allowance float64
lastCheck time.Time
}
func newRateLimitedConn(conn net.Conn, bytesPerSec int64) net.Conn {
if bytesPerSec <= 0 {
return conn
}
return &rateLimitedConn{
Conn: conn,
bytesPerSec: bytesPerSec,
lastCheck: time.Now(),
allowance: float64(bytesPerSec),
}
}
func (c *rateLimitedConn) wait(n int) {
if c.bytesPerSec <= 0 || n <= 0 {
return
}
c.mu.Lock()
defer c.mu.Unlock()
now := time.Now()
elapsed := now.Sub(c.lastCheck).Seconds()
c.lastCheck = now
c.allowance += elapsed * float64(c.bytesPerSec)
if c.allowance > float64(c.bytesPerSec)*2 {
c.allowance = float64(c.bytesPerSec) * 2
}
for float64(n) > c.allowance {
deficit := float64(n) - c.allowance
sleep := time.Duration(deficit/float64(c.bytesPerSec)*1e9) * time.Nanosecond
if sleep < time.Millisecond {
sleep = time.Millisecond
}
c.mu.Unlock()
time.Sleep(sleep)
c.mu.Lock()
now = time.Now()
elapsed = now.Sub(c.lastCheck).Seconds()
c.lastCheck = now
c.allowance += elapsed * float64(c.bytesPerSec)
if c.allowance > float64(c.bytesPerSec)*2 {
c.allowance = float64(c.bytesPerSec) * 2
}
}
c.allowance -= float64(n)
}
func (c *rateLimitedConn) Read(b []byte) (int, error) {
n, err := c.Conn.Read(b)
if n > 0 {
c.wait(n)
}
return n, err
}
func (c *rateLimitedConn) Write(b []byte) (int, error) {
c.wait(len(b))
return c.Conn.Write(b)
}