diff --git a/go.mod b/go.mod index 22f8f90..c906155 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.24.0 require ( github.com/cloudflare/circl v1.6.3 - github.com/juju/ratelimit v1.0.2 github.com/pires/go-proxyproto v0.11.0 github.com/refraction-networking/utls v1.8.2 golang.org/x/crypto v0.48.0 @@ -14,5 +13,4 @@ require ( require ( github.com/andybalholm/brotli v1.0.6 // indirect github.com/klauspost/compress v1.17.4 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index 72fddb4..d4f64e7 100644 --- a/go.sum +++ b/go.sum @@ -2,15 +2,8 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= -github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= -github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4= github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo= @@ -19,5 +12,3 @@ golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/tls.go b/tls.go index 4c8ef86..e85afd7 100644 --- a/tls.go +++ b/tls.go @@ -50,7 +50,6 @@ import ( "sync" "time" - "github.com/juju/ratelimit" "github.com/pires/go-proxyproto" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/hkdf" @@ -101,10 +100,52 @@ func (c *MirrorConn) SetWriteDeadline(t time.Time) error { return nil } +// tokenBucket is a license-clean replacement for the LGPL-3.0 +// github.com/juju/ratelimit dependency. It implements exactly the +// NewBucketWithRate/Wait(count) semantics the fallback shaper below uses: +// tokens refill continuously at rate per second up to capacity, Wait takes +// count tokens immediately (going into debt if needed) and sleeps until the +// debt would be repaid. +type tokenBucket struct { + mu sync.Mutex + rate float64 + capacity int64 + tokens float64 + last time.Time +} + +func newTokenBucketWithRate(rate float64, capacity int64) *tokenBucket { + return &tokenBucket{ + rate: rate, + capacity: capacity, + tokens: float64(capacity), + last: time.Now(), + } +} + +func (b *tokenBucket) Wait(count int64) { + b.mu.Lock() + now := time.Now() + b.tokens += now.Sub(b.last).Seconds() * b.rate + if b.tokens > float64(b.capacity) { + b.tokens = float64(b.capacity) + } + b.last = now + b.tokens -= float64(count) + var wait time.Duration + if b.tokens < 0 && b.rate > 0 { + wait = time.Duration(-b.tokens / b.rate * float64(time.Second)) + } + b.mu.Unlock() + if wait > 0 { + time.Sleep(wait) + } +} + type RatelimitedConn struct { net.Conn After int64 - Bucket *ratelimit.Bucket + Bucket *tokenBucket } func (c *RatelimitedConn) Read(b []byte) (int, error) { @@ -132,7 +173,7 @@ func NewRatelimitedConn(conn net.Conn, limit *LimitFallback) net.Conn { return &RatelimitedConn{ Conn: conn, After: int64(limit.AfterBytes), - Bucket: ratelimit.NewBucketWithRate(float64(limit.BytesPerSec), int64(burstBytesPerSec)), + Bucket: newTokenBucketWithRate(float64(limit.BytesPerSec), int64(burstBytesPerSec)), } }